diff options
author | Lars Knoll <lars.knoll@nokia.com> | 2009-03-23 09:34:13 (GMT) |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2009-03-23 09:34:13 (GMT) |
commit | 67ad0519fd165acee4a4d2a94fa502e9e4847bd0 (patch) | |
tree | 1dbf50b3dff8d5ca7e9344733968c72704eb15ff /src/3rdparty/webkit/WebCore/rendering | |
download | Qt-67ad0519fd165acee4a4d2a94fa502e9e4847bd0.zip Qt-67ad0519fd165acee4a4d2a94fa502e9e4847bd0.tar.gz Qt-67ad0519fd165acee4a4d2a94fa502e9e4847bd0.tar.bz2 |
Long live Qt!
Diffstat (limited to 'src/3rdparty/webkit/WebCore/rendering')
243 files changed, 66681 insertions, 0 deletions
diff --git a/src/3rdparty/webkit/WebCore/rendering/AutoTableLayout.cpp b/src/3rdparty/webkit/WebCore/rendering/AutoTableLayout.cpp new file mode 100644 index 0000000..8f9feec --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/AutoTableLayout.cpp @@ -0,0 +1,785 @@ +/* + * Copyright (C) 2002 Lars Knoll (knoll@kde.org) + * (C) 2002 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2006, 2008 Apple Inc. All rights reserved. + * + * 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. + * + * 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" +#include "AutoTableLayout.h" + +#include "RenderTable.h" +#include "RenderTableCell.h" +#include "RenderTableCol.h" +#include "RenderTableSection.h" + +using namespace std; + +namespace WebCore { + +AutoTableLayout::AutoTableLayout(RenderTable* table) + : TableLayout(table) + , m_hasPercent(false) + , m_percentagesDirty(true) + , m_effWidthDirty(true) + , m_totalPercent(0) +{ +} + +AutoTableLayout::~AutoTableLayout() +{ +} + +/* recalculates the full structure needed to do layouting and minmax calculations. + This is usually calculated on the fly, but needs to be done fully when table cells change + dynamically +*/ +void AutoTableLayout::recalcColumn(int effCol) +{ + Layout &l = m_layoutStruct[effCol]; + + RenderObject* child = m_table->firstChild(); + // first we iterate over all rows. + + RenderTableCell* fixedContributor = 0; + RenderTableCell* maxContributor = 0; + + while (child) { + if (child->isTableSection()) { + RenderTableSection* section = static_cast<RenderTableSection*>(child); + int numRows = section->numRows(); + RenderTableCell* last = 0; + for (int i = 0; i < numRows; i++) { + RenderTableSection::CellStruct current = section->cellAt(i, effCol); + RenderTableCell* cell = current.cell; + + bool cellHasContent = cell && (cell->firstChild() || cell->style()->hasBorder() || cell->style()->hasPadding()); + if (cellHasContent) + l.emptyCellsOnly = false; + + if (current.inColSpan) + continue; + if (cell && cell->colSpan() == 1) { + // A cell originates in this column. Ensure we have + // a min/max width of at least 1px for this column now. + l.minWidth = max(l.minWidth, cellHasContent ? 1 : 0); + l.maxWidth = max(l.maxWidth, 1); + if (cell->prefWidthsDirty()) + cell->calcPrefWidths(); + l.minWidth = max(cell->minPrefWidth(), l.minWidth); + if (cell->maxPrefWidth() > l.maxWidth) { + l.maxWidth = cell->maxPrefWidth(); + maxContributor = cell; + } + + Length w = cell->styleOrColWidth(); + // FIXME: What is this arbitrary value? + if (w.rawValue() > 32760) + w.setRawValue(32760); + if (w.isNegative()) + w.setValue(0); + switch(w.type()) { + case Fixed: + // ignore width=0 + if (w.value() > 0 && (int)l.width.type() != Percent) { + int wval = cell->calcBorderBoxWidth(w.value()); + if (l.width.isFixed()) { + // Nav/IE weirdness + if ((wval > l.width.value()) || + ((l.width.value() == wval) && (maxContributor == cell))) { + l.width.setValue(wval); + fixedContributor = cell; + } + } else { + l.width.setValue(Fixed, wval); + fixedContributor = cell; + } + } + break; + case Percent: + m_hasPercent = true; + if (w.isPositive() && (!l.width.isPercent() || w.rawValue() > l.width.rawValue())) + l.width = w; + break; + case Relative: + // FIXME: Need to understand this case and whether it makes sense to compare values + // which are not necessarily of the same type. + if (w.isAuto() || (w.isRelative() && w.value() > l.width.rawValue())) + l.width = w; + default: + break; + } + } else { + if (cell && (!effCol || section->cellAt(i, effCol-1).cell != cell)) { + // This spanning cell originates in this column. Ensure we have + // a min/max width of at least 1px for this column now. + l.minWidth = max(l.minWidth, cellHasContent ? 1 : 0); + l.maxWidth = max(l.maxWidth, 1); + insertSpanCell(cell); + } + last = cell; + } + } + } + child = child->nextSibling(); + } + + // Nav/IE weirdness + if (l.width.isFixed()) { + if (m_table->style()->htmlHacks() && l.maxWidth > l.width.value() && fixedContributor != maxContributor) { + l.width = Length(); + fixedContributor = 0; + } + } + + l.maxWidth = max(l.maxWidth, l.minWidth); + + // ### we need to add col elements as well +} + +void AutoTableLayout::fullRecalc() +{ + m_percentagesDirty = true; + m_hasPercent = false; + m_effWidthDirty = true; + + int nEffCols = m_table->numEffCols(); + m_layoutStruct.resize(nEffCols); + m_layoutStruct.fill(Layout()); + m_spanCells.fill(0); + + RenderObject *child = m_table->firstChild(); + Length grpWidth; + int cCol = 0; + while (child) { + if (child->isTableCol()) { + RenderTableCol *col = static_cast<RenderTableCol*>(child); + int span = col->span(); + if (col->firstChild()) { + grpWidth = col->style()->width(); + } else { + Length w = col->style()->width(); + if (w.isAuto()) + w = grpWidth; + if ((w.isFixed() || w.isPercent()) && w.isZero()) + w = Length(); + int cEffCol = m_table->colToEffCol(cCol); + if (!w.isAuto() && span == 1 && cEffCol < nEffCols) { + if (m_table->spanOfEffCol(cEffCol) == 1) { + m_layoutStruct[cEffCol].width = w; + if (w.isFixed() && m_layoutStruct[cEffCol].maxWidth < w.value()) + m_layoutStruct[cEffCol].maxWidth = w.value(); + } + } + cCol += span; + } + } else { + break; + } + + RenderObject *next = child->firstChild(); + if (!next) + next = child->nextSibling(); + if (!next && child->parent()->isTableCol()) { + next = child->parent()->nextSibling(); + grpWidth = Length(); + } + child = next; + } + + + for (int i = 0; i < nEffCols; i++) + recalcColumn(i); +} + +static bool shouldScaleColumns(RenderTable* table) +{ + // A special case. If this table is not fixed width and contained inside + // a cell, then don't bloat the maxwidth by examining percentage growth. + bool scale = true; + while (table) { + Length tw = table->style()->width(); + if ((tw.isAuto() || tw.isPercent()) && !table->isPositioned()) { + RenderBlock* cb = table->containingBlock(); + while (cb && !cb->isRenderView() && !cb->isTableCell() && + cb->style()->width().isAuto() && !cb->isPositioned()) + cb = cb->containingBlock(); + + table = 0; + if (cb && cb->isTableCell() && + (cb->style()->width().isAuto() || cb->style()->width().isPercent())) { + if (tw.isPercent()) + scale = false; + else { + RenderTableCell* cell = static_cast<RenderTableCell*>(cb); + if (cell->colSpan() > 1 || cell->table()->style()->width().isAuto()) + scale = false; + else + table = cell->table(); + } + } + } + else + table = 0; + } + return scale; +} + +void AutoTableLayout::calcPrefWidths(int& minWidth, int& maxWidth) +{ + fullRecalc(); + + int spanMaxWidth = calcEffectiveWidth(); + minWidth = 0; + maxWidth = 0; + float maxPercent = 0; + float maxNonPercent = 0; + bool scaleColumns = shouldScaleColumns(m_table); + + // We substitute 0 percent by (epsilon / percentScaleFactor) percent in two places below to avoid division by zero. + // FIXME: Handle the 0% cases properly. + const int epsilon = 1; + + int remainingPercent = 100 * percentScaleFactor; + for (unsigned int i = 0; i < m_layoutStruct.size(); i++) { + minWidth += m_layoutStruct[i].effMinWidth; + maxWidth += m_layoutStruct[i].effMaxWidth; + if (scaleColumns) { + if (m_layoutStruct[i].effWidth.isPercent()) { + int percent = min(m_layoutStruct[i].effWidth.rawValue(), remainingPercent); + float pw = static_cast<float>(m_layoutStruct[i].effMaxWidth) * 100 * percentScaleFactor / max(percent, epsilon); + maxPercent = max(pw, maxPercent); + remainingPercent -= percent; + } else + maxNonPercent += m_layoutStruct[i].effMaxWidth; + } + } + + if (scaleColumns) { + maxNonPercent = maxNonPercent * 100 * percentScaleFactor / max(remainingPercent, epsilon); + maxWidth = max(maxWidth, static_cast<int>(min(maxNonPercent, INT_MAX / 2.0f))); + maxWidth = max(maxWidth, static_cast<int>(min(maxPercent, INT_MAX / 2.0f))); + } + + maxWidth = max(maxWidth, spanMaxWidth); + + int bs = m_table->bordersPaddingAndSpacing(); + minWidth += bs; + maxWidth += bs; + + Length tw = m_table->style()->width(); + if (tw.isFixed() && tw.value() > 0) { + minWidth = max(minWidth, tw.value()); + maxWidth = minWidth; + } +} + +/* + This method takes care of colspans. + effWidth is the same as width for cells without colspans. If we have colspans, they get modified. + */ +int AutoTableLayout::calcEffectiveWidth() +{ + float tMaxWidth = 0; + + unsigned int nEffCols = m_layoutStruct.size(); + int hspacing = m_table->hBorderSpacing(); + + for (unsigned int i = 0; i < nEffCols; i++) { + m_layoutStruct[i].effWidth = m_layoutStruct[i].width; + m_layoutStruct[i].effMinWidth = m_layoutStruct[i].minWidth; + m_layoutStruct[i].effMaxWidth = m_layoutStruct[i].maxWidth; + } + + for (unsigned int i = 0; i < m_spanCells.size(); i++) { + RenderTableCell *cell = m_spanCells[i]; + if (!cell) + break; + int span = cell->colSpan(); + + Length w = cell->styleOrColWidth(); + if (!w.isRelative() && w.isZero()) + w = Length(); // make it Auto + + int col = m_table->colToEffCol(cell->col()); + unsigned int lastCol = col; + int cMinWidth = cell->minPrefWidth() + hspacing; + float cMaxWidth = cell->maxPrefWidth() + hspacing; + int totalPercent = 0; + int minWidth = 0; + float maxWidth = 0; + bool allColsArePercent = true; + bool allColsAreFixed = true; + bool haveAuto = false; + bool spanHasEmptyCellsOnly = true; + int fixedWidth = 0; + while (lastCol < nEffCols && span > 0) { + switch (m_layoutStruct[lastCol].width.type()) { + case Percent: + totalPercent += m_layoutStruct[lastCol].width.rawValue(); + allColsAreFixed = false; + break; + case Fixed: + if (m_layoutStruct[lastCol].width.value() > 0) { + fixedWidth += m_layoutStruct[lastCol].width.value(); + allColsArePercent = false; + // IE resets effWidth to Auto here, but this breaks the konqueror about page and seems to be some bad + // legacy behaviour anyway. mozilla doesn't do this so I decided we don't neither. + break; + } + // fall through + case Auto: + haveAuto = true; + // fall through + default: + // If the column is a percentage width, do not let the spanning cell overwrite the + // width value. This caused a mis-rendering on amazon.com. + // Sample snippet: + // <table border=2 width=100%>< + // <tr><td>1</td><td colspan=2>2-3</tr> + // <tr><td>1</td><td colspan=2 width=100%>2-3</td></tr> + // </table> + if (!m_layoutStruct[lastCol].effWidth.isPercent()) { + m_layoutStruct[lastCol].effWidth = Length(); + allColsArePercent = false; + } + else + totalPercent += m_layoutStruct[lastCol].effWidth.rawValue(); + allColsAreFixed = false; + } + if (!m_layoutStruct[lastCol].emptyCellsOnly) + spanHasEmptyCellsOnly = false; + span -= m_table->spanOfEffCol(lastCol); + minWidth += m_layoutStruct[lastCol].effMinWidth; + maxWidth += m_layoutStruct[lastCol].effMaxWidth; + lastCol++; + cMinWidth -= hspacing; + cMaxWidth -= hspacing; + } + + // adjust table max width if needed + if (w.isPercent()) { + if (totalPercent > w.rawValue() || allColsArePercent) { + // can't satify this condition, treat as variable + w = Length(); + } else { + float spanMax = max(maxWidth, cMaxWidth); + tMaxWidth = max(tMaxWidth, spanMax * 100 * percentScaleFactor / w.rawValue()); + + // all non percent columns in the span get percent vlaues to sum up correctly. + int percentMissing = w.rawValue() - totalPercent; + float totalWidth = 0; + for (unsigned int pos = col; pos < lastCol; pos++) { + if (!(m_layoutStruct[pos].effWidth.isPercent())) + totalWidth += m_layoutStruct[pos].effMaxWidth; + } + + for (unsigned int pos = col; pos < lastCol && totalWidth > 0; pos++) { + if (!(m_layoutStruct[pos].effWidth.isPercent())) { + int percent = static_cast<int>(percentMissing * static_cast<float>(m_layoutStruct[pos].effMaxWidth) / totalWidth); + totalWidth -= m_layoutStruct[pos].effMaxWidth; + percentMissing -= percent; + if (percent > 0) + m_layoutStruct[pos].effWidth.setRawValue(Percent, percent); + else + m_layoutStruct[pos].effWidth = Length(); + } + } + + } + } + + // make sure minWidth and maxWidth of the spanning cell are honoured + if (cMinWidth > minWidth) { + if (allColsAreFixed) { + for (unsigned int pos = col; fixedWidth > 0 && pos < lastCol; pos++) { + int w = max(m_layoutStruct[pos].effMinWidth, cMinWidth * m_layoutStruct[pos].width.value() / fixedWidth); + fixedWidth -= m_layoutStruct[pos].width.value(); + cMinWidth -= w; + m_layoutStruct[pos].effMinWidth = w; + } + + } else { + float maxw = maxWidth; + int minw = minWidth; + + // Give min to variable first, to fixed second, and to others third. + for (unsigned int pos = col; maxw >= 0 && pos < lastCol; pos++) { + if (m_layoutStruct[pos].width.isFixed() && haveAuto && fixedWidth <= cMinWidth) { + int w = max(m_layoutStruct[pos].effMinWidth, m_layoutStruct[pos].width.value()); + fixedWidth -= m_layoutStruct[pos].width.value(); + minw -= m_layoutStruct[pos].effMinWidth; + maxw -= m_layoutStruct[pos].effMaxWidth; + cMinWidth -= w; + m_layoutStruct[pos].effMinWidth = w; + } + } + + for (unsigned int pos = col; maxw >= 0 && pos < lastCol && minw < cMinWidth; pos++) { + if (!(m_layoutStruct[pos].width.isFixed() && haveAuto && fixedWidth <= cMinWidth)) { + int w = max(m_layoutStruct[pos].effMinWidth, static_cast<int>(maxw ? cMinWidth * static_cast<float>(m_layoutStruct[pos].effMaxWidth) / maxw : cMinWidth)); + w = min(m_layoutStruct[pos].effMinWidth+(cMinWidth-minw), w); + + maxw -= m_layoutStruct[pos].effMaxWidth; + minw -= m_layoutStruct[pos].effMinWidth; + cMinWidth -= w; + m_layoutStruct[pos].effMinWidth = w; + } + } + } + } + if (!(w.isPercent())) { + if (cMaxWidth > maxWidth) { + for (unsigned int pos = col; maxWidth >= 0 && pos < lastCol; pos++) { + int w = max(m_layoutStruct[pos].effMaxWidth, static_cast<int>(maxWidth ? cMaxWidth * static_cast<float>(m_layoutStruct[pos].effMaxWidth) / maxWidth : cMaxWidth)); + maxWidth -= m_layoutStruct[pos].effMaxWidth; + cMaxWidth -= w; + m_layoutStruct[pos].effMaxWidth = w; + } + } + } else { + for (unsigned int pos = col; pos < lastCol; pos++) + m_layoutStruct[pos].maxWidth = max(m_layoutStruct[pos].maxWidth, m_layoutStruct[pos].minWidth); + } + // treat span ranges consisting of empty cells only as if they had content + if (spanHasEmptyCellsOnly) + for (unsigned int pos = col; pos < lastCol; pos++) + m_layoutStruct[pos].emptyCellsOnly = false; + } + m_effWidthDirty = false; + + return static_cast<int>(min(tMaxWidth, INT_MAX / 2.0f)); +} + +/* gets all cells that originate in a column and have a cellspan > 1 + Sorts them by increasing cellspan +*/ +void AutoTableLayout::insertSpanCell(RenderTableCell *cell) +{ + if (!cell || cell->colSpan() == 1) + return; + + int size = m_spanCells.size(); + if (!size || m_spanCells[size-1] != 0) { + m_spanCells.grow(size + 10); + for (int i = 0; i < 10; i++) + m_spanCells[size+i] = 0; + size += 10; + } + + // add them in sort. This is a slow algorithm, and a binary search or a fast sorting after collection would be better + unsigned int pos = 0; + int span = cell->colSpan(); + while (pos < m_spanCells.size() && m_spanCells[pos] && span > m_spanCells[pos]->colSpan()) + pos++; + memmove(m_spanCells.data()+pos+1, m_spanCells.data()+pos, (size-pos-1)*sizeof(RenderTableCell *)); + m_spanCells[pos] = cell; +} + + +void AutoTableLayout::layout() +{ + // table layout based on the values collected in the layout structure. + int tableWidth = m_table->width() - m_table->bordersPaddingAndSpacing(); + int available = tableWidth; + int nEffCols = m_table->numEffCols(); + + if (nEffCols != (int)m_layoutStruct.size()) { + fullRecalc(); + nEffCols = m_table->numEffCols(); + } + + if (m_effWidthDirty) + calcEffectiveWidth(); + + bool havePercent = false; + bool haveRelative = false; + int totalRelative = 0; + int numAuto = 0; + int numFixed = 0; + float totalAuto = 0; + float totalFixed = 0; + int totalPercent = 0; + int allocAuto = 0; + int numAutoEmptyCellsOnly = 0; + + // fill up every cell with its minWidth + for (int i = 0; i < nEffCols; i++) { + int w = m_layoutStruct[i].effMinWidth; + m_layoutStruct[i].calcWidth = w; + available -= w; + Length& width = m_layoutStruct[i].effWidth; + switch (width.type()) { + case Percent: + havePercent = true; + totalPercent += width.rawValue(); + break; + case Relative: + haveRelative = true; + totalRelative += width.value(); + break; + case Fixed: + numFixed++; + totalFixed += m_layoutStruct[i].effMaxWidth; + // fall through + break; + case Auto: + case Static: + if (m_layoutStruct[i].emptyCellsOnly) + numAutoEmptyCellsOnly++; + else { + numAuto++; + totalAuto += m_layoutStruct[i].effMaxWidth; + allocAuto += w; + } + break; + default: + break; + } + } + + // allocate width to percent cols + if (available > 0 && havePercent) { + for (int i = 0; i < nEffCols; i++) { + Length &width = m_layoutStruct[i].effWidth; + if (width.isPercent()) { + int w = max(int(m_layoutStruct[i].effMinWidth), width.calcMinValue(tableWidth)); + available += m_layoutStruct[i].calcWidth - w; + m_layoutStruct[i].calcWidth = w; + } + } + if (totalPercent > 100 * percentScaleFactor) { + // remove overallocated space from the last columns + int excess = tableWidth*(totalPercent - 100 * percentScaleFactor) / (100 * percentScaleFactor); + for (int i = nEffCols-1; i >= 0; i--) { + if (m_layoutStruct[i].effWidth.isPercent()) { + int w = m_layoutStruct[i].calcWidth; + int reduction = min(w, excess); + // the lines below might look inconsistent, but that's the way it's handled in mozilla + excess -= reduction; + int newWidth = max(int (m_layoutStruct[i].effMinWidth), w - reduction); + available += w - newWidth; + m_layoutStruct[i].calcWidth = newWidth; + } + } + } + } + + // then allocate width to fixed cols + if (available > 0) { + for (int i = 0; i < nEffCols; ++i) { + Length &width = m_layoutStruct[i].effWidth; + if (width.isFixed() && width.value() > m_layoutStruct[i].calcWidth) { + available += m_layoutStruct[i].calcWidth - width.value(); + m_layoutStruct[i].calcWidth = width.value(); + } + } + } + + // now satisfy relative + if (available > 0) { + for (int i = 0; i < nEffCols; i++) { + Length &width = m_layoutStruct[i].effWidth; + if (width.isRelative() && width.value() != 0) { + // width=0* gets effMinWidth. + int w = width.value() * tableWidth / totalRelative; + available += m_layoutStruct[i].calcWidth - w; + m_layoutStruct[i].calcWidth = w; + } + } + } + + // now satisfy variable + if (available > 0 && numAuto) { + available += allocAuto; // this gets redistributed + for (int i = 0; i < nEffCols; i++) { + Length &width = m_layoutStruct[i].effWidth; + if (width.isAuto() && totalAuto != 0 && !m_layoutStruct[i].emptyCellsOnly) { + int w = max(m_layoutStruct[i].calcWidth, static_cast<int>(available * static_cast<float>(m_layoutStruct[i].effMaxWidth) / totalAuto)); + available -= w; + totalAuto -= m_layoutStruct[i].effMaxWidth; + m_layoutStruct[i].calcWidth = w; + } + } + } + + // spread over fixed columns + if (available > 0 && numFixed) { + // still have some width to spread, distribute to fixed columns + for (int i = 0; i < nEffCols; i++) { + Length &width = m_layoutStruct[i].effWidth; + if (width.isFixed()) { + int w = static_cast<int>(available * static_cast<float>(m_layoutStruct[i].effMaxWidth) / totalFixed); + available -= w; + totalFixed -= m_layoutStruct[i].effMaxWidth; + m_layoutStruct[i].calcWidth += w; + } + } + } + + // spread over percent colums + if (available > 0 && m_hasPercent && totalPercent < 100 * percentScaleFactor) { + // still have some width to spread, distribute weighted to percent columns + for (int i = 0; i < nEffCols; i++) { + Length &width = m_layoutStruct[i].effWidth; + if (width.isPercent()) { + int w = available * width.rawValue() / totalPercent; + available -= w; + totalPercent -= width.rawValue(); + m_layoutStruct[i].calcWidth += w; + if (!available || !totalPercent) break; + } + } + } + + // spread over the rest + if (available > 0 && nEffCols > numAutoEmptyCellsOnly) { + int total = nEffCols - numAutoEmptyCellsOnly; + // still have some width to spread + int i = nEffCols; + while (i--) { + // variable columns with empty cells only don't get any width + if (m_layoutStruct[i].effWidth.isAuto() && m_layoutStruct[i].emptyCellsOnly) + continue; + int w = available / total; + available -= w; + total--; + m_layoutStruct[i].calcWidth += w; + } + } + + // if we have overallocated, reduce every cell according to the difference between desired width and minwidth + // this seems to produce to the pixel exaxt results with IE. Wonder is some of this also holds for width distributing. + if (available < 0) { + // Need to reduce cells with the following prioritization: + // (1) Auto + // (2) Relative + // (3) Fixed + // (4) Percent + // This is basically the reverse of how we grew the cells. + if (available < 0) { + int mw = 0; + for (int i = nEffCols-1; i >= 0; i--) { + Length &width = m_layoutStruct[i].effWidth; + if (width.isAuto()) + mw += m_layoutStruct[i].calcWidth - m_layoutStruct[i].effMinWidth; + } + + for (int i = nEffCols-1; i >= 0 && mw > 0; i--) { + Length &width = m_layoutStruct[i].effWidth; + if (width.isAuto()) { + int minMaxDiff = m_layoutStruct[i].calcWidth-m_layoutStruct[i].effMinWidth; + int reduce = available * minMaxDiff / mw; + m_layoutStruct[i].calcWidth += reduce; + available -= reduce; + mw -= minMaxDiff; + if (available >= 0) + break; + } + } + } + + if (available < 0) { + int mw = 0; + for (int i = nEffCols-1; i >= 0; i--) { + Length& width = m_layoutStruct[i].effWidth; + if (width.isRelative()) + mw += m_layoutStruct[i].calcWidth - m_layoutStruct[i].effMinWidth; + } + + for (int i = nEffCols-1; i >= 0 && mw > 0; i--) { + Length& width = m_layoutStruct[i].effWidth; + if (width.isRelative()) { + int minMaxDiff = m_layoutStruct[i].calcWidth-m_layoutStruct[i].effMinWidth; + int reduce = available * minMaxDiff / mw; + m_layoutStruct[i].calcWidth += reduce; + available -= reduce; + mw -= minMaxDiff; + if (available >= 0) + break; + } + } + } + + if (available < 0) { + int mw = 0; + for (int i = nEffCols-1; i >= 0; i--) { + Length& width = m_layoutStruct[i].effWidth; + if (width.isFixed()) + mw += m_layoutStruct[i].calcWidth - m_layoutStruct[i].effMinWidth; + } + + for (int i = nEffCols-1; i >= 0 && mw > 0; i--) { + Length& width = m_layoutStruct[i].effWidth; + if (width.isFixed()) { + int minMaxDiff = m_layoutStruct[i].calcWidth-m_layoutStruct[i].effMinWidth; + int reduce = available * minMaxDiff / mw; + m_layoutStruct[i].calcWidth += reduce; + available -= reduce; + mw -= minMaxDiff; + if (available >= 0) + break; + } + } + } + + if (available < 0) { + int mw = 0; + for (int i = nEffCols-1; i >= 0; i--) { + Length& width = m_layoutStruct[i].effWidth; + if (width.isPercent()) + mw += m_layoutStruct[i].calcWidth - m_layoutStruct[i].effMinWidth; + } + + for (int i = nEffCols-1; i >= 0 && mw > 0; i--) { + Length& width = m_layoutStruct[i].effWidth; + if (width.isPercent()) { + int minMaxDiff = m_layoutStruct[i].calcWidth-m_layoutStruct[i].effMinWidth; + int reduce = available * minMaxDiff / mw; + m_layoutStruct[i].calcWidth += reduce; + available -= reduce; + mw -= minMaxDiff; + if (available >= 0) + break; + } + } + } + } + + int pos = 0; + for (int i = 0; i < nEffCols; i++) { + m_table->columnPositions()[i] = pos; + pos += m_layoutStruct[i].calcWidth + m_table->hBorderSpacing(); + } + m_table->columnPositions()[m_table->columnPositions().size() - 1] = pos; +} + + +void AutoTableLayout::calcPercentages() const +{ + unsigned totalPercent = 0; + for (unsigned i = 0; i < m_layoutStruct.size(); i++) { + if (m_layoutStruct[i].width.isPercent()) + totalPercent += m_layoutStruct[i].width.rawValue(); + } + m_totalPercent = totalPercent / percentScaleFactor; + m_percentagesDirty = false; +} + +#undef DEBUG_LAYOUT + +} diff --git a/src/3rdparty/webkit/WebCore/rendering/AutoTableLayout.h b/src/3rdparty/webkit/WebCore/rendering/AutoTableLayout.h new file mode 100644 index 0000000..641a68b --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/AutoTableLayout.h @@ -0,0 +1,87 @@ +/* + * This file is part of the HTML rendering engine for KDE. + * + * Copyright (C) 2002 Lars Knoll (knoll@kde.org) + * (C) 2002 Dirk Mueller (mueller@kde.org) + * + * 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. + * + * 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. + */ + +#ifndef AutoTableLayout_h +#define AutoTableLayout_h + +#include "Length.h" +#include "TableLayout.h" +#include <wtf/Vector.h> + +namespace WebCore { + +class RenderTable; +class RenderTableCell; + +class AutoTableLayout : public TableLayout { +public: + AutoTableLayout(RenderTable*); + ~AutoTableLayout(); + + virtual void calcPrefWidths(int& minWidth, int& maxWidth); + virtual void layout(); + +protected: + void fullRecalc(); + void recalcColumn(int effCol); + + void calcPercentages() const; + int totalPercent() const + { + if (m_percentagesDirty) + calcPercentages(); + return m_totalPercent; + } + + int calcEffectiveWidth(); + + void insertSpanCell(RenderTableCell*); + + struct Layout { + Layout() + : minWidth(0) + , maxWidth(0) + , effMinWidth(0) + , effMaxWidth(0) + , calcWidth(0) + , emptyCellsOnly(true) {} + Length width; + Length effWidth; + int minWidth; + int maxWidth; + int effMinWidth; + int effMaxWidth; + int calcWidth; + bool emptyCellsOnly; + }; + + Vector<Layout, 4> m_layoutStruct; + Vector<RenderTableCell*, 4> m_spanCells; + bool m_hasPercent : 1; + mutable bool m_percentagesDirty : 1; + mutable bool m_effWidthDirty : 1; + mutable unsigned short m_totalPercent; +}; + +} // namespace WebCore + +#endif // AutoTableLayout_h diff --git a/src/3rdparty/webkit/WebCore/rendering/CounterNode.cpp b/src/3rdparty/webkit/WebCore/rendering/CounterNode.cpp new file mode 100644 index 0000000..c30ca9a --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/CounterNode.cpp @@ -0,0 +1,192 @@ +/* + * This file is part of the HTML rendering engine for KDE. + * + * Copyright (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com) + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * + * 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" +#include "CounterNode.h" + +#include "RenderObject.h" +#include <stdio.h> + +// FIXME: There's currently no strategy for getting the counter tree updated when new +// elements with counter-reset and counter-increment styles are added to the render tree. +// Also, the code can't handle changes where an existing node needs to change into a +// "reset" node, or from a "reset" node back to not a "reset" node. As of this writing, +// at least some of these problems manifest as failures in the t1204-increment and +// t1204-reset tests in the CSS 2.1 test suite. + +namespace WebCore { + +CounterNode::CounterNode(RenderObject* o, bool isReset, int value) + : m_isReset(isReset) + , m_value(value) + , m_countInParent(0) + , m_renderer(o) + , m_parent(0) + , m_previousSibling(0) + , m_nextSibling(0) + , m_firstChild(0) + , m_lastChild(0) +{ +} + +int CounterNode::computeCountInParent() const +{ + int increment = m_isReset ? 0 : m_value; + if (m_previousSibling) + return m_previousSibling->m_countInParent + increment; + ASSERT(m_parent->m_firstChild == this); + return m_parent->m_value + increment; +} + +void CounterNode::recount() +{ + for (CounterNode* c = this; c; c = c->m_nextSibling) { + int oldCount = c->m_countInParent; + int newCount = c->computeCountInParent(); + c->m_countInParent = newCount; + if (oldCount == newCount) + break; + if (c->m_renderer->isCounter()) + c->m_renderer->setNeedsLayoutAndPrefWidthsRecalc(); + } +} + +void CounterNode::insertAfter(CounterNode* newChild, CounterNode* refChild) +{ + ASSERT(newChild); + ASSERT(!newChild->m_parent); + ASSERT(!newChild->m_previousSibling); + ASSERT(!newChild->m_nextSibling); + ASSERT(!refChild || refChild->m_parent == this); + + CounterNode* next; + + if (refChild) { + next = refChild->m_nextSibling; + refChild->m_nextSibling = newChild; + } else { + next = m_firstChild; + m_firstChild = newChild; + } + + if (next) { + ASSERT(next->m_previousSibling == refChild); + next->m_previousSibling = newChild; + } else { + ASSERT(m_lastChild == refChild); + m_lastChild = newChild; + } + + newChild->m_parent = this; + newChild->m_previousSibling = refChild; + newChild->m_nextSibling = next; + + newChild->m_countInParent = newChild->computeCountInParent(); + if (next) + next->recount(); +} + +void CounterNode::removeChild(CounterNode* oldChild) +{ + ASSERT(oldChild); + ASSERT(!oldChild->m_firstChild); + ASSERT(!oldChild->m_lastChild); + + CounterNode* next = oldChild->m_nextSibling; + CounterNode* prev = oldChild->m_previousSibling; + + oldChild->m_nextSibling = 0; + oldChild->m_previousSibling = 0; + oldChild->m_parent = 0; + + if (prev) + prev->m_nextSibling = next; + else { + ASSERT(m_firstChild == oldChild); + m_firstChild = next; + } + + if (next) + next->m_previousSibling = prev; + else { + ASSERT(m_lastChild == oldChild); + m_lastChild = prev; + } + + if (next) + next->recount(); +} + +#ifndef NDEBUG + +static const CounterNode* nextInPreOrderAfterChildren(const CounterNode* node) +{ + CounterNode* next = node->nextSibling(); + if (!next) { + next = node->parent(); + while (next && !next->nextSibling()) + next = next->parent(); + if (next) + next = next->nextSibling(); + } + return next; +} + +static const CounterNode* nextInPreOrder(const CounterNode* node) +{ + if (CounterNode* child = node->firstChild()) + return child; + return nextInPreOrderAfterChildren(node); +} + +static void showTreeAndMark(const CounterNode* node) +{ + const CounterNode* root = node; + while (root->parent()) + root = root->parent(); + + for (const CounterNode* c = root; c; c = nextInPreOrder(c)) { + if (c == node) + fprintf(stderr, "*"); + for (const CounterNode* d = c; d && d != root; d = d->parent()) + fprintf(stderr, "\t"); + if (c->isReset()) + fprintf(stderr, "reset: %d\n", c->value()); + else + fprintf(stderr, "increment: %d\n", c->value()); + } +} + +#endif + +} // namespace WebCore + +#ifndef NDEBUG + +void showTree(const WebCore::CounterNode* counter) +{ + if (counter) + showTreeAndMark(counter); +} + +#endif diff --git a/src/3rdparty/webkit/WebCore/rendering/CounterNode.h b/src/3rdparty/webkit/WebCore/rendering/CounterNode.h new file mode 100644 index 0000000..57f9563 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/CounterNode.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com) + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * + * 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. + * +*/ + +#ifndef CounterNode_h +#define CounterNode_h + +#include <wtf/Noncopyable.h> + +// This implements a counter tree that is used for finding parents in counters() lookup, +// and for propagating count changes when nodes are added or removed. + +// Parents represent unique counters and their scope, which are created either explicitly +// by "counter-reset" style rules or implicitly by referring to a counter that is not in scope. +// Such nodes are tagged as "reset" nodes, although they are not all due to "counter-reset". + +// Not that render tree children are often counter tree siblings due to counter scoping rules. + +namespace WebCore { + +class RenderObject; + +class CounterNode : Noncopyable { +public: + CounterNode(RenderObject*, bool isReset, int value); + + bool isReset() const { return m_isReset; } + int value() const { return m_value; } + int countInParent() const { return m_countInParent; } + RenderObject* renderer() const { return m_renderer; } + + CounterNode* parent() const { return m_parent; } + CounterNode* previousSibling() const { return m_previousSibling; } + CounterNode* nextSibling() const { return m_nextSibling; } + CounterNode* firstChild() const { return m_firstChild; } + CounterNode* lastChild() const { return m_lastChild; } + + void insertAfter(CounterNode* newChild, CounterNode* beforeChild); + void removeChild(CounterNode*); + +private: + int computeCountInParent() const; + void recount(); + + bool m_isReset; + int m_value; + int m_countInParent; + RenderObject* m_renderer; + + CounterNode* m_parent; + CounterNode* m_previousSibling; + CounterNode* m_nextSibling; + CounterNode* m_firstChild; + CounterNode* m_lastChild; +}; + +} // namespace WebCore + +#ifndef NDEBUG +// Outside the WebCore namespace for ease of invocation from gdb. +void showTree(const WebCore::CounterNode*); +#endif + +#endif // CounterNode_h diff --git a/src/3rdparty/webkit/WebCore/rendering/EllipsisBox.cpp b/src/3rdparty/webkit/WebCore/rendering/EllipsisBox.cpp new file mode 100644 index 0000000..0bd1d1a --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/EllipsisBox.cpp @@ -0,0 +1,85 @@ +/** +* This file is part of the html renderer for KDE. + * + * Copyright (C) 2003, 2006 Apple Computer, Inc. + * + * 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" +#include "EllipsisBox.h" + +#include "Document.h" +#include "GraphicsContext.h" +#include "HitTestResult.h" + +namespace WebCore { + +void EllipsisBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) +{ + GraphicsContext* context = paintInfo.context; + RenderStyle* style = m_object->style(m_firstLine); + if (style->font() != context->font()) + context->setFont(style->font()); + + Color textColor = style->color(); + if (textColor != context->fillColor()) + context->setFillColor(textColor); + bool setShadow = false; + if (style->textShadow()) { + context->setShadow(IntSize(style->textShadow()->x, style->textShadow()->y), + style->textShadow()->blur, style->textShadow()->color); + setShadow = true; + } + + const String& str = m_str; + context->drawText(TextRun(str.characters(), str.length(), false, 0, 0, false, style->visuallyOrdered()), IntPoint(m_x + tx, m_y + ty + m_baseline)); + + if (setShadow) + context->clearShadow(); + + if (m_markupBox) { + // Paint the markup box + tx += m_x + m_width - m_markupBox->xPos(); + ty += m_y + m_baseline - (m_markupBox->yPos() + m_markupBox->baseline()); + m_markupBox->paint(paintInfo, tx, ty); + } +} + +bool EllipsisBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty) +{ + tx += m_x; + ty += m_y; + + // Hit test the markup box. + if (m_markupBox) { + int mtx = tx + m_width - m_markupBox->xPos(); + int mty = ty + m_baseline - (m_markupBox->yPos() + m_markupBox->baseline()); + if (m_markupBox->nodeAtPoint(request, result, x, y, mtx, mty)) { + object()->updateHitTestResult(result, IntPoint(x - mtx, y - mty)); + return true; + } + } + + if (visibleToHitTesting() && IntRect(tx, ty, m_width, m_height).contains(x, y)) { + object()->updateHitTestResult(result, IntPoint(x - tx, y - ty)); + return true; + } + + return false; +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/EllipsisBox.h b/src/3rdparty/webkit/WebCore/rendering/EllipsisBox.h new file mode 100644 index 0000000..dbb30cd --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/EllipsisBox.h @@ -0,0 +1,53 @@ +/** +* This file is part of the html renderer for KDE. + * + * Copyright (C) 2003, 2006 Apple Computer, Inc. + * + * 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. + */ + +#ifndef EllipsisBox_h +#define EllipsisBox_h + +#include "InlineBox.h" + +namespace WebCore { + +class HitTestResult; + +struct HitTestRequest; + +class EllipsisBox : public InlineBox { +public: + EllipsisBox(RenderObject* obj, const AtomicString& ellipsisStr, InlineFlowBox* parent, + int width, int y, int height, int baseline, bool firstLine, InlineBox* markupBox) + : InlineBox(obj, 0, y, width, height, baseline, firstLine, true, false, false, 0, 0, parent) + , m_str(ellipsisStr) + , m_markupBox(markupBox) + { + } + + virtual void paint(RenderObject::PaintInfo&, int tx, int ty); + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty); + +private: + AtomicString m_str; + InlineBox* m_markupBox; +}; + +} // namespace WebCore + +#endif // EllipsisBox_h diff --git a/src/3rdparty/webkit/WebCore/rendering/FixedTableLayout.cpp b/src/3rdparty/webkit/WebCore/rendering/FixedTableLayout.cpp new file mode 100644 index 0000000..d7c1293 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/FixedTableLayout.cpp @@ -0,0 +1,309 @@ +/* + * This file is part of the HTML rendering engine for KDE. + * + * Copyright (C) 2002 Lars Knoll (knoll@kde.org) + * (C) 2002 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2006 Apple Computer, Inc. + * + * 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. + * + * 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" +#include "FixedTableLayout.h" + +#include "RenderTable.h" +#include "RenderTableCell.h" +#include "RenderTableCol.h" +#include "RenderTableSection.h" + +/* + The text below is from the CSS 2.1 specs. + + Fixed table layout + + With this (fast) algorithm, the horizontal layout of the table does + not depend on the contents of the cells; it only depends on the + table's width, the width of the columns, and borders or cell + spacing. + + The table's width may be specified explicitly with the 'width' + property. A value of 'auto' (for both 'display: table' and 'display: + inline-table') means use the automatic table layout algorithm. + + In the fixed table layout algorithm, the width of each column is + determined as follows: + + 1. A column element with a value other than 'auto' for the 'width' + property sets the width for that column. + + 2. Otherwise, a cell in the first row with a value other than + 'auto' for the 'width' property sets the width for that column. If + the cell spans more than one column, the width is divided over the + columns. + + 3. Any remaining columns equally divide the remaining horizontal + table space (minus borders or cell spacing). + + The width of the table is then the greater of the value of the + 'width' property for the table element and the sum of the column + widths (plus cell spacing or borders). If the table is wider than + the columns, the extra space should be distributed over the columns. + + + In this manner, the user agent can begin to lay out the table once + the entire first row has been received. Cells in subsequent rows do + not affect column widths. Any cell that has content that overflows + uses the 'overflow' property to determine whether to clip the + overflow content. +*/ + +using namespace std; + +namespace WebCore { + +FixedTableLayout::FixedTableLayout(RenderTable* table) + : TableLayout(table) +{ +} + +int FixedTableLayout::calcWidthArray(int) +{ + int usedWidth = 0; + + // iterate over all <col> elements + RenderObject* child = m_table->firstChild(); + int cCol = 0; + int nEffCols = m_table->numEffCols(); + m_width.resize(nEffCols); + m_width.fill(Length(Auto)); + + Length grpWidth; + while (child) { + if (child->isTableCol()) { + RenderTableCol *col = static_cast<RenderTableCol *>(child); + int span = col->span(); + if (col->firstChild()) + grpWidth = col->style()->width(); + else { + Length w = col->style()->width(); + if (w.isAuto()) + w = grpWidth; + int effWidth = 0; + if (w.isFixed() && w.value() > 0) + effWidth = w.value(); + + int usedSpan = 0; + int i = 0; + while (usedSpan < span) { + if(cCol + i >= nEffCols) { + m_table->appendColumn(span - usedSpan); + nEffCols++; + m_width.resize(nEffCols); + m_width[nEffCols-1] = Length(); + } + int eSpan = m_table->spanOfEffCol(cCol+i); + if ((w.isFixed() || w.isPercent()) && w.isPositive()) { + m_width[cCol + i].setRawValue(w.type(), w.rawValue() * eSpan); + usedWidth += effWidth * eSpan; + } + usedSpan += eSpan; + i++; + } + cCol += i; + } + } else + break; + + RenderObject *next = child->firstChild(); + if (!next) + next = child->nextSibling(); + if (!next && child->parent()->isTableCol()) { + next = child->parent()->nextSibling(); + grpWidth = Length(); + } + child = next; + } + + // Iterate over the first row in case some are unspecified. + RenderTableSection* section = m_table->header(); + if (!section) + section = m_table->firstBody(); + if (!section) + section = m_table->footer(); + if (section && !section->numRows()) + section = m_table->sectionBelow(section, true); + if (section) { + cCol = 0; + RenderObject* firstRow = section->firstChild(); + child = firstRow->firstChild(); + while (child) { + if (child->isTableCell()) { + RenderTableCell* cell = static_cast<RenderTableCell*>(child); + if (cell->prefWidthsDirty()) + cell->calcPrefWidths(); + + Length w = cell->styleOrColWidth(); + int span = cell->colSpan(); + int effWidth = 0; + if (w.isFixed() && w.isPositive()) + effWidth = w.value(); + + int usedSpan = 0; + int i = 0; + while (usedSpan < span) { + ASSERT(cCol + i < nEffCols); + int eSpan = m_table->spanOfEffCol(cCol + i); + // Only set if no col element has already set it. + if (m_width[cCol + i].isAuto() && w.type() != Auto) { + m_width[cCol + i].setRawValue(w.type(), w.rawValue() * eSpan / span); + usedWidth += effWidth * eSpan / span; + } + usedSpan += eSpan; + i++; + } + cCol += i; + } + child = child->nextSibling(); + } + } + + return usedWidth; +} + +void FixedTableLayout::calcPrefWidths(int& minWidth, int& maxWidth) +{ + // FIXME: This entire calculation is incorrect for both minwidth and maxwidth. + + // we might want to wait until we have all of the first row before + // layouting for the first time. + + // only need to calculate the minimum width as the sum of the + // cols/cells with a fixed width. + // + // The maximum width is max(minWidth, tableWidth). + int bs = m_table->bordersPaddingAndSpacing(); + + int tableWidth = m_table->style()->width().isFixed() ? m_table->style()->width().value() - bs : 0; + int mw = calcWidthArray(tableWidth) + bs; + + minWidth = max(mw, tableWidth); + maxWidth = minWidth; +} + +void FixedTableLayout::layout() +{ + int tableWidth = m_table->width() - m_table->bordersPaddingAndSpacing(); + int nEffCols = m_table->numEffCols(); + Vector<int> calcWidth(nEffCols, 0); + + int numAuto = 0; + int autoSpan = 0; + int totalFixedWidth = 0; + int totalPercentWidth = 0; + int totalRawPercent = 0; + + // Compute requirements and try to satisfy fixed and percent widths. + // Percentages are of the table's width, so for example + // for a table width of 100px with columns (40px, 10%), the 10% compute + // to 10px here, and will scale up to 20px in the final (80px, 20px). + for (int i = 0; i < nEffCols; i++) { + if (m_width[i].isFixed()) { + calcWidth[i] = m_width[i].value(); + totalFixedWidth += calcWidth[i]; + } else if (m_width[i].isPercent()) { + calcWidth[i] = m_width[i].calcValue(tableWidth); + totalPercentWidth += calcWidth[i]; + totalRawPercent += m_width[i].rawValue(); + } else if (m_width[i].isAuto()) { + numAuto++; + autoSpan += m_table->spanOfEffCol(i); + } + } + + int hspacing = m_table->hBorderSpacing(); + int totalWidth = totalFixedWidth + totalPercentWidth; + if (!numAuto || totalWidth > tableWidth) { + // If there are no auto columns, or if the total is too wide, take + // what we have and scale it to fit as necessary. + if (totalWidth != tableWidth) { + // Fixed widths only scale up + if (totalFixedWidth && totalWidth < tableWidth) { + totalFixedWidth = 0; + for (int i = 0; i < nEffCols; i++) { + if (m_width[i].isFixed()) { + calcWidth[i] = calcWidth[i] * tableWidth / totalWidth; + totalFixedWidth += calcWidth[i]; + } + } + } + if (totalRawPercent) { + totalPercentWidth = 0; + for (int i = 0; i < nEffCols; i++) { + if (m_width[i].isPercent()) { + calcWidth[i] = m_width[i].rawValue() * (tableWidth - totalFixedWidth) / totalRawPercent; + totalPercentWidth += calcWidth[i]; + } + } + } + totalWidth = totalFixedWidth + totalPercentWidth; + } + } else { + // Divide the remaining width among the auto columns. + int remainingWidth = tableWidth - totalFixedWidth - totalPercentWidth - hspacing * (autoSpan - numAuto); + int lastAuto = 0; + for (int i = 0; i < nEffCols; i++) { + if (m_width[i].isAuto()) { + int span = m_table->spanOfEffCol(i); + int w = remainingWidth * span / autoSpan; + calcWidth[i] = w + hspacing * (span - 1); + remainingWidth -= w; + if (!remainingWidth) + break; + lastAuto = i; + numAuto--; + autoSpan -= span; + } + } + // Last one gets the remainder. + if (remainingWidth) + calcWidth[lastAuto] += remainingWidth; + totalWidth = tableWidth; + } + + if (totalWidth < tableWidth) { + // Spread extra space over columns. + int remainingWidth = tableWidth - totalWidth; + int total = nEffCols; + while (total) { + int w = remainingWidth / total; + remainingWidth -= w; + calcWidth[--total] += w; + } + if (nEffCols > 0) + calcWidth[nEffCols - 1] += remainingWidth; + } + + int pos = 0; + for (int i = 0; i < nEffCols; i++) { + m_table->columnPositions()[i] = pos; + pos += calcWidth[i] + hspacing; + } + int colPositionsSize = m_table->columnPositions().size(); + if (colPositionsSize > 0) + m_table->columnPositions()[colPositionsSize - 1] = pos; +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/FixedTableLayout.h b/src/3rdparty/webkit/WebCore/rendering/FixedTableLayout.h new file mode 100644 index 0000000..ed7c089 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/FixedTableLayout.h @@ -0,0 +1,49 @@ +/* + * This file is part of the HTML rendering engine for KDE. + * + * Copyright (C) 2002 Lars Knoll (knoll@kde.org) + * (C) 2002 Dirk Mueller (mueller@kde.org) + * + * 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. + * + * 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. + */ + +#ifndef FixedTableLayout_h +#define FixedTableLayout_h + +#include "Length.h" +#include "TableLayout.h" +#include <wtf/Vector.h> + +namespace WebCore { + +class RenderTable; + +class FixedTableLayout : public TableLayout { +public: + FixedTableLayout(RenderTable*); + + virtual void calcPrefWidths(int& minWidth, int& maxWidth); + virtual void layout(); + +protected: + int calcWidthArray(int tableWidth); + + Vector<Length> m_width; +}; + +} // namespace WebCore + +#endif // FixedTableLayout_h diff --git a/src/3rdparty/webkit/WebCore/rendering/GapRects.h b/src/3rdparty/webkit/WebCore/rendering/GapRects.h new file mode 100644 index 0000000..a762ae5 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/GapRects.h @@ -0,0 +1,62 @@ +/* + Copyright (C) 2005, 2006 Apple Inc. All rights reserved. + + 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. + + + Some useful definitions needed for laying out elements +*/ + +#ifndef GapRects_h +#define GapRects_h + +#include "IntRect.h" + +namespace WebCore { + + struct GapRects { + const IntRect& left() const { return m_left; } + const IntRect& center() const { return m_center; } + const IntRect& right() const { return m_right; } + + void uniteLeft(const IntRect& r) { m_left.unite(r); } + void uniteCenter(const IntRect& r) { m_center.unite(r); } + void uniteRight(const IntRect& r) { m_right.unite(r); } + void unite(const GapRects& o) { uniteLeft(o.left()); uniteCenter(o.center()); uniteRight(o.right()); } + + operator IntRect() const + { + IntRect result = m_left; + result.unite(m_center); + result.unite(m_right); + return result; + } + + bool operator==(const GapRects& other) + { + return m_left == other.left() && m_center == other.center() && m_right == other.right(); + } + bool operator!=(const GapRects& other) { return !(*this == other); } + + private: + IntRect m_left; + IntRect m_center; + IntRect m_right; + }; + +} // namespace WebCore + +#endif // GapRects_h diff --git a/src/3rdparty/webkit/WebCore/rendering/HitTestRequest.h b/src/3rdparty/webkit/WebCore/rendering/HitTestRequest.h new file mode 100644 index 0000000..11dca4b --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/HitTestRequest.h @@ -0,0 +1,44 @@ +/* + * This file is part of the HTML rendering engine for KDE. + * + * Copyright (C) 2006 Apple Computer, Inc. + * + * 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. + * +*/ +#ifndef HitTestRequest_h +#define HitTestRequest_h + +namespace WebCore { + +struct HitTestRequest { + HitTestRequest(bool r, bool a, bool m = false, bool u = false) + : readonly(r) + , active(a) + , mouseMove(m) + , mouseUp(u) + { + } + + bool readonly; + bool active; + bool mouseMove; + bool mouseUp; +}; + +} // namespace WebCore + +#endif // HitTestRequest_h diff --git a/src/3rdparty/webkit/WebCore/rendering/HitTestResult.cpp b/src/3rdparty/webkit/WebCore/rendering/HitTestResult.cpp new file mode 100644 index 0000000..5a041ed --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/HitTestResult.cpp @@ -0,0 +1,334 @@ +/* + * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * + * 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" +#include "HitTestResult.h" + +#include "Frame.h" +#include "FrameTree.h" +#include "HTMLAnchorElement.h" +#include "HTMLImageElement.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include "RenderImage.h" +#include "Scrollbar.h" +#include "SelectionController.h" + +#if ENABLE(SVG) +#include "SVGNames.h" +#include "XLinkNames.h" +#endif + +#if ENABLE(WML) +#include "WMLImageElement.h" +#include "WMLNames.h" +#endif + +namespace WebCore { + +using namespace HTMLNames; + +HitTestResult::HitTestResult(const IntPoint& point) + : m_point(point) + , m_isOverWidget(false) +{ +} + +HitTestResult::HitTestResult(const HitTestResult& other) + : m_innerNode(other.innerNode()) + , m_innerNonSharedNode(other.innerNonSharedNode()) + , m_point(other.point()) + , m_localPoint(other.localPoint()) + , m_innerURLElement(other.URLElement()) + , m_scrollbar(other.scrollbar()) + , m_isOverWidget(other.isOverWidget()) +{ +} + +HitTestResult::~HitTestResult() +{ +} + +HitTestResult& HitTestResult::operator=(const HitTestResult& other) +{ + m_innerNode = other.innerNode(); + m_innerNonSharedNode = other.innerNonSharedNode(); + m_point = other.point(); + m_localPoint = other.localPoint(); + m_innerURLElement = other.URLElement(); + m_scrollbar = other.scrollbar(); + m_isOverWidget = other.isOverWidget(); + return *this; +} + +void HitTestResult::setToNonShadowAncestor() +{ + Node* node = innerNode(); + if (node) + node = node->shadowAncestorNode(); + setInnerNode(node); + node = innerNonSharedNode(); + if (node) + node = node->shadowAncestorNode(); + setInnerNonSharedNode(node); +} + +void HitTestResult::setInnerNode(Node* n) +{ + m_innerNode = n; +} + +void HitTestResult::setInnerNonSharedNode(Node* n) +{ + m_innerNonSharedNode = n; +} + +void HitTestResult::setURLElement(Element* n) +{ + m_innerURLElement = n; +} + +void HitTestResult::setScrollbar(Scrollbar* s) +{ + m_scrollbar = s; +} + +Frame* HitTestResult::targetFrame() const +{ + if (!m_innerURLElement) + return 0; + + Frame* frame = m_innerURLElement->document()->frame(); + if (!frame) + return 0; + + return frame->tree()->find(m_innerURLElement->target()); +} + +IntRect HitTestResult::boundingBox() const +{ + if (m_innerNonSharedNode) { + RenderObject* renderer = m_innerNonSharedNode->renderer(); + if (renderer) + return renderer->absoluteBoundingBoxRect(); + } + + return IntRect(); +} + +bool HitTestResult::isSelected() const +{ + if (!m_innerNonSharedNode) + return false; + + Frame* frame = m_innerNonSharedNode->document()->frame(); + if (!frame) + return false; + + return frame->selection()->contains(m_point); +} + +String HitTestResult::spellingToolTip() const +{ + // Return the tool tip string associated with this point, if any. Only markers associated with bad grammar + // currently supply strings, but maybe someday markers associated with misspelled words will also. + if (!m_innerNonSharedNode) + return String(); + + DocumentMarker* marker = m_innerNonSharedNode->document()->markerContainingPoint(m_point, DocumentMarker::Grammar); + if (!marker) + return String(); + + return marker->description; +} + +String HitTestResult::title() const +{ + // Find the title in the nearest enclosing DOM node. + // For <area> tags in image maps, walk the tree for the <area>, not the <img> using it. + for (Node* titleNode = m_innerNode.get(); titleNode; titleNode = titleNode->parentNode()) { + if (titleNode->isElementNode()) { + String title = static_cast<Element*>(titleNode)->title(); + if (!title.isEmpty()) + return title; + } + } + return String(); +} + +String displayString(const String& string, const Node* node) +{ + if (!node) + return string; + String copy(string); + copy.replace('\\', node->document()->backslashAsCurrencySymbol()); + return copy; +} + +String HitTestResult::altDisplayString() const +{ + if (!m_innerNonSharedNode) + return String(); + + if (m_innerNonSharedNode->hasTagName(imgTag)) { + HTMLImageElement* image = static_cast<HTMLImageElement*>(m_innerNonSharedNode.get()); + return displayString(image->alt(), m_innerNonSharedNode.get()); + } + + if (m_innerNonSharedNode->hasTagName(inputTag)) { + HTMLInputElement* input = static_cast<HTMLInputElement*>(m_innerNonSharedNode.get()); + return displayString(input->alt(), m_innerNonSharedNode.get()); + } + +#if ENABLE(WML) + if (m_innerNonSharedNode->hasTagName(WMLNames::imgTag)) { + WMLImageElement* image = static_cast<WMLImageElement*>(m_innerNonSharedNode.get()); + return displayString(image->altText(), m_innerNonSharedNode.get()); + } +#endif + + return String(); +} + +Image* HitTestResult::image() const +{ + if (!m_innerNonSharedNode) + return 0; + + RenderObject* renderer = m_innerNonSharedNode->renderer(); + if (renderer && renderer->isImage()) { + RenderImage* image = static_cast<WebCore::RenderImage*>(renderer); + if (image->cachedImage() && !image->cachedImage()->errorOccurred()) + return image->cachedImage()->image(); + } + + return 0; +} + +IntRect HitTestResult::imageRect() const +{ + if (!image()) + return IntRect(); + return m_innerNonSharedNode->renderer()->absoluteContentBox(); +} + +KURL HitTestResult::absoluteImageURL() const +{ + if (!(m_innerNonSharedNode && m_innerNonSharedNode->document())) + return KURL(); + + if (!(m_innerNonSharedNode->renderer() && m_innerNonSharedNode->renderer()->isImage())) + return KURL(); + + AtomicString urlString; + if (m_innerNonSharedNode->hasTagName(embedTag) + || m_innerNonSharedNode->hasTagName(imgTag) + || m_innerNonSharedNode->hasTagName(inputTag) + || m_innerNonSharedNode->hasTagName(objectTag) +#if ENABLE(SVG) + || m_innerNonSharedNode->hasTagName(SVGNames::imageTag) +#endif +#if ENABLE(WML) + || m_innerNonSharedNode->hasTagName(WMLNames::imgTag) +#endif + ) { + Element* element = static_cast<Element*>(m_innerNonSharedNode.get()); + urlString = element->getAttribute(element->imageSourceAttributeName()); + } else + return KURL(); + + return m_innerNonSharedNode->document()->completeURL(parseURL(urlString)); +} + +KURL HitTestResult::absoluteLinkURL() const +{ + if (!(m_innerURLElement && m_innerURLElement->document())) + return KURL(); + + AtomicString urlString; + if (m_innerURLElement->hasTagName(aTag) || m_innerURLElement->hasTagName(areaTag) || m_innerURLElement->hasTagName(linkTag)) + urlString = m_innerURLElement->getAttribute(hrefAttr); +#if ENABLE(SVG) + else if (m_innerURLElement->hasTagName(SVGNames::aTag)) + urlString = m_innerURLElement->getAttribute(XLinkNames::hrefAttr); +#endif +#if ENABLE(WML) + else if (m_innerURLElement->hasTagName(WMLNames::aTag)) + urlString = m_innerURLElement->getAttribute(hrefAttr); +#endif + else + return KURL(); + + return m_innerURLElement->document()->completeURL(parseURL(urlString)); +} + +bool HitTestResult::isLiveLink() const +{ + if (!(m_innerURLElement && m_innerURLElement->document())) + return false; + + if (m_innerURLElement->hasTagName(aTag)) + return static_cast<HTMLAnchorElement*>(m_innerURLElement.get())->isLiveLink(); +#if ENABLE(SVG) + if (m_innerURLElement->hasTagName(SVGNames::aTag)) + return m_innerURLElement->isLink(); +#endif +#if ENABLE(WML) + if (m_innerURLElement->hasTagName(WMLNames::aTag)) + return m_innerURLElement->isLink(); +#endif + + return false; +} + +String HitTestResult::titleDisplayString() const +{ + if (!m_innerURLElement) + return String(); + + return displayString(m_innerURLElement->title(), m_innerURLElement.get()); +} + +String HitTestResult::textContent() const +{ + if (!m_innerURLElement) + return String(); + return m_innerURLElement->textContent(); +} + +// FIXME: This function needs a better name and may belong in a different class. It's not +// really isContentEditable(); it's more like needsEditingContextMenu(). In many ways, this +// function would make more sense in the ContextMenu class, except that WebElementDictionary +// hooks into it. Anyway, we should architect this better. +bool HitTestResult::isContentEditable() const +{ + if (!m_innerNonSharedNode) + return false; + + if (m_innerNonSharedNode->hasTagName(textareaTag) || m_innerNonSharedNode->hasTagName(isindexTag)) + return true; + + if (m_innerNonSharedNode->hasTagName(inputTag)) + return static_cast<HTMLInputElement*>(m_innerNonSharedNode.get())->isTextField(); + + return m_innerNonSharedNode->isContentEditable(); +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/HitTestResult.h b/src/3rdparty/webkit/WebCore/rendering/HitTestResult.h new file mode 100644 index 0000000..5ed9b34 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/HitTestResult.h @@ -0,0 +1,94 @@ +/* + * This file is part of the HTML rendering engine for KDE. + * + * Copyright (C) 2006 Apple Computer, Inc. + * + * 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. + * +*/ +#ifndef HitTestResult_h +#define HitTestResult_h + +#include "IntPoint.h" +#include <wtf/RefPtr.h> + +namespace WebCore { + +class Element; +class Frame; +class Image; +class KURL; +class IntRect; +class Node; +class Scrollbar; +class String; + +class HitTestResult { +public: + HitTestResult(const IntPoint&); + HitTestResult(const HitTestResult&); + ~HitTestResult(); + HitTestResult& operator=(const HitTestResult&); + + Node* innerNode() const { return m_innerNode.get(); } + Node* innerNonSharedNode() const { return m_innerNonSharedNode.get(); } + IntPoint point() const { return m_point; } + IntPoint localPoint() const { return m_localPoint; } + Element* URLElement() const { return m_innerURLElement.get(); } + Scrollbar* scrollbar() const { return m_scrollbar.get(); } + bool isOverWidget() const { return m_isOverWidget; } + + void setToNonShadowAncestor(); + + void setInnerNode(Node*); + void setInnerNonSharedNode(Node*); + void setPoint(const IntPoint& p) { m_point = p; } + void setLocalPoint(const IntPoint& p) { m_localPoint = p; } + void setURLElement(Element*); + void setScrollbar(Scrollbar*); + void setIsOverWidget(bool b) { m_isOverWidget = b; } + + Frame* targetFrame() const; + IntRect boundingBox() const; + bool isSelected() const; + String spellingToolTip() const; + String title() const; + String altDisplayString() const; + String titleDisplayString() const; + Image* image() const; + IntRect imageRect() const; + KURL absoluteImageURL() const; + KURL absoluteLinkURL() const; + String textContent() const; + bool isLiveLink() const; + bool isContentEditable() const; + +private: + RefPtr<Node> m_innerNode; + RefPtr<Node> m_innerNonSharedNode; + IntPoint m_point; + IntPoint m_localPoint; // A point in the local coordinate space of m_innerNonSharedNode's renderer. Allows us to efficiently + // determine where inside the renderer we hit on subsequent operations. + RefPtr<Element> m_innerURLElement; + RefPtr<Scrollbar> m_scrollbar; + bool m_isOverWidget; // Returns true if we are over a widget (and not in the border/padding area of a RenderWidget for example). +}; + +String displayString(const String&, const Node*); + +} // namespace WebCore + +#endif // HitTestResult_h diff --git a/src/3rdparty/webkit/WebCore/rendering/InlineBox.cpp b/src/3rdparty/webkit/WebCore/rendering/InlineBox.cpp new file mode 100644 index 0000000..f5d0de5 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/InlineBox.cpp @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * 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" +#include "InlineBox.h" + +#include "HitTestResult.h" +#include "InlineFlowBox.h" +#include "RenderArena.h" +#include "RootInlineBox.h" + +using namespace std; + +namespace WebCore { + +#ifndef NDEBUG +static bool inInlineBoxDetach; +#endif + +#ifndef NDEBUG + +InlineBox::~InlineBox() +{ + if (!m_hasBadParent && m_parent) + m_parent->setHasBadChildList(); +} + +#endif + +void InlineBox::remove() +{ + if (parent()) + parent()->removeChild(this); +} + +void InlineBox::destroy(RenderArena* renderArena) +{ +#ifndef NDEBUG + inInlineBoxDetach = true; +#endif + delete this; +#ifndef NDEBUG + inInlineBoxDetach = false; +#endif + + // Recover the size left there for us by operator delete and free the memory. + renderArena->free(*(size_t *)this, this); +} + +void* InlineBox::operator new(size_t sz, RenderArena* renderArena) throw() +{ + return renderArena->allocate(sz); +} + +void InlineBox::operator delete(void* ptr, size_t sz) +{ + ASSERT(inInlineBoxDetach); + + // Stash size where destroy can find it. + *(size_t *)ptr = sz; +} + +#ifndef NDEBUG +void InlineBox::showTreeForThis() const +{ + if (m_object) + m_object->showTreeForThis(); +} +#endif + +int InlineBox::caretMinOffset() const +{ + return m_object->caretMinOffset(); +} + +int InlineBox::caretMaxOffset() const +{ + return m_object->caretMaxOffset(); +} + +unsigned InlineBox::caretMaxRenderedOffset() const +{ + return 1; +} + +void InlineBox::dirtyLineBoxes() +{ + markDirty(); + for (InlineFlowBox* curr = parent(); curr && !curr->isDirty(); curr = curr->parent()) + curr->markDirty(); +} + +void InlineBox::deleteLine(RenderArena* arena) +{ + if (!m_extracted) + m_object->setInlineBoxWrapper(0); + destroy(arena); +} + +void InlineBox::extractLine() +{ + m_extracted = true; + m_object->setInlineBoxWrapper(0); +} + +void InlineBox::attachLine() +{ + m_extracted = false; + m_object->setInlineBoxWrapper(this); +} + +void InlineBox::adjustPosition(int dx, int dy) +{ + m_x += dx; + m_y += dy; + if (m_object->isReplaced() || m_object->isBR()) + m_object->setPos(m_object->xPos() + dx, m_object->yPos() + dy); +} + +void InlineBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) +{ + if (!object()->shouldPaintWithinRoot(paintInfo) || (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection)) + return; + + // Paint all phases of replaced elements atomically, as though the replaced element established its + // own stacking context. (See Appendix E.2, section 6.4 on inline block/table elements in the CSS2.1 + // specification.) + bool preservePhase = paintInfo.phase == PaintPhaseSelection || paintInfo.phase == PaintPhaseTextClip; + RenderObject::PaintInfo info(paintInfo); + info.phase = preservePhase ? paintInfo.phase : PaintPhaseBlockBackground; + object()->paint(info, tx, ty); + if (!preservePhase) { + info.phase = PaintPhaseChildBlockBackgrounds; + object()->paint(info, tx, ty); + info.phase = PaintPhaseFloat; + object()->paint(info, tx, ty); + info.phase = PaintPhaseForeground; + object()->paint(info, tx, ty); + info.phase = PaintPhaseOutline; + object()->paint(info, tx, ty); + } +} + +bool InlineBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty) +{ + // Hit test all phases of replaced elements atomically, as though the replaced element established its + // own stacking context. (See Appendix E.2, section 6.4 on inline block/table elements in the CSS2.1 + // specification.) + return object()->hitTest(request, result, IntPoint(x, y), tx, ty); +} + +RootInlineBox* InlineBox::root() +{ + if (m_parent) + return m_parent->root(); + ASSERT(isRootInlineBox()); + return static_cast<RootInlineBox*>(this); +} + +bool InlineBox::nextOnLineExists() const +{ + if (!m_determinedIfNextOnLineExists) { + m_determinedIfNextOnLineExists = true; + + if (!parent()) + m_nextOnLineExists = false; + else if (nextOnLine()) + m_nextOnLineExists = true; + else + m_nextOnLineExists = parent()->nextOnLineExists(); + } + return m_nextOnLineExists; +} + +bool InlineBox::prevOnLineExists() const +{ + if (!m_determinedIfPrevOnLineExists) { + m_determinedIfPrevOnLineExists = true; + + if (!parent()) + m_prevOnLineExists = false; + else if (prevOnLine()) + m_prevOnLineExists = true; + else + m_prevOnLineExists = parent()->prevOnLineExists(); + } + return m_prevOnLineExists; +} + +InlineBox* InlineBox::firstLeafChild() +{ + return this; +} + +InlineBox* InlineBox::lastLeafChild() +{ + return this; +} + +InlineBox* InlineBox::nextLeafChild() +{ + return parent() ? parent()->firstLeafChildAfterBox(this) : 0; +} + +InlineBox* InlineBox::prevLeafChild() +{ + return parent() ? parent()->lastLeafChildBeforeBox(this) : 0; +} + +RenderObject::SelectionState InlineBox::selectionState() +{ + return object()->selectionState(); +} + +bool InlineBox::canAccommodateEllipsis(bool ltr, int blockEdge, int ellipsisWidth) +{ + // Non-replaced elements can always accommodate an ellipsis. + if (!m_object || !m_object->isReplaced()) + return true; + + IntRect boxRect(m_x, 0, m_width, 10); + IntRect ellipsisRect(ltr ? blockEdge - ellipsisWidth : blockEdge, 0, ellipsisWidth, 10); + return !(boxRect.intersects(ellipsisRect)); +} + +int InlineBox::placeEllipsisBox(bool, int, int, bool&) +{ + // Use -1 to mean "we didn't set the position." + return -1; +} + +} // namespace WebCore + +#ifndef NDEBUG + +void showTree(const WebCore::InlineBox* b) +{ + if (b) + b->showTreeForThis(); +} + +#endif diff --git a/src/3rdparty/webkit/WebCore/rendering/InlineBox.h b/src/3rdparty/webkit/WebCore/rendering/InlineBox.h new file mode 100644 index 0000000..41ad72a --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/InlineBox.h @@ -0,0 +1,316 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef InlineBox_h +#define InlineBox_h + +#include "RenderObject.h" // needed for RenderObject::PaintInfo +#include "TextDirection.h" + +namespace WebCore { + +class HitTestResult; +class RootInlineBox; + +struct HitTestRequest; + +// InlineBox represents a rectangle that occurs on a line. It corresponds to +// some RenderObject (i.e., it represents a portion of that RenderObject). +class InlineBox { +public: + InlineBox(RenderObject* obj) + : m_object(obj) + , m_x(0) + , m_y(0) + , m_width(0) + , m_height(0) + , m_baseline(0) + , m_next(0) + , m_prev(0) + , m_parent(0) + , m_firstLine(false) + , m_constructed(false) + , m_bidiEmbeddingLevel(0) + , m_dirty(false) + , m_extracted(false) + , m_includeLeftEdge(false) + , m_includeRightEdge(false) + , m_hasTextChildren(true) + , m_endsWithBreak(false) + , m_hasSelectedChildren(false) + , m_hasEllipsisBox(false) + , m_dirOverride(false) + , m_treatAsText(true) + , m_determinedIfNextOnLineExists(false) + , m_determinedIfPrevOnLineExists(false) + , m_nextOnLineExists(false) + , m_prevOnLineExists(false) + , m_toAdd(0) +#ifndef NDEBUG + , m_hasBadParent(false) +#endif + { + } + + InlineBox(RenderObject* obj, int x, int y, int width, int height, int baseline, bool firstLine, bool constructed, + bool dirty, bool extracted, InlineBox* next, InlineBox* prev, InlineFlowBox* parent) + : m_object(obj) + , m_x(x) + , m_y(y) + , m_width(width) + , m_height(height) + , m_baseline(baseline) + , m_next(next) + , m_prev(prev) + , m_parent(parent) + , m_firstLine(firstLine) + , m_constructed(constructed) + , m_bidiEmbeddingLevel(0) + , m_dirty(dirty) + , m_extracted(extracted) + , m_includeLeftEdge(false) + , m_includeRightEdge(false) + , m_hasTextChildren(true) + , m_endsWithBreak(false) + , m_hasSelectedChildren(false) + , m_hasEllipsisBox(false) + , m_dirOverride(false) + , m_treatAsText(true) + , m_determinedIfNextOnLineExists(false) + , m_determinedIfPrevOnLineExists(false) + , m_nextOnLineExists(false) + , m_prevOnLineExists(false) + , m_toAdd(0) +#ifndef NDEBUG + , m_hasBadParent(false) +#endif + { + } + + virtual ~InlineBox(); + + virtual void destroy(RenderArena*); + + virtual void deleteLine(RenderArena*); + virtual void extractLine(); + virtual void attachLine(); + + virtual bool isLineBreak() const { return false; } + + virtual void adjustPosition(int dx, int dy); + + virtual void paint(RenderObject::PaintInfo&, int tx, int ty); + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty); + + // Overloaded new operator. + void* operator new(size_t, RenderArena*) throw(); + + // Overridden to prevent the normal delete from being called. + void operator delete(void*, size_t); + +private: + // The normal operator new is disallowed. + void* operator new(size_t) throw(); + +public: +#ifndef NDEBUG + void showTreeForThis() const; +#endif + virtual bool isInlineBox() { return false; } + virtual bool isInlineFlowBox() { return false; } + virtual bool isContainer() { return false; } + virtual bool isInlineTextBox() { return false; } + virtual bool isRootInlineBox() { return false; } +#if ENABLE(SVG) + virtual bool isSVGRootInlineBox() { return false; } +#endif + virtual bool isText() const { return false; } + + bool isConstructed() { return m_constructed; } + virtual void setConstructed() + { + m_constructed = true; + if (m_next) + m_next->setConstructed(); + } + + void setExtracted(bool b = true) { m_extracted = b; } + + void setFirstLineStyleBit(bool f) { m_firstLine = f; } + bool isFirstLineStyle() const { return m_firstLine; } + + void remove(); + + InlineBox* nextOnLine() const { return m_next; } + InlineBox* prevOnLine() const { return m_prev; } + void setNextOnLine(InlineBox* next) + { + ASSERT(m_parent || !next); + m_next = next; + } + void setPrevOnLine(InlineBox* prev) + { + ASSERT(m_parent || !prev); + m_prev = prev; + } + bool nextOnLineExists() const; + bool prevOnLineExists() const; + + virtual InlineBox* firstLeafChild(); + virtual InlineBox* lastLeafChild(); + InlineBox* nextLeafChild(); + InlineBox* prevLeafChild(); + + RenderObject* object() const { return m_object; } + + InlineFlowBox* parent() const + { + ASSERT(!m_hasBadParent); + return m_parent; + } + void setParent(InlineFlowBox* par) { m_parent = par; } + + RootInlineBox* root(); + + void setWidth(int w) { m_width = w; } + int width() const { return m_width; } + + void setXPos(int x) { m_x = x; } + int xPos() const { return m_x; } + + void setYPos(int y) { m_y = y; } + int yPos() const { return m_y; } + + void setHeight(int h) { m_height = h; } + int height() const { return m_height; } + + void setBaseline(int b) { m_baseline = b; } + int baseline() const { return m_baseline; } + + bool hasTextChildren() const { return m_hasTextChildren; } + + virtual int topOverflow() { return yPos(); } + virtual int bottomOverflow() { return yPos() + height(); } + virtual int leftOverflow() { return xPos(); } + virtual int rightOverflow() { return xPos() + width(); } + + virtual int caretMinOffset() const; + virtual int caretMaxOffset() const; + virtual unsigned caretMaxRenderedOffset() const; + + unsigned char bidiLevel() const { return m_bidiEmbeddingLevel; } + void setBidiLevel(unsigned char level) { m_bidiEmbeddingLevel = level; } + TextDirection direction() const { return m_bidiEmbeddingLevel % 2 ? RTL : LTR; } + int caretLeftmostOffset() const { return direction() == LTR ? caretMinOffset() : caretMaxOffset(); } + int caretRightmostOffset() const { return direction() == LTR ? caretMaxOffset() : caretMinOffset(); } + + virtual void clearTruncation() { } + + bool isDirty() const { return m_dirty; } + void markDirty(bool dirty = true) { m_dirty = dirty; } + + void dirtyLineBoxes(); + + virtual RenderObject::SelectionState selectionState(); + + virtual bool canAccommodateEllipsis(bool ltr, int blockEdge, int ellipsisWidth); + virtual int placeEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth, bool&); + + void setHasBadParent(); + + int toAdd() const { return m_toAdd; } + + bool visibleToHitTesting() const { return object()->style()->visibility() == VISIBLE && object()->style()->pointerEvents() != PE_NONE; } + +public: + RenderObject* m_object; + + int m_x; + int m_y; + int m_width; + int m_height; + int m_baseline; + +private: + InlineBox* m_next; // The next element on the same line as us. + InlineBox* m_prev; // The previous element on the same line as us. + + InlineFlowBox* m_parent; // The box that contains us. + + // Some of these bits are actually for subclasses and moved here to compact the structures. + + // for this class +protected: + bool m_firstLine : 1; +private: + bool m_constructed : 1; + unsigned char m_bidiEmbeddingLevel : 6; +protected: + bool m_dirty : 1; + bool m_extracted : 1; + + // for InlineFlowBox + bool m_includeLeftEdge : 1; + bool m_includeRightEdge : 1; + bool m_hasTextChildren : 1; + + // for RootInlineBox + bool m_endsWithBreak : 1; // Whether the line ends with a <br>. + bool m_hasSelectedChildren : 1; // Whether we have any children selected (this bit will also be set if the <br> that terminates our line is selected). + bool m_hasEllipsisBox : 1; + + // for InlineTextBox +public: + bool m_dirOverride : 1; + bool m_treatAsText : 1; // Whether or not to treat a <br> as text for the purposes of line height. +protected: + mutable bool m_determinedIfNextOnLineExists : 1; + mutable bool m_determinedIfPrevOnLineExists : 1; + mutable bool m_nextOnLineExists : 1; + mutable bool m_prevOnLineExists : 1; + int m_toAdd : 13; // for justified text + +#ifndef NDEBUG +private: + bool m_hasBadParent; +#endif +}; + +#ifdef NDEBUG +inline InlineBox::~InlineBox() +{ +} +#endif + +inline void InlineBox::setHasBadParent() +{ +#ifndef NDEBUG + m_hasBadParent = true; +#endif +} + +} // namespace WebCore + +#ifndef NDEBUG +// Outside the WebCore namespace for ease of invocation from gdb. +void showTree(const WebCore::InlineBox*); +#endif + +#endif // InlineBox_h diff --git a/src/3rdparty/webkit/WebCore/rendering/InlineFlowBox.cpp b/src/3rdparty/webkit/WebCore/rendering/InlineFlowBox.cpp new file mode 100644 index 0000000..b4d240e --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/InlineFlowBox.cpp @@ -0,0 +1,1064 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * 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" +#include "InlineFlowBox.h" + +#include "CachedImage.h" +#include "Document.h" +#include "EllipsisBox.h" +#include "GraphicsContext.h" +#include "InlineTextBox.h" +#include "HitTestResult.h" +#include "RootInlineBox.h" +#include "RenderBlock.h" +#include "RenderFlow.h" +#include "RenderListMarker.h" +#include "RenderTableCell.h" +#include "RootInlineBox.h" +#include "Text.h" + +#include <math.h> + +using namespace std; + +namespace WebCore { + +#ifndef NDEBUG + +InlineFlowBox::~InlineFlowBox() +{ + if (!m_hasBadChildList) + for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) + child->setHasBadParent(); +} + +#endif + +RenderFlow* InlineFlowBox::flowObject() +{ + return static_cast<RenderFlow*>(m_object); +} + +int InlineFlowBox::marginLeft() +{ + if (!includeLeftEdge()) + return 0; + + Length margin = object()->style()->marginLeft(); + if (margin.isAuto()) + return 0; + if (margin.isFixed()) + return margin.value(); + return object()->marginLeft(); +} + +int InlineFlowBox::marginRight() +{ + if (!includeRightEdge()) + return 0; + + Length margin = object()->style()->marginRight(); + if (margin.isAuto()) + return 0; + if (margin.isFixed()) + return margin.value(); + return object()->marginRight(); +} + +int InlineFlowBox::marginBorderPaddingLeft() +{ + return marginLeft() + borderLeft() + paddingLeft(); +} + +int InlineFlowBox::marginBorderPaddingRight() +{ + return marginRight() + borderRight() + paddingRight(); +} + +int InlineFlowBox::getFlowSpacingWidth() +{ + int totWidth = marginBorderPaddingLeft() + marginBorderPaddingRight(); + for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { + if (curr->isInlineFlowBox()) + totWidth += static_cast<InlineFlowBox*>(curr)->getFlowSpacingWidth(); + } + return totWidth; +} + +void InlineFlowBox::addToLine(InlineBox* child) +{ + ASSERT(!child->parent()); + ASSERT(!child->nextOnLine()); + ASSERT(!child->prevOnLine()); + checkConsistency(); + + child->setParent(this); + if (!m_firstChild) { + m_firstChild = child; + m_lastChild = child; + } else { + m_lastChild->setNextOnLine(child); + child->setPrevOnLine(m_lastChild); + m_lastChild = child; + } + child->setFirstLineStyleBit(m_firstLine); + if (child->isText()) + m_hasTextChildren = true; + if (child->object()->selectionState() != RenderObject::SelectionNone) + root()->setHasSelectedChildren(true); + + checkConsistency(); +} + +void InlineFlowBox::removeChild(InlineBox* child) +{ + checkConsistency(); + + if (!m_dirty) + dirtyLineBoxes(); + + root()->childRemoved(child); + + if (child == m_firstChild) + m_firstChild = child->nextOnLine(); + if (child == m_lastChild) + m_lastChild = child->prevOnLine(); + if (child->nextOnLine()) + child->nextOnLine()->setPrevOnLine(child->prevOnLine()); + if (child->prevOnLine()) + child->prevOnLine()->setNextOnLine(child->nextOnLine()); + + child->setParent(0); + + checkConsistency(); +} + +void InlineFlowBox::deleteLine(RenderArena* arena) +{ + InlineBox* child = firstChild(); + InlineBox* next = 0; + while (child) { + ASSERT(this == child->parent()); + next = child->nextOnLine(); +#ifndef NDEBUG + child->setParent(0); +#endif + child->deleteLine(arena); + child = next; + } +#ifndef NDEBUG + m_firstChild = 0; + m_lastChild = 0; +#endif + + static_cast<RenderFlow*>(m_object)->removeLineBox(this); + destroy(arena); +} + +void InlineFlowBox::extractLine() +{ + if (!m_extracted) + static_cast<RenderFlow*>(m_object)->extractLineBox(this); + for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) + child->extractLine(); +} + +void InlineFlowBox::attachLine() +{ + if (m_extracted) + static_cast<RenderFlow*>(m_object)->attachLineBox(this); + for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) + child->attachLine(); +} + +void InlineFlowBox::adjustPosition(int dx, int dy) +{ + InlineRunBox::adjustPosition(dx, dy); + for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) + child->adjustPosition(dx, dy); +} + +bool InlineFlowBox::onEndChain(RenderObject* endObject) +{ + if (!endObject) + return false; + + if (endObject == object()) + return true; + + RenderObject* curr = endObject; + RenderObject* parent = curr->parent(); + while (parent && !parent->isRenderBlock()) { + if (parent->lastChild() != curr || parent == object()) + return false; + + curr = parent; + parent = curr->parent(); + } + + return true; +} + +void InlineFlowBox::determineSpacingForFlowBoxes(bool lastLine, RenderObject* endObject) +{ + // All boxes start off open. They will not apply any margins/border/padding on + // any side. + bool includeLeftEdge = false; + bool includeRightEdge = false; + + RenderFlow* flow = static_cast<RenderFlow*>(object()); + + if (!flow->firstChild()) + includeLeftEdge = includeRightEdge = true; // Empty inlines never split across lines. + else if (parent()) { // The root inline box never has borders/margins/padding. + bool ltr = flow->style()->direction() == LTR; + + // Check to see if all initial lines are unconstructed. If so, then + // we know the inline began on this line. + if (!flow->firstLineBox()->isConstructed()) { + if (ltr && flow->firstLineBox() == this) + includeLeftEdge = true; + else if (!ltr && flow->lastLineBox() == this) + includeRightEdge = true; + } + + // In order to determine if the inline ends on this line, we check three things: + // (1) If we are the last line and we don't have a continuation(), then we can + // close up. + // (2) If the last line box for the flow has an object following it on the line (ltr, + // reverse for rtl), then the inline has closed. + // (3) The line may end on the inline. If we are the last child (climbing up + // the end object's chain), then we just closed as well. + if (!flow->lastLineBox()->isConstructed()) { + if (ltr) { + if (!nextLineBox() && + ((lastLine && !object()->continuation()) || nextOnLineExists() || onEndChain(endObject))) + includeRightEdge = true; + } else { + if ((!prevLineBox() || prevLineBox()->isConstructed()) && + ((lastLine && !object()->continuation()) || prevOnLineExists() || onEndChain(endObject))) + includeLeftEdge = true; + } + } + } + + setEdges(includeLeftEdge, includeRightEdge); + + // Recur into our children. + for (InlineBox* currChild = firstChild(); currChild; currChild = currChild->nextOnLine()) { + if (currChild->isInlineFlowBox()) { + InlineFlowBox* currFlow = static_cast<InlineFlowBox*>(currChild); + currFlow->determineSpacingForFlowBoxes(lastLine, endObject); + } + } +} + +int InlineFlowBox::placeBoxesHorizontally(int x, int& leftPosition, int& rightPosition, bool& needsWordSpacing) +{ + // Set our x position. + setXPos(x); + + int boxShadowLeft = 0; + int boxShadowRight = 0; + for (ShadowData* boxShadow = object()->style(m_firstLine)->boxShadow(); boxShadow; boxShadow = boxShadow->next) { + boxShadowLeft = min(boxShadow->x - boxShadow->blur, boxShadowLeft); + boxShadowRight = max(boxShadow->x + boxShadow->blur, boxShadowRight); + } + leftPosition = min(x + boxShadowLeft, leftPosition); + + int startX = x; + x += borderLeft() + paddingLeft(); + + for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { + if (curr->object()->isText()) { + InlineTextBox* text = static_cast<InlineTextBox*>(curr); + RenderText* rt = static_cast<RenderText*>(text->object()); + if (rt->textLength()) { + if (needsWordSpacing && isSpaceOrNewline(rt->characters()[text->start()])) + x += rt->style(m_firstLine)->font().wordSpacing(); + needsWordSpacing = !isSpaceOrNewline(rt->characters()[text->end()]); + } + text->setXPos(x); + + int strokeOverflow = static_cast<int>(ceilf(rt->style()->textStrokeWidth() / 2.0f)); + + // If letter-spacing is negative, we should factor that into right overflow. (Even in RTL, letter-spacing is + // applied to the right, so this is not an issue with left overflow. + int letterSpacing = min(0, (int)rt->style(m_firstLine)->font().letterSpacing()); + + int leftGlyphOverflow = -strokeOverflow; + int rightGlyphOverflow = strokeOverflow - letterSpacing; + + int visualOverflowLeft = leftGlyphOverflow; + int visualOverflowRight = rightGlyphOverflow; + for (ShadowData* shadow = rt->style()->textShadow(); shadow; shadow = shadow->next) { + visualOverflowLeft = min(visualOverflowLeft, shadow->x - shadow->blur + leftGlyphOverflow); + visualOverflowRight = max(visualOverflowRight, shadow->x + shadow->blur + rightGlyphOverflow); + } + + leftPosition = min(x + visualOverflowLeft, leftPosition); + rightPosition = max(x + text->width() + visualOverflowRight, rightPosition); + m_maxHorizontalVisualOverflow = max(max(visualOverflowRight, -visualOverflowLeft), m_maxHorizontalVisualOverflow); + x += text->width(); + } else { + if (curr->object()->isPositioned()) { + if (curr->object()->parent()->style()->direction() == LTR) + curr->setXPos(x); + else + // Our offset that we cache needs to be from the edge of the right border box and + // not the left border box. We have to subtract |x| from the width of the block + // (which can be obtained from the root line box). + curr->setXPos(root()->object()->width()-x); + continue; // The positioned object has no effect on the width. + } + if (curr->object()->isInlineFlow()) { + InlineFlowBox* flow = static_cast<InlineFlowBox*>(curr); + if (curr->object()->isCompact()) { + int ignoredX = x; + flow->placeBoxesHorizontally(ignoredX, leftPosition, rightPosition, needsWordSpacing); + } else { + x += flow->marginLeft(); + x = flow->placeBoxesHorizontally(x, leftPosition, rightPosition, needsWordSpacing); + x += flow->marginRight(); + } + } else if (!curr->object()->isCompact() && (!curr->object()->isListMarker() || static_cast<RenderListMarker*>(curr->object())->isInside())) { + x += curr->object()->marginLeft(); + curr->setXPos(x); + leftPosition = min(x + curr->object()->overflowLeft(false), leftPosition); + rightPosition = max(x + curr->object()->overflowWidth(false), rightPosition); + x += curr->width() + curr->object()->marginRight(); + } + } + } + + x += borderRight() + paddingRight(); + setWidth(x - startX); + rightPosition = max(xPos() + width() + boxShadowRight, rightPosition); + + return x; +} + +void InlineFlowBox::verticallyAlignBoxes(int& heightOfBlock) +{ + int maxPositionTop = 0; + int maxPositionBottom = 0; + int maxAscent = 0; + int maxDescent = 0; + + // Figure out if we're in strict mode. Note that we can't simply use !style()->htmlHacks(), + // because that would match almost strict mode as well. + RenderObject* curr = object(); + while (curr && !curr->element()) + curr = curr->container(); + bool strictMode = (curr && curr->document()->inStrictMode()); + + computeLogicalBoxHeights(maxPositionTop, maxPositionBottom, maxAscent, maxDescent, strictMode); + + if (maxAscent + maxDescent < max(maxPositionTop, maxPositionBottom)) + adjustMaxAscentAndDescent(maxAscent, maxDescent, maxPositionTop, maxPositionBottom); + + int maxHeight = maxAscent + maxDescent; + int topPosition = heightOfBlock; + int bottomPosition = heightOfBlock; + int selectionTop = heightOfBlock; + int selectionBottom = heightOfBlock; + placeBoxesVertically(heightOfBlock, maxHeight, maxAscent, strictMode, topPosition, bottomPosition, selectionTop, selectionBottom); + + setVerticalOverflowPositions(topPosition, bottomPosition); + setVerticalSelectionPositions(selectionTop, selectionBottom); + + // Shrink boxes with no text children in quirks and almost strict mode. + if (!strictMode) + shrinkBoxesWithNoTextChildren(topPosition, bottomPosition); + + heightOfBlock += maxHeight; +} + +void InlineFlowBox::adjustMaxAscentAndDescent(int& maxAscent, int& maxDescent, + int maxPositionTop, int maxPositionBottom) +{ + for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { + // The computed lineheight needs to be extended for the + // positioned elements + if (curr->object()->isPositioned()) + continue; // Positioned placeholders don't affect calculations. + if (curr->yPos() == PositionTop || curr->yPos() == PositionBottom) { + if (curr->yPos() == PositionTop) { + if (maxAscent + maxDescent < curr->height()) + maxDescent = curr->height() - maxAscent; + } + else { + if (maxAscent + maxDescent < curr->height()) + maxAscent = curr->height() - maxDescent; + } + + if (maxAscent + maxDescent >= max(maxPositionTop, maxPositionBottom)) + break; + } + + if (curr->isInlineFlowBox()) + static_cast<InlineFlowBox*>(curr)->adjustMaxAscentAndDescent(maxAscent, maxDescent, maxPositionTop, maxPositionBottom); + } +} + +void InlineFlowBox::computeLogicalBoxHeights(int& maxPositionTop, int& maxPositionBottom, + int& maxAscent, int& maxDescent, bool strictMode) +{ + if (isRootInlineBox()) { + // Examine our root box. + setHeight(object()->lineHeight(m_firstLine, true)); + bool isTableCell = object()->isTableCell(); + if (isTableCell) { + RenderTableCell* tableCell = static_cast<RenderTableCell*>(object()); + setBaseline(tableCell->RenderBlock::baselinePosition(m_firstLine, true)); + } + else + setBaseline(object()->baselinePosition(m_firstLine, true)); + if (hasTextChildren() || strictMode) { + int ascent = baseline(); + int descent = height() - ascent; + if (maxAscent < ascent) + maxAscent = ascent; + if (maxDescent < descent) + maxDescent = descent; + } + } + + for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { + if (curr->object()->isPositioned()) + continue; // Positioned placeholders don't affect calculations. + + curr->setHeight(curr->object()->lineHeight(m_firstLine)); + curr->setBaseline(curr->object()->baselinePosition(m_firstLine)); + curr->setYPos(curr->object()->verticalPositionHint(m_firstLine)); + if (curr->yPos() == PositionTop) { + if (maxPositionTop < curr->height()) + maxPositionTop = curr->height(); + } + else if (curr->yPos() == PositionBottom) { + if (maxPositionBottom < curr->height()) + maxPositionBottom = curr->height(); + } + else if (curr->hasTextChildren() || curr->object()->hasHorizontalBordersOrPadding() || strictMode) { + int ascent = curr->baseline() - curr->yPos(); + int descent = curr->height() - ascent; + if (maxAscent < ascent) + maxAscent = ascent; + if (maxDescent < descent) + maxDescent = descent; + } + + if (curr->isInlineFlowBox()) + static_cast<InlineFlowBox*>(curr)->computeLogicalBoxHeights(maxPositionTop, maxPositionBottom, maxAscent, maxDescent, strictMode); + } +} + +void InlineFlowBox::placeBoxesVertically(int y, int maxHeight, int maxAscent, bool strictMode, + int& topPosition, int& bottomPosition, int& selectionTop, int& selectionBottom) +{ + if (isRootInlineBox()) + setYPos(y + maxAscent - baseline());// Place our root box. + + for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { + if (curr->object()->isPositioned()) + continue; // Positioned placeholders don't affect calculations. + + // Adjust boxes to use their real box y/height and not the logical height (as dictated by + // line-height). + if (curr->isInlineFlowBox()) + static_cast<InlineFlowBox*>(curr)->placeBoxesVertically(y, maxHeight, maxAscent, strictMode, topPosition, bottomPosition, selectionTop, selectionBottom); + + bool childAffectsTopBottomPos = true; + if (curr->yPos() == PositionTop) + curr->setYPos(y); + else if (curr->yPos() == PositionBottom) + curr->setYPos(y + maxHeight - curr->height()); + else { + if (!curr->hasTextChildren() && !curr->object()->hasHorizontalBordersOrPadding() && !strictMode) + childAffectsTopBottomPos = false; + curr->setYPos(curr->yPos() + y + maxAscent - curr->baseline()); + } + + int newY = curr->yPos(); + int newHeight = curr->height(); + int newBaseline = curr->baseline(); + int overflowTop = 0; + int overflowBottom = 0; + if (curr->isText() || curr->isInlineFlowBox()) { + const Font& font = curr->object()->style(m_firstLine)->font(); + newBaseline = font.ascent(); + newY += curr->baseline() - newBaseline; + newHeight = newBaseline + font.descent(); + for (ShadowData* shadow = curr->object()->style()->textShadow(); shadow; shadow = shadow->next) { + overflowTop = min(overflowTop, shadow->y - shadow->blur); + overflowBottom = max(overflowBottom, shadow->y + shadow->blur); + } + + for (ShadowData* boxShadow = curr->object()->style(m_firstLine)->boxShadow(); boxShadow; boxShadow = boxShadow->next) { + overflowTop = min(overflowTop, boxShadow->y - boxShadow->blur); + overflowBottom = max(overflowBottom, boxShadow->y + boxShadow->blur); + } + + for (ShadowData* textShadow = curr->object()->style(m_firstLine)->textShadow(); textShadow; textShadow = textShadow->next) { + overflowTop = min(overflowTop, textShadow->y - textShadow->blur); + overflowBottom = max(overflowBottom, textShadow->y + textShadow->blur); + } + + if (curr->object()->hasReflection()) { + overflowTop = min(overflowTop, curr->object()->reflectionBox().y()); + overflowBottom = max(overflowBottom, curr->object()->reflectionBox().bottom()); + } + + if (curr->isInlineFlowBox()) { + newHeight += curr->object()->borderTop() + curr->object()->paddingTop() + + curr->object()->borderBottom() + curr->object()->paddingBottom(); + newY -= curr->object()->borderTop() + curr->object()->paddingTop(); + newBaseline += curr->object()->borderTop() + curr->object()->paddingTop(); + } + } else if (!curr->object()->isBR()) { + newY += curr->object()->marginTop(); + newHeight = curr->height() - (curr->object()->marginTop() + curr->object()->marginBottom()); + overflowTop = curr->object()->overflowTop(false); + overflowBottom = curr->object()->overflowHeight(false) - newHeight; + } + + curr->setYPos(newY); + curr->setHeight(newHeight); + curr->setBaseline(newBaseline); + + if (childAffectsTopBottomPos) { + selectionTop = min(selectionTop, newY); + selectionBottom = max(selectionBottom, newY + newHeight); + topPosition = min(topPosition, newY + overflowTop); + bottomPosition = max(bottomPosition, newY + newHeight + overflowBottom); + } + } + + if (isRootInlineBox()) { + const Font& font = object()->style(m_firstLine)->font(); + setHeight(font.ascent() + font.descent()); + setYPos(yPos() + baseline() - font.ascent()); + setBaseline(font.ascent()); + if (hasTextChildren() || strictMode) { + selectionTop = min(selectionTop, yPos()); + selectionBottom = max(selectionBottom, yPos() + height()); + } + } +} + +void InlineFlowBox::shrinkBoxesWithNoTextChildren(int topPos, int bottomPos) +{ + // First shrink our kids. + for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { + if (curr->object()->isPositioned()) + continue; // Positioned placeholders don't affect calculations. + + if (curr->isInlineFlowBox()) + static_cast<InlineFlowBox*>(curr)->shrinkBoxesWithNoTextChildren(topPos, bottomPos); + } + + // See if we have text children. If not, then we need to shrink ourselves to fit on the line. + if (!hasTextChildren() && !object()->hasHorizontalBordersOrPadding()) { + if (yPos() < topPos) + setYPos(topPos); + if (yPos() + height() > bottomPos) + setHeight(bottomPos - yPos()); + if (baseline() > height()) + setBaseline(height()); + } +} + +bool InlineFlowBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty) +{ + // Check children first. + for (InlineBox* curr = lastChild(); curr; curr = curr->prevOnLine()) { + if (!curr->object()->hasLayer() && curr->nodeAtPoint(request, result, x, y, tx, ty)) { + object()->updateHitTestResult(result, IntPoint(x - tx, y - ty)); + return true; + } + } + + // Now check ourselves. + IntRect rect(tx + m_x, ty + m_y, m_width, m_height); + if (visibleToHitTesting() && rect.contains(x, y)) { + object()->updateHitTestResult(result, IntPoint(x - tx, y - ty)); // Don't add in m_x or m_y here, we want coords in the containing block's space. + return true; + } + + return false; +} + +void InlineFlowBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) +{ + int xPos = tx + m_x - object()->maximalOutlineSize(paintInfo.phase); + int w = width() + 2 * object()->maximalOutlineSize(paintInfo.phase); + int shadowLeft = 0; + int shadowRight = 0; + for (ShadowData* boxShadow = object()->style(m_firstLine)->boxShadow(); boxShadow; boxShadow = boxShadow->next) { + shadowLeft = min(boxShadow->x - boxShadow->blur, shadowLeft); + shadowRight = max(boxShadow->x + boxShadow->blur, shadowRight); + } + for (ShadowData* textShadow = object()->style(m_firstLine)->textShadow(); textShadow; textShadow = textShadow->next) { + shadowLeft = min(textShadow->x - textShadow->blur, shadowLeft); + shadowRight = max(textShadow->x + textShadow->blur, shadowRight); + } + xPos += shadowLeft; + w += -shadowLeft + shadowRight; + bool intersectsDamageRect = xPos < paintInfo.rect.right() && xPos + w > paintInfo.rect.x(); + + if (intersectsDamageRect && paintInfo.phase != PaintPhaseChildOutlines) { + if (paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) { + // Add ourselves to the paint info struct's list of inlines that need to paint their + // outlines. + if (object()->style()->visibility() == VISIBLE && object()->hasOutline() && !isRootInlineBox()) { + if ((object()->continuation() || object()->isInlineContinuation()) && !object()->hasLayer()) { + // Add ourselves to the containing block of the entire continuation so that it can + // paint us atomically. + RenderBlock* block = object()->containingBlock()->containingBlock(); + block->addContinuationWithOutline(static_cast<RenderFlow*>(object()->element()->renderer())); + } else if (!object()->isInlineContinuation()) + paintInfo.outlineObjects->add(flowObject()); + } + } else if (paintInfo.phase == PaintPhaseMask) { + paintMask(paintInfo, tx, ty); + return; + } else { + // 1. Paint our background, border and box-shadow. + paintBoxDecorations(paintInfo, tx, ty); + + // 2. Paint our underline and overline. + paintTextDecorations(paintInfo, tx, ty, false); + } + } + + if (paintInfo.phase == PaintPhaseMask) + return; + + PaintPhase paintPhase = paintInfo.phase == PaintPhaseChildOutlines ? PaintPhaseOutline : paintInfo.phase; + RenderObject::PaintInfo childInfo(paintInfo); + childInfo.phase = paintPhase; + childInfo.paintingRoot = object()->paintingRootForChildren(paintInfo); + + // 3. Paint our children. + if (paintPhase != PaintPhaseSelfOutline) { + for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { + if (!curr->object()->hasLayer()) + curr->paint(childInfo, tx, ty); + } + } + + // 4. Paint our strike-through + if (intersectsDamageRect && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection)) + paintTextDecorations(paintInfo, tx, ty, true); +} + +void InlineFlowBox::paintFillLayers(const RenderObject::PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, + int my, int mh, int _tx, int _ty, int w, int h, CompositeOperator op) +{ + if (!fillLayer) + return; + paintFillLayers(paintInfo, c, fillLayer->next(), my, mh, _tx, _ty, w, h, op); + paintFillLayer(paintInfo, c, fillLayer, my, mh, _tx, _ty, w, h, op); +} + +void InlineFlowBox::paintFillLayer(const RenderObject::PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, + int my, int mh, int tx, int ty, int w, int h, CompositeOperator op) +{ + StyleImage* img = fillLayer->image(); + bool hasFillImage = img && img->canRender(object()->style()->effectiveZoom()); + if ((!hasFillImage && !object()->style()->hasBorderRadius()) || (!prevLineBox() && !nextLineBox()) || !parent()) + object()->paintFillLayerExtended(paintInfo, c, fillLayer, my, mh, tx, ty, w, h, this, op); + else { + // We have a fill image that spans multiple lines. + // We need to adjust _tx and _ty by the width of all previous lines. + // Think of background painting on inlines as though you had one long line, a single continuous + // strip. Even though that strip has been broken up across multiple lines, you still paint it + // as though you had one single line. This means each line has to pick up the background where + // the previous line left off. + // FIXME: What the heck do we do with RTL here? The math we're using is obviously not right, + // but it isn't even clear how this should work at all. + int xOffsetOnLine = 0; + for (InlineRunBox* curr = prevLineBox(); curr; curr = curr->prevLineBox()) + xOffsetOnLine += curr->width(); + int startX = tx - xOffsetOnLine; + int totalWidth = xOffsetOnLine; + for (InlineRunBox* curr = this; curr; curr = curr->nextLineBox()) + totalWidth += curr->width(); + paintInfo.context->save(); + paintInfo.context->clip(IntRect(tx, ty, width(), height())); + object()->paintFillLayerExtended(paintInfo, c, fillLayer, my, mh, startX, ty, totalWidth, h, this, op); + paintInfo.context->restore(); + } +} + +void InlineFlowBox::paintBoxShadow(GraphicsContext* context, RenderStyle* s, int tx, int ty, int w, int h) +{ + if ((!prevLineBox() && !nextLineBox()) || !parent()) + object()->paintBoxShadow(context, tx, ty, w, h, s); + else { + // FIXME: We can do better here in the multi-line case. We want to push a clip so that the shadow doesn't + // protrude incorrectly at the edges, and we want to possibly include shadows cast from the previous/following lines + object()->paintBoxShadow(context, tx, ty, w, h, s, includeLeftEdge(), includeRightEdge()); + } +} + +void InlineFlowBox::paintBoxDecorations(RenderObject::PaintInfo& paintInfo, int tx, int ty) +{ + if (!object()->shouldPaintWithinRoot(paintInfo) || object()->style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseForeground) + return; + + // Move x/y to our coordinates. + tx += m_x; + ty += m_y; + + int w = width(); + int h = height(); + + int my = max(ty, paintInfo.rect.y()); + int mh; + if (ty < paintInfo.rect.y()) + mh = max(0, h - (paintInfo.rect.y() - ty)); + else + mh = min(paintInfo.rect.height(), h); + + GraphicsContext* context = paintInfo.context; + + // You can use p::first-line to specify a background. If so, the root line boxes for + // a line may actually have to paint a background. + RenderStyle* styleToUse = object()->style(m_firstLine); + if ((!parent() && m_firstLine && styleToUse != object()->style()) || (parent() && object()->hasBoxDecorations())) { + // Shadow comes first and is behind the background and border. + if (styleToUse->boxShadow()) + paintBoxShadow(context, styleToUse, tx, ty, w, h); + + Color c = styleToUse->backgroundColor(); + paintFillLayers(paintInfo, c, styleToUse->backgroundLayers(), my, mh, tx, ty, w, h); + + // :first-line cannot be used to put borders on a line. Always paint borders with our + // non-first-line style. + if (parent() && object()->style()->hasBorder()) { + StyleImage* borderImage = object()->style()->borderImage().image(); + bool hasBorderImage = borderImage && borderImage->canRender(styleToUse->effectiveZoom()); + if (hasBorderImage && !borderImage->isLoaded()) + return; // Don't paint anything while we wait for the image to load. + + // The simple case is where we either have no border image or we are the only box for this object. In those + // cases only a single call to draw is required. + if (!hasBorderImage || (!prevLineBox() && !nextLineBox())) + object()->paintBorder(context, tx, ty, w, h, object()->style(), includeLeftEdge(), includeRightEdge()); + else { + // We have a border image that spans multiple lines. + // We need to adjust _tx and _ty by the width of all previous lines. + // Think of border image painting on inlines as though you had one long line, a single continuous + // strip. Even though that strip has been broken up across multiple lines, you still paint it + // as though you had one single line. This means each line has to pick up the image where + // the previous line left off. + // FIXME: What the heck do we do with RTL here? The math we're using is obviously not right, + // but it isn't even clear how this should work at all. + int xOffsetOnLine = 0; + for (InlineRunBox* curr = prevLineBox(); curr; curr = curr->prevLineBox()) + xOffsetOnLine += curr->width(); + int startX = tx - xOffsetOnLine; + int totalWidth = xOffsetOnLine; + for (InlineRunBox* curr = this; curr; curr = curr->nextLineBox()) + totalWidth += curr->width(); + context->save(); + context->clip(IntRect(tx, ty, width(), height())); + object()->paintBorder(context, startX, ty, totalWidth, h, object()->style()); + context->restore(); + } + } + } +} + +void InlineFlowBox::paintMask(RenderObject::PaintInfo& paintInfo, int tx, int ty) +{ + if (!object()->shouldPaintWithinRoot(paintInfo) || object()->style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) + return; + + // Move x/y to our coordinates. + tx += m_x; + ty += m_y; + + int w = width(); + int h = height(); + + int my = max(ty, paintInfo.rect.y()); + int mh; + if (ty < paintInfo.rect.y()) + mh = max(0, h - (paintInfo.rect.y() - ty)); + else + mh = min(paintInfo.rect.height(), h); + + + // Figure out if we need to push a transparency layer to render our mask. + bool pushTransparencyLayer = false; + const NinePieceImage& maskNinePieceImage = object()->style()->maskBoxImage(); + StyleImage* maskBoxImage = object()->style()->maskBoxImage().image(); + if ((maskBoxImage && object()->style()->maskLayers()->hasImage()) || object()->style()->maskLayers()->next()) + pushTransparencyLayer = true; + + CompositeOperator compositeOp = CompositeDestinationIn; + if (pushTransparencyLayer) { + paintInfo.context->setCompositeOperation(CompositeDestinationIn); + paintInfo.context->beginTransparencyLayer(1.0f); + compositeOp = CompositeSourceOver; + } + + paintFillLayers(paintInfo, Color(), object()->style()->maskLayers(), my, mh, tx, ty, w, h, compositeOp); + + bool hasBoxImage = maskBoxImage && maskBoxImage->canRender(object()->style()->effectiveZoom()); + if (!hasBoxImage || !maskBoxImage->isLoaded()) + return; // Don't paint anything while we wait for the image to load. + + // The simple case is where we are the only box for this object. In those + // cases only a single call to draw is required. + if (!prevLineBox() && !nextLineBox()) { + object()->paintNinePieceImage(paintInfo.context, tx, ty, w, h, object()->style(), maskNinePieceImage, compositeOp); + } else { + // We have a mask image that spans multiple lines. + // We need to adjust _tx and _ty by the width of all previous lines. + int xOffsetOnLine = 0; + for (InlineRunBox* curr = prevLineBox(); curr; curr = curr->prevLineBox()) + xOffsetOnLine += curr->width(); + int startX = tx - xOffsetOnLine; + int totalWidth = xOffsetOnLine; + for (InlineRunBox* curr = this; curr; curr = curr->nextLineBox()) + totalWidth += curr->width(); + paintInfo.context->save(); + paintInfo.context->clip(IntRect(tx, ty, width(), height())); + object()->paintNinePieceImage(paintInfo.context, startX, ty, totalWidth, h, object()->style(), maskNinePieceImage, compositeOp); + paintInfo.context->restore(); + } + + if (pushTransparencyLayer) + paintInfo.context->endTransparencyLayer(); +} + +static bool shouldDrawTextDecoration(RenderObject* obj) +{ + for (RenderObject* curr = obj->firstChild(); curr; curr = curr->nextSibling()) { + if (curr->isInlineFlow()) + return true; + if (curr->isText() && !curr->isBR()) { + if (!curr->style()->collapseWhiteSpace()) + return true; + Node* currElement = curr->element(); + if (!currElement) + return true; + if (!currElement->isTextNode()) + return true; + if (!static_cast<Text*>(currElement)->containsOnlyWhitespace()) + return true; + } + } + return false; +} + +void InlineFlowBox::paintTextDecorations(RenderObject::PaintInfo& paintInfo, int tx, int ty, bool paintedChildren) +{ + // Paint text decorations like underlines/overlines. We only do this if we aren't in quirks mode (i.e., in + // almost-strict mode or strict mode). + if (object()->style()->htmlHacks() || !object()->shouldPaintWithinRoot(paintInfo) || + object()->style()->visibility() != VISIBLE) + return; + + // We don't want underlines or other decorations when we're trying to draw nothing but the selection as white text. + if (paintInfo.phase == PaintPhaseSelection && paintInfo.forceBlackText) + return; + + GraphicsContext* context = paintInfo.context; + tx += m_x; + ty += m_y; + RenderStyle* styleToUse = object()->style(m_firstLine); + int deco = parent() ? styleToUse->textDecoration() : styleToUse->textDecorationsInEffect(); + if (deco != TDNONE && + ((!paintedChildren && ((deco & UNDERLINE) || (deco & OVERLINE))) || (paintedChildren && (deco & LINE_THROUGH))) && + shouldDrawTextDecoration(object())) { + int x = m_x + borderLeft() + paddingLeft(); + int w = m_width - (borderLeft() + paddingLeft() + borderRight() + paddingRight()); + RootInlineBox* rootLine = root(); + if (rootLine->ellipsisBox()) { + int ellipsisX = rootLine->ellipsisBox()->xPos(); + int ellipsisWidth = rootLine->ellipsisBox()->width(); + + // FIXME: Will need to work with RTL + if (rootLine == this) { + if (x + w >= ellipsisX + ellipsisWidth) + w -= (x + w - ellipsisX - ellipsisWidth); + } else { + if (x >= ellipsisX) + return; + if (x + w >= ellipsisX) + w -= (x + w - ellipsisX); + } + } + + // We must have child boxes and have decorations defined. + tx += borderLeft() + paddingLeft(); + + Color underline, overline, linethrough; + underline = overline = linethrough = styleToUse->color(); + if (!parent()) + object()->getTextDecorationColors(deco, underline, overline, linethrough); + + bool isPrinting = object()->document()->printing(); + context->setStrokeThickness(1.0f); // FIXME: We should improve this rule and not always just assume 1. + + bool paintUnderline = deco & UNDERLINE && !paintedChildren; + bool paintOverline = deco & OVERLINE && !paintedChildren; + bool paintLineThrough = deco & LINE_THROUGH && paintedChildren; + + bool linesAreOpaque = !isPrinting && (!paintUnderline || underline.alpha() == 255) && (!paintOverline || overline.alpha() == 255) && (!paintLineThrough || linethrough.alpha() == 255); + + bool setClip = false; + int extraOffset = 0; + ShadowData* shadow = styleToUse->textShadow(); + if (!linesAreOpaque && shadow && shadow->next) { + IntRect clipRect(tx, ty, w, m_baseline + 2); + for (ShadowData* s = shadow; s; s = s->next) { + IntRect shadowRect(tx, ty, w, m_baseline + 2); + shadowRect.inflate(s->blur); + shadowRect.move(s->x, s->y); + clipRect.unite(shadowRect); + extraOffset = max(extraOffset, max(0, s->y) + s->blur); + } + context->save(); + context->clip(clipRect); + extraOffset += m_baseline + 2; + ty += extraOffset; + setClip = true; + } + + bool setShadow = false; + do { + if (shadow) { + if (!shadow->next) { + // The last set of lines paints normally inside the clip. + ty -= extraOffset; + extraOffset = 0; + } + context->setShadow(IntSize(shadow->x, shadow->y - extraOffset), shadow->blur, shadow->color); + setShadow = true; + shadow = shadow->next; + } + + if (paintUnderline) { + context->setStrokeColor(underline); + // Leave one pixel of white between the baseline and the underline. + context->drawLineForText(IntPoint(tx, ty + m_baseline + 1), w, isPrinting); + } + if (paintOverline) { + context->setStrokeColor(overline); + context->drawLineForText(IntPoint(tx, ty), w, isPrinting); + } + if (paintLineThrough) { + context->setStrokeColor(linethrough); + context->drawLineForText(IntPoint(tx, ty + 2 * m_baseline / 3), w, isPrinting); + } + } while (shadow); + + if (setClip) + context->restore(); + else if (setShadow) + context->clearShadow(); + } +} + +InlineBox* InlineFlowBox::firstLeafChild() +{ + return firstLeafChildAfterBox(); +} + +InlineBox* InlineFlowBox::lastLeafChild() +{ + return lastLeafChildBeforeBox(); +} + +InlineBox* InlineFlowBox::firstLeafChildAfterBox(InlineBox* start) +{ + InlineBox* leaf = 0; + for (InlineBox* box = start ? start->nextOnLine() : firstChild(); box && !leaf; box = box->nextOnLine()) + leaf = box->firstLeafChild(); + if (start && !leaf && parent()) + return parent()->firstLeafChildAfterBox(this); + return leaf; +} + +InlineBox* InlineFlowBox::lastLeafChildBeforeBox(InlineBox* start) +{ + InlineBox* leaf = 0; + for (InlineBox* box = start ? start->prevOnLine() : lastChild(); box && !leaf; box = box->prevOnLine()) + leaf = box->lastLeafChild(); + if (start && !leaf && parent()) + return parent()->lastLeafChildBeforeBox(this); + return leaf; +} + +RenderObject::SelectionState InlineFlowBox::selectionState() +{ + return RenderObject::SelectionNone; +} + +bool InlineFlowBox::canAccommodateEllipsis(bool ltr, int blockEdge, int ellipsisWidth) +{ + for (InlineBox *box = firstChild(); box; box = box->nextOnLine()) { + if (!box->canAccommodateEllipsis(ltr, blockEdge, ellipsisWidth)) + return false; + } + return true; +} + +int InlineFlowBox::placeEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth, bool& foundBox) +{ + int result = -1; + for (InlineBox *box = firstChild(); box; box = box->nextOnLine()) { + int currResult = box->placeEllipsisBox(ltr, blockEdge, ellipsisWidth, foundBox); + if (currResult != -1 && result == -1) + result = currResult; + } + return result; +} + +void InlineFlowBox::clearTruncation() +{ + for (InlineBox *box = firstChild(); box; box = box->nextOnLine()) + box->clearTruncation(); +} + +#ifndef NDEBUG + +void InlineFlowBox::checkConsistency() const +{ +#ifdef CHECK_CONSISTENCY + ASSERT(!m_hasBadChildList); + const InlineBox* prev = 0; + for (const InlineBox* child = m_firstChild; child; child = child->nextOnLine()) { + ASSERT(child->parent() == this); + ASSERT(child->prevOnLine() == prev); + prev = child; + } + ASSERT(prev == m_lastChild); +#endif +} + +#endif + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/InlineFlowBox.h b/src/3rdparty/webkit/WebCore/rendering/InlineFlowBox.h new file mode 100644 index 0000000..30dad38 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/InlineFlowBox.h @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef InlineFlowBox_h +#define InlineFlowBox_h + +#include "InlineRunBox.h" + +namespace WebCore { + +class HitTestResult; + +struct HitTestRequest; + +class InlineFlowBox : public InlineRunBox { +public: + InlineFlowBox(RenderObject* obj) + : InlineRunBox(obj) + , m_firstChild(0) + , m_lastChild(0) + , m_maxHorizontalVisualOverflow(0) +#ifndef NDEBUG + , m_hasBadChildList(false) +#endif + { + // Internet Explorer and Firefox always create a marker for list items, even when the list-style-type is none. We do not make a marker + // in the list-style-type: none case, since it is wasteful to do so. However, in order to match other browsers we have to pretend like + // an invisible marker exists. The side effect of having an invisible marker is that the quirks mode behavior of shrinking lines with no + // text children must not apply. This change also means that gaps will exist between image bullet list items. Even when the list bullet + // is an image, the line is still considered to be immune from the quirk. + m_hasTextChildren = obj->style()->display() == LIST_ITEM; + } + +#ifndef NDEBUG + virtual ~InlineFlowBox(); +#endif + + RenderFlow* flowObject(); + + virtual bool isInlineFlowBox() { return true; } + + InlineFlowBox* prevFlowBox() const { return static_cast<InlineFlowBox*>(m_prevLine); } + InlineFlowBox* nextFlowBox() const { return static_cast<InlineFlowBox*>(m_nextLine); } + + InlineBox* firstChild() { checkConsistency(); return m_firstChild; } + InlineBox* lastChild() { checkConsistency(); return m_lastChild; } + + virtual InlineBox* firstLeafChild(); + virtual InlineBox* lastLeafChild(); + InlineBox* firstLeafChildAfterBox(InlineBox* start = 0); + InlineBox* lastLeafChildBeforeBox(InlineBox* start = 0); + + virtual void setConstructed() + { + InlineBox::setConstructed(); + if (firstChild()) + firstChild()->setConstructed(); + } + + void addToLine(InlineBox* child); + virtual void deleteLine(RenderArena*); + virtual void extractLine(); + virtual void attachLine(); + virtual void adjustPosition(int dx, int dy); + + virtual void clearTruncation(); + + virtual void paintBoxDecorations(RenderObject::PaintInfo&, int tx, int ty); + virtual void paintMask(RenderObject::PaintInfo&, int tx, int ty); + void paintFillLayers(const RenderObject::PaintInfo&, const Color&, const FillLayer*, + int my, int mh, int tx, int ty, int w, int h, CompositeOperator = CompositeSourceOver); + void paintFillLayer(const RenderObject::PaintInfo&, const Color&, const FillLayer*, + int my, int mh, int tx, int ty, int w, int h, CompositeOperator = CompositeSourceOver); + void paintBoxShadow(GraphicsContext*, RenderStyle*, int tx, int ty, int w, int h); + virtual void paintTextDecorations(RenderObject::PaintInfo&, int tx, int ty, bool paintedChildren = false); + virtual void paint(RenderObject::PaintInfo&, int tx, int ty); + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty); + + int marginBorderPaddingLeft(); + int marginBorderPaddingRight(); + int marginLeft(); + int marginRight(); + int borderLeft() { if (includeLeftEdge()) return object()->borderLeft(); return 0; } + int borderRight() { if (includeRightEdge()) return object()->borderRight(); return 0; } + int paddingLeft() { if (includeLeftEdge()) return object()->paddingLeft(); return 0; } + int paddingRight() { if (includeRightEdge()) return object()->paddingRight(); return 0; } + + bool includeLeftEdge() { return m_includeLeftEdge; } + bool includeRightEdge() { return m_includeRightEdge; } + void setEdges(bool includeLeft, bool includeRight) + { + m_includeLeftEdge = includeLeft; + m_includeRightEdge = includeRight; + } + + // Helper functions used during line construction and placement. + void determineSpacingForFlowBoxes(bool lastLine, RenderObject* endObject); + int getFlowSpacingWidth(); + bool onEndChain(RenderObject* endObject); + virtual int placeBoxesHorizontally(int x, int& leftPosition, int& rightPosition, bool& needsWordSpacing); + virtual void verticallyAlignBoxes(int& heightOfBlock); + void computeLogicalBoxHeights(int& maxPositionTop, int& maxPositionBottom, + int& maxAscent, int& maxDescent, bool strictMode); + void adjustMaxAscentAndDescent(int& maxAscent, int& maxDescent, + int maxPositionTop, int maxPositionBottom); + void placeBoxesVertically(int y, int maxHeight, int maxAscent, bool strictMode, + int& topPosition, int& bottomPosition, int& selectionTop, int& selectionBottom); + void shrinkBoxesWithNoTextChildren(int topPosition, int bottomPosition); + + virtual void setVerticalOverflowPositions(int /*top*/, int /*bottom*/) { } + virtual void setVerticalSelectionPositions(int /*top*/, int /*bottom*/) { } + int maxHorizontalVisualOverflow() const { return m_maxHorizontalVisualOverflow; } + + void removeChild(InlineBox* child); + + virtual RenderObject::SelectionState selectionState(); + + virtual bool canAccommodateEllipsis(bool ltr, int blockEdge, int ellipsisWidth); + virtual int placeEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth, bool&); + + void checkConsistency() const; + void setHasBadChildList(); + +private: + InlineBox* m_firstChild; + InlineBox* m_lastChild; + int m_maxHorizontalVisualOverflow; + +#ifndef NDEBUG + bool m_hasBadChildList; +#endif +}; + +#ifdef NDEBUG +inline void InlineFlowBox::checkConsistency() const +{ +} +#endif + +inline void InlineFlowBox::setHasBadChildList() +{ +#ifndef NDEBUG + m_hasBadChildList = true; +#endif +} + +} // namespace WebCore + +#ifndef NDEBUG +// Outside the WebCore namespace for ease of invocation from gdb. +void showTree(const WebCore::InlineBox*); +#endif + +#endif // InlineFlowBox_h diff --git a/src/3rdparty/webkit/WebCore/rendering/InlineRunBox.h b/src/3rdparty/webkit/WebCore/rendering/InlineRunBox.h new file mode 100644 index 0000000..0f7c29b --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/InlineRunBox.h @@ -0,0 +1,54 @@ +/* + * This file is part of the line box implementation for KDE. + * + * Copyright (C) 2003, 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef InlineRunBox_h +#define InlineRunBox_h + +#include "InlineBox.h" + +namespace WebCore { + +class InlineRunBox : public InlineBox { +public: + InlineRunBox(RenderObject* obj) + : InlineBox(obj) + , m_prevLine(0) + , m_nextLine(0) + { + } + + InlineRunBox* prevLineBox() const { return m_prevLine; } + InlineRunBox* nextLineBox() const { return m_nextLine; } + void setNextLineBox(InlineRunBox* n) { m_nextLine = n; } + void setPreviousLineBox(InlineRunBox* p) { m_prevLine = p; } + + virtual void paintBoxDecorations(RenderObject::PaintInfo&, int /*tx*/, int /*ty*/) { } + virtual void paintTextDecorations(RenderObject::PaintInfo&, int /*tx*/, int /*ty*/, bool /*paintedChildren*/ = false) { } + +protected: + InlineRunBox* m_prevLine; // The previous box that also uses our RenderObject + InlineRunBox* m_nextLine; // The next box that also uses our RenderObject +}; + +} // namespace WebCore + +#endif // InlineRunBox_h diff --git a/src/3rdparty/webkit/WebCore/rendering/InlineTextBox.cpp b/src/3rdparty/webkit/WebCore/rendering/InlineTextBox.cpp new file mode 100644 index 0000000..a5857e0 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/InlineTextBox.cpp @@ -0,0 +1,909 @@ +/* + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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" +#include "InlineTextBox.h" + +#include "ChromeClient.h" +#include "Document.h" +#include "Editor.h" +#include "Frame.h" +#include "GraphicsContext.h" +#include "HitTestResult.h" +#include "Page.h" +#include "RenderArena.h" +#include "RenderBlock.h" +#include "RenderTheme.h" +#include "Text.h" +#include "break_lines.h" +#include <wtf/AlwaysInline.h> + +using namespace std; + +namespace WebCore { + +int InlineTextBox::selectionTop() +{ + return root()->selectionTop(); +} + +int InlineTextBox::selectionHeight() +{ + return root()->selectionHeight(); +} + +bool InlineTextBox::isSelected(int startPos, int endPos) const +{ + int sPos = max(startPos - m_start, 0); + int ePos = min(endPos - m_start, (int)m_len); + return (sPos < ePos); +} + +RenderObject::SelectionState InlineTextBox::selectionState() +{ + RenderObject::SelectionState state = object()->selectionState(); + if (state == RenderObject::SelectionStart || state == RenderObject::SelectionEnd || state == RenderObject::SelectionBoth) { + int startPos, endPos; + object()->selectionStartEnd(startPos, endPos); + // The position after a hard line break is considered to be past its end. + int lastSelectable = start() + len() - (isLineBreak() ? 1 : 0); + + bool start = (state != RenderObject::SelectionEnd && startPos >= m_start && startPos < m_start + m_len); + bool end = (state != RenderObject::SelectionStart && endPos > m_start && endPos <= lastSelectable); + if (start && end) + state = RenderObject::SelectionBoth; + else if (start) + state = RenderObject::SelectionStart; + else if (end) + state = RenderObject::SelectionEnd; + else if ((state == RenderObject::SelectionEnd || startPos < m_start) && + (state == RenderObject::SelectionStart || endPos > lastSelectable)) + state = RenderObject::SelectionInside; + else if (state == RenderObject::SelectionBoth) + state = RenderObject::SelectionNone; + } + return state; +} + +IntRect InlineTextBox::selectionRect(int tx, int ty, int startPos, int endPos) +{ + int sPos = max(startPos - m_start, 0); + int ePos = min(endPos - m_start, (int)m_len); + + if (sPos >= ePos) + return IntRect(); + + RenderText* textObj = textObject(); + int selTop = selectionTop(); + int selHeight = selectionHeight(); + const Font& f = textObj->style(m_firstLine)->font(); + + IntRect r = enclosingIntRect(f.selectionRectForText(TextRun(textObj->text()->characters() + m_start, m_len, textObj->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride), + IntPoint(tx + m_x, ty + selTop), selHeight, sPos, ePos)); + if (r.x() > tx + m_x + m_width) + r.setWidth(0); + else if (r.right() - 1 > tx + m_x + m_width) + r.setWidth(tx + m_x + m_width - r.x()); + return r; +} + +void InlineTextBox::deleteLine(RenderArena* arena) +{ + static_cast<RenderText*>(m_object)->removeTextBox(this); + destroy(arena); +} + +void InlineTextBox::extractLine() +{ + if (m_extracted) + return; + + static_cast<RenderText*>(m_object)->extractTextBox(this); +} + +void InlineTextBox::attachLine() +{ + if (!m_extracted) + return; + + static_cast<RenderText*>(m_object)->attachTextBox(this); +} + +int InlineTextBox::placeEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth, bool& foundBox) +{ + if (foundBox) { + m_truncation = cFullTruncation; + return -1; + } + + int ellipsisX = ltr ? blockEdge - ellipsisWidth : blockEdge + ellipsisWidth; + + // For LTR, if the left edge of the ellipsis is to the left of our text run, then we are the run that will get truncated. + if (ltr) { + if (ellipsisX <= m_x) { + // Too far. Just set full truncation, but return -1 and let the ellipsis just be placed at the edge of the box. + m_truncation = cFullTruncation; + foundBox = true; + return -1; + } + + if (ellipsisX < m_x + m_width) { + if (direction() == RTL) + return -1; // FIXME: Support LTR truncation when the last run is RTL someday. + + foundBox = true; + + int offset = offsetForPosition(ellipsisX, false); + if (offset == 0) { + // No characters should be rendered. Set ourselves to full truncation and place the ellipsis at the min of our start + // and the ellipsis edge. + m_truncation = cFullTruncation; + return min(ellipsisX, m_x); + } + + // Set the truncation index on the text run. The ellipsis needs to be placed just after the last visible character. + m_truncation = offset; + return m_x + static_cast<RenderText*>(m_object)->width(m_start, offset, textPos(), m_firstLine); + } + } + else { + // FIXME: Support RTL truncation someday, including both modes (when the leftmost run on the line is either RTL or LTR) + } + return -1; +} + +Color correctedTextColor(Color textColor, Color backgroundColor) +{ + // Adjust the text color if it is too close to the background color, + // by darkening or lightening it to move it further away. + + int d = differenceSquared(textColor, backgroundColor); + // semi-arbitrarily chose 65025 (255^2) value here after a few tests; + if (d > 65025) { + return textColor; + } + + int distanceFromWhite = differenceSquared(textColor, Color::white); + int distanceFromBlack = differenceSquared(textColor, Color::black); + + if (distanceFromWhite < distanceFromBlack) { + return textColor.dark(); + } + + return textColor.light(); +} + +void updateGraphicsContext(GraphicsContext* context, const Color& fillColor, const Color& strokeColor, float strokeThickness) +{ + int mode = context->textDrawingMode(); + if (strokeThickness > 0) { + int newMode = mode | cTextStroke; + if (mode != newMode) { + context->setTextDrawingMode(newMode); + mode = newMode; + } + } + + if (mode & cTextFill && fillColor != context->fillColor()) + context->setFillColor(fillColor); + + if (mode & cTextStroke) { + if (strokeColor != context->strokeColor()) + context->setStrokeColor(strokeColor); + if (strokeThickness != context->strokeThickness()) + context->setStrokeThickness(strokeThickness); + } +} + +bool InlineTextBox::isLineBreak() const +{ + return object()->isBR() || (object()->style()->preserveNewline() && len() == 1 && (*textObject()->text())[start()] == '\n'); +} + +bool InlineTextBox::nodeAtPoint(const HitTestRequest&, HitTestResult& result, int x, int y, int tx, int ty) +{ + if (isLineBreak()) + return false; + + IntRect rect(tx + m_x, ty + m_y, m_width, m_height); + if (m_truncation != cFullTruncation && visibleToHitTesting() && rect.contains(x, y)) { + object()->updateHitTestResult(result, IntPoint(x - tx, y - ty)); + return true; + } + return false; +} + +static void paintTextWithShadows(GraphicsContext* context, const TextRun& textRun, int startOffset, int endOffset, const IntPoint& textOrigin, int x, int y, int w, int h, ShadowData* shadow, bool stroked) +{ + do { + IntSize extraOffset; + + if (shadow) { + IntSize shadowOffset(shadow->x, shadow->y); + int shadowBlur = shadow->blur; + const Color& shadowColor = shadow->color; + + if (shadow->next || stroked) { + IntRect shadowRect(x, y, w, h); + shadowRect.inflate(shadowBlur); + shadowRect.move(shadowOffset); + context->save(); + context->clip(shadowRect); + + extraOffset = IntSize(0, 2 * h + max(0, shadowOffset.height()) + shadowBlur); + shadowOffset -= extraOffset; + } + context->setShadow(shadowOffset, shadowBlur, shadowColor); + } + + if (startOffset <= endOffset) + context->drawText(textRun, textOrigin + extraOffset, startOffset, endOffset); + else { + if (endOffset > 0) + context->drawText(textRun, textOrigin + extraOffset, 0, endOffset); + if (startOffset < textRun.length()) + context->drawText(textRun, textOrigin + extraOffset, startOffset); + } + + if (!shadow) + break; + + if (shadow->next || stroked) + context->restore(); + else + context->clearShadow(); + + shadow = shadow->next; + } while (shadow || stroked); +} + +void InlineTextBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) +{ + if (isLineBreak() || !object()->shouldPaintWithinRoot(paintInfo) || object()->style()->visibility() != VISIBLE || + m_truncation == cFullTruncation || paintInfo.phase == PaintPhaseOutline) + return; + + ASSERT(paintInfo.phase != PaintPhaseSelfOutline && paintInfo.phase != PaintPhaseChildOutlines); + + int xPos = tx + m_x - parent()->maxHorizontalVisualOverflow(); + int w = width() + 2 * parent()->maxHorizontalVisualOverflow(); + if (xPos >= paintInfo.rect.right() || xPos + w <= paintInfo.rect.x()) + return; + + bool isPrinting = textObject()->document()->printing(); + + // Determine whether or not we're selected. + bool haveSelection = !isPrinting && paintInfo.phase != PaintPhaseTextClip && selectionState() != RenderObject::SelectionNone; + if (!haveSelection && paintInfo.phase == PaintPhaseSelection) + // When only painting the selection, don't bother to paint if there is none. + return; + + GraphicsContext* context = paintInfo.context; + + // Determine whether or not we have composition underlines to draw. + bool containsComposition = object()->document()->frame()->editor()->compositionNode() == object()->node(); + bool useCustomUnderlines = containsComposition && object()->document()->frame()->editor()->compositionUsesCustomUnderlines(); + + // Set our font. + RenderStyle* styleToUse = object()->style(m_firstLine); + int d = styleToUse->textDecorationsInEffect(); + const Font* font = &styleToUse->font(); + if (*font != context->font()) + context->setFont(*font); + + // 1. Paint backgrounds behind text if needed. Examples of such backgrounds include selection + // and composition underlines. + if (paintInfo.phase != PaintPhaseSelection && paintInfo.phase != PaintPhaseTextClip && !isPrinting) { +#if PLATFORM(MAC) + // Custom highlighters go behind everything else. + if (styleToUse->highlight() != nullAtom && !context->paintingDisabled()) + paintCustomHighlight(tx, ty, styleToUse->highlight()); +#endif + + if (containsComposition && !useCustomUnderlines) + paintCompositionBackground(context, tx, ty, styleToUse, font, + object()->document()->frame()->editor()->compositionStart(), + object()->document()->frame()->editor()->compositionEnd()); + + paintDocumentMarkers(context, tx, ty, styleToUse, font, true); + + if (haveSelection && !useCustomUnderlines) + paintSelection(context, tx, ty, styleToUse, font); + } + + // 2. Now paint the foreground, including text and decorations like underline/overline (in quirks mode only). + if (m_len <= 0) + return; + + Color textFillColor; + Color textStrokeColor; + float textStrokeWidth = styleToUse->textStrokeWidth(); + ShadowData* textShadow = paintInfo.forceBlackText ? 0 : styleToUse->textShadow(); + + if (paintInfo.forceBlackText) { + textFillColor = Color::black; + textStrokeColor = Color::black; + } else { + textFillColor = styleToUse->textFillColor(); + if (!textFillColor.isValid()) + textFillColor = styleToUse->color(); + + // Make the text fill color legible against a white background + if (styleToUse->forceBackgroundsToWhite()) + textFillColor = correctedTextColor(textFillColor, Color::white); + + textStrokeColor = styleToUse->textStrokeColor(); + if (!textStrokeColor.isValid()) + textStrokeColor = styleToUse->color(); + + // Make the text stroke color legible against a white background + if (styleToUse->forceBackgroundsToWhite()) + textStrokeColor = correctedTextColor(textStrokeColor, Color::white); + } + + bool paintSelectedTextOnly = (paintInfo.phase == PaintPhaseSelection); + bool paintSelectedTextSeparately = false; + + Color selectionFillColor = textFillColor; + Color selectionStrokeColor = textStrokeColor; + float selectionStrokeWidth = textStrokeWidth; + ShadowData* selectionShadow = textShadow; + if (haveSelection) { + // Check foreground color first. + Color foreground = paintInfo.forceBlackText ? Color::black : object()->selectionForegroundColor(); + if (foreground.isValid() && foreground != selectionFillColor) { + if (!paintSelectedTextOnly) + paintSelectedTextSeparately = true; + selectionFillColor = foreground; + } + + if (RenderStyle* pseudoStyle = object()->getCachedPseudoStyle(RenderStyle::SELECTION)) { + ShadowData* shadow = paintInfo.forceBlackText ? 0 : pseudoStyle->textShadow(); + if (shadow != selectionShadow) { + if (!paintSelectedTextOnly) + paintSelectedTextSeparately = true; + selectionShadow = shadow; + } + + float strokeWidth = pseudoStyle->textStrokeWidth(); + if (strokeWidth != selectionStrokeWidth) { + if (!paintSelectedTextOnly) + paintSelectedTextSeparately = true; + selectionStrokeWidth = strokeWidth; + } + + Color stroke = paintInfo.forceBlackText ? Color::black : pseudoStyle->textStrokeColor(); + if (!stroke.isValid()) + stroke = pseudoStyle->color(); + if (stroke != selectionStrokeColor) { + if (!paintSelectedTextOnly) + paintSelectedTextSeparately = true; + selectionStrokeColor = stroke; + } + } + } + + IntPoint textOrigin(m_x + tx, m_y + ty + m_baseline); + TextRun textRun(textObject()->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || styleToUse->visuallyOrdered()); + + int sPos = 0; + int ePos = 0; + if (paintSelectedTextOnly || paintSelectedTextSeparately) + selectionStartEnd(sPos, ePos); + + if (!paintSelectedTextOnly) { + // For stroked painting, we have to change the text drawing mode. It's probably dangerous to leave that mutated as a side + // effect, so only when we know we're stroking, do a save/restore. + if (textStrokeWidth > 0) + context->save(); + + updateGraphicsContext(context, textFillColor, textStrokeColor, textStrokeWidth); + if (!paintSelectedTextSeparately || ePos <= sPos) { + // FIXME: Truncate right-to-left text correctly. + paintTextWithShadows(context, textRun, 0, m_truncation == cNoTruncation ? m_len : m_truncation, textOrigin, m_x + tx, m_y + ty, width(), height(), textShadow, textStrokeWidth > 0); + } else + paintTextWithShadows(context, textRun, ePos, sPos, textOrigin, m_x + tx, m_y + ty, width(), height(), textShadow, textStrokeWidth > 0); + + if (textStrokeWidth > 0) + context->restore(); + } + + if ((paintSelectedTextOnly || paintSelectedTextSeparately) && sPos < ePos) { + // paint only the text that is selected + if (selectionStrokeWidth > 0) + context->save(); + + updateGraphicsContext(context, selectionFillColor, selectionStrokeColor, selectionStrokeWidth); + paintTextWithShadows(context, textRun, sPos, ePos, textOrigin, m_x + tx, m_y + ty, width(), height(), selectionShadow, selectionStrokeWidth > 0); + + if (selectionStrokeWidth > 0) + context->restore(); + } + + // Paint decorations + if (d != TDNONE && paintInfo.phase != PaintPhaseSelection && styleToUse->htmlHacks()) { + context->setStrokeColor(styleToUse->color()); + paintDecoration(context, tx, ty, d, textShadow); + } + + if (paintInfo.phase == PaintPhaseForeground) { + paintDocumentMarkers(context, tx, ty, styleToUse, font, false); + + if (useCustomUnderlines) { + const Vector<CompositionUnderline>& underlines = object()->document()->frame()->editor()->customCompositionUnderlines(); + size_t numUnderlines = underlines.size(); + + for (size_t index = 0; index < numUnderlines; ++index) { + const CompositionUnderline& underline = underlines[index]; + + if (underline.endOffset <= start()) + // underline is completely before this run. This might be an underline that sits + // before the first run we draw, or underlines that were within runs we skipped + // due to truncation. + continue; + + if (underline.startOffset <= end()) { + // underline intersects this run. Paint it. + paintCompositionUnderline(context, tx, ty, underline); + if (underline.endOffset > end() + 1) + // underline also runs into the next run. Bail now, no more marker advancement. + break; + } else + // underline is completely after this run, bail. A later run will paint it. + break; + } + } + } +} + +void InlineTextBox::selectionStartEnd(int& sPos, int& ePos) +{ + int startPos, endPos; + if (object()->selectionState() == RenderObject::SelectionInside) { + startPos = 0; + endPos = textObject()->textLength(); + } else { + textObject()->selectionStartEnd(startPos, endPos); + if (object()->selectionState() == RenderObject::SelectionStart) + endPos = textObject()->textLength(); + else if (object()->selectionState() == RenderObject::SelectionEnd) + startPos = 0; + } + + sPos = max(startPos - m_start, 0); + ePos = min(endPos - m_start, (int)m_len); +} + +void InlineTextBox::paintSelection(GraphicsContext* context, int tx, int ty, RenderStyle* style, const Font*) +{ + // See if we have a selection to paint at all. + int sPos, ePos; + selectionStartEnd(sPos, ePos); + if (sPos >= ePos) + return; + + Color textColor = style->color(); + Color c = object()->selectionBackgroundColor(); + if (!c.isValid() || c.alpha() == 0) + return; + + // If the text color ends up being the same as the selection background, invert the selection + // background. This should basically never happen, since the selection has transparency. + if (textColor == c) + c = Color(0xff - c.red(), 0xff - c.green(), 0xff - c.blue()); + + context->save(); + updateGraphicsContext(context, c, c, 0); // Don't draw text at all! + int y = selectionTop(); + int h = selectionHeight(); + context->clip(IntRect(m_x + tx, y + ty, m_width, h)); + context->drawHighlightForText(TextRun(textObject()->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || style->visuallyOrdered()), + IntPoint(m_x + tx, y + ty), h, c, sPos, ePos); + context->restore(); +} + +void InlineTextBox::paintCompositionBackground(GraphicsContext* context, int tx, int ty, RenderStyle* style, const Font*, int startPos, int endPos) +{ + int offset = m_start; + int sPos = max(startPos - offset, 0); + int ePos = min(endPos - offset, (int)m_len); + + if (sPos >= ePos) + return; + + context->save(); + + Color c = Color(225, 221, 85); + + updateGraphicsContext(context, c, c, 0); // Don't draw text at all! + + int y = selectionTop(); + int h = selectionHeight(); + context->drawHighlightForText(TextRun(textObject()->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || style->visuallyOrdered()), + IntPoint(m_x + tx, y + ty), h, c, sPos, ePos); + context->restore(); +} + +#if PLATFORM(MAC) + +void InlineTextBox::paintCustomHighlight(int tx, int ty, const AtomicString& type) +{ + Frame* frame = object()->document()->frame(); + if (!frame) + return; + Page* page = frame->page(); + if (!page) + return; + + RootInlineBox* r = root(); + FloatRect rootRect(tx + r->xPos(), ty + selectionTop(), r->width(), selectionHeight()); + FloatRect textRect(tx + xPos(), rootRect.y(), width(), rootRect.height()); + + page->chrome()->client()->paintCustomHighlight(object()->node(), type, textRect, rootRect, true, false); +} + +#endif + +void InlineTextBox::paintDecoration(GraphicsContext* context, int tx, int ty, int deco, ShadowData* shadow) +{ + tx += m_x; + ty += m_y; + + if (m_truncation == cFullTruncation) + return; + + int width = (m_truncation == cNoTruncation) ? m_width + : static_cast<RenderText*>(m_object)->width(m_start, m_truncation, textPos(), m_firstLine); + + // Get the text decoration colors. + Color underline, overline, linethrough; + object()->getTextDecorationColors(deco, underline, overline, linethrough, true); + + // Use a special function for underlines to get the positioning exactly right. + bool isPrinting = textObject()->document()->printing(); + context->setStrokeThickness(1.0f); // FIXME: We should improve this rule and not always just assume 1. + + bool linesAreOpaque = !isPrinting && (!(deco & UNDERLINE) || underline.alpha() == 255) && (!(deco & OVERLINE) || overline.alpha() == 255) && (!(deco & LINE_THROUGH) || linethrough.alpha() == 255); + + bool setClip = false; + int extraOffset = 0; + if (!linesAreOpaque && shadow && shadow->next) { + context->save(); + IntRect clipRect(tx, ty, width, m_baseline + 2); + for (ShadowData* s = shadow; s; s = s->next) { + IntRect shadowRect(tx, ty, width, m_baseline + 2); + shadowRect.inflate(s->blur); + shadowRect.move(s->x, s->y); + clipRect.unite(shadowRect); + extraOffset = max(extraOffset, max(0, s->y) + s->blur); + } + context->save(); + context->clip(clipRect); + extraOffset += m_baseline + 2; + ty += extraOffset; + setClip = true; + } + + bool setShadow = false; + do { + if (shadow) { + if (!shadow->next) { + // The last set of lines paints normally inside the clip. + ty -= extraOffset; + extraOffset = 0; + } + context->setShadow(IntSize(shadow->x, shadow->y - extraOffset), shadow->blur, shadow->color); + setShadow = true; + shadow = shadow->next; + } + + if (deco & UNDERLINE) { + context->setStrokeColor(underline); + // Leave one pixel of white between the baseline and the underline. + context->drawLineForText(IntPoint(tx, ty + m_baseline + 1), width, isPrinting); + } + if (deco & OVERLINE) { + context->setStrokeColor(overline); + context->drawLineForText(IntPoint(tx, ty), width, isPrinting); + } + if (deco & LINE_THROUGH) { + context->setStrokeColor(linethrough); + context->drawLineForText(IntPoint(tx, ty + 2 * m_baseline / 3), width, isPrinting); + } + } while (shadow); + + if (setClip) + context->restore(); + else if (setShadow) + context->clearShadow(); +} + +void InlineTextBox::paintSpellingOrGrammarMarker(GraphicsContext* pt, int tx, int ty, DocumentMarker marker, RenderStyle* style, const Font* f, bool grammar) +{ + // Never print spelling/grammar markers (5327887) + if (textObject()->document()->printing()) + return; + + if (m_truncation == cFullTruncation) + return; + + int start = 0; // start of line to draw, relative to tx + int width = m_width; // how much line to draw + + // Determine whether we need to measure text + bool markerSpansWholeBox = true; + if (m_start <= (int)marker.startOffset) + markerSpansWholeBox = false; + if ((end() + 1) != marker.endOffset) // end points at the last char, not past it + markerSpansWholeBox = false; + if (m_truncation != cNoTruncation) + markerSpansWholeBox = false; + + if (!markerSpansWholeBox || grammar) { + int startPosition = max<int>(marker.startOffset - m_start, 0); + int endPosition = min<int>(marker.endOffset - m_start, m_len); + + if (m_truncation != cNoTruncation) + endPosition = min<int>(endPosition, m_truncation); + + // Calculate start & width + IntPoint startPoint(tx + m_x, ty + selectionTop()); + TextRun run(textObject()->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || style->visuallyOrdered()); + int h = selectionHeight(); + + IntRect markerRect = enclosingIntRect(f->selectionRectForText(run, startPoint, h, startPosition, endPosition)); + start = markerRect.x() - startPoint.x(); + width = markerRect.width(); + + // Store rendered rects for bad grammar markers, so we can hit-test against it elsewhere in order to + // display a toolTip. We don't do this for misspelling markers. + if (grammar) + object()->document()->setRenderedRectForMarker(object()->node(), marker, markerRect); + } + + // IMPORTANT: The misspelling underline is not considered when calculating the text bounds, so we have to + // make sure to fit within those bounds. This means the top pixel(s) of the underline will overlap the + // bottom pixel(s) of the glyphs in smaller font sizes. The alternatives are to increase the line spacing (bad!!) + // or decrease the underline thickness. The overlap is actually the most useful, and matches what AppKit does. + // So, we generally place the underline at the bottom of the text, but in larger fonts that's not so good so + // we pin to two pixels under the baseline. + int lineThickness = cMisspellingLineThickness; + int descent = m_height - m_baseline; + int underlineOffset; + if (descent <= (2 + lineThickness)) { + // place the underline at the very bottom of the text in small/medium fonts + underlineOffset = m_height - lineThickness; + } else { + // in larger fonts, tho, place the underline up near the baseline to prevent big gap + underlineOffset = m_baseline + 2; + } + pt->drawLineForMisspellingOrBadGrammar(IntPoint(tx + m_x + start, ty + m_y + underlineOffset), width, grammar); +} + +void InlineTextBox::paintTextMatchMarker(GraphicsContext* pt, int tx, int ty, DocumentMarker marker, RenderStyle* style, const Font* f) +{ + // Use same y positioning and height as for selection, so that when the selection and this highlight are on + // the same word there are no pieces sticking out. + int y = selectionTop(); + int h = selectionHeight(); + + int sPos = max(marker.startOffset - m_start, (unsigned)0); + int ePos = min(marker.endOffset - m_start, (unsigned)m_len); + TextRun run(textObject()->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || style->visuallyOrdered()); + IntPoint startPoint = IntPoint(m_x + tx, y + ty); + + // Always compute and store the rect associated with this marker + IntRect markerRect = enclosingIntRect(f->selectionRectForText(run, startPoint, h, sPos, ePos)); + object()->document()->setRenderedRectForMarker(object()->node(), marker, markerRect); + + // Optionally highlight the text + if (object()->document()->frame()->markedTextMatchesAreHighlighted()) { + Color color = theme()->platformTextSearchHighlightColor(); + pt->save(); + updateGraphicsContext(pt, color, color, 0); // Don't draw text at all! + pt->clip(IntRect(tx + m_x, ty + y, m_width, h)); + pt->drawHighlightForText(run, startPoint, h, color, sPos, ePos); + pt->restore(); + } +} + +void InlineTextBox::paintDocumentMarkers(GraphicsContext* pt, int tx, int ty, RenderStyle* style, const Font* f, bool background) +{ + Vector<DocumentMarker> markers = object()->document()->markersForNode(object()->node()); + Vector<DocumentMarker>::iterator markerIt = markers.begin(); + + // Give any document markers that touch this run a chance to draw before the text has been drawn. + // Note end() points at the last char, not one past it like endOffset and ranges do. + for ( ; markerIt != markers.end(); markerIt++) { + DocumentMarker marker = *markerIt; + + // Paint either the background markers or the foreground markers, but not both + switch (marker.type) { + case DocumentMarker::Grammar: + case DocumentMarker::Spelling: + if (background) + continue; + break; + + case DocumentMarker::TextMatch: + if (!background) + continue; + break; + + default: + ASSERT_NOT_REACHED(); + } + + if (marker.endOffset <= start()) + // marker is completely before this run. This might be a marker that sits before the + // first run we draw, or markers that were within runs we skipped due to truncation. + continue; + + if (marker.startOffset > end()) + // marker is completely after this run, bail. A later run will paint it. + break; + + // marker intersects this run. Paint it. + switch (marker.type) { + case DocumentMarker::Spelling: + paintSpellingOrGrammarMarker(pt, tx, ty, marker, style, f, false); + break; + case DocumentMarker::Grammar: + paintSpellingOrGrammarMarker(pt, tx, ty, marker, style, f, true); + break; + case DocumentMarker::TextMatch: + paintTextMatchMarker(pt, tx, ty, marker, style, f); + break; + default: + ASSERT_NOT_REACHED(); + } + + } +} + + +void InlineTextBox::paintCompositionUnderline(GraphicsContext* ctx, int tx, int ty, const CompositionUnderline& underline) +{ + tx += m_x; + ty += m_y; + + if (m_truncation == cFullTruncation) + return; + + int start = 0; // start of line to draw, relative to tx + int width = m_width; // how much line to draw + bool useWholeWidth = true; + unsigned paintStart = m_start; + unsigned paintEnd = end() + 1; // end points at the last char, not past it + if (paintStart <= underline.startOffset) { + paintStart = underline.startOffset; + useWholeWidth = false; + start = static_cast<RenderText*>(m_object)->width(m_start, paintStart - m_start, textPos(), m_firstLine); + } + if (paintEnd != underline.endOffset) { // end points at the last char, not past it + paintEnd = min(paintEnd, (unsigned)underline.endOffset); + useWholeWidth = false; + } + if (m_truncation != cNoTruncation) { + paintEnd = min(paintEnd, (unsigned)m_start + m_truncation); + useWholeWidth = false; + } + if (!useWholeWidth) { + width = static_cast<RenderText*>(m_object)->width(paintStart, paintEnd - paintStart, textPos() + start, m_firstLine); + } + + // Thick marked text underlines are 2px thick as long as there is room for the 2px line under the baseline. + // All other marked text underlines are 1px thick. + // If there's not enough space the underline will touch or overlap characters. + int lineThickness = 1; + if (underline.thick && m_height - m_baseline >= 2) + lineThickness = 2; + + // We need to have some space between underlines of subsequent clauses, because some input methods do not use different underline styles for those. + // We make each line shorter, which has a harmless side effect of shortening the first and last clauses, too. + start += 1; + width -= 2; + + ctx->setStrokeColor(underline.color); + ctx->setStrokeThickness(lineThickness); + ctx->drawLineForText(IntPoint(tx + start, ty + m_height - lineThickness), width, textObject()->document()->printing()); +} + +int InlineTextBox::caretMinOffset() const +{ + return m_start; +} + +int InlineTextBox::caretMaxOffset() const +{ + return m_start + m_len; +} + +unsigned InlineTextBox::caretMaxRenderedOffset() const +{ + return m_start + m_len; +} + +int InlineTextBox::textPos() const +{ + if (xPos() == 0) + return 0; + + RenderBlock *blockElement = object()->containingBlock(); + return direction() == RTL ? xPos() - blockElement->borderRight() - blockElement->paddingRight() + : xPos() - blockElement->borderLeft() - blockElement->paddingLeft(); +} + +int InlineTextBox::offsetForPosition(int _x, bool includePartialGlyphs) const +{ + if (isLineBreak()) + return 0; + + RenderText* text = static_cast<RenderText*>(m_object); + RenderStyle *style = text->style(m_firstLine); + const Font* f = &style->font(); + return f->offsetForPosition(TextRun(textObject()->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride || style->visuallyOrdered()), + _x - m_x, includePartialGlyphs); +} + +int InlineTextBox::positionForOffset(int offset) const +{ + ASSERT(offset >= m_start); + ASSERT(offset <= m_start + m_len); + + if (isLineBreak()) + return m_x; + + RenderText* text = static_cast<RenderText*>(m_object); + const Font& f = text->style(m_firstLine)->font(); + int from = direction() == RTL ? offset - m_start : 0; + int to = direction() == RTL ? m_len : offset - m_start; + // FIXME: Do we need to add rightBearing here? + return enclosingIntRect(f.selectionRectForText(TextRun(text->text()->characters() + m_start, m_len, textObject()->allowTabs(), textPos(), m_toAdd, direction() == RTL, m_dirOverride), + IntPoint(m_x, 0), 0, from, to)).right(); +} + +bool InlineTextBox::containsCaretOffset(int offset) const +{ + // Offsets before the box are never "in". + if (offset < m_start) + return false; + + int pastEnd = m_start + m_len; + + // Offsets inside the box (not at either edge) are always "in". + if (offset < pastEnd) + return true; + + // Offsets outside the box are always "out". + if (offset > pastEnd) + return false; + + // Offsets at the end are "out" for line breaks (they are on the next line). + if (isLineBreak()) + return false; + + // Offsets at the end are "in" for normal boxes (but the caller has to check affinity). + return true; +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/InlineTextBox.h b/src/3rdparty/webkit/WebCore/rendering/InlineTextBox.h new file mode 100644 index 0000000..d8a250b --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/InlineTextBox.h @@ -0,0 +1,139 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef InlineTextBox_h +#define InlineTextBox_h + +#include "DocumentMarker.h" +#include "InlineRunBox.h" +#include "RenderText.h" + +namespace WebCore { + +const unsigned short cNoTruncation = USHRT_MAX; +const unsigned short cFullTruncation = USHRT_MAX - 1; + +class String; +class StringImpl; +class HitTestResult; +class Position; + +struct CompositionUnderline; + +// Helper functions shared by InlineTextBox / SVGRootInlineBox +void updateGraphicsContext(GraphicsContext* context, const Color& fillColor, const Color& strokeColor, float strokeThickness); +Color correctedTextColor(Color textColor, Color backgroundColor); + +class InlineTextBox : public InlineRunBox { +public: + InlineTextBox(RenderObject* obj) + : InlineRunBox(obj) + , m_start(0) + , m_len(0) + , m_truncation(cNoTruncation) + { + } + + InlineTextBox* nextTextBox() const { return static_cast<InlineTextBox*>(nextLineBox()); } + InlineTextBox* prevTextBox() const { return static_cast<InlineTextBox*>(prevLineBox()); } + + unsigned start() const { return m_start; } + unsigned end() const { return m_len ? m_start + m_len - 1 : m_start; } + unsigned len() const { return m_len; } + + void setStart(unsigned start) { m_start = start; } + void setLen(unsigned len) { m_len = len; } + + void offsetRun(int d) { m_start += d; } + + virtual int selectionTop(); + virtual int selectionHeight(); + + virtual IntRect selectionRect(int absx, int absy, int startPos, int endPos); + bool isSelected(int startPos, int endPos) const; + void selectionStartEnd(int& sPos, int& ePos); + + virtual void paint(RenderObject::PaintInfo&, int tx, int ty); + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty); + + RenderText* textObject() const; + + virtual void deleteLine(RenderArena*); + virtual void extractLine(); + virtual void attachLine(); + + virtual RenderObject::SelectionState selectionState(); + + virtual void clearTruncation() { m_truncation = cNoTruncation; } + virtual int placeEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth, bool& foundBox); + + virtual bool isLineBreak() const; + + void setSpaceAdd(int add) { m_width -= m_toAdd; m_toAdd = add; m_width += m_toAdd; } + int spaceAdd() { return m_toAdd; } + + virtual bool isInlineTextBox() { return true; } + virtual bool isText() const { return m_treatAsText; } + void setIsText(bool b) { m_treatAsText = b; } + + virtual int caretMinOffset() const; + virtual int caretMaxOffset() const; + virtual unsigned caretMaxRenderedOffset() const; + + int textPos() const; + virtual int offsetForPosition(int x, bool includePartialGlyphs = true) const; + virtual int positionForOffset(int offset) const; + + bool containsCaretOffset(int offset) const; // false for offset after line break + + int m_start; + unsigned short m_len; + + unsigned short m_truncation; // Where to truncate when text overflow is applied. We use special constants to + // denote no truncation (the whole run paints) and full truncation (nothing paints at all). + +protected: + void paintCompositionBackground(GraphicsContext*, int tx, int ty, RenderStyle*, const Font*, int startPos, int endPos); + void paintDocumentMarkers(GraphicsContext*, int tx, int ty, RenderStyle*, const Font*, bool background); + void paintCompositionUnderline(GraphicsContext*, int tx, int ty, const CompositionUnderline&); +#if PLATFORM(MAC) + void paintCustomHighlight(int tx, int ty, const AtomicString& type); +#endif + +private: + void paintDecoration(GraphicsContext*, int tx, int ty, int decoration, ShadowData* shadow); + void paintSelection(GraphicsContext*, int tx, int ty, RenderStyle*, const Font*); + void paintSpellingOrGrammarMarker(GraphicsContext*, int tx, int ty, DocumentMarker, RenderStyle*, const Font*, bool grammar); + void paintTextMatchMarker(GraphicsContext*, int tx, int ty, DocumentMarker, RenderStyle*, const Font*); + friend class RenderText; +}; + +inline RenderText* InlineTextBox::textObject() const +{ + return static_cast<RenderText*>(m_object); +} + +} // namespace WebCore + +#endif // InlineTextBox_h diff --git a/src/3rdparty/webkit/WebCore/rendering/LayoutState.cpp b/src/3rdparty/webkit/WebCore/rendering/LayoutState.cpp new file mode 100644 index 0000000..20d0b99 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/LayoutState.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "LayoutState.h" + +#include "RenderArena.h" +#include "RenderLayer.h" +#include "RenderView.h" + +namespace WebCore { + +LayoutState::LayoutState(LayoutState* prev, RenderBox* renderer, const IntSize& offset) +{ + ASSERT(prev); + + m_next = prev; + + bool fixed = renderer->isPositioned() && renderer->style()->position() == FixedPosition; + if (fixed) { + // FIXME: This doesn't work correctly with transforms. + FloatPoint fixedOffset = renderer->view()->localToAbsolute(FloatPoint(), true); + m_offset = IntSize(fixedOffset.x(), fixedOffset.y()) + offset; + } else + m_offset = prev->m_offset + offset; + + if (renderer->isRelPositioned()) { + if (renderer->hasLayer()) + m_offset += renderer->layer()->relativePositionOffset(); + } else if (renderer->isPositioned() && !fixed) { + if (RenderObject* container = renderer->container()) + m_offset += renderer->offsetForPositionedInContainer(container); + } + + m_clipped = !fixed && prev->m_clipped; + if (m_clipped) + m_clipRect = prev->m_clipRect; + if (renderer->hasOverflowClip()) { + int x = m_offset.width(); + int y = m_offset.height(); + RenderLayer* layer = renderer->layer(); + IntRect clipRect(x, y, layer->width(), layer->height()); + clipRect.move(renderer->view()->layoutDelta()); + if (m_clipped) + m_clipRect.intersect(clipRect); + else { + m_clipRect = clipRect; + m_clipped = true; + } + layer->subtractScrolledContentOffset(x, y); + m_offset = IntSize(x, y); + } + // FIXME: <http://bugs.webkit.org/show_bug.cgi?id=13443> Apply control clip if present. +} + +LayoutState::LayoutState(RenderObject* root) + : m_clipped(false) +{ + RenderObject* container = root->container(); + FloatPoint absContentPoint = container->localToAbsoluteForContent(FloatPoint(), false, true); + m_offset = IntSize(absContentPoint.x(), absContentPoint.y()); + m_next = 0; +} + +#ifndef NDEBUG +static bool inLayoutStateDestroy; +#endif + +void LayoutState::destroy(RenderArena* renderArena) +{ +#ifndef NDEBUG + inLayoutStateDestroy = true; +#endif + delete this; +#ifndef NDEBUG + inLayoutStateDestroy = false; +#endif + renderArena->free(*(size_t*)this, this); +} + +void* LayoutState::operator new(size_t sz, RenderArena* renderArena) throw() +{ + return renderArena->allocate(sz); +} + +void LayoutState::operator delete(void* ptr, size_t sz) +{ + ASSERT(inLayoutStateDestroy); + *(size_t*)ptr = sz; +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/LayoutState.h b/src/3rdparty/webkit/WebCore/rendering/LayoutState.h new file mode 100644 index 0000000..551c74a --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/LayoutState.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef LayoutState_h +#define LayoutState_h + +#include "IntRect.h" +#include "IntSize.h" +#include <wtf/Noncopyable.h> + +namespace WebCore { + +class RenderArena; +class RenderBox; +class RenderObject; + +class LayoutState : Noncopyable { +public: + LayoutState() + : m_clipped(false) + , m_next(0) + { + } + + LayoutState(LayoutState*, RenderBox*, const IntSize& offset); + LayoutState(RenderObject*); + + void destroy(RenderArena*); + + // Overloaded new operator. + void* operator new(size_t, RenderArena*) throw(); + + // Overridden to prevent the normal delete from being called. + void operator delete(void*, size_t); + +private: + // The normal operator new is disallowed. + void* operator new(size_t) throw(); + +public: + bool m_clipped; + IntRect m_clipRect; + IntSize m_offset; + LayoutState* m_next; +}; + +} // namespace WebCore + +#endif // LayoutState_h diff --git a/src/3rdparty/webkit/WebCore/rendering/ListMarkerBox.cpp b/src/3rdparty/webkit/WebCore/rendering/ListMarkerBox.cpp new file mode 100644 index 0000000..0455eae --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/ListMarkerBox.cpp @@ -0,0 +1,45 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. + * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net) + * + * 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" +#include "ListMarkerBox.h" + +#include "InlineFlowBox.h" +#include "RenderArena.h" +#include "RenderListMarker.h" + +namespace WebCore { + +ListMarkerBox::ListMarkerBox(RenderObject* obj) + : InlineBox(obj) +{ +} + +bool ListMarkerBox::isText() const +{ + return static_cast<RenderListMarker*>(object())->isText(); +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/ListMarkerBox.h b/src/3rdparty/webkit/WebCore/rendering/ListMarkerBox.h new file mode 100644 index 0000000..47ae256 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/ListMarkerBox.h @@ -0,0 +1,41 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef ListMarkerBox_h +#define ListMarkerBox_h + +#include "InlineBox.h" + +namespace WebCore { + +class ListMarkerBox : public InlineBox { +public: + ListMarkerBox(RenderObject*); + + virtual bool isText() const; +}; + +} // namespace WebCore + +#endif // ListMarkerBox_h diff --git a/src/3rdparty/webkit/WebCore/rendering/MediaControlElements.cpp b/src/3rdparty/webkit/WebCore/rendering/MediaControlElements.cpp new file mode 100644 index 0000000..48ea3ab --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/MediaControlElements.cpp @@ -0,0 +1,254 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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 + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(VIDEO) + +#include "MediaControlElements.h" + +#include "Event.h" +#include "EventNames.h" +#include "EventHandler.h" +#include "FloatConversion.h" +#include "Frame.h" +#include "HTMLNames.h" +#include "RenderSlider.h" +#include "RenderTheme.h" + +namespace WebCore { + +using namespace HTMLNames; + +// FIXME: These constants may need to be tweaked to better match the seeking in the QT plugin +static const float cSeekRepeatDelay = 0.1f; +static const float cStepTime = 0.07f; +static const float cSeekTime = 0.2f; + +MediaControlShadowRootElement::MediaControlShadowRootElement(Document* doc, HTMLMediaElement* mediaElement) + : HTMLDivElement(divTag, doc) + , m_mediaElement(mediaElement) +{ + RefPtr<RenderStyle> rootStyle = RenderStyle::create(); + rootStyle->inheritFrom(mediaElement->renderer()->style()); + rootStyle->setDisplay(BLOCK); + rootStyle->setPosition(RelativePosition); + RenderMediaControlShadowRoot* renderer = new (mediaElement->renderer()->renderArena()) RenderMediaControlShadowRoot(this); + renderer->setParent(mediaElement->renderer()); + renderer->setStyle(rootStyle.release()); + setRenderer(renderer); + setAttached(); + setInDocument(true); +} + +// ---------------------------- + +MediaControlInputElement::MediaControlInputElement(Document* doc, RenderStyle::PseudoId pseudo, const String& type, HTMLMediaElement* mediaElement) + : HTMLInputElement(inputTag, doc) + , m_mediaElement(mediaElement) +{ + setInputType(type); + RenderStyle* style = m_mediaElement->renderer()->getCachedPseudoStyle(pseudo); + RenderObject* renderer = createRenderer(m_mediaElement->renderer()->renderArena(), style); + if (renderer) { + setRenderer(renderer); + renderer->setStyle(style); + } + setAttached(); + setInDocument(true); +} + +void MediaControlInputElement::attachToParent(Element* parent) +{ + parent->addChild(this); + parent->renderer()->addChild(renderer()); +} + +void MediaControlInputElement::update() +{ + if (renderer()) + renderer()->updateFromElement(); +} + +bool MediaControlInputElement::hitTest(const IntPoint& absPoint) +{ + if (renderer() && renderer()->style()->hasAppearance()) + return theme()->hitTestMediaControlPart(renderer(), absPoint); + + return false; +} + +// ---------------------------- + +MediaControlMuteButtonElement::MediaControlMuteButtonElement(Document* doc, HTMLMediaElement* element) + : MediaControlInputElement(doc, RenderStyle::MEDIA_CONTROLS_MUTE_BUTTON, "button", element) +{ +} + +void MediaControlMuteButtonElement::defaultEventHandler(Event* event) +{ + if (event->type() == eventNames().clickEvent) { + m_mediaElement->setMuted(!m_mediaElement->muted()); + event->setDefaultHandled(); + } + HTMLInputElement::defaultEventHandler(event); +} + +// ---------------------------- + +MediaControlPlayButtonElement::MediaControlPlayButtonElement(Document* doc, HTMLMediaElement* element) + : MediaControlInputElement(doc, RenderStyle::MEDIA_CONTROLS_PLAY_BUTTON, "button", element) +{ +} + +void MediaControlPlayButtonElement::defaultEventHandler(Event* event) +{ + if (event->type() == eventNames().clickEvent) { + ExceptionCode ec; + if (m_mediaElement->canPlay()) + m_mediaElement->play(ec); + else + m_mediaElement->pause(ec); + event->setDefaultHandled(); + } + HTMLInputElement::defaultEventHandler(event); +} + +// ---------------------------- + +MediaControlSeekButtonElement::MediaControlSeekButtonElement(Document* doc, HTMLMediaElement* element, bool forward) + : MediaControlInputElement(doc, forward ? RenderStyle::MEDIA_CONTROLS_SEEK_FORWARD_BUTTON : RenderStyle::MEDIA_CONTROLS_SEEK_BACK_BUTTON, "button", element) + , m_forward(forward) + , m_seeking(false) + , m_capturing(false) + , m_seekTimer(this, &MediaControlSeekButtonElement::seekTimerFired) +{ +} + +void MediaControlSeekButtonElement::defaultEventHandler(Event* event) +{ + if (event->type() == eventNames().mousedownEvent) { + if (Frame* frame = document()->frame()) { + m_capturing = true; + frame->eventHandler()->setCapturingMouseEventsNode(this); + } + ExceptionCode ec; + m_mediaElement->pause(ec); + m_seekTimer.startRepeating(cSeekRepeatDelay); + event->setDefaultHandled(); + } else if (event->type() == eventNames().mouseupEvent) { + if (m_capturing) + if (Frame* frame = document()->frame()) { + m_capturing = false; + frame->eventHandler()->setCapturingMouseEventsNode(0); + } + ExceptionCode ec; + if (m_seeking || m_seekTimer.isActive()) { + if (!m_seeking) { + float stepTime = m_forward ? cStepTime : -cStepTime; + m_mediaElement->setCurrentTime(m_mediaElement->currentTime() + stepTime, ec); + } + m_seekTimer.stop(); + m_seeking = false; + event->setDefaultHandled(); + } + } + HTMLInputElement::defaultEventHandler(event); +} + +void MediaControlSeekButtonElement::seekTimerFired(Timer<MediaControlSeekButtonElement>*) +{ + ExceptionCode ec; + m_seeking = true; + float seekTime = m_forward ? cSeekTime : -cSeekTime; + m_mediaElement->setCurrentTime(m_mediaElement->currentTime() + seekTime, ec); +} + +// ---------------------------- + +MediaControlTimelineElement::MediaControlTimelineElement(Document* doc, HTMLMediaElement* element) + : MediaControlInputElement(doc, RenderStyle::MEDIA_CONTROLS_TIMELINE, "range", element) +{ + setAttribute(precisionAttr, "float"); +} + +void MediaControlTimelineElement::defaultEventHandler(Event* event) +{ + RenderSlider* slider = static_cast<RenderSlider*>(renderer()); + bool oldInDragMode = slider && slider->inDragMode(); + float oldTime = narrowPrecisionToFloat(value().toDouble()); + bool oldEnded = m_mediaElement->ended(); + + HTMLInputElement::defaultEventHandler(event); + + float time = narrowPrecisionToFloat(value().toDouble()); + if (oldTime != time || event->type() == eventNames().inputEvent) { + ExceptionCode ec; + m_mediaElement->setCurrentTime(time, ec); + } + // Media element stays in non-paused state when it reaches end. If the slider is now dragged + // to some other position the playback resumes which does not match usual media player UIs. + // Get the expected behavior by pausing explicitly in this case. + if (oldEnded && !m_mediaElement->ended() && !m_mediaElement->paused()) { + ExceptionCode ec; + m_mediaElement->pause(ec); + } + // Pause playback during drag, but do it without using DOM API which would generate events + bool inDragMode = slider && slider->inDragMode(); + if (inDragMode != oldInDragMode) + m_mediaElement->setPausedInternal(inDragMode); +} + +void MediaControlTimelineElement::update(bool updateDuration) +{ + if (updateDuration) { + float dur = m_mediaElement->duration(); + setAttribute(maxAttr, String::number(isfinite(dur) ? dur : 0)); + } + setValue(String::number(m_mediaElement->currentTime())); +} + +// ---------------------------- + +MediaControlFullscreenButtonElement::MediaControlFullscreenButtonElement(Document* doc, HTMLMediaElement* element) + : MediaControlInputElement(doc, RenderStyle::MEDIA_CONTROLS_FULLSCREEN_BUTTON, "button", element) +{ +} + +void MediaControlFullscreenButtonElement::defaultEventHandler(Event* event) +{ + if (event->type() == eventNames().clickEvent) { + event->setDefaultHandled(); + } + HTMLInputElement::defaultEventHandler(event); +} + +// ---------------------------- + +} //namespace WebCore +#endif // enable(video) diff --git a/src/3rdparty/webkit/WebCore/rendering/MediaControlElements.h b/src/3rdparty/webkit/WebCore/rendering/MediaControlElements.h new file mode 100644 index 0000000..4741534 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/MediaControlElements.h @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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 + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MediaControlElements_h +#define MediaControlElements_h + +#if ENABLE(VIDEO) + +#include "HTMLDivElement.h" +#include "HTMLInputElement.h" +#include "HTMLMediaElement.h" +#include "RenderBlock.h" + +// These are the shadow elements used in RenderMedia + +namespace WebCore { + +class Event; +class Frame; + +enum MediaControlElements { + MediaFullscreenButton, MediaMuteButton, MediaPlayButton, + MediaSeekBackButton, MediaSeekForwardButton, MediaSlider, MediaSliderThumb, + MediaUnMuteButton, MediaPauseButton, MediaTimelineContainer, MediaCurrentTimeDisplay, + MediaTimeRemainingDisplay, MediaControlsPanel +}; + +class MediaControlShadowRootElement : public HTMLDivElement { +public: + MediaControlShadowRootElement(Document*, HTMLMediaElement*); + + virtual bool isShadowNode() const { return true; } + virtual Node* shadowParentNode() { return m_mediaElement; } + +private: + HTMLMediaElement* m_mediaElement; +}; + +// ---------------------------- + +class MediaControlInputElement : public HTMLInputElement { +public: + MediaControlInputElement(Document*, RenderStyle::PseudoId, const String& type, HTMLMediaElement*); + void attachToParent(Element*); + void update(); + bool hitTest(const IntPoint& absPoint); +protected: + HTMLMediaElement* m_mediaElement; +}; + +// ---------------------------- + +class MediaControlMuteButtonElement : public MediaControlInputElement { +public: + MediaControlMuteButtonElement(Document*, HTMLMediaElement*); + virtual void defaultEventHandler(Event*); +}; + +// ---------------------------- + +class MediaControlPlayButtonElement : public MediaControlInputElement { +public: + MediaControlPlayButtonElement(Document*, HTMLMediaElement*); + virtual void defaultEventHandler(Event*); +}; + +// ---------------------------- + +class MediaControlSeekButtonElement : public MediaControlInputElement { +public: + MediaControlSeekButtonElement(Document*, HTMLMediaElement*, bool forward); + virtual void defaultEventHandler(Event*); + void seekTimerFired(Timer<MediaControlSeekButtonElement>*); + +private: + bool m_forward; + bool m_seeking; + bool m_capturing; + Timer<MediaControlSeekButtonElement> m_seekTimer; +}; + +// ---------------------------- + +class MediaControlTimelineElement : public MediaControlInputElement { +public: + MediaControlTimelineElement(Document*, HTMLMediaElement*); + virtual void defaultEventHandler(Event*); + void update(bool updateDuration = true); +}; + +// ---------------------------- + +class MediaControlFullscreenButtonElement : public MediaControlInputElement { +public: + MediaControlFullscreenButtonElement(Document*, HTMLMediaElement*); + virtual void defaultEventHandler(Event*); +}; + +// ---------------------------- + +class RenderMediaControlShadowRoot : public RenderBlock { +public: + RenderMediaControlShadowRoot(Element* e) : RenderBlock(e) { } + void setParent(RenderObject* p) { RenderObject::setParent(p); } +}; + +// ---------------------------- + +} //namespace WebCore +#endif // enable(video) +#endif // MediaControlElements_h diff --git a/src/3rdparty/webkit/WebCore/rendering/PointerEventsHitRules.cpp b/src/3rdparty/webkit/WebCore/rendering/PointerEventsHitRules.cpp new file mode 100644 index 0000000..214fb09 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/PointerEventsHitRules.cpp @@ -0,0 +1,110 @@ +/* + Copyright (C) 2007 Rob Buis <buis@kde.org> + + This file is part of the KDE project + + 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 + aint 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" +#include "PointerEventsHitRules.h" + +namespace WebCore { + +PointerEventsHitRules::PointerEventsHitRules(EHitTesting hitTesting, EPointerEvents pointerEvents) + : requireVisible(false) + , requireFill(false) + , requireStroke(false) + , canHitStroke(false) + , canHitFill(false) +{ + if (hitTesting == SVG_PATH_HITTESTING) { + switch (pointerEvents) + { + case PE_VISIBLE_PAINTED: + case PE_AUTO: // "auto" is like "visiblePainted" when in SVG content + requireFill = true; + requireStroke = true; + case PE_VISIBLE: + requireVisible = true; + canHitFill = true; + canHitStroke = true; + break; + case PE_VISIBLE_FILL: + requireVisible = true; + canHitFill = true; + break; + case PE_VISIBLE_STROKE: + requireVisible = true; + canHitStroke = true; + break; + case PE_PAINTED: + requireFill = true; + requireStroke = true; + case PE_ALL: + canHitFill = true; + canHitStroke = true; + break; + case PE_FILL: + canHitFill = true; + break; + case PE_STROKE: + canHitStroke = true; + break; + case PE_NONE: + // nothing to do here, defaults are all false. + break; + } + } else { + switch (pointerEvents) + { + case PE_VISIBLE_PAINTED: + case PE_AUTO: // "auto" is like "visiblePainted" when in SVG content + requireVisible = true; + requireFill = true; + requireStroke = true; + canHitFill = true; + canHitStroke = true; + break; + case PE_VISIBLE_FILL: + case PE_VISIBLE_STROKE: + case PE_VISIBLE: + requireVisible = true; + canHitFill = true; + canHitStroke = true; + break; + case PE_PAINTED: + requireFill = true; + requireStroke = true; + canHitFill = true; + canHitStroke = true; + break; + case PE_FILL: + case PE_STROKE: + case PE_ALL: + canHitFill = true; + canHitStroke = true; + break; + case PE_NONE: + // nothing to do here, defaults are all false. + break; + } + } +} + +} + +// vim:ts=4:noet diff --git a/src/3rdparty/webkit/WebCore/rendering/PointerEventsHitRules.h b/src/3rdparty/webkit/WebCore/rendering/PointerEventsHitRules.h new file mode 100644 index 0000000..e2dae3b --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/PointerEventsHitRules.h @@ -0,0 +1,50 @@ +/* + Copyright (C) 2007 Rob Buis <buis@kde.org> + + This file is part of the KDE project + + 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 + aint 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. +*/ + +#ifndef PointerEventsHitRules_h +#define PointerEventsHitRules_h + +#include "RenderStyle.h" + +namespace WebCore { + +class PointerEventsHitRules { +public: + enum EHitTesting { + SVG_IMAGE_HITTESTING, + SVG_PATH_HITTESTING, + SVG_TEXT_HITTESTING + }; + + PointerEventsHitRules(EHitTesting, EPointerEvents); + + bool requireVisible; + bool requireFill; + bool requireStroke; + bool canHitStroke; + bool canHitFill; +}; + +} + +#endif + +// vim:ts=4:noet diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderApplet.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderApplet.cpp new file mode 100644 index 0000000..7483943 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderApplet.cpp @@ -0,0 +1,98 @@ +/** + * This file is part of the HTML widget for KDE. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * Copyright (C) 2003, 2006 Apple Computer, Inc. + * + * 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" +#include "RenderApplet.h" + +#include "Document.h" +#include "Frame.h" +#include "FrameLoader.h" +#include "HTMLAppletElement.h" +#include "HTMLNames.h" +#include "HTMLParamElement.h" +#include "Widget.h" + +namespace WebCore { + +using namespace HTMLNames; + +RenderApplet::RenderApplet(HTMLAppletElement* applet, const HashMap<String, String>& args) + : RenderWidget(applet) + , m_args(args) +{ + setInline(true); +} + +RenderApplet::~RenderApplet() +{ +} + +IntSize RenderApplet::intrinsicSize() const +{ + // FIXME: This doesn't make sense. We can't just start returning + // a different size once we've created the widget and expect + // layout and sizing to be correct. We should remove this and + // pass the appropriate intrinsic size in the constructor. + return m_widget ? IntSize(50, 50) : IntSize(150, 150); +} + +void RenderApplet::createWidgetIfNecessary() +{ + HTMLAppletElement* element = static_cast<HTMLAppletElement*>(node()); + if (m_widget || !element->isFinishedParsingChildren()) + return; + + // FIXME: Java applets can't be resized (this is a bug in Apple's Java implementation). + // In order to work around this problem and have a correct size from the start, we will + // use fixed widths/heights from the style system when we can, since the widget might + // not have an accurate m_width/m_height. + int width = style()->width().isFixed() ? style()->width().value() : + m_width - borderLeft() - borderRight() - paddingLeft() - paddingRight(); + int height = style()->height().isFixed() ? style()->height().value() : + m_height - borderTop() - borderBottom() - paddingTop() - paddingBottom(); + for (Node* child = element->firstChild(); child; child = child->nextSibling()) { + if (child->hasTagName(paramTag)) { + HTMLParamElement* p = static_cast<HTMLParamElement*>(child); + if (!p->name().isEmpty()) + m_args.set(p->name(), p->value()); + } + } + + Frame* frame = document()->frame(); + ASSERT(frame); + setWidget(frame->loader()->createJavaAppletWidget(IntSize(width, height), element, m_args)); +} + +void RenderApplet::layout() +{ + ASSERT(needsLayout()); + + calcWidth(); + calcHeight(); + + // The applet's widget gets created lazily upon first layout. + createWidgetIfNecessary(); + setNeedsLayout(false); +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderApplet.h b/src/3rdparty/webkit/WebCore/rendering/RenderApplet.h new file mode 100644 index 0000000..6746c22 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderApplet.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef RenderApplet_h +#define RenderApplet_h + +#include "RenderWidget.h" +#include "StringHash.h" + +namespace WebCore { + + class HTMLAppletElement; + + class RenderApplet : public RenderWidget { + public: + RenderApplet(HTMLAppletElement*, const HashMap<String, String>& args); + virtual ~RenderApplet(); + + virtual const char* renderName() const { return "RenderApplet"; } + + virtual bool isApplet() const { return true; } + + virtual void layout(); + virtual IntSize intrinsicSize() const; + + void createWidgetIfNecessary(); + + private: + HashMap<String, String> m_args; + }; + +} // namespace WebCore + +#endif // RenderApplet_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderArena.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderArena.cpp new file mode 100644 index 0000000..69d08a5 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderArena.cpp @@ -0,0 +1,135 @@ +/* + * Copyright (C) 2003 Apple Computer, Inc. + * + * Portions are Copyright (C) 1998 Netscape Communications Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Alternatively, the contents of this file may be used under the terms + * of either the Mozilla Public License Version 1.1, found at + * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public + * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html + * (the "GPL"), in which case the provisions of the MPL or the GPL are + * applicable instead of those above. If you wish to allow use of your + * version of this file only under the terms of one of those two + * licenses (the MPL or the GPL) and not to allow others to use your + * version of this file under the LGPL, indicate your decision by + * deletingthe provisions above and replace them with the notice and + * other provisions required by the MPL or the GPL, as the case may be. + * If you do not delete the provisions above, a recipient may use your + * version of this file under any of the LGPL, the MPL or the GPL. + */ + +#include "config.h" +#include "RenderArena.h" + +#include <stdlib.h> +#include <string.h> +#include <wtf/Assertions.h> + +#define ROUNDUP(x,y) ((((x)+((y)-1))/(y))*(y)) + +namespace WebCore { + +#ifndef NDEBUG + +const int signature = 0xDBA00AEA; +const int signatureDead = 0xDBA00AED; + +typedef struct { + RenderArena* arena; + size_t size; + int signature; +} RenderArenaDebugHeader; + +#endif + +RenderArena::RenderArena(unsigned arenaSize) +{ + // Initialize the arena pool + INIT_ARENA_POOL(&m_pool, "RenderArena", arenaSize); + + // Zero out the recyclers array + memset(m_recyclers, 0, sizeof(m_recyclers)); +} + +RenderArena::~RenderArena() +{ + FinishArenaPool(&m_pool); +} + +void* RenderArena::allocate(size_t size) +{ +#ifndef NDEBUG + // Use standard malloc so that memory debugging tools work. + ASSERT(this); + void* block = ::malloc(sizeof(RenderArenaDebugHeader) + size); + RenderArenaDebugHeader* header = (RenderArenaDebugHeader*)block; + header->arena = this; + header->size = size; + header->signature = signature; + return header + 1; +#else + void* result = 0; + + // Ensure we have correct alignment for pointers. Important for Tru64 + size = ROUNDUP(size, sizeof(void*)); + + // Check recyclers first + if (size < gMaxRecycledSize) { + const int index = size >> 2; + + result = m_recyclers[index]; + if (result) { + // Need to move to the next object + void* next = *((void**)result); + m_recyclers[index] = next; + } + } + + if (!result) { + // Allocate a new chunk from the arena + ARENA_ALLOCATE(result, &m_pool, size); + } + + return result; +#endif +} + +void RenderArena::free(size_t size, void* ptr) +{ +#ifndef NDEBUG + // Use standard free so that memory debugging tools work. + RenderArenaDebugHeader* header = (RenderArenaDebugHeader*)ptr - 1; + ASSERT(header->signature == signature); + ASSERT(header->size == size); + ASSERT(header->arena == this); + header->signature = signatureDead; + ::free(header); +#else + // Ensure we have correct alignment for pointers. Important for Tru64 + size = ROUNDUP(size, sizeof(void*)); + + // See if it's a size that we recycle + if (size < gMaxRecycledSize) { + const int index = size >> 2; + void* currentTop = m_recyclers[index]; + m_recyclers[index] = ptr; + *((void**)ptr) = currentTop; + } +#endif +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderArena.h b/src/3rdparty/webkit/WebCore/rendering/RenderArena.h new file mode 100644 index 0000000..3c27d15 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderArena.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2003 Apple Computer, Inc. + * + * Portions are Copyright (C) 1998 Netscape Communications Corporation. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Alternatively, the contents of this file may be used under the terms + * of either the Mozilla Public License Version 1.1, found at + * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public + * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html + * (the "GPL"), in which case the provisions of the MPL or the GPL are + * applicable instead of those above. If you wish to allow use of your + * version of this file only under the terms of one of those two + * licenses (the MPL or the GPL) and not to allow others to use your + * version of this file under the LGPL, indicate your decision by + * deletingthe provisions above and replace them with the notice and + * other provisions required by the MPL or the GPL, as the case may be. + * If you do not delete the provisions above, a recipient may use your + * version of this file under any of the LGPL, the MPL or the GPL. + */ + +#ifndef RenderArena_h +#define RenderArena_h + +#include "Arena.h" + +namespace WebCore { + +static const size_t gMaxRecycledSize = 400; + +class RenderArena { +public: + RenderArena(unsigned arenaSize = 4096); + ~RenderArena(); + + // Memory management functions + void* allocate(size_t); + void free(size_t, void*); + +private: + // Underlying arena pool + ArenaPool m_pool; + + // The recycler array is sparse with the indices being multiples of 4, + // i.e., 0, 4, 8, 12, 16, 20, ... + void* m_recyclers[gMaxRecycledSize >> 2]; +}; + +} // namespace WebCore + +#endif // RenderArena_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderBR.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderBR.cpp new file mode 100644 index 0000000..2532c5b --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderBR.cpp @@ -0,0 +1,111 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * Copyright (C) 2006 Apple Computer, Inc. + * + * 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" +#include "RenderBR.h" + +#include "Document.h" +#include "InlineTextBox.h" +#include "VisiblePosition.h" + +namespace WebCore { + +RenderBR::RenderBR(Node* node) + : RenderText(node, StringImpl::create("\n")) + , m_lineHeight(-1) +{ +} + +RenderBR::~RenderBR() +{ +} + +InlineBox* RenderBR::createInlineBox(bool makePlaceholder, bool isRootLineBox, bool isOnlyRun) +{ + // We only treat a box as text for a <br> if we are on a line by ourself or in strict mode + // (Note the use of strict mode. In "almost strict" mode, we don't treat the box for <br> as text.) + InlineTextBox* box = static_cast<InlineTextBox*>(RenderText::createInlineBox(makePlaceholder, isRootLineBox, isOnlyRun)); + box->setIsText(isOnlyRun || document()->inStrictMode()); + return box; +} + +int RenderBR::baselinePosition(bool firstLine, bool isRootLineBox) const +{ + if (firstTextBox() && !firstTextBox()->isText()) + return 0; + return RenderText::baselinePosition(firstLine, isRootLineBox); +} + +int RenderBR::lineHeight(bool firstLine, bool /*isRootLineBox*/) const +{ + if (firstTextBox() && !firstTextBox()->isText()) + return 0; + + if (firstLine) { + RenderStyle* s = style(firstLine); + Length lh = s->lineHeight(); + if (lh.isNegative()) { + if (s == style()) { + if (m_lineHeight == -1) + m_lineHeight = RenderObject::lineHeight(false); + return m_lineHeight; + } + return s->font().lineSpacing(); + } + if (lh.isPercent()) + return lh.calcMinValue(s->fontSize()); + return lh.value(); + } + + if (m_lineHeight == -1) + m_lineHeight = RenderObject::lineHeight(false); + return m_lineHeight; +} + +void RenderBR::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +{ + RenderText::styleDidChange(diff, oldStyle); + m_lineHeight = -1; +} + +int RenderBR::caretMinOffset() const +{ + return 0; +} + +int RenderBR::caretMaxOffset() const +{ + return 1; +} + +unsigned RenderBR::caretMaxRenderedOffset() const +{ + return 1; +} + +VisiblePosition RenderBR::positionForCoordinates(int /*x*/, int /*y*/) +{ + return VisiblePosition(element(), 0, DOWNSTREAM); +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderBR.h b/src/3rdparty/webkit/WebCore/rendering/RenderBR.h new file mode 100644 index 0000000..b65bb58 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderBR.h @@ -0,0 +1,71 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * + * 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. + * + */ + +#ifndef RenderBR_h +#define RenderBR_h + +#include "RenderText.h" + +/* + * The whole class here is a hack to get <br> working, as long as we don't have support for + * CSS2 :before and :after pseudo elements + */ +namespace WebCore { + +class Position; + +class RenderBR : public RenderText { +public: + RenderBR(Node*); + virtual ~RenderBR(); + + virtual const char* renderName() const { return "RenderBR"; } + + virtual IntRect selectionRect(bool) { return IntRect(); } + + virtual unsigned width(unsigned /*from*/, unsigned /*len*/, const Font&, int /*xpos*/) const { return 0; } + virtual unsigned width(unsigned /*from*/, unsigned /*len*/, int /*xpos*/, bool /*firstLine = false*/) const { return 0; } + + virtual int lineHeight(bool firstLine, bool isRootLineBox = false) const; + virtual int baselinePosition(bool firstLine, bool isRootLineBox = false) const; + + // overrides + virtual InlineBox* createInlineBox(bool, bool, bool isOnlyRun = false); + + virtual bool isBR() const { return true; } + + virtual int caretMinOffset() const; + virtual int caretMaxOffset() const; + virtual unsigned caretMaxRenderedOffset() const; + + virtual VisiblePosition positionForCoordinates(int x, int y); + +protected: + virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + +private: + mutable int m_lineHeight; +}; + +} // namespace WebCore + +#endif // RenderBR_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderBlock.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderBlock.cpp new file mode 100644 index 0000000..0aa58da --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderBlock.cpp @@ -0,0 +1,4671 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2007 David Smith (catfish.man@gmail.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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" +#include "RenderBlock.h" + +#include "Document.h" +#include "Element.h" +#include "Frame.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "HTMLNames.h" +#include "HitTestResult.h" +#include "InlineTextBox.h" +#include "RenderImage.h" +#include "RenderMarquee.h" +#include "RenderReplica.h" +#include "RenderTableCell.h" +#include "RenderTextFragment.h" +#include "RenderTheme.h" +#include "RenderView.h" +#include "SelectionController.h" +#include <wtf/StdLibExtras.h> + +using namespace std; +using namespace WTF; +using namespace Unicode; + +namespace WebCore { + +// Number of pixels to allow as a fudge factor when clicking above or below a line. +// clicking up to verticalLineClickFudgeFactor pixels above a line will correspond to the closest point on the line. +const int verticalLineClickFudgeFactor= 3; + +using namespace HTMLNames; + +struct ColumnInfo { + ColumnInfo() + : m_desiredColumnWidth(0) + , m_desiredColumnCount(1) + { } + int m_desiredColumnWidth; + unsigned m_desiredColumnCount; + Vector<IntRect> m_columnRects; +}; + +typedef WTF::HashMap<const RenderBox*, ColumnInfo*> ColumnInfoMap; +static ColumnInfoMap* gColumnInfoMap = 0; + +typedef WTF::HashMap<const RenderBlock*, HashSet<RenderBox*>*> PercentHeightDescendantsMap; +static PercentHeightDescendantsMap* gPercentHeightDescendantsMap = 0; + +typedef WTF::HashMap<const RenderBox*, HashSet<RenderBlock*>*> PercentHeightContainerMap; +static PercentHeightContainerMap* gPercentHeightContainerMap = 0; + +typedef WTF::HashMap<RenderBlock*, RenderFlowSequencedSet*> ContinuationOutlineTableMap; + +// Our MarginInfo state used when laying out block children. +RenderBlock::MarginInfo::MarginInfo(RenderBlock* block, int top, int bottom) +{ + // Whether or not we can collapse our own margins with our children. We don't do this + // if we had any border/padding (obviously), if we're the root or HTML elements, or if + // we're positioned, floating, a table cell. + m_canCollapseWithChildren = !block->isRenderView() && !block->isRoot() && !block->isPositioned() && + !block->isFloating() && !block->isTableCell() && !block->hasOverflowClip() && !block->isInlineBlockOrInlineTable(); + + m_canCollapseTopWithChildren = m_canCollapseWithChildren && (top == 0) && block->style()->marginTopCollapse() != MSEPARATE; + + // If any height other than auto is specified in CSS, then we don't collapse our bottom + // margins with our children's margins. To do otherwise would be to risk odd visual + // effects when the children overflow out of the parent block and yet still collapse + // with it. We also don't collapse if we have any bottom border/padding. + m_canCollapseBottomWithChildren = m_canCollapseWithChildren && (bottom == 0) && + (block->style()->height().isAuto() && block->style()->height().value() == 0) && block->style()->marginBottomCollapse() != MSEPARATE; + + m_quirkContainer = block->isTableCell() || block->isBody() || block->style()->marginTopCollapse() == MDISCARD || + block->style()->marginBottomCollapse() == MDISCARD; + + m_atTopOfBlock = true; + m_atBottomOfBlock = false; + + m_posMargin = m_canCollapseTopWithChildren ? block->maxTopMargin(true) : 0; + m_negMargin = m_canCollapseTopWithChildren ? block->maxTopMargin(false) : 0; + + m_selfCollapsingBlockClearedFloat = false; + + m_topQuirk = m_bottomQuirk = m_determinedTopQuirk = false; +} + +// ------------------------------------------------------------------------------------------------------- + +RenderBlock::RenderBlock(Node* node) + : RenderFlow(node) + , m_floatingObjects(0) + , m_positionedObjects(0) + , m_maxMargin(0) + , m_overflowHeight(0) + , m_overflowWidth(0) + , m_overflowLeft(0) + , m_overflowTop(0) +{ +} + +RenderBlock::~RenderBlock() +{ + delete m_floatingObjects; + delete m_positionedObjects; + delete m_maxMargin; + + if (m_hasColumns) + delete gColumnInfoMap->take(this); + + if (gPercentHeightDescendantsMap) { + if (HashSet<RenderBox*>* descendantSet = gPercentHeightDescendantsMap->take(this)) { + HashSet<RenderBox*>::iterator end = descendantSet->end(); + for (HashSet<RenderBox*>::iterator descendant = descendantSet->begin(); descendant != end; ++descendant) { + HashSet<RenderBlock*>* containerSet = gPercentHeightContainerMap->get(*descendant); + ASSERT(containerSet); + if (!containerSet) + continue; + ASSERT(containerSet->contains(this)); + containerSet->remove(this); + if (containerSet->isEmpty()) { + gPercentHeightContainerMap->remove(*descendant); + delete containerSet; + } + } + delete descendantSet; + } + } +} + +void RenderBlock::styleWillChange(RenderStyle::Diff diff, const RenderStyle* newStyle) +{ + setReplaced(newStyle->isDisplayReplacedType()); + RenderFlow::styleWillChange(diff, newStyle); +} + +void RenderBlock::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +{ + RenderFlow::styleDidChange(diff, oldStyle); + + // FIXME: We could save this call when the change only affected non-inherited properties + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { + if (child->isAnonymousBlock()) { + RefPtr<RenderStyle> newStyle = RenderStyle::create(); + newStyle->inheritFrom(style()); + newStyle->setDisplay(BLOCK); + child->setStyle(newStyle.release()); + } + } + + m_lineHeight = -1; + + // Update pseudos for :before and :after now. + if (!isAnonymous() && canHaveChildren()) { + updateBeforeAfterContent(RenderStyle::BEFORE); + updateBeforeAfterContent(RenderStyle::AFTER); + } + updateFirstLetter(); +} + +void RenderBlock::addChildToFlow(RenderObject* newChild, RenderObject* beforeChild) +{ + // Make sure we don't append things after :after-generated content if we have it. + if (!beforeChild && isAfterContent(lastChild())) + beforeChild = lastChild(); + + bool madeBoxesNonInline = false; + + // If the requested beforeChild is not one of our children, then this is because + // there is an anonymous container within this object that contains the beforeChild. + if (beforeChild && beforeChild->parent() != this) { + RenderObject* anonymousChild = beforeChild->parent(); + ASSERT(anonymousChild); + + while (anonymousChild->parent() != this) + anonymousChild = anonymousChild->parent(); + + ASSERT(anonymousChild->isAnonymous()); + + if (anonymousChild->isAnonymousBlock()) { + // Insert the child into the anonymous block box instead of here. + if (newChild->isInline() || beforeChild->parent()->firstChild() != beforeChild) + beforeChild->parent()->addChild(newChild, beforeChild); + else + addChildToFlow(newChild, beforeChild->parent()); + return; + } + + ASSERT(anonymousChild->isTable()); + if (newChild->isTableCol() && newChild->style()->display() == TABLE_COLUMN_GROUP + || newChild->isRenderBlock() && newChild->style()->display() == TABLE_CAPTION + || newChild->isTableSection() + || newChild->isTableRow() + || newChild->isTableCell()) { + // Insert into the anonymous table. + anonymousChild->addChild(newChild, beforeChild); + return; + } + + // Go on to insert before the anonymous table. + beforeChild = anonymousChild; + } + + // A block has to either have all of its children inline, or all of its children as blocks. + // So, if our children are currently inline and a block child has to be inserted, we move all our + // inline children into anonymous block boxes. + if (m_childrenInline && !newChild->isInline() && !newChild->isFloatingOrPositioned()) { + // This is a block with inline content. Wrap the inline content in anonymous blocks. + makeChildrenNonInline(beforeChild); + madeBoxesNonInline = true; + + if (beforeChild && beforeChild->parent() != this) { + beforeChild = beforeChild->parent(); + ASSERT(beforeChild->isAnonymousBlock()); + ASSERT(beforeChild->parent() == this); + } + } else if (!m_childrenInline && (newChild->isFloatingOrPositioned() || newChild->isInline())) { + // If we're inserting an inline child but all of our children are blocks, then we have to make sure + // it is put into an anomyous block box. We try to use an existing anonymous box if possible, otherwise + // a new one is created and inserted into our list of children in the appropriate position. + RenderObject* afterChild = beforeChild ? beforeChild->previousSibling() : lastChild(); + + if (afterChild && afterChild->isAnonymousBlock()) { + afterChild->addChild(newChild); + return; + } + + if (newChild->isInline()) { + // No suitable existing anonymous box - create a new one. + RenderBlock* newBox = createAnonymousBlock(); + RenderContainer::addChild(newBox, beforeChild); + newBox->addChild(newChild); + return; + } + } + + RenderContainer::addChild(newChild, beforeChild); + // ### care about aligned stuff + + if (madeBoxesNonInline && parent() && isAnonymousBlock()) + parent()->removeLeftoverAnonymousBlock(this); + // this object may be dead here +} + +static void getInlineRun(RenderObject* start, RenderObject* boundary, + RenderObject*& inlineRunStart, + RenderObject*& inlineRunEnd) +{ + // Beginning at |start| we find the largest contiguous run of inlines that + // we can. We denote the run with start and end points, |inlineRunStart| + // and |inlineRunEnd|. Note that these two values may be the same if + // we encounter only one inline. + // + // We skip any non-inlines we encounter as long as we haven't found any + // inlines yet. + // + // |boundary| indicates a non-inclusive boundary point. Regardless of whether |boundary| + // is inline or not, we will not include it in a run with inlines before it. It's as though we encountered + // a non-inline. + + // Start by skipping as many non-inlines as we can. + RenderObject * curr = start; + bool sawInline; + do { + while (curr && !(curr->isInline() || curr->isFloatingOrPositioned())) + curr = curr->nextSibling(); + + inlineRunStart = inlineRunEnd = curr; + + if (!curr) + return; // No more inline children to be found. + + sawInline = curr->isInline(); + + curr = curr->nextSibling(); + while (curr && (curr->isInline() || curr->isFloatingOrPositioned()) && (curr != boundary)) { + inlineRunEnd = curr; + if (curr->isInline()) + sawInline = true; + curr = curr->nextSibling(); + } + } while (!sawInline); +} + +void RenderBlock::deleteLineBoxTree() +{ + InlineFlowBox* line = m_firstLineBox; + InlineFlowBox* nextLine; + while (line) { + nextLine = line->nextFlowBox(); + line->deleteLine(renderArena()); + line = nextLine; + } + m_firstLineBox = m_lastLineBox = 0; +} + +void RenderBlock::makeChildrenNonInline(RenderObject *insertionPoint) +{ + // makeChildrenNonInline takes a block whose children are *all* inline and it + // makes sure that inline children are coalesced under anonymous + // blocks. If |insertionPoint| is defined, then it represents the insertion point for + // the new block child that is causing us to have to wrap all the inlines. This + // means that we cannot coalesce inlines before |insertionPoint| with inlines following + // |insertionPoint|, because the new child is going to be inserted in between the inlines, + // splitting them. + ASSERT(isInlineBlockOrInlineTable() || !isInline()); + ASSERT(!insertionPoint || insertionPoint->parent() == this); + + m_childrenInline = false; + + RenderObject *child = firstChild(); + if (!child) + return; + + deleteLineBoxTree(); + + while (child) { + RenderObject *inlineRunStart, *inlineRunEnd; + getInlineRun(child, insertionPoint, inlineRunStart, inlineRunEnd); + + if (!inlineRunStart) + break; + + child = inlineRunEnd->nextSibling(); + + RenderBlock* box = createAnonymousBlock(); + insertChildNode(box, inlineRunStart); + RenderObject* o = inlineRunStart; + while(o != inlineRunEnd) + { + RenderObject* no = o; + o = no->nextSibling(); + box->moveChildNode(no); + } + box->moveChildNode(inlineRunEnd); + } + +#ifndef NDEBUG + for (RenderObject *c = firstChild(); c; c = c->nextSibling()) + ASSERT(!c->isInline()); +#endif + + repaint(); +} + +void RenderBlock::removeChild(RenderObject *oldChild) +{ + // If this child is a block, and if our previous and next siblings are + // both anonymous blocks with inline content, then we can go ahead and + // fold the inline content back together. + RenderObject* prev = oldChild->previousSibling(); + RenderObject* next = oldChild->nextSibling(); + bool canDeleteAnonymousBlocks = !documentBeingDestroyed() && !isInline() && !oldChild->isInline() && + !oldChild->continuation() && + (!prev || (prev->isAnonymousBlock() && prev->childrenInline())) && + (!next || (next->isAnonymousBlock() && next->childrenInline())); + if (canDeleteAnonymousBlocks && prev && next) { + // Take all the children out of the |next| block and put them in + // the |prev| block. + prev->setNeedsLayoutAndPrefWidthsRecalc(); + RenderObject* o = next->firstChild(); + while (o) { + RenderObject* no = o; + o = no->nextSibling(); + prev->moveChildNode(no); + } + + RenderBlock* nextBlock = static_cast<RenderBlock*>(next); + nextBlock->deleteLineBoxTree(); + + // Nuke the now-empty block. + next->destroy(); + } + + RenderFlow::removeChild(oldChild); + + RenderObject* child = prev ? prev : next; + if (canDeleteAnonymousBlocks && child && !child->previousSibling() && !child->nextSibling() && !isFlexibleBox()) { + // The removal has knocked us down to containing only a single anonymous + // box. We can go ahead and pull the content right back up into our + // box. + setNeedsLayoutAndPrefWidthsRecalc(); + RenderBlock* anonBlock = static_cast<RenderBlock*>(removeChildNode(child, false)); + m_childrenInline = true; + RenderObject* o = anonBlock->firstChild(); + while (o) { + RenderObject* no = o; + o = no->nextSibling(); + moveChildNode(no); + } + + // Delete the now-empty block's lines and nuke it. + anonBlock->deleteLineBoxTree(); + anonBlock->destroy(); + } +} + +int RenderBlock::overflowHeight(bool includeInterior) const +{ + if (!includeInterior && hasOverflowClip()) { + int shadowHeight = 0; + for (ShadowData* boxShadow = style()->boxShadow(); boxShadow; boxShadow = boxShadow->next) + shadowHeight = max(boxShadow->y + boxShadow->blur, shadowHeight); + int height = m_height + shadowHeight; + if (hasReflection()) + height = max(height, reflectionBox().bottom()); + return height; + } + return m_overflowHeight; +} + +int RenderBlock::overflowWidth(bool includeInterior) const +{ + if (!includeInterior && hasOverflowClip()) { + int shadowWidth = 0; + for (ShadowData* boxShadow = style()->boxShadow(); boxShadow; boxShadow = boxShadow->next) + shadowWidth = max(boxShadow->x + boxShadow->blur, shadowWidth); + int width = m_width + shadowWidth; + if (hasReflection()) + width = max(width, reflectionBox().right()); + return width; + } + return m_overflowWidth; +} + +int RenderBlock::overflowLeft(bool includeInterior) const +{ + if (!includeInterior && hasOverflowClip()) { + int shadowLeft = 0; + for (ShadowData* boxShadow = style()->boxShadow(); boxShadow; boxShadow = boxShadow->next) + shadowLeft = min(boxShadow->x - boxShadow->blur, shadowLeft); + int left = shadowLeft; + if (hasReflection()) + left = min(left, reflectionBox().x()); + return left; + } + return m_overflowLeft; +} + +int RenderBlock::overflowTop(bool includeInterior) const +{ + if (!includeInterior && hasOverflowClip()) { + int shadowTop = 0; + for (ShadowData* boxShadow = style()->boxShadow(); boxShadow; boxShadow = boxShadow->next) + shadowTop = min(boxShadow->y - boxShadow->blur, shadowTop); + int top = shadowTop; + if (hasReflection()) + top = min(top, reflectionBox().y()); + return top; + } + return m_overflowTop; +} + +IntRect RenderBlock::overflowRect(bool includeInterior) const +{ + if (!includeInterior && hasOverflowClip()) { + IntRect box = borderBox(); + int shadowLeft = 0; + int shadowRight = 0; + int shadowTop = 0; + int shadowBottom = 0; + + for (ShadowData* boxShadow = style()->boxShadow(); boxShadow; boxShadow = boxShadow->next) { + shadowLeft = min(boxShadow->x - boxShadow->blur, shadowLeft); + shadowRight = max(boxShadow->x + boxShadow->blur, shadowRight); + shadowTop = min(boxShadow->y - boxShadow->blur, shadowTop); + shadowBottom = max(boxShadow->y + boxShadow->blur, shadowBottom); + } + + box.move(shadowLeft, shadowTop); + box.setWidth(box.width() - shadowLeft + shadowRight); + box.setHeight(box.height() - shadowTop + shadowBottom); + + if (hasReflection()) { + IntRect reflection(reflectionBox()); + int reflectTop = min(box.y(), reflection.y()); + int reflectBottom = max(box.bottom(), reflection.bottom()); + box.setHeight(reflectBottom - reflectTop); + box.setY(reflectTop); + + int reflectLeft = min(box.x(), reflection.x()); + int reflectRight = max(box.right(), reflection.right()); + box.setWidth(reflectRight - reflectLeft); + box.setX(reflectLeft); + } + return box; + } + + if (!includeInterior && hasOverflowClip()) + return borderBox(); + int l = overflowLeft(includeInterior); + int t = min(overflowTop(includeInterior), -borderTopExtra()); + return IntRect(l, t, overflowWidth(includeInterior) - l, max(overflowHeight(includeInterior), height() + borderBottomExtra()) - t); +} + +bool RenderBlock::isSelfCollapsingBlock() const +{ + // We are not self-collapsing if we + // (a) have a non-zero height according to layout (an optimization to avoid wasting time) + // (b) are a table, + // (c) have border/padding, + // (d) have a min-height + // (e) have specified that one of our margins can't collapse using a CSS extension + if (m_height > 0 || + isTable() || (borderBottom() + paddingBottom() + borderTop() + paddingTop()) != 0 || + style()->minHeight().isPositive() || + style()->marginTopCollapse() == MSEPARATE || style()->marginBottomCollapse() == MSEPARATE) + return false; + + bool hasAutoHeight = style()->height().isAuto(); + if (style()->height().isPercent() && !style()->htmlHacks()) { + hasAutoHeight = true; + for (RenderBlock* cb = containingBlock(); !cb->isRenderView(); cb = cb->containingBlock()) { + if (cb->style()->height().isFixed() || cb->isTableCell()) + hasAutoHeight = false; + } + } + + // If the height is 0 or auto, then whether or not we are a self-collapsing block depends + // on whether we have content that is all self-collapsing or not. + if (hasAutoHeight || ((style()->height().isFixed() || style()->height().isPercent()) && style()->height().isZero())) { + // If the block has inline children, see if we generated any line boxes. If we have any + // line boxes, then we can't be self-collapsing, since we have content. + if (childrenInline()) + return !firstLineBox(); + + // Whether or not we collapse is dependent on whether all our normal flow children + // are also self-collapsing. + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { + if (child->isFloatingOrPositioned()) + continue; + if (!child->isSelfCollapsingBlock()) + return false; + } + return true; + } + return false; +} + +void RenderBlock::layout() +{ + // Update our first letter info now. + updateFirstLetter(); + + // Table cells call layoutBlock directly, so don't add any logic here. Put code into + // layoutBlock(). + layoutBlock(false); + + // It's safe to check for control clip here, since controls can never be table cells. + if (hasControlClip()) { + // Because of the lightweight clip, there can never be any overflow from children. + m_overflowWidth = m_width; + m_overflowHeight = m_height; + m_overflowLeft = 0; + m_overflowTop = 0; + } +} + +void RenderBlock::layoutBlock(bool relayoutChildren) +{ + ASSERT(needsLayout()); + + if (isInline() && !isInlineBlockOrInlineTable()) // Inline <form>s inside various table elements can + return; // cause us to come in here. Just bail. + + if (!relayoutChildren && layoutOnlyPositionedObjects()) + return; + + IntRect oldBounds; + IntRect oldOutlineBox; + bool checkForRepaint = m_everHadLayout && checkForRepaintDuringLayout(); + if (checkForRepaint) { + oldBounds = absoluteClippedOverflowRect(); + oldOutlineBox = absoluteOutlineBounds(); + } + + LayoutStateMaintainer statePusher(view(), this, IntSize(xPos(), yPos()), !m_hasColumns && !hasReflection()); + + int oldWidth = m_width; + int oldColumnWidth = desiredColumnWidth(); + + calcWidth(); + calcColumnWidth(); + + m_overflowWidth = m_width; + m_overflowLeft = 0; + + if (oldWidth != m_width || oldColumnWidth != desiredColumnWidth()) + relayoutChildren = true; + + clearFloats(); + + int previousHeight = m_height; + m_height = 0; + m_overflowHeight = 0; + + // We use four values, maxTopPos, maxPosNeg, maxBottomPos, and maxBottomNeg, to track + // our current maximal positive and negative margins. These values are used when we + // are collapsed with adjacent blocks, so for example, if you have block A and B + // collapsing together, then you'd take the maximal positive margin from both A and B + // and subtract it from the maximal negative margin from both A and B to get the + // true collapsed margin. This algorithm is recursive, so when we finish layout() + // our block knows its current maximal positive/negative values. + // + // Start out by setting our margin values to our current margins. Table cells have + // no margins, so we don't fill in the values for table cells. + bool isCell = isTableCell(); + if (!isCell) { + initMaxMarginValues(); + + m_topMarginQuirk = style()->marginTop().quirk(); + m_bottomMarginQuirk = style()->marginBottom().quirk(); + + Node* node = element(); + if (node && node->hasTagName(formTag) && static_cast<HTMLFormElement*>(node)->isMalformed()) { + // See if this form is malformed (i.e., unclosed). If so, don't give the form + // a bottom margin. + setMaxBottomMargins(0, 0); + } + } + + // For overflow:scroll blocks, ensure we have both scrollbars in place always. + if (scrollsOverflow()) { + if (style()->overflowX() == OSCROLL) + m_layer->setHasHorizontalScrollbar(true); + if (style()->overflowY() == OSCROLL) + m_layer->setHasVerticalScrollbar(true); + } + + int repaintTop = 0; + int repaintBottom = 0; + int maxFloatBottom = 0; + if (childrenInline()) + layoutInlineChildren(relayoutChildren, repaintTop, repaintBottom); + else + layoutBlockChildren(relayoutChildren, maxFloatBottom); + + // Expand our intrinsic height to encompass floats. + int toAdd = borderBottom() + paddingBottom() + horizontalScrollbarHeight(); + if (floatBottom() > (m_height - toAdd) && (isInlineBlockOrInlineTable() || isFloatingOrPositioned() || hasOverflowClip() || + (parent() && parent()->isFlexibleBox() || m_hasColumns))) + m_height = floatBottom() + toAdd; + + // Now lay out our columns within this intrinsic height, since they can slightly affect the intrinsic height as + // we adjust for clean column breaks. + int singleColumnBottom = layoutColumns(); + + // Calculate our new height. + int oldHeight = m_height; + calcHeight(); + if (oldHeight != m_height) { + if (oldHeight > m_height && maxFloatBottom > m_height && !childrenInline()) { + // One of our children's floats may have become an overhanging float for us. We need to look for it. + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { + if (child->isBlockFlow() && !child->isFloatingOrPositioned()) { + RenderBlock* block = static_cast<RenderBlock*>(child); + if (block->floatBottom() + block->yPos() > m_height) + addOverhangingFloats(block, -block->xPos(), -block->yPos(), false); + } + } + } + // We have to rebalance columns to the new height. + layoutColumns(singleColumnBottom); + + // If the block got expanded in size, then increase our overflowheight to match. + if (m_overflowHeight > m_height) + m_overflowHeight -= toAdd; + if (m_overflowHeight < m_height) + m_overflowHeight = m_height; + } + if (previousHeight != m_height) + relayoutChildren = true; + + // Some classes of objects (floats and fieldsets with no specified heights and table cells) expand to encompass + // overhanging floats. + if (hasOverhangingFloats() && expandsToEncloseOverhangingFloats()) { + m_height = floatBottom(); + m_height += borderBottom() + paddingBottom(); + } + + if ((isCell || isInline() || isFloatingOrPositioned() || isRoot()) && !hasOverflowClip() && !hasControlClip()) + addVisualOverflow(floatRect()); + + layoutPositionedObjects(relayoutChildren || isRoot()); + + positionListMarker(); + + // Always ensure our overflow width/height are at least as large as our width/height. + m_overflowWidth = max(m_overflowWidth, m_width); + m_overflowHeight = max(m_overflowHeight, m_height); + + if (!hasOverflowClip()) { + for (ShadowData* boxShadow = style()->boxShadow(); boxShadow; boxShadow = boxShadow->next) { + m_overflowLeft = min(m_overflowLeft, boxShadow->x - boxShadow->blur); + m_overflowWidth = max(m_overflowWidth, m_width + boxShadow->x + boxShadow->blur); + m_overflowTop = min(m_overflowTop, boxShadow->y - boxShadow->blur); + m_overflowHeight = max(m_overflowHeight, m_height + boxShadow->y + boxShadow->blur); + } + + if (hasReflection()) { + m_overflowTop = min(m_overflowTop, reflectionBox().y()); + m_overflowHeight = max(m_overflowHeight, reflectionBox().bottom()); + } + } + + statePusher.pop(); + + // Update our scroll information if we're overflow:auto/scroll/hidden now that we know if + // we overflow or not. + if (hasOverflowClip()) + m_layer->updateScrollInfoAfterLayout(); + + // Repaint with our new bounds if they are different from our old bounds. + bool didFullRepaint = false; + if (checkForRepaint) + didFullRepaint = repaintAfterLayoutIfNeeded(oldBounds, oldOutlineBox); + if (!didFullRepaint && repaintTop != repaintBottom && (style()->visibility() == VISIBLE || enclosingLayer()->hasVisibleContent())) { + IntRect repaintRect(m_overflowLeft, repaintTop, m_overflowWidth - m_overflowLeft, repaintBottom - repaintTop); + + // FIXME: Deal with multiple column repainting. We have to split the repaint + // rect up into multiple rects if it spans columns. + + repaintRect.inflate(maximalOutlineSize(PaintPhaseOutline)); + + if (hasOverflowClip()) { + // Adjust repaint rect for scroll offset + int x = repaintRect.x(); + int y = repaintRect.y(); + layer()->subtractScrolledContentOffset(x, y); + repaintRect.setX(x); + repaintRect.setY(y); + + // Don't allow this rect to spill out of our overflow box. + repaintRect.intersect(IntRect(0, 0, m_width, m_height)); + } + + // Make sure the rect is still non-empty after intersecting for overflow above + if (!repaintRect.isEmpty()) { + repaintRectangle(repaintRect); // We need to do a partial repaint of our content. + if (hasReflection()) + layer()->reflection()->repaintRectangle(repaintRect); + } + } + setNeedsLayout(false); +} + +void RenderBlock::adjustPositionedBlock(RenderObject* child, const MarginInfo& marginInfo) +{ + if (child->hasStaticX()) { + if (style()->direction() == LTR) + child->setStaticX(borderLeft() + paddingLeft()); + else + child->setStaticX(borderRight() + paddingRight()); + } + + if (child->hasStaticY()) { + int y = m_height; + if (!marginInfo.canCollapseWithTop()) { + child->calcVerticalMargins(); + int marginTop = child->marginTop(); + int collapsedTopPos = marginInfo.posMargin(); + int collapsedTopNeg = marginInfo.negMargin(); + if (marginTop > 0) { + if (marginTop > collapsedTopPos) + collapsedTopPos = marginTop; + } else { + if (-marginTop > collapsedTopNeg) + collapsedTopNeg = -marginTop; + } + y += (collapsedTopPos - collapsedTopNeg) - marginTop; + } + child->setStaticY(y); + } +} + +void RenderBlock::adjustFloatingBlock(const MarginInfo& marginInfo) +{ + // The float should be positioned taking into account the bottom margin + // of the previous flow. We add that margin into the height, get the + // float positioned properly, and then subtract the margin out of the + // height again. In the case of self-collapsing blocks, we always just + // use the top margins, since the self-collapsing block collapsed its + // own bottom margin into its top margin. + // + // Note also that the previous flow may collapse its margin into the top of + // our block. If this is the case, then we do not add the margin in to our + // height when computing the position of the float. This condition can be tested + // for by simply calling canCollapseWithTop. See + // http://www.hixie.ch/tests/adhoc/css/box/block/margin-collapse/046.html for + // an example of this scenario. + int marginOffset = marginInfo.canCollapseWithTop() ? 0 : marginInfo.margin(); + m_height += marginOffset; + positionNewFloats(); + m_height -= marginOffset; +} + +RenderObject* RenderBlock::handleSpecialChild(RenderObject* child, const MarginInfo& marginInfo, CompactInfo& compactInfo, bool& handled) +{ + // Handle positioned children first. + RenderObject* next = handlePositionedChild(child, marginInfo, handled); + if (handled) return next; + + // Handle floating children next. + next = handleFloatingChild(child, marginInfo, handled); + if (handled) return next; + + // See if we have a compact element. If we do, then try to tuck the compact element into the margin space of the next block. + next = handleCompactChild(child, compactInfo, handled); + if (handled) return next; + + // Finally, see if we have a run-in element. + return handleRunInChild(child, handled); +} + + +RenderObject* RenderBlock::handlePositionedChild(RenderObject* child, const MarginInfo& marginInfo, bool& handled) +{ + if (child->isPositioned()) { + handled = true; + child->containingBlock()->insertPositionedObject(child); + adjustPositionedBlock(child, marginInfo); + return child->nextSibling(); + } + + return 0; +} + +RenderObject* RenderBlock::handleFloatingChild(RenderObject* child, const MarginInfo& marginInfo, bool& handled) +{ + if (child->isFloating()) { + handled = true; + insertFloatingObject(child); + adjustFloatingBlock(marginInfo); + return child->nextSibling(); + } + + return 0; +} + +RenderObject* RenderBlock::handleCompactChild(RenderObject* child, CompactInfo& compactInfo, bool& handled) +{ + // FIXME: We only deal with one compact at a time. It is unclear what should be + // done if multiple contiguous compacts are encountered. For now we assume that + // compact A followed by another compact B should simply be treated as block A. + if (child->isCompact() && !compactInfo.compact() && (child->childrenInline() || child->isReplaced())) { + // Get the next non-positioned/non-floating RenderBlock. + RenderObject* next = child->nextSibling(); + RenderObject* curr = next; + while (curr && curr->isFloatingOrPositioned()) + curr = curr->nextSibling(); + if (curr && curr->isRenderBlock() && !curr->isCompact() && !curr->isRunIn()) { + curr->calcWidth(); // So that horizontal margins are correct. + + child->setInline(true); // Need to compute the margins/width for the child as though it is an inline, so that it won't try to puff up the margins to + // fill the containing block width. + child->calcWidth(); + int childMargins = child->marginLeft() + child->marginRight(); + int margin = style()->direction() == LTR ? curr->marginLeft() : curr->marginRight(); + if (margin >= (childMargins + child->maxPrefWidth())) { + // The compact will fit in the margin. + handled = true; + compactInfo.set(child, curr); + child->setPos(0,0); // This position will be updated to reflect the compact's + // desired position and the line box for the compact will + // pick that position up. + + // Remove the child. + RenderObject* next = child->nextSibling(); + removeChildNode(child); + + // Now insert the child under |curr|. + curr->insertChildNode(child, curr->firstChild()); + return next; + } + else + child->setInline(false); // We didn't fit, so we remain a block-level element. + } + } + return 0; +} + +void RenderBlock::insertCompactIfNeeded(RenderObject* child, CompactInfo& compactInfo) +{ + if (compactInfo.matches(child)) { + // We have a compact child to squeeze in. + RenderObject* compactChild = compactInfo.compact(); + int compactXPos = borderLeft() + paddingLeft() + compactChild->marginLeft(); + if (style()->direction() == RTL) { + compactChild->calcWidth(); // have to do this because of the capped maxwidth + compactXPos = width() - borderRight() - paddingRight() - marginRight() - + compactChild->width() - compactChild->marginRight(); + } + compactXPos -= child->xPos(); // Put compactXPos into the child's coordinate space. + compactChild->setPos(compactXPos, compactChild->yPos()); // Set the x position. + compactInfo.clear(); + } +} + +RenderObject* RenderBlock::handleRunInChild(RenderObject* child, bool& handled) +{ + // See if we have a run-in element with inline children. If the + // children aren't inline, then just treat the run-in as a normal + // block. + if (child->isRunIn() && (child->childrenInline() || child->isReplaced())) { + // Get the next non-positioned/non-floating RenderBlock. + RenderObject* curr = child->nextSibling(); + while (curr && curr->isFloatingOrPositioned()) + curr = curr->nextSibling(); + if (curr && (curr->isRenderBlock() && curr->childrenInline() && !curr->isCompact() && !curr->isRunIn())) { + // The block acts like an inline, so just null out its + // position. + handled = true; + child->setInline(true); + child->setPos(0,0); + + // Remove the child. + RenderObject* next = child->nextSibling(); + removeChildNode(child); + + // Now insert the child under |curr|. + curr->insertChildNode(child, curr->firstChild()); + return next; + } + } + return 0; +} + +void RenderBlock::collapseMargins(RenderObject* child, MarginInfo& marginInfo, int yPosEstimate) +{ + // Get our max pos and neg top margins. + int posTop = child->maxTopMargin(true); + int negTop = child->maxTopMargin(false); + + // For self-collapsing blocks, collapse our bottom margins into our + // top to get new posTop and negTop values. + if (child->isSelfCollapsingBlock()) { + posTop = max(posTop, child->maxBottomMargin(true)); + negTop = max(negTop, child->maxBottomMargin(false)); + } + + // See if the top margin is quirky. We only care if this child has + // margins that will collapse with us. + bool topQuirk = child->isTopMarginQuirk() || style()->marginTopCollapse() == MDISCARD; + + if (marginInfo.canCollapseWithTop()) { + // This child is collapsing with the top of the + // block. If it has larger margin values, then we need to update + // our own maximal values. + if (!style()->htmlHacks() || !marginInfo.quirkContainer() || !topQuirk) + setMaxTopMargins(max(posTop, maxTopPosMargin()), max(negTop, maxTopNegMargin())); + + // The minute any of the margins involved isn't a quirk, don't + // collapse it away, even if the margin is smaller (www.webreference.com + // has an example of this, a <dt> with 0.8em author-specified inside + // a <dl> inside a <td>. + if (!marginInfo.determinedTopQuirk() && !topQuirk && (posTop-negTop)) { + m_topMarginQuirk = false; + marginInfo.setDeterminedTopQuirk(true); + } + + if (!marginInfo.determinedTopQuirk() && topQuirk && marginTop() == 0) + // We have no top margin and our top child has a quirky margin. + // We will pick up this quirky margin and pass it through. + // This deals with the <td><div><p> case. + // Don't do this for a block that split two inlines though. You do + // still apply margins in this case. + m_topMarginQuirk = true; + } + + if (marginInfo.quirkContainer() && marginInfo.atTopOfBlock() && (posTop - negTop)) + marginInfo.setTopQuirk(topQuirk); + + int ypos = m_height; + if (child->isSelfCollapsingBlock()) { + // This child has no height. We need to compute our + // position before we collapse the child's margins together, + // so that we can get an accurate position for the zero-height block. + int collapsedTopPos = max(marginInfo.posMargin(), child->maxTopMargin(true)); + int collapsedTopNeg = max(marginInfo.negMargin(), child->maxTopMargin(false)); + marginInfo.setMargin(collapsedTopPos, collapsedTopNeg); + + // Now collapse the child's margins together, which means examining our + // bottom margin values as well. + marginInfo.setPosMarginIfLarger(child->maxBottomMargin(true)); + marginInfo.setNegMarginIfLarger(child->maxBottomMargin(false)); + + if (!marginInfo.canCollapseWithTop()) + // We need to make sure that the position of the self-collapsing block + // is correct, since it could have overflowing content + // that needs to be positioned correctly (e.g., a block that + // had a specified height of 0 but that actually had subcontent). + ypos = m_height + collapsedTopPos - collapsedTopNeg; + } + else { + if (child->style()->marginTopCollapse() == MSEPARATE) { + m_height += marginInfo.margin() + child->marginTop(); + ypos = m_height; + } + else if (!marginInfo.atTopOfBlock() || + (!marginInfo.canCollapseTopWithChildren() + && (!style()->htmlHacks() || !marginInfo.quirkContainer() || !marginInfo.topQuirk()))) { + // We're collapsing with a previous sibling's margins and not + // with the top of the block. + m_height += max(marginInfo.posMargin(), posTop) - max(marginInfo.negMargin(), negTop); + ypos = m_height; + } + + marginInfo.setPosMargin(child->maxBottomMargin(true)); + marginInfo.setNegMargin(child->maxBottomMargin(false)); + + if (marginInfo.margin()) + marginInfo.setBottomQuirk(child->isBottomMarginQuirk() || style()->marginBottomCollapse() == MDISCARD); + + marginInfo.setSelfCollapsingBlockClearedFloat(false); + } + + view()->addLayoutDelta(IntSize(0, yPosEstimate - ypos)); + child->setPos(child->xPos(), ypos); + if (ypos != yPosEstimate) { + if (child->shrinkToAvoidFloats()) + // The child's width depends on the line width. + // When the child shifts to clear an item, its width can + // change (because it has more available line width). + // So go ahead and mark the item as dirty. + child->setChildNeedsLayout(true, false); + + if (!child->avoidsFloats() && child->containsFloats()) + child->markAllDescendantsWithFloatsForLayout(); + + // Our guess was wrong. Make the child lay itself out again. + child->layoutIfNeeded(); + } +} + +void RenderBlock::clearFloatsIfNeeded(RenderObject* child, MarginInfo& marginInfo, int oldTopPosMargin, int oldTopNegMargin) +{ + int heightIncrease = getClearDelta(child); + if (!heightIncrease) + return; + + // The child needs to be lowered. Move the child so that it just clears the float. + view()->addLayoutDelta(IntSize(0, -heightIncrease)); + child->setPos(child->xPos(), child->yPos() + heightIncrease); + + if (child->isSelfCollapsingBlock()) { + // For self-collapsing blocks that clear, they can still collapse their + // margins with following siblings. Reset the current margins to represent + // the self-collapsing block's margins only. + marginInfo.setPosMargin(max(child->maxTopMargin(true), child->maxBottomMargin(true))); + marginInfo.setNegMargin(max(child->maxTopMargin(false), child->maxBottomMargin(false))); + + // Adjust our height such that we are ready to be collapsed with subsequent siblings. + m_height = child->yPos() - max(0, marginInfo.margin()); + + // Set a flag that we cleared a float so that we know both to increase the height of the block + // to compensate for the clear and to avoid collapsing our margins with the parent block's + // bottom margin. + marginInfo.setSelfCollapsingBlockClearedFloat(true); + } else + // Increase our height by the amount we had to clear. + m_height += heightIncrease; + + if (marginInfo.canCollapseWithTop()) { + // We can no longer collapse with the top of the block since a clear + // occurred. The empty blocks collapse into the cleared block. + // FIXME: This isn't quite correct. Need clarification for what to do + // if the height the cleared block is offset by is smaller than the + // margins involved. + setMaxTopMargins(oldTopPosMargin, oldTopNegMargin); + marginInfo.setAtTopOfBlock(false); + } + + // If our value of clear caused us to be repositioned vertically to be + // underneath a float, we might have to do another layout to take into account + // the extra space we now have available. + if (child->shrinkToAvoidFloats()) + // The child's width depends on the line width. + // When the child shifts to clear an item, its width can + // change (because it has more available line width). + // So go ahead and mark the item as dirty. + child->setChildNeedsLayout(true, false); + if (!child->avoidsFloats() && child->containsFloats()) + child->markAllDescendantsWithFloatsForLayout(); + child->layoutIfNeeded(); +} + +int RenderBlock::estimateVerticalPosition(RenderObject* child, const MarginInfo& marginInfo) +{ + // FIXME: We need to eliminate the estimation of vertical position, because when it's wrong we sometimes trigger a pathological + // relayout if there are intruding floats. + int yPosEstimate = m_height; + if (!marginInfo.canCollapseWithTop()) { + int childMarginTop = child->selfNeedsLayout() ? child->marginTop() : child->collapsedMarginTop(); + yPosEstimate += max(marginInfo.margin(), childMarginTop); + } + return yPosEstimate; +} + +void RenderBlock::determineHorizontalPosition(RenderObject* child) +{ + if (style()->direction() == LTR) { + int xPos = borderLeft() + paddingLeft(); + + // Add in our left margin. + int chPos = xPos + child->marginLeft(); + + // Some objects (e.g., tables, horizontal rules, overflow:auto blocks) avoid floats. They need + // to shift over as necessary to dodge any floats that might get in the way. + if (child->avoidsFloats()) { + int leftOff = leftOffset(m_height); + if (style()->textAlign() != WEBKIT_CENTER && child->style()->marginLeft().type() != Auto) { + if (child->marginLeft() < 0) + leftOff += child->marginLeft(); + chPos = max(chPos, leftOff); // Let the float sit in the child's margin if it can fit. + } + else if (leftOff != xPos) { + // The object is shifting right. The object might be centered, so we need to + // recalculate our horizontal margins. Note that the containing block content + // width computation will take into account the delta between |leftOff| and |xPos| + // so that we can just pass the content width in directly to the |calcHorizontalMargins| + // function. + static_cast<RenderBox*>(child)->calcHorizontalMargins(child->style()->marginLeft(), child->style()->marginRight(), lineWidth(child->yPos())); + chPos = leftOff + child->marginLeft(); + } + } + view()->addLayoutDelta(IntSize(child->xPos() - chPos, 0)); + child->setPos(chPos, child->yPos()); + } else { + int xPos = m_width - borderRight() - paddingRight() - verticalScrollbarWidth(); + int chPos = xPos - (child->width() + child->marginRight()); + if (child->avoidsFloats()) { + int rightOff = rightOffset(m_height); + if (style()->textAlign() != WEBKIT_CENTER && child->style()->marginRight().type() != Auto) { + if (child->marginRight() < 0) + rightOff -= child->marginRight(); + chPos = min(chPos, rightOff - child->width()); // Let the float sit in the child's margin if it can fit. + } else if (rightOff != xPos) { + // The object is shifting left. The object might be centered, so we need to + // recalculate our horizontal margins. Note that the containing block content + // width computation will take into account the delta between |rightOff| and |xPos| + // so that we can just pass the content width in directly to the |calcHorizontalMargins| + // function. + static_cast<RenderBox*>(child)->calcHorizontalMargins(child->style()->marginLeft(), child->style()->marginRight(), lineWidth(child->yPos())); + chPos = rightOff - child->marginRight() - child->width(); + } + } + view()->addLayoutDelta(IntSize(child->xPos() - chPos, 0)); + child->setPos(chPos, child->yPos()); + } +} + +void RenderBlock::setCollapsedBottomMargin(const MarginInfo& marginInfo) +{ + if (marginInfo.canCollapseWithBottom() && !marginInfo.canCollapseWithTop()) { + // Update our max pos/neg bottom margins, since we collapsed our bottom margins + // with our children. + setMaxBottomMargins(max(maxBottomPosMargin(), marginInfo.posMargin()), max(maxBottomNegMargin(), marginInfo.negMargin())); + + if (!marginInfo.bottomQuirk()) + m_bottomMarginQuirk = false; + + if (marginInfo.bottomQuirk() && marginBottom() == 0) + // We have no bottom margin and our last child has a quirky margin. + // We will pick up this quirky margin and pass it through. + // This deals with the <td><div><p> case. + m_bottomMarginQuirk = true; + } +} + +void RenderBlock::handleBottomOfBlock(int top, int bottom, MarginInfo& marginInfo) +{ + // If our last flow was a self-collapsing block that cleared a float, then we don't + // collapse it with the bottom of the block. + if (!marginInfo.selfCollapsingBlockClearedFloat()) + marginInfo.setAtBottomOfBlock(true); + else { + // We have to special case the negative margin situation (where the collapsed + // margin of the self-collapsing block is negative), since there's no need + // to make an adjustment in that case. + if (marginInfo.margin() < 0) + marginInfo.clearMargin(); + } + + // If we can't collapse with children then go ahead and add in the bottom margin. + if (!marginInfo.canCollapseWithBottom() && !marginInfo.canCollapseWithTop() + && (!style()->htmlHacks() || !marginInfo.quirkContainer() || !marginInfo.bottomQuirk())) + m_height += marginInfo.margin(); + + // Now add in our bottom border/padding. + m_height += bottom; + + // Negative margins can cause our height to shrink below our minimal height (border/padding). + // If this happens, ensure that the computed height is increased to the minimal height. + m_height = max(m_height, top + bottom); + + // Always make sure our overflow height is at least our height. + m_overflowHeight = max(m_height, m_overflowHeight); + + // Update our bottom collapsed margin info. + setCollapsedBottomMargin(marginInfo); +} + +void RenderBlock::layoutBlockChildren(bool relayoutChildren, int& maxFloatBottom) +{ + if (gPercentHeightDescendantsMap) { + if (HashSet<RenderBox*>* descendants = gPercentHeightDescendantsMap->get(this)) { + HashSet<RenderBox*>::iterator end = descendants->end(); + for (HashSet<RenderBox*>::iterator it = descendants->begin(); it != end; ++it) { + RenderBox* box = *it; + while (box != this) { + if (box->normalChildNeedsLayout()) + break; + box->setChildNeedsLayout(true, false); + box = box->containingBlock(); + ASSERT(box); + if (!box) + break; + } + } + } + } + + int top = borderTop() + paddingTop(); + int bottom = borderBottom() + paddingBottom() + horizontalScrollbarHeight(); + + m_height = m_overflowHeight = top; + + // The margin struct caches all our current margin collapsing state. The compact struct caches state when we encounter compacts, + MarginInfo marginInfo(this, top, bottom); + CompactInfo compactInfo; + + // Fieldsets need to find their legend and position it inside the border of the object. + // The legend then gets skipped during normal layout. + RenderObject* legend = layoutLegend(relayoutChildren); + + int previousFloatBottom = 0; + maxFloatBottom = 0; + + RenderObject* child = firstChild(); + while (child) { + if (legend == child) { + child = child->nextSibling(); + continue; // Skip the legend, since it has already been positioned up in the fieldset's border. + } + + int oldTopPosMargin = maxTopPosMargin(); + int oldTopNegMargin = maxTopNegMargin(); + + // Make sure we layout children if they need it. + // FIXME: Technically percentage height objects only need a relayout if their percentage isn't going to be turned into + // an auto value. Add a method to determine this, so that we can avoid the relayout. + if (relayoutChildren || ((child->style()->height().isPercent() || child->style()->minHeight().isPercent() || child->style()->maxHeight().isPercent()) && !isRenderView())) + child->setChildNeedsLayout(true, false); + + // If relayoutChildren is set and we have percentage padding, we also need to invalidate the child's pref widths. + if (relayoutChildren && (child->style()->paddingLeft().isPercent() || child->style()->paddingRight().isPercent())) + child->setPrefWidthsDirty(true, false); + + // Handle the four types of special elements first. These include positioned content, floating content, compacts and + // run-ins. When we encounter these four types of objects, we don't actually lay them out as normal flow blocks. + bool handled = false; + RenderObject* next = handleSpecialChild(child, marginInfo, compactInfo, handled); + if (handled) { + child = next; + continue; + } + + // The child is a normal flow object. Compute its vertical margins now. + child->calcVerticalMargins(); + + // Do not allow a collapse if the margin top collapse style is set to SEPARATE. + if (child->style()->marginTopCollapse() == MSEPARATE) { + marginInfo.setAtTopOfBlock(false); + marginInfo.clearMargin(); + } + + // Try to guess our correct y position. In most cases this guess will + // be correct. Only if we're wrong (when we compute the real y position) + // will we have to potentially relayout. + int yPosEstimate = estimateVerticalPosition(child, marginInfo); + + // Cache our old rect so that we can dirty the proper repaint rects if the child moves. + IntRect oldRect(child->xPos(), child->yPos() , child->width(), child->height()); + + // Go ahead and position the child as though it didn't collapse with the top. + view()->addLayoutDelta(IntSize(0, child->yPos() - yPosEstimate)); + child->setPos(child->xPos(), yPosEstimate); + + bool markDescendantsWithFloats = false; + if (yPosEstimate != oldRect.y() && !child->avoidsFloats() && child->containsFloats()) + markDescendantsWithFloats = true; + else if (!child->avoidsFloats() || child->shrinkToAvoidFloats()) { + // If an element might be affected by the presence of floats, then always mark it for + // layout. + int fb = max(previousFloatBottom, floatBottom()); + if (fb > m_height || fb > yPosEstimate) + markDescendantsWithFloats = true; + } + + if (markDescendantsWithFloats) + child->markAllDescendantsWithFloatsForLayout(); + + if (child->isRenderBlock()) + previousFloatBottom = max(previousFloatBottom, oldRect.y() + static_cast<RenderBlock*>(child)->floatBottom()); + + bool childHadLayout = child->m_everHadLayout; + bool childNeededLayout = child->needsLayout(); + if (childNeededLayout) + child->layout(); + + // Now determine the correct ypos based off examination of collapsing margin + // values. + collapseMargins(child, marginInfo, yPosEstimate); + + // Now check for clear. + clearFloatsIfNeeded(child, marginInfo, oldTopPosMargin, oldTopNegMargin); + + // We are no longer at the top of the block if we encounter a non-empty child. + // This has to be done after checking for clear, so that margins can be reset if a clear occurred. + if (marginInfo.atTopOfBlock() && !child->isSelfCollapsingBlock()) + marginInfo.setAtTopOfBlock(false); + + // Now place the child in the correct horizontal position + determineHorizontalPosition(child); + + // Update our height now that the child has been placed in the correct position. + m_height += child->height(); + if (child->style()->marginBottomCollapse() == MSEPARATE) { + m_height += child->marginBottom(); + marginInfo.clearMargin(); + } + // If the child has overhanging floats that intrude into following siblings (or possibly out + // of this block), then the parent gets notified of the floats now. + maxFloatBottom = max(maxFloatBottom, addOverhangingFloats(static_cast<RenderBlock *>(child), -child->xPos(), -child->yPos(), !childNeededLayout)); + + // Update our overflow in case the child spills out the block. + m_overflowTop = min(m_overflowTop, child->yPos() + child->overflowTop(false)); + m_overflowHeight = max(m_overflowHeight, m_height + child->overflowHeight(false) - child->height()); + m_overflowWidth = max(child->xPos() + child->overflowWidth(false), m_overflowWidth); + m_overflowLeft = min(child->xPos() + child->overflowLeft(false), m_overflowLeft); + + // Insert our compact into the block margin if we have one. + insertCompactIfNeeded(child, compactInfo); + + IntSize childOffset(child->xPos() - oldRect.x(), child->yPos() - oldRect.y()); + if (childOffset.width() || childOffset.height()) { + view()->addLayoutDelta(childOffset); + + // If the child moved, we have to repaint it as well as any floating/positioned + // descendants. An exception is if we need a layout. In this case, we know we're going to + // repaint ourselves (and the child) anyway. + if (childHadLayout && !selfNeedsLayout() && child->checkForRepaintDuringLayout()) + child->repaintDuringLayoutIfMoved(oldRect); + } + + if (!childHadLayout && child->checkForRepaintDuringLayout()) + child->repaint(); + + child = child->nextSibling(); + } + + // Now do the handling of the bottom of the block, adding in our bottom border/padding and + // determining the correct collapsed bottom margin information. + handleBottomOfBlock(top, bottom, marginInfo); +} + +bool RenderBlock::layoutOnlyPositionedObjects() +{ + if (!posChildNeedsLayout() || normalChildNeedsLayout() || selfNeedsLayout()) + return false; + + LayoutStateMaintainer statePusher(view(), this, IntSize(xPos(), yPos()), !m_hasColumns); + + if (needsPositionedMovementLayout()) { + tryLayoutDoingPositionedMovementOnly(); + if (needsLayout()) + return false; + } + + // All we have to is lay out our positioned objects. + layoutPositionedObjects(false); + + statePusher.pop(); + + if (hasOverflowClip()) + m_layer->updateScrollInfoAfterLayout(); + + setNeedsLayout(false); + return true; +} + +void RenderBlock::layoutPositionedObjects(bool relayoutChildren) +{ + if (m_positionedObjects) { + RenderObject* r; + Iterator end = m_positionedObjects->end(); + for (Iterator it = m_positionedObjects->begin(); it != end; ++it) { + r = *it; + // When a non-positioned block element moves, it may have positioned children that are implicitly positioned relative to the + // non-positioned block. Rather than trying to detect all of these movement cases, we just always lay out positioned + // objects that are positioned implicitly like this. Such objects are rare, and so in typical DHTML menu usage (where everything is + // positioned explicitly) this should not incur a performance penalty. + if (relayoutChildren || (r->hasStaticY() && r->parent() != this && r->parent()->isBlockFlow())) + r->setChildNeedsLayout(true, false); + + // If relayoutChildren is set and we have percentage padding, we also need to invalidate the child's pref widths. + if (relayoutChildren && (r->style()->paddingLeft().isPercent() || r->style()->paddingRight().isPercent())) + r->setPrefWidthsDirty(true, false); + + // We don't have to do a full layout. We just have to update our position. Try that first. If we have shrink-to-fit width + // and we hit the available width constraint, the layoutIfNeeded() will catch it and do a full layout. + if (r->needsPositionedMovementLayoutOnly()) + r->tryLayoutDoingPositionedMovementOnly(); + r->layoutIfNeeded(); + } + } +} + +void RenderBlock::markPositionedObjectsForLayout() +{ + if (m_positionedObjects) { + RenderObject* r; + Iterator end = m_positionedObjects->end(); + for (Iterator it = m_positionedObjects->begin(); it != end; ++it) { + r = *it; + r->setChildNeedsLayout(true); + } + } +} + +void RenderBlock::repaintOverhangingFloats(bool paintAllDescendants) +{ + // Repaint any overhanging floats (if we know we're the one to paint them). + if (hasOverhangingFloats()) { + // We think that we must be in a bad state if m_floatingObjects is nil at this point, so + // we assert on Debug builds and nil-check Release builds. + ASSERT(m_floatingObjects); + if (!m_floatingObjects) + return; + + FloatingObject* r; + DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); + + // FIXME: Avoid disabling LayoutState. At the very least, don't disable it for floats originating + // in this block. Better yet would be to push extra state for the containers of other floats. + view()->disableLayoutState(); + for ( ; (r = it.current()); ++it) { + // Only repaint the object if it is overhanging, is not in its own layer, and + // is our responsibility to paint (m_shouldPaint is set). When paintAllDescendants is true, the latter + // condition is replaced with being a descendant of us. + if (r->m_bottom > m_height && (paintAllDescendants && r->m_renderer->isDescendantOf(this) || r->m_shouldPaint) && !r->m_renderer->hasLayer()) { + r->m_renderer->repaint(); + r->m_renderer->repaintOverhangingFloats(); + } + } + view()->enableLayoutState(); + } +} + +void RenderBlock::paint(PaintInfo& paintInfo, int tx, int ty) +{ + tx += m_x; + ty += m_y; + + PaintPhase phase = paintInfo.phase; + + // Check if we need to do anything at all. + // FIXME: Could eliminate the isRoot() check if we fix background painting so that the RenderView + // paints the root's background. + if (!isInlineFlow() && !isRoot()) { + IntRect overflowBox = overflowRect(false); + overflowBox.inflate(maximalOutlineSize(paintInfo.phase)); + overflowBox.move(tx, ty); + if (!overflowBox.intersects(paintInfo.rect)) + return; + } + + bool useControlClip = phase != PaintPhaseBlockBackground && phase != PaintPhaseSelfOutline && phase != PaintPhaseMask && hasControlClip(); + + // Push a clip. + if (useControlClip) { + if (phase == PaintPhaseOutline) + paintInfo.phase = PaintPhaseChildOutlines; + else if (phase == PaintPhaseChildBlockBackground) { + paintInfo.phase = PaintPhaseBlockBackground; + paintObject(paintInfo, tx, ty); + paintInfo.phase = PaintPhaseChildBlockBackgrounds; + } + IntRect clipRect(controlClipRect(tx, ty)); + if (clipRect.isEmpty()) + return; + paintInfo.context->save(); + paintInfo.context->clip(clipRect); + } + + paintObject(paintInfo, tx, ty); + + // Pop the clip. + if (useControlClip) { + paintInfo.context->restore(); + if (phase == PaintPhaseOutline) { + paintInfo.phase = PaintPhaseSelfOutline; + paintObject(paintInfo, tx, ty); + paintInfo.phase = phase; + } else if (phase == PaintPhaseChildBlockBackground) + paintInfo.phase = phase; + } +} + +void RenderBlock::paintColumns(PaintInfo& paintInfo, int tx, int ty, bool paintingFloats) +{ + // We need to do multiple passes, breaking up our child painting into strips. + GraphicsContext* context = paintInfo.context; + int currXOffset = 0; + int currYOffset = 0; + int ruleAdd = borderLeft() + paddingLeft(); + int ruleX = 0; + int colGap = columnGap(); + const Color& ruleColor = style()->columnRuleColor(); + bool ruleTransparent = style()->columnRuleIsTransparent(); + EBorderStyle ruleStyle = style()->columnRuleStyle(); + int ruleWidth = style()->columnRuleWidth(); + bool renderRule = !paintingFloats && ruleStyle > BHIDDEN && !ruleTransparent && ruleWidth <= colGap; + Vector<IntRect>* colRects = columnRects(); + unsigned colCount = colRects->size(); + for (unsigned i = 0; i < colCount; i++) { + // For each rect, we clip to the rect, and then we adjust our coords. + IntRect colRect = colRects->at(i); + colRect.move(tx, ty); + context->save(); + + // Each strip pushes a clip, since column boxes are specified as being + // like overflow:hidden. + context->clip(colRect); + + // Adjust tx and ty to change where we paint. + PaintInfo info(paintInfo); + info.rect.intersect(colRect); + + // Adjust our x and y when painting. + int finalX = tx + currXOffset; + int finalY = ty + currYOffset; + if (paintingFloats) + paintFloats(info, finalX, finalY, paintInfo.phase == PaintPhaseSelection || paintInfo.phase == PaintPhaseTextClip); + else + paintContents(info, finalX, finalY); + + // Move to the next position. + if (style()->direction() == LTR) { + ruleX += colRect.width() + colGap / 2; + currXOffset += colRect.width() + colGap; + } else { + ruleX -= (colRect.width() + colGap / 2); + currXOffset -= (colRect.width() + colGap); + } + + currYOffset -= colRect.height(); + + context->restore(); + + // Now paint the column rule. + if (renderRule && paintInfo.phase == PaintPhaseForeground && i < colCount - 1) { + int ruleStart = ruleX - ruleWidth / 2 + ruleAdd; + int ruleEnd = ruleStart + ruleWidth; + drawBorder(paintInfo.context, tx + ruleStart, ty + borderTop() + paddingTop(), tx + ruleEnd, ty + borderTop() + paddingTop() + contentHeight(), + style()->direction() == LTR ? BSLeft : BSRight, ruleColor, style()->color(), ruleStyle, 0, 0); + } + + ruleX = currXOffset; + } +} + +void RenderBlock::paintContents(PaintInfo& paintInfo, int tx, int ty) +{ + // Avoid painting descendants of the root element when stylesheets haven't loaded. This eliminates FOUC. + // It's ok not to draw, because later on, when all the stylesheets do load, updateStyleSelector on the Document + // will do a full repaint(). + if (document()->didLayoutWithPendingStylesheets() && !isRenderView()) + return; + + if (childrenInline()) + paintLines(paintInfo, tx, ty); + else + paintChildren(paintInfo, tx, ty); +} + +void RenderBlock::paintChildren(PaintInfo& paintInfo, int tx, int ty) +{ + PaintPhase newPhase = (paintInfo.phase == PaintPhaseChildOutlines) ? PaintPhaseOutline : paintInfo.phase; + newPhase = (newPhase == PaintPhaseChildBlockBackgrounds) ? PaintPhaseChildBlockBackground : newPhase; + + // We don't paint our own background, but we do let the kids paint their backgrounds. + PaintInfo info(paintInfo); + info.phase = newPhase; + info.paintingRoot = paintingRootForChildren(paintInfo); + bool isPrinting = document()->printing(); + + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { + // Check for page-break-before: always, and if it's set, break and bail. + if (isPrinting && !childrenInline() && child->style()->pageBreakBefore() == PBALWAYS && + inRootBlockContext() && (ty + child->yPos()) > paintInfo.rect.y() && + (ty + child->yPos()) < paintInfo.rect.bottom()) { + view()->setBestTruncatedAt(ty + child->yPos(), this, true); + return; + } + + if (!child->hasLayer() && !child->isFloating()) + child->paint(info, tx, ty); + + // Check for page-break-after: always, and if it's set, break and bail. + if (isPrinting && !childrenInline() && child->style()->pageBreakAfter() == PBALWAYS && + inRootBlockContext() && (ty + child->yPos() + child->height()) > paintInfo.rect.y() && + (ty + child->yPos() + child->height()) < paintInfo.rect.bottom()) { + view()->setBestTruncatedAt(ty + child->yPos() + child->height() + max(0, child->collapsedMarginBottom()), this, true); + return; + } + } +} + +void RenderBlock::paintCaret(PaintInfo& paintInfo, int tx, int ty, CaretType type) +{ + SelectionController* selection = type == CursorCaret ? document()->frame()->selection() : document()->frame()->dragCaretController(); + + // Ask the SelectionController if the caret should be painted by this block + RenderObject* caretPainter = selection->caretRenderer(); + if (caretPainter == this && selection->isContentEditable()) { + // Convert the painting offset into the local coordinate system of this renderer, + // to match the localCaretRect computed by the SelectionController + offsetForContents(tx, ty); + + if (type == CursorCaret) + document()->frame()->paintCaret(paintInfo.context, tx, ty, paintInfo.rect); + else + document()->frame()->paintDragCaret(paintInfo.context, tx, ty, paintInfo.rect); + } +} + +void RenderBlock::paintObject(PaintInfo& paintInfo, int tx, int ty) +{ + PaintPhase paintPhase = paintInfo.phase; + + // If we're a repositioned run-in or a compact, don't paint background/borders. + bool inlineFlow = isInlineFlow(); + + // 1. paint background, borders etc + if (!inlineFlow && + (paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && + hasBoxDecorations() && style()->visibility() == VISIBLE) { + paintBoxDecorations(paintInfo, tx, ty); + } + + if (paintPhase == PaintPhaseMask && style()->visibility() == VISIBLE) { + paintMask(paintInfo, tx, ty); + return; + } + + // We're done. We don't bother painting any children. + if (paintPhase == PaintPhaseBlockBackground) + return; + + // Adjust our painting position if we're inside a scrolled layer (e.g., an overflow:auto div).s + int scrolledX = tx; + int scrolledY = ty; + if (hasOverflowClip()) + m_layer->subtractScrolledContentOffset(scrolledX, scrolledY); + + // 2. paint contents + if (paintPhase != PaintPhaseSelfOutline) { + if (m_hasColumns) + paintColumns(paintInfo, scrolledX, scrolledY); + else + paintContents(paintInfo, scrolledX, scrolledY); + } + + // 3. paint selection + // FIXME: Make this work with multi column layouts. For now don't fill gaps. + bool isPrinting = document()->printing(); + if (!inlineFlow && !isPrinting && !m_hasColumns) + paintSelection(paintInfo, scrolledX, scrolledY); // Fill in gaps in selection on lines and between blocks. + + // 4. paint floats. + if (!inlineFlow && (paintPhase == PaintPhaseFloat || paintPhase == PaintPhaseSelection || paintPhase == PaintPhaseTextClip)) { + if (m_hasColumns) + paintColumns(paintInfo, scrolledX, scrolledY, true); + else + paintFloats(paintInfo, scrolledX, scrolledY, paintPhase == PaintPhaseSelection || paintPhase == PaintPhaseTextClip); + } + + // 5. paint outline. + if (!inlineFlow && (paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseSelfOutline) && hasOutline() && style()->visibility() == VISIBLE) + RenderObject::paintOutline(paintInfo.context, tx, ty, width(), height(), style()); + + // 6. paint continuation outlines. + if (!inlineFlow && (paintPhase == PaintPhaseOutline || paintPhase == PaintPhaseChildOutlines)) { + if (continuation() && continuation()->hasOutline() && continuation()->style()->visibility() == VISIBLE) { + RenderFlow* inlineFlow = static_cast<RenderFlow*>(continuation()->element()->renderer()); + if (!inlineFlow->hasLayer()) + containingBlock()->addContinuationWithOutline(inlineFlow); + else if (!inlineFlow->firstLineBox()) + inlineFlow->paintOutline(paintInfo.context, tx - xPos() + inlineFlow->containingBlock()->xPos(), + ty - yPos() + inlineFlow->containingBlock()->yPos()); + } + paintContinuationOutlines(paintInfo, tx, ty); + } + + // 7. paint caret. + // If the caret's node's render object's containing block is this block, and the paint action is PaintPhaseForeground, + // then paint the caret. + if (!inlineFlow && paintPhase == PaintPhaseForeground) { + paintCaret(paintInfo, scrolledX, scrolledY, CursorCaret); + paintCaret(paintInfo, scrolledX, scrolledY, DragCaret); + } +} + +void RenderBlock::paintFloats(PaintInfo& paintInfo, int tx, int ty, bool preservePhase) +{ + if (!m_floatingObjects) + return; + + FloatingObject* r; + DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); + for (; (r = it.current()); ++it) { + // Only paint the object if our m_shouldPaint flag is set. + if (r->m_shouldPaint && !r->m_renderer->hasLayer()) { + PaintInfo currentPaintInfo(paintInfo); + currentPaintInfo.phase = preservePhase ? paintInfo.phase : PaintPhaseBlockBackground; + int currentTX = tx + r->m_left - r->m_renderer->xPos() + r->m_renderer->marginLeft(); + int currentTY = ty + r->m_top - r->m_renderer->yPos() + r->m_renderer->marginTop(); + r->m_renderer->paint(currentPaintInfo, currentTX, currentTY); + if (!preservePhase) { + currentPaintInfo.phase = PaintPhaseChildBlockBackgrounds; + r->m_renderer->paint(currentPaintInfo, currentTX, currentTY); + currentPaintInfo.phase = PaintPhaseFloat; + r->m_renderer->paint(currentPaintInfo, currentTX, currentTY); + currentPaintInfo.phase = PaintPhaseForeground; + r->m_renderer->paint(currentPaintInfo, currentTX, currentTY); + currentPaintInfo.phase = PaintPhaseOutline; + r->m_renderer->paint(currentPaintInfo, currentTX, currentTY); + } + } + } +} + +void RenderBlock::paintEllipsisBoxes(PaintInfo& paintInfo, int tx, int ty) +{ + if (!shouldPaintWithinRoot(paintInfo) || !firstLineBox()) + return; + + if (style()->visibility() == VISIBLE && paintInfo.phase == PaintPhaseForeground) { + // We can check the first box and last box and avoid painting if we don't + // intersect. + int yPos = ty + firstLineBox()->yPos(); + int h = lastLineBox()->yPos() + lastLineBox()->height() - firstLineBox()->yPos(); + if (yPos >= paintInfo.rect.bottom() || yPos + h <= paintInfo.rect.y()) + return; + + // See if our boxes intersect with the dirty rect. If so, then we paint + // them. Note that boxes can easily overlap, so we can't make any assumptions + // based off positions of our first line box or our last line box. + for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) { + yPos = ty + curr->yPos(); + h = curr->height(); + if (curr->ellipsisBox() && yPos < paintInfo.rect.bottom() && yPos + h > paintInfo.rect.y()) + curr->paintEllipsisBox(paintInfo, tx, ty); + } + } +} + +ContinuationOutlineTableMap* continuationOutlineTable() +{ + DEFINE_STATIC_LOCAL(ContinuationOutlineTableMap, table, ()); + return &table; +} + +void RenderBlock::addContinuationWithOutline(RenderFlow* flow) +{ + // We can't make this work if the inline is in a layer. We'll just rely on the broken + // way of painting. + ASSERT(!flow->layer()); + + ContinuationOutlineTableMap* table = continuationOutlineTable(); + RenderFlowSequencedSet* continuations = table->get(this); + if (!continuations) { + continuations = new RenderFlowSequencedSet; + table->set(this, continuations); + } + + continuations->add(flow); +} + +void RenderBlock::paintContinuationOutlines(PaintInfo& info, int tx, int ty) +{ + ContinuationOutlineTableMap* table = continuationOutlineTable(); + if (table->isEmpty()) + return; + + RenderFlowSequencedSet* continuations = table->get(this); + if (!continuations) + return; + + // Paint each continuation outline. + RenderFlowSequencedSet::iterator end = continuations->end(); + for (RenderFlowSequencedSet::iterator it = continuations->begin(); it != end; ++it) { + // Need to add in the coordinates of the intervening blocks. + RenderFlow* flow = *it; + RenderBlock* block = flow->containingBlock(); + for ( ; block && block != this; block = block->containingBlock()) { + tx += block->xPos(); + ty += block->yPos(); + } + ASSERT(block); + flow->paintOutline(info.context, tx, ty); + } + + // Delete + delete continuations; + table->remove(this); +} + +void RenderBlock::setSelectionState(SelectionState s) +{ + if (selectionState() == s) + return; + + if (s == SelectionInside && selectionState() != SelectionNone) + return; + + if ((s == SelectionStart && selectionState() == SelectionEnd) || + (s == SelectionEnd && selectionState() == SelectionStart)) + m_selectionState = SelectionBoth; + else + m_selectionState = s; + + RenderBlock* cb = containingBlock(); + if (cb && !cb->isRenderView()) + cb->setSelectionState(s); +} + +bool RenderBlock::shouldPaintSelectionGaps() const +{ + return m_selectionState != SelectionNone && style()->visibility() == VISIBLE && isSelectionRoot(); +} + +bool RenderBlock::isSelectionRoot() const +{ + if (!element()) + return false; + + // FIXME: Eventually tables should have to learn how to fill gaps between cells, at least in simple non-spanning cases. + if (isTable()) + return false; + + if (isBody() || isRoot() || hasOverflowClip() || isRelPositioned() || + isFloatingOrPositioned() || isTableCell() || isInlineBlockOrInlineTable() || hasTransform() || + hasReflection() || hasMask()) + return true; + + if (view() && view()->selectionStart()) { + Node* startElement = view()->selectionStart()->element(); + if (startElement && startElement->rootEditableElement() == element()) + return true; + } + + return false; +} + +GapRects RenderBlock::selectionGapRects() +{ + ASSERT(!needsLayout()); + + if (!shouldPaintSelectionGaps()) + return GapRects(); + + // FIXME: this is broken with transforms + FloatPoint absContentPoint = localToAbsoluteForContent(FloatPoint()); + if (hasOverflowClip()) + absContentPoint -= layer()->scrolledContentOffset(); + + int lastTop = -borderTopExtra(); + int lastLeft = leftSelectionOffset(this, lastTop); + int lastRight = rightSelectionOffset(this, lastTop); + + return fillSelectionGaps(this, absContentPoint.x(), absContentPoint.y(), absContentPoint.x(), absContentPoint.y(), lastTop, lastLeft, lastRight); +} + +void RenderBlock::paintSelection(PaintInfo& paintInfo, int tx, int ty) +{ + if (shouldPaintSelectionGaps() && paintInfo.phase == PaintPhaseForeground) { + int lastTop = -borderTopExtra(); + int lastLeft = leftSelectionOffset(this, lastTop); + int lastRight = rightSelectionOffset(this, lastTop); + paintInfo.context->save(); + fillSelectionGaps(this, tx, ty, tx, ty, lastTop, lastLeft, lastRight, &paintInfo); + paintInfo.context->restore(); + } +} + +static void clipOutPositionedObjects(const RenderObject::PaintInfo* paintInfo, int tx, int ty, ListHashSet<RenderObject*>* positionedObjects) +{ + if (!positionedObjects) + return; + + ListHashSet<RenderObject*>::const_iterator end = positionedObjects->end(); + for (ListHashSet<RenderObject*>::const_iterator it = positionedObjects->begin(); it != end; ++it) { + RenderObject* r = *it; + paintInfo->context->clipOut(IntRect(tx + r->xPos(), ty + r->yPos(), r->width(), r->height())); + } +} + +GapRects RenderBlock::fillSelectionGaps(RenderBlock* rootBlock, int blockX, int blockY, int tx, int ty, + int& lastTop, int& lastLeft, int& lastRight, const PaintInfo* paintInfo) +{ + // IMPORTANT: Callers of this method that intend for painting to happen need to do a save/restore. + // Clip out floating and positioned objects when painting selection gaps. + if (paintInfo) { + // Note that we don't clip out overflow for positioned objects. We just stick to the border box. + clipOutPositionedObjects(paintInfo, tx, ty, m_positionedObjects); + if (isBody() || isRoot()) // The <body> must make sure to examine its containingBlock's positioned objects. + for (RenderBlock* cb = containingBlock(); cb && !cb->isRenderView(); cb = cb->containingBlock()) + clipOutPositionedObjects(paintInfo, cb->xPos(), cb->yPos(), cb->m_positionedObjects); + if (m_floatingObjects) { + for (DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); it.current(); ++it) { + FloatingObject* r = it.current(); + paintInfo->context->clipOut(IntRect(tx + r->m_left + r->m_renderer->marginLeft(), + ty + r->m_top + r->m_renderer->marginTop(), + r->m_renderer->width(), r->m_renderer->height())); + } + } + } + + // FIXME: overflow: auto/scroll regions need more math here, since painting in the border box is different from painting in the padding box (one is scrolled, the other is + // fixed). + GapRects result; + if (!isBlockFlow()) // FIXME: Make multi-column selection gap filling work someday. + return result; + + if (m_hasColumns || hasTransform()) { + // FIXME: We should learn how to gap fill multiple columns and transforms eventually. + lastTop = (ty - blockY) + height(); + lastLeft = leftSelectionOffset(rootBlock, height()); + lastRight = rightSelectionOffset(rootBlock, height()); + return result; + } + + if (childrenInline()) + result = fillInlineSelectionGaps(rootBlock, blockX, blockY, tx, ty, lastTop, lastLeft, lastRight, paintInfo); + else + result = fillBlockSelectionGaps(rootBlock, blockX, blockY, tx, ty, lastTop, lastLeft, lastRight, paintInfo); + + // Go ahead and fill the vertical gap all the way to the bottom of our block if the selection extends past our block. + if (rootBlock == this && (m_selectionState != SelectionBoth && m_selectionState != SelectionEnd)) + result.uniteCenter(fillVerticalSelectionGap(lastTop, lastLeft, lastRight, ty + height() + borderBottomExtra(), + rootBlock, blockX, blockY, paintInfo)); + return result; +} + +GapRects RenderBlock::fillInlineSelectionGaps(RenderBlock* rootBlock, int blockX, int blockY, int tx, int ty, + int& lastTop, int& lastLeft, int& lastRight, const PaintInfo* paintInfo) +{ + GapRects result; + + bool containsStart = selectionState() == SelectionStart || selectionState() == SelectionBoth; + + if (!firstLineBox()) { + if (containsStart) { + // Go ahead and update our lastY to be the bottom of the block. <hr>s or empty blocks with height can trip this + // case. + lastTop = (ty - blockY) + height(); + lastLeft = leftSelectionOffset(rootBlock, height()); + lastRight = rightSelectionOffset(rootBlock, height()); + } + return result; + } + + RootInlineBox* lastSelectedLine = 0; + RootInlineBox* curr; + for (curr = firstRootBox(); curr && !curr->hasSelectedChildren(); curr = curr->nextRootBox()) { } + + // Now paint the gaps for the lines. + for (; curr && curr->hasSelectedChildren(); curr = curr->nextRootBox()) { + int selTop = curr->selectionTop(); + int selHeight = curr->selectionHeight(); + + if (!containsStart && !lastSelectedLine && + selectionState() != SelectionStart && selectionState() != SelectionBoth) + result.uniteCenter(fillVerticalSelectionGap(lastTop, lastLeft, lastRight, ty + selTop, + rootBlock, blockX, blockY, paintInfo)); + + if (!paintInfo || ty + selTop < paintInfo->rect.bottom() && ty + selTop + selHeight > paintInfo->rect.y()) + result.unite(curr->fillLineSelectionGap(selTop, selHeight, rootBlock, blockX, blockY, tx, ty, paintInfo)); + + lastSelectedLine = curr; + } + + if (containsStart && !lastSelectedLine) + // Selection must start just after our last line. + lastSelectedLine = lastRootBox(); + + if (lastSelectedLine && selectionState() != SelectionEnd && selectionState() != SelectionBoth) { + // Go ahead and update our lastY to be the bottom of the last selected line. + lastTop = (ty - blockY) + lastSelectedLine->bottomOverflow(); + lastLeft = leftSelectionOffset(rootBlock, lastSelectedLine->bottomOverflow()); + lastRight = rightSelectionOffset(rootBlock, lastSelectedLine->bottomOverflow()); + } + return result; +} + +GapRects RenderBlock::fillBlockSelectionGaps(RenderBlock* rootBlock, int blockX, int blockY, int tx, int ty, + int& lastTop, int& lastLeft, int& lastRight, const PaintInfo* paintInfo) +{ + GapRects result; + + // Go ahead and jump right to the first block child that contains some selected objects. + RenderObject* curr; + for (curr = firstChild(); curr && curr->selectionState() == SelectionNone; curr = curr->nextSibling()) { } + + for (bool sawSelectionEnd = false; curr && !sawSelectionEnd; curr = curr->nextSibling()) { + SelectionState childState = curr->selectionState(); + if (childState == SelectionBoth || childState == SelectionEnd) + sawSelectionEnd = true; + + if (curr->isFloatingOrPositioned()) + continue; // We must be a normal flow object in order to even be considered. + + if (curr->isRelPositioned() && curr->hasLayer()) { + // If the relposition offset is anything other than 0, then treat this just like an absolute positioned element. + // Just disregard it completely. + IntSize relOffset = curr->layer()->relativePositionOffset(); + if (relOffset.width() || relOffset.height()) + continue; + } + + bool paintsOwnSelection = curr->shouldPaintSelectionGaps() || curr->isTable(); // FIXME: Eventually we won't special-case table like this. + bool fillBlockGaps = paintsOwnSelection || (curr->canBeSelectionLeaf() && childState != SelectionNone); + if (fillBlockGaps) { + // We need to fill the vertical gap above this object. + if (childState == SelectionEnd || childState == SelectionInside) + // Fill the gap above the object. + result.uniteCenter(fillVerticalSelectionGap(lastTop, lastLeft, lastRight, + ty + curr->yPos(), rootBlock, blockX, blockY, paintInfo)); + + // Only fill side gaps for objects that paint their own selection if we know for sure the selection is going to extend all the way *past* + // our object. We know this if the selection did not end inside our object. + if (paintsOwnSelection && (childState == SelectionStart || sawSelectionEnd)) + childState = SelectionNone; + + // Fill side gaps on this object based off its state. + bool leftGap, rightGap; + getHorizontalSelectionGapInfo(childState, leftGap, rightGap); + + if (leftGap) + result.uniteLeft(fillLeftSelectionGap(this, curr->xPos(), curr->yPos(), curr->height(), rootBlock, blockX, blockY, tx, ty, paintInfo)); + if (rightGap) + result.uniteRight(fillRightSelectionGap(this, curr->xPos() + curr->width(), curr->yPos(), curr->height(), rootBlock, blockX, blockY, tx, ty, paintInfo)); + + // Update lastTop to be just underneath the object. lastLeft and lastRight extend as far as + // they can without bumping into floating or positioned objects. Ideally they will go right up + // to the border of the root selection block. + lastTop = (ty - blockY) + (curr->yPos() + curr->height()); + lastLeft = leftSelectionOffset(rootBlock, curr->yPos() + curr->height()); + lastRight = rightSelectionOffset(rootBlock, curr->yPos() + curr->height()); + } else if (childState != SelectionNone) + // We must be a block that has some selected object inside it. Go ahead and recur. + result.unite(static_cast<RenderBlock*>(curr)->fillSelectionGaps(rootBlock, blockX, blockY, tx + curr->xPos(), ty + curr->yPos(), + lastTop, lastLeft, lastRight, paintInfo)); + } + return result; +} + +IntRect RenderBlock::fillHorizontalSelectionGap(RenderObject* selObj, int xPos, int yPos, int width, int height, const PaintInfo* paintInfo) +{ + if (width <= 0 || height <= 0) + return IntRect(); + IntRect gapRect(xPos, yPos, width, height); + if (paintInfo && selObj->style()->visibility() == VISIBLE) + paintInfo->context->fillRect(gapRect, selObj->selectionBackgroundColor()); + return gapRect; +} + +IntRect RenderBlock::fillVerticalSelectionGap(int lastTop, int lastLeft, int lastRight, int bottomY, RenderBlock* rootBlock, + int blockX, int blockY, const PaintInfo* paintInfo) +{ + int top = blockY + lastTop; + int height = bottomY - top; + if (height <= 0) + return IntRect(); + + // Get the selection offsets for the bottom of the gap + int left = blockX + max(lastLeft, leftSelectionOffset(rootBlock, bottomY)); + int right = blockX + min(lastRight, rightSelectionOffset(rootBlock, bottomY)); + int width = right - left; + if (width <= 0) + return IntRect(); + + IntRect gapRect(left, top, width, height); + if (paintInfo) + paintInfo->context->fillRect(gapRect, selectionBackgroundColor()); + return gapRect; +} + +IntRect RenderBlock::fillLeftSelectionGap(RenderObject* selObj, int xPos, int yPos, int height, RenderBlock* rootBlock, + int blockX, int /*blockY*/, int tx, int ty, const PaintInfo* paintInfo) +{ + int top = yPos + ty; + int left = blockX + max(leftSelectionOffset(rootBlock, yPos), leftSelectionOffset(rootBlock, yPos + height)); + int right = min(xPos + tx, blockX + min(rightSelectionOffset(rootBlock, yPos), rightSelectionOffset(rootBlock, yPos + height))); + int width = right - left; + if (width <= 0) + return IntRect(); + + IntRect gapRect(left, top, width, height); + if (paintInfo) + paintInfo->context->fillRect(gapRect, selObj->selectionBackgroundColor()); + return gapRect; +} + +IntRect RenderBlock::fillRightSelectionGap(RenderObject* selObj, int xPos, int yPos, int height, RenderBlock* rootBlock, + int blockX, int /*blockY*/, int tx, int ty, const PaintInfo* paintInfo) +{ + int left = max(xPos + tx, blockX + max(leftSelectionOffset(rootBlock, yPos), leftSelectionOffset(rootBlock, yPos + height))); + int top = yPos + ty; + int right = blockX + min(rightSelectionOffset(rootBlock, yPos), rightSelectionOffset(rootBlock, yPos + height)); + int width = right - left; + if (width <= 0) + return IntRect(); + + IntRect gapRect(left, top, width, height); + if (paintInfo) + paintInfo->context->fillRect(gapRect, selObj->selectionBackgroundColor()); + return gapRect; +} + +void RenderBlock::getHorizontalSelectionGapInfo(SelectionState state, bool& leftGap, bool& rightGap) +{ + bool ltr = style()->direction() == LTR; + leftGap = (state == RenderObject::SelectionInside) || + (state == RenderObject::SelectionEnd && ltr) || + (state == RenderObject::SelectionStart && !ltr); + rightGap = (state == RenderObject::SelectionInside) || + (state == RenderObject::SelectionStart && ltr) || + (state == RenderObject::SelectionEnd && !ltr); +} + +int RenderBlock::leftSelectionOffset(RenderBlock* rootBlock, int y) +{ + int left = leftOffset(y); + if (left == borderLeft() + paddingLeft()) { + if (rootBlock != this) + // The border can potentially be further extended by our containingBlock(). + return containingBlock()->leftSelectionOffset(rootBlock, y + yPos()); + return left; + } + else { + RenderBlock* cb = this; + while (cb != rootBlock) { + left += cb->xPos(); + cb = cb->containingBlock(); + } + } + + return left; +} + +int RenderBlock::rightSelectionOffset(RenderBlock* rootBlock, int y) +{ + int right = rightOffset(y); + if (right == (contentWidth() + (borderLeft() + paddingLeft()))) { + if (rootBlock != this) + // The border can potentially be further extended by our containingBlock(). + return containingBlock()->rightSelectionOffset(rootBlock, y + yPos()); + return right; + } + else { + RenderBlock* cb = this; + while (cb != rootBlock) { + right += cb->xPos(); + cb = cb->containingBlock(); + } + } + return right; +} + +void RenderBlock::insertPositionedObject(RenderObject *o) +{ + // Create the list of special objects if we don't aleady have one + if (!m_positionedObjects) + m_positionedObjects = new ListHashSet<RenderObject*>; + + m_positionedObjects->add(o); +} + +void RenderBlock::removePositionedObject(RenderObject *o) +{ + if (m_positionedObjects) + m_positionedObjects->remove(o); +} + +void RenderBlock::removePositionedObjects(RenderBlock* o) +{ + if (!m_positionedObjects) + return; + + RenderObject* r; + + Iterator end = m_positionedObjects->end(); + + Vector<RenderObject*, 16> deadObjects; + + for (Iterator it = m_positionedObjects->begin(); it != end; ++it) { + r = *it; + if (!o || r->isDescendantOf(o)) { + if (o) + r->setChildNeedsLayout(true, false); + + // It is parent blocks job to add positioned child to positioned objects list of its containing block + // Parent layout needs to be invalidated to ensure this happens. + RenderObject* p = r->parent(); + while (p && !p->isRenderBlock()) + p = p->parent(); + if (p) + p->setChildNeedsLayout(true); + + deadObjects.append(r); + } + } + + for (unsigned i = 0; i < deadObjects.size(); i++) + m_positionedObjects->remove(deadObjects.at(i)); +} + +void RenderBlock::insertFloatingObject(RenderObject *o) +{ + ASSERT(o->isFloating()); + + // Create the list of special objects if we don't aleady have one + if (!m_floatingObjects) { + m_floatingObjects = new DeprecatedPtrList<FloatingObject>; + m_floatingObjects->setAutoDelete(true); + } else { + // Don't insert the object again if it's already in the list + DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); + FloatingObject* f; + while ( (f = it.current()) ) { + if (f->m_renderer == o) return; + ++it; + } + } + + // Create the special object entry & append it to the list + + o->layoutIfNeeded(); + + FloatingObject* newObj = new FloatingObject(o->style()->floating() == FLEFT ? FloatingObject::FloatLeft : FloatingObject::FloatRight); + + newObj->m_top = -1; + newObj->m_bottom = -1; + newObj->m_width = o->width() + o->marginLeft() + o->marginRight(); + newObj->m_shouldPaint = !o->hasLayer(); // If a layer exists, the float will paint itself. Otherwise someone else will. + newObj->m_isDescendant = true; + newObj->m_renderer = o; + + m_floatingObjects->append(newObj); +} + +void RenderBlock::removeFloatingObject(RenderObject *o) +{ + if (m_floatingObjects) { + DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); + while (it.current()) { + if (it.current()->m_renderer == o) { + if (childrenInline()) + markLinesDirtyInVerticalRange(0, it.current()->m_bottom); + m_floatingObjects->removeRef(it.current()); + } + ++it; + } + } +} + +bool RenderBlock::positionNewFloats() +{ + if (!m_floatingObjects) + return false; + + FloatingObject* f = m_floatingObjects->last(); + + // If all floats have already been positioned, then we have no work to do. + if (!f || f->m_top != -1) + return false; + + // Move backwards through our floating object list until we find a float that has + // already been positioned. Then we'll be able to move forward, positioning all of + // the new floats that need it. + FloatingObject* lastFloat = m_floatingObjects->getPrev(); + while (lastFloat && lastFloat->m_top == -1) { + f = m_floatingObjects->prev(); + lastFloat = m_floatingObjects->getPrev(); + } + + int y = m_height; + + // The float cannot start above the y position of the last positioned float. + if (lastFloat) + y = max(lastFloat->m_top, y); + + // Now walk through the set of unpositioned floats and place them. + while (f) { + // The containing block is responsible for positioning floats, so if we have floats in our + // list that come from somewhere else, do not attempt to position them. + if (f->m_renderer->containingBlock() != this) { + f = m_floatingObjects->next(); + continue; + } + + RenderObject* o = f->m_renderer; + int _height = o->height() + o->marginTop() + o->marginBottom(); + + int ro = rightOffset(); // Constant part of right offset. + int lo = leftOffset(); // Constat part of left offset. + int fwidth = f->m_width; // The width we look for. + if (ro - lo < fwidth) + fwidth = ro - lo; // Never look for more than what will be available. + + IntRect oldRect(o->xPos(), o->yPos() , o->width(), o->height()); + + if (o->style()->clear() & CLEFT) + y = max(leftBottom(), y); + if (o->style()->clear() & CRIGHT) + y = max(rightBottom(), y); + + if (o->style()->floating() == FLEFT) { + int heightRemainingLeft = 1; + int heightRemainingRight = 1; + int fx = leftRelOffset(y,lo, false, &heightRemainingLeft); + while (rightRelOffset(y,ro, false, &heightRemainingRight)-fx < fwidth) { + y += min(heightRemainingLeft, heightRemainingRight); + fx = leftRelOffset(y,lo, false, &heightRemainingLeft); + } + fx = max(0, fx); + f->m_left = fx; + o->setPos(fx + o->marginLeft(), y + o->marginTop()); + } else { + int heightRemainingLeft = 1; + int heightRemainingRight = 1; + int fx = rightRelOffset(y,ro, false, &heightRemainingRight); + while (fx - leftRelOffset(y,lo, false, &heightRemainingLeft) < fwidth) { + y += min(heightRemainingLeft, heightRemainingRight); + fx = rightRelOffset(y, ro, false, &heightRemainingRight); + } + f->m_left = fx - f->m_width; + o->setPos(fx - o->marginRight() - o->width(), y + o->marginTop()); + } + + f->m_top = y; + f->m_bottom = f->m_top + _height; + + // If the child moved, we have to repaint it. + if (o->checkForRepaintDuringLayout()) + o->repaintDuringLayoutIfMoved(oldRect); + + f = m_floatingObjects->next(); + } + return true; +} + +void RenderBlock::newLine(EClear clear) +{ + positionNewFloats(); + // set y position + int newY = 0; + switch(clear) + { + case CLEFT: + newY = leftBottom(); + break; + case CRIGHT: + newY = rightBottom(); + break; + case CBOTH: + newY = floatBottom(); + default: + break; + } + if (m_height < newY) + m_height = newY; +} + +void RenderBlock::addPercentHeightDescendant(RenderBox* descendant) +{ + if (!gPercentHeightDescendantsMap) { + gPercentHeightDescendantsMap = new PercentHeightDescendantsMap; + gPercentHeightContainerMap = new PercentHeightContainerMap; + } + + HashSet<RenderBox*>* descendantSet = gPercentHeightDescendantsMap->get(this); + if (!descendantSet) { + descendantSet = new HashSet<RenderBox*>; + gPercentHeightDescendantsMap->set(this, descendantSet); + } + bool added = descendantSet->add(descendant).second; + if (!added) { + ASSERT(gPercentHeightContainerMap->get(descendant)); + ASSERT(gPercentHeightContainerMap->get(descendant)->contains(this)); + return; + } + + HashSet<RenderBlock*>* containerSet = gPercentHeightContainerMap->get(descendant); + if (!containerSet) { + containerSet = new HashSet<RenderBlock*>; + gPercentHeightContainerMap->set(descendant, containerSet); + } + ASSERT(!containerSet->contains(this)); + containerSet->add(this); +} + +void RenderBlock::removePercentHeightDescendant(RenderBox* descendant) +{ + if (!gPercentHeightContainerMap) + return; + + HashSet<RenderBlock*>* containerSet = gPercentHeightContainerMap->take(descendant); + if (!containerSet) + return; + + HashSet<RenderBlock*>::iterator end = containerSet->end(); + for (HashSet<RenderBlock*>::iterator it = containerSet->begin(); it != end; ++it) { + RenderBlock* container = *it; + HashSet<RenderBox*>* descendantSet = gPercentHeightDescendantsMap->get(container); + ASSERT(descendantSet); + if (!descendantSet) + continue; + ASSERT(descendantSet->contains(descendant)); + descendantSet->remove(descendant); + if (descendantSet->isEmpty()) { + gPercentHeightDescendantsMap->remove(container); + delete descendantSet; + } + } + + delete containerSet; +} + +int +RenderBlock::leftOffset() const +{ + return borderLeft()+paddingLeft(); +} + +int +RenderBlock::leftRelOffset(int y, int fixedOffset, bool applyTextIndent, + int *heightRemaining ) const +{ + int left = fixedOffset; + if (m_floatingObjects) { + if ( heightRemaining ) *heightRemaining = 1; + FloatingObject* r; + DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); + for ( ; (r = it.current()); ++it ) + { + if (r->m_top <= y && r->m_bottom > y && + r->type() == FloatingObject::FloatLeft && + r->m_left + r->m_width > left) { + left = r->m_left + r->m_width; + if ( heightRemaining ) *heightRemaining = r->m_bottom - y; + } + } + } + + if (applyTextIndent && m_firstLine && style()->direction() == LTR) { + int cw = 0; + if (style()->textIndent().isPercent()) + cw = containingBlock()->availableWidth(); + left += style()->textIndent().calcMinValue(cw); + } + + return left; +} + +int +RenderBlock::rightOffset() const +{ + return borderLeft() + paddingLeft() + availableWidth(); +} + +int +RenderBlock::rightRelOffset(int y, int fixedOffset, bool applyTextIndent, + int *heightRemaining ) const +{ + int right = fixedOffset; + + if (m_floatingObjects) { + if (heightRemaining) *heightRemaining = 1; + FloatingObject* r; + DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); + for ( ; (r = it.current()); ++it ) + { + if (r->m_top <= y && r->m_bottom > y && + r->type() == FloatingObject::FloatRight && + r->m_left < right) { + right = r->m_left; + if ( heightRemaining ) *heightRemaining = r->m_bottom - y; + } + } + } + + if (applyTextIndent && m_firstLine && style()->direction() == RTL) { + int cw = 0; + if (style()->textIndent().isPercent()) + cw = containingBlock()->availableWidth(); + right -= style()->textIndent().calcMinValue(cw); + } + + return right; +} + +int +RenderBlock::lineWidth(int y) const +{ + int result = rightOffset(y) - leftOffset(y); + return (result < 0) ? 0 : result; +} + +int RenderBlock::nextFloatBottomBelow(int height) const +{ + if (!m_floatingObjects) + return 0; + + int bottom = INT_MAX; + FloatingObject* r; + DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); + for ( ; (r = it.current()); ++it) { + if (r->m_bottom > height) + bottom = min(r->m_bottom, bottom); + } + + return bottom == INT_MAX ? 0 : bottom; +} + +int +RenderBlock::floatBottom() const +{ + if (!m_floatingObjects) return 0; + int bottom=0; + FloatingObject* r; + DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); + for ( ; (r = it.current()); ++it ) + if (r->m_bottom>bottom) + bottom=r->m_bottom; + return bottom; +} + +IntRect RenderBlock::floatRect() const +{ + IntRect result; + if (!m_floatingObjects || hasOverflowClip()) + return result; + FloatingObject* r; + DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); + for (; (r = it.current()); ++it) { + if (r->m_shouldPaint && !r->m_renderer->hasLayer()) { + IntRect childRect = r->m_renderer->overflowRect(false); + childRect.move(r->m_left + r->m_renderer->marginLeft(), r->m_top + r->m_renderer->marginTop()); + result.unite(childRect); + } + } + + return result; +} + +int RenderBlock::lowestPosition(bool includeOverflowInterior, bool includeSelf) const +{ + int bottom = RenderFlow::lowestPosition(includeOverflowInterior, includeSelf); + if (!includeOverflowInterior && hasOverflowClip()) + return bottom; + + int relativeOffset = includeSelf && isRelPositioned() ? relativePositionOffsetY() : 0; + + if (includeSelf) + bottom = max(bottom, m_overflowHeight + relativeOffset); + + if (m_positionedObjects) { + RenderObject* r; + Iterator end = m_positionedObjects->end(); + for (Iterator it = m_positionedObjects->begin(); it != end; ++it) { + r = *it; + // Fixed positioned objects do not scroll and thus should not constitute + // part of the lowest position. + if (r->style()->position() != FixedPosition) { + // FIXME: Should work for overflow sections too. + // If a positioned object lies completely to the left of the root it will be unreachable via scrolling. + // Therefore we should not allow it to contribute to the lowest position. + if (!isRenderView() || r->xPos() + r->width() > 0 || r->xPos() + r->rightmostPosition(false) > 0) { + int lp = r->yPos() + r->lowestPosition(false); + bottom = max(bottom, lp + relativeOffset); + } + } + } + } + + if (m_hasColumns) { + Vector<IntRect>* colRects = columnRects(); + for (unsigned i = 0; i < colRects->size(); i++) + bottom = max(bottom, colRects->at(i).bottom() + relativeOffset); + return bottom; + } + + if (m_floatingObjects) { + FloatingObject* r; + DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); + for ( ; (r = it.current()); ++it ) { + if (r->m_shouldPaint || r->m_renderer->hasLayer()) { + int lp = r->m_top + r->m_renderer->marginTop() + r->m_renderer->lowestPosition(false); + bottom = max(bottom, lp + relativeOffset); + } + } + } + + + if (!includeSelf && lastLineBox()) { + int lp = lastLineBox()->yPos() + lastLineBox()->height(); + bottom = max(bottom, lp); + } + + return bottom; +} + +int RenderBlock::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const +{ + int right = RenderFlow::rightmostPosition(includeOverflowInterior, includeSelf); + if (!includeOverflowInterior && hasOverflowClip()) + return right; + + int relativeOffset = includeSelf && isRelPositioned() ? relativePositionOffsetX() : 0; + + if (includeSelf) + right = max(right, m_overflowWidth + relativeOffset); + + if (m_positionedObjects) { + RenderObject* r; + Iterator end = m_positionedObjects->end(); + for (Iterator it = m_positionedObjects->begin() ; it != end; ++it) { + r = *it; + // Fixed positioned objects do not scroll and thus should not constitute + // part of the rightmost position. + if (r->style()->position() != FixedPosition) { + // FIXME: Should work for overflow sections too. + // If a positioned object lies completely above the root it will be unreachable via scrolling. + // Therefore we should not allow it to contribute to the rightmost position. + if (!isRenderView() || r->yPos() + r->height() > 0 || r->yPos() + r->lowestPosition(false) > 0) { + int rp = r->xPos() + r->rightmostPosition(false); + right = max(right, rp + relativeOffset); + } + } + } + } + + if (m_hasColumns) { + // This only matters for LTR + if (style()->direction() == LTR) + right = max(columnRects()->last().right() + relativeOffset, right); + return right; + } + + if (m_floatingObjects) { + FloatingObject* r; + DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); + for ( ; (r = it.current()); ++it ) { + if (r->m_shouldPaint || r->m_renderer->hasLayer()) { + int rp = r->m_left + r->m_renderer->marginLeft() + r->m_renderer->rightmostPosition(false); + right = max(right, rp + relativeOffset); + } + } + } + + if (!includeSelf && firstLineBox()) { + for (InlineRunBox* currBox = firstLineBox(); currBox; currBox = currBox->nextLineBox()) { + int rp = currBox->xPos() + currBox->width(); + // If this node is a root editable element, then the rightmostPosition should account for a caret at the end. + // FIXME: Need to find another way to do this, since scrollbars could show when we don't want them to. + if (node()->isContentEditable() && node() == node()->rootEditableElement() && style()->direction() == LTR) + rp += 1; + right = max(right, rp); + } + } + + return right; +} + +int RenderBlock::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const +{ + int left = RenderFlow::leftmostPosition(includeOverflowInterior, includeSelf); + if (!includeOverflowInterior && hasOverflowClip()) + return left; + + int relativeOffset = includeSelf && isRelPositioned() ? relativePositionOffsetX() : 0; + + if (includeSelf) + left = min(left, m_overflowLeft + relativeOffset); + + if (m_positionedObjects) { + RenderObject* r; + Iterator end = m_positionedObjects->end(); + for (Iterator it = m_positionedObjects->begin(); it != end; ++it) { + r = *it; + // Fixed positioned objects do not scroll and thus should not constitute + // part of the leftmost position. + if (r->style()->position() != FixedPosition) { + // FIXME: Should work for overflow sections too. + // If a positioned object lies completely above the root it will be unreachable via scrolling. + // Therefore we should not allow it to contribute to the leftmost position. + if (!isRenderView() || r->yPos() + r->height() > 0 || r->yPos() + r->lowestPosition(false) > 0) { + int lp = r->xPos() + r->leftmostPosition(false); + left = min(left, lp + relativeOffset); + } + } + } + } + + if (m_hasColumns) { + // This only matters for RTL + if (style()->direction() == RTL) + left = min(columnRects()->last().x() + relativeOffset, left); + return left; + } + + if (m_floatingObjects) { + FloatingObject* r; + DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); + for ( ; (r = it.current()); ++it ) { + if (r->m_shouldPaint || r->m_renderer->hasLayer()) { + int lp = r->m_left + r->m_renderer->marginLeft() + r->m_renderer->leftmostPosition(false); + left = min(left, lp + relativeOffset); + } + } + } + + if (!includeSelf && firstLineBox()) { + for (InlineRunBox* currBox = firstLineBox(); currBox; currBox = currBox->nextLineBox()) + left = min(left, (int)currBox->xPos()); + } + + return left; +} + +int +RenderBlock::leftBottom() +{ + if (!m_floatingObjects) return 0; + int bottom=0; + FloatingObject* r; + DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); + for ( ; (r = it.current()); ++it ) + if (r->m_bottom > bottom && r->type() == FloatingObject::FloatLeft) + bottom=r->m_bottom; + + return bottom; +} + +int +RenderBlock::rightBottom() +{ + if (!m_floatingObjects) return 0; + int bottom=0; + FloatingObject* r; + DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); + for ( ; (r = it.current()); ++it ) + if (r->m_bottom>bottom && r->type() == FloatingObject::FloatRight) + bottom=r->m_bottom; + + return bottom; +} + +void RenderBlock::markLinesDirtyInVerticalRange(int top, int bottom) +{ + if (top >= bottom) + return; + + RootInlineBox* lowestDirtyLine = lastRootBox(); + RootInlineBox* afterLowest = lowestDirtyLine; + while (lowestDirtyLine && lowestDirtyLine->blockHeight() >= bottom) { + afterLowest = lowestDirtyLine; + lowestDirtyLine = lowestDirtyLine->prevRootBox(); + } + + while (afterLowest && afterLowest->blockHeight() >= top) { + afterLowest->markDirty(); + afterLowest = afterLowest->prevRootBox(); + } +} + +void RenderBlock::clearFloats() +{ + // Inline blocks are covered by the isReplaced() check in the avoidFloats method. + if (avoidsFloats() || isRoot() || isRenderView() || isFloatingOrPositioned() || isTableCell()) { + if (m_floatingObjects) + m_floatingObjects->clear(); + return; + } + + typedef HashMap<RenderObject*, FloatingObject*> RendererToFloatInfoMap; + RendererToFloatInfoMap floatMap; + + if (m_floatingObjects) { + if (childrenInline()) { + m_floatingObjects->first(); + while (FloatingObject* f = m_floatingObjects->take()) + floatMap.add(f->m_renderer, f); + } else + m_floatingObjects->clear(); + } + + // Attempt to locate a previous sibling with overhanging floats. We skip any elements that are + // out of flow (like floating/positioned elements), and we also skip over any objects that may have shifted + // to avoid floats. + bool parentHasFloats = false; + RenderObject *prev = previousSibling(); + while (prev && (!prev->isRenderBlock() || prev->avoidsFloats() || prev->isFloatingOrPositioned())) { + if (prev->isFloating()) + parentHasFloats = true; + prev = prev->previousSibling(); + } + + // First add in floats from the parent. + int offset = m_y; + if (parentHasFloats) + addIntrudingFloats(static_cast<RenderBlock *>(parent()), + parent()->borderLeft() + parent()->paddingLeft(), offset); + + int xoffset = 0; + if (prev) + offset -= prev->yPos(); + else { + prev = parent(); + xoffset += prev->borderLeft() + prev->paddingLeft(); + } + + // Add overhanging floats from the previous RenderBlock, but only if it has a float that intrudes into our space. + if (!prev->isRenderBlock()) return; + RenderBlock* block = static_cast<RenderBlock *>(prev); + + if (block->m_floatingObjects && block->floatBottom() > offset) + addIntrudingFloats(block, xoffset, offset); + + if (childrenInline()) { + int changeTop = INT_MAX; + int changeBottom = INT_MIN; + if (m_floatingObjects) { + for (FloatingObject* f = m_floatingObjects->first(); f; f = m_floatingObjects->next()) { + FloatingObject* oldFloatingObject = floatMap.get(f->m_renderer); + if (oldFloatingObject) { + if (f->m_width != oldFloatingObject->m_width || f->m_left != oldFloatingObject->m_left) { + changeTop = 0; + changeBottom = max(changeBottom, max(f->m_bottom, oldFloatingObject->m_bottom)); + } else if (f->m_bottom != oldFloatingObject->m_bottom) { + changeTop = min(changeTop, min(f->m_bottom, oldFloatingObject->m_bottom)); + changeBottom = max(changeBottom, max(f->m_bottom, oldFloatingObject->m_bottom)); + } + + floatMap.remove(f->m_renderer); + delete oldFloatingObject; + } else { + changeTop = 0; + changeBottom = max(changeBottom, f->m_bottom); + } + } + } + + RendererToFloatInfoMap::iterator end = floatMap.end(); + for (RendererToFloatInfoMap::iterator it = floatMap.begin(); it != end; ++it) { + FloatingObject* floatingObject = (*it).second; + if (!floatingObject->m_isDescendant) { + changeTop = 0; + changeBottom = max(changeBottom, floatingObject->m_bottom); + } + } + deleteAllValues(floatMap); + + markLinesDirtyInVerticalRange(changeTop, changeBottom); + } +} + +int RenderBlock::addOverhangingFloats(RenderBlock* child, int xoff, int yoff, bool makeChildPaintOtherFloats) +{ + // Prevent floats from being added to the canvas by the root element, e.g., <html>. + if (child->hasOverflowClip() || !child->containsFloats() || child->isRoot()) + return 0; + + int lowestFloatBottom = 0; + + // Floats that will remain the child's responsiblity to paint should factor into its + // visual overflow. + IntRect floatsOverflowRect; + DeprecatedPtrListIterator<FloatingObject> it(*child->m_floatingObjects); + for (FloatingObject* r; (r = it.current()); ++it) { + int bottom = child->yPos() + r->m_bottom; + lowestFloatBottom = max(lowestFloatBottom, bottom); + + if (bottom > height()) { + // If the object is not in the list, we add it now. + if (!containsFloat(r->m_renderer)) { + FloatingObject *floatingObj = new FloatingObject(r->type()); + floatingObj->m_top = r->m_top - yoff; + floatingObj->m_bottom = r->m_bottom - yoff; + floatingObj->m_left = r->m_left - xoff; + floatingObj->m_width = r->m_width; + floatingObj->m_renderer = r->m_renderer; + + // The nearest enclosing layer always paints the float (so that zindex and stacking + // behaves properly). We always want to propagate the desire to paint the float as + // far out as we can, to the outermost block that overlaps the float, stopping only + // if we hit a layer boundary. + if (r->m_renderer->enclosingLayer() == enclosingLayer()) + r->m_shouldPaint = false; + else + floatingObj->m_shouldPaint = false; + + // We create the floating object list lazily. + if (!m_floatingObjects) { + m_floatingObjects = new DeprecatedPtrList<FloatingObject>; + m_floatingObjects->setAutoDelete(true); + } + m_floatingObjects->append(floatingObj); + } + } else if (makeChildPaintOtherFloats && !r->m_shouldPaint && !r->m_renderer->hasLayer() && r->m_renderer->isDescendantOf(child) && r->m_renderer->enclosingLayer() == child->enclosingLayer()) + // The float is not overhanging from this block, so if it is a descendant of the child, the child should + // paint it (the other case is that it is intruding into the child), unless it has its own layer or enclosing + // layer. + // If makeChildPaintOtherFloats is false, it means that the child must already know about all the floats + // it should paint. + r->m_shouldPaint = true; + + if (r->m_shouldPaint && !r->m_renderer->hasLayer()) { + IntRect floatOverflowRect = r->m_renderer->overflowRect(false); + floatOverflowRect.move(r->m_left + r->m_renderer->marginLeft(), r->m_top + r->m_renderer->marginTop()); + floatsOverflowRect.unite(floatOverflowRect); + } + } + child->addVisualOverflow(floatsOverflowRect); + return lowestFloatBottom; +} + +void RenderBlock::addIntrudingFloats(RenderBlock* prev, int xoff, int yoff) +{ + // If the parent or previous sibling doesn't have any floats to add, don't bother. + if (!prev->m_floatingObjects) + return; + + DeprecatedPtrListIterator<FloatingObject> it(*prev->m_floatingObjects); + for (FloatingObject *r; (r = it.current()); ++it) { + if (r->m_bottom > yoff) { + // The object may already be in our list. Check for it up front to avoid + // creating duplicate entries. + FloatingObject* f = 0; + if (m_floatingObjects) { + DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); + while ((f = it.current())) { + if (f->m_renderer == r->m_renderer) break; + ++it; + } + } + if (!f) { + FloatingObject *floatingObj = new FloatingObject(r->type()); + floatingObj->m_top = r->m_top - yoff; + floatingObj->m_bottom = r->m_bottom - yoff; + floatingObj->m_left = r->m_left - xoff; + // Applying the child's margin makes no sense in the case where the child was passed in. + // since his own margin was added already through the subtraction of the |xoff| variable + // above. |xoff| will equal -flow->marginLeft() in this case, so it's already been taken + // into account. Only apply this code if |child| is false, since otherwise the left margin + // will get applied twice. + if (prev != parent()) + floatingObj->m_left += prev->marginLeft(); + floatingObj->m_left -= marginLeft(); + floatingObj->m_shouldPaint = false; // We are not in the direct inheritance chain for this float. We will never paint it. + floatingObj->m_width = r->m_width; + floatingObj->m_renderer = r->m_renderer; + + // We create the floating object list lazily. + if (!m_floatingObjects) { + m_floatingObjects = new DeprecatedPtrList<FloatingObject>; + m_floatingObjects->setAutoDelete(true); + } + m_floatingObjects->append(floatingObj); + } + } + } +} + +bool RenderBlock::avoidsFloats() const +{ + // Floats can't intrude into our box if we have a non-auto column count or width. + return RenderFlow::avoidsFloats() || !style()->hasAutoColumnCount() || !style()->hasAutoColumnWidth(); +} + +bool RenderBlock::containsFloat(RenderObject* o) +{ + if (m_floatingObjects) { + DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); + while (it.current()) { + if (it.current()->m_renderer == o) + return true; + ++it; + } + } + return false; +} + +void RenderBlock::markAllDescendantsWithFloatsForLayout(RenderObject* floatToRemove) +{ + setChildNeedsLayout(true); + + if (floatToRemove) + removeFloatingObject(floatToRemove); + + // Iterate over our children and mark them as needed. + if (!childrenInline()) { + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { + if (isBlockFlow() && !child->isFloatingOrPositioned() && + ((floatToRemove ? child->containsFloat(floatToRemove) : child->containsFloats()) || child->shrinkToAvoidFloats())) + child->markAllDescendantsWithFloatsForLayout(floatToRemove); + } + } +} + +int RenderBlock::getClearDelta(RenderObject *child) +{ + // There is no need to compute clearance if we have no floats. + if (!containsFloats()) + return 0; + + // At least one float is present. We need to perform the clearance computation. + bool clearSet = child->style()->clear() != CNONE; + int bottom = 0; + switch (child->style()->clear()) { + case CNONE: + break; + case CLEFT: + bottom = leftBottom(); + break; + case CRIGHT: + bottom = rightBottom(); + break; + case CBOTH: + bottom = floatBottom(); + break; + } + + // We also clear floats if we are too big to sit on the same line as a float (and wish to avoid floats by default). + // FIXME: Note that the remaining space checks aren't quite accurate, since you should be able to clear only some floats (the minimum # needed + // to fit) and not all (we should be using nextFloatBottomBelow and looping). + // Do not allow tables to wrap in quirks or even in almost strict mode + // (ebay on the PLT, finance.yahoo.com in the real world, versiontracker.com forces even almost strict mode not to work) + int result = clearSet ? max(0, bottom - child->yPos()) : 0; + if (!result && child->avoidsFloats() && child->style()->width().isFixed() && + child->minPrefWidth() > lineWidth(child->yPos()) && child->minPrefWidth() <= availableWidth() && + document()->inStrictMode()) + result = max(0, floatBottom() - child->yPos()); + return result; +} + +void RenderBlock::addVisualOverflow(const IntRect& r) +{ + if (r.isEmpty()) + return; + m_overflowLeft = min(m_overflowLeft, r.x()); + m_overflowWidth = max(m_overflowWidth, r.right()); + m_overflowTop = min(m_overflowTop, r.y()); + m_overflowHeight = max(m_overflowHeight, r.bottom()); +} + +bool RenderBlock::isPointInOverflowControl(HitTestResult& result, int, int, int, int) +{ + if (!scrollsOverflow()) + return false; + + return layer()->hitTestOverflowControls(result); +} + +bool RenderBlock::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction) +{ + bool inlineFlow = isInlineFlow(); + + int tx = _tx + m_x; + int ty = _ty + m_y + borderTopExtra(); + + if (!inlineFlow && !isRenderView()) { + // Check if we need to do anything at all. + IntRect overflowBox = overflowRect(false); + overflowBox.move(tx, ty); + if (!overflowBox.contains(_x, _y)) + return false; + } + + if (isPointInOverflowControl(result, _x, _y, tx, ty)) { + if (hitTestAction == HitTestBlockBackground) { + updateHitTestResult(result, IntPoint(_x - tx, _y - ty)); + return true; + } + return false; + } + + // If we have lightweight control clipping, then we can't have any spillout. + if (!hasControlClip() || controlClipRect(tx, ty).contains(_x, _y)) { + // Hit test descendants first. + int scrolledX = tx; + int scrolledY = ty; + if (hasOverflowClip()) + m_layer->subtractScrolledContentOffset(scrolledX, scrolledY); + + // Hit test contents if we don't have columns. + if (!m_hasColumns && hitTestContents(request, result, _x, _y, scrolledX, scrolledY, hitTestAction)) + return true; + + // Hit test our columns if we do have them. + if (m_hasColumns && hitTestColumns(request, result, _x, _y, scrolledX, scrolledY, hitTestAction)) + return true; + + // Hit test floats. + if (hitTestAction == HitTestFloat && m_floatingObjects) { + if (isRenderView()) { + scrolledX += static_cast<RenderView*>(this)->frameView()->scrollX(); + scrolledY += static_cast<RenderView*>(this)->frameView()->scrollY(); + } + + FloatingObject* o; + DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); + for (it.toLast(); (o = it.current()); --it) { + if (o->m_shouldPaint && !o->m_renderer->hasLayer()) { + int xoffset = scrolledX + o->m_left + o->m_renderer->marginLeft() - o->m_renderer->xPos(); + int yoffset = scrolledY + o->m_top + o->m_renderer->marginTop() - o->m_renderer->yPos(); + if (o->m_renderer->hitTest(request, result, IntPoint(_x, _y), xoffset, yoffset)) { + updateHitTestResult(result, IntPoint(_x - xoffset, _y - yoffset)); + return true; + } + } + } + } + } + + // Now hit test our background + if (!inlineFlow && (hitTestAction == HitTestBlockBackground || hitTestAction == HitTestChildBlockBackground)) { + int topExtra = borderTopExtra(); + IntRect boundsRect(tx, ty - topExtra, m_width, m_height + topExtra + borderBottomExtra()); + if (visibleToHitTesting() && boundsRect.contains(_x, _y)) { + updateHitTestResult(result, IntPoint(_x - tx, _y - ty + topExtra)); + return true; + } + } + + return false; +} + +bool RenderBlock::hitTestColumns(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction) +{ + // We need to do multiple passes, breaking up our hit testing into strips. + // We can always go left to right, since column contents are clipped (meaning that there + // can't be any overlap). + int currXOffset = 0; + int currYOffset = 0; + int colGap = columnGap(); + Vector<IntRect>* colRects = columnRects(); + for (unsigned i = 0; i < colRects->size(); i++) { + IntRect colRect = colRects->at(i); + colRect.move(tx, ty); + + if (colRect.contains(x, y)) { + // The point is inside this column. + // Adjust tx and ty to change where we hit test. + + int finalX = tx + currXOffset; + int finalY = ty + currYOffset; + return hitTestContents(request, result, x, y, finalX, finalY, hitTestAction); + } + + // Move to the next position. + if (style()->direction() == LTR) + currXOffset += colRect.width() + colGap; + else + currXOffset -= (colRect.width() + colGap); + + currYOffset -= colRect.height(); + } + + return false; +} + +bool RenderBlock::hitTestContents(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction) +{ + if (childrenInline() && !isTable()) { + // We have to hit-test our line boxes. + if (hitTestLines(request, result, x, y, tx, ty, hitTestAction)) { + updateHitTestResult(result, IntPoint(x - tx, y - ty)); + return true; + } + } else { + // Hit test our children. + HitTestAction childHitTest = hitTestAction; + if (hitTestAction == HitTestChildBlockBackgrounds) + childHitTest = HitTestChildBlockBackground; + for (RenderObject* child = lastChild(); child; child = child->previousSibling()) { + // FIXME: We have to skip over inline flows, since they can show up inside RenderTables at the moment (a demoted inline <form> for example). If we ever implement a + // table-specific hit-test method (which we should do for performance reasons anyway), then we can remove this check. + if (!child->hasLayer() && !child->isFloating() && !child->isInlineFlow() && child->nodeAtPoint(request, result, x, y, tx, ty, childHitTest)) { + updateHitTestResult(result, IntPoint(x - tx, y - ty)); + return true; + } + } + } + + return false; +} + +Position RenderBlock::positionForBox(InlineBox *box, bool start) const +{ + if (!box) + return Position(); + + if (!box->object()->element()) + return Position(element(), start ? caretMinOffset() : caretMaxOffset()); + + if (!box->isInlineTextBox()) + return Position(box->object()->element(), start ? box->object()->caretMinOffset() : box->object()->caretMaxOffset()); + + InlineTextBox *textBox = static_cast<InlineTextBox *>(box); + return Position(box->object()->element(), start ? textBox->start() : textBox->start() + textBox->len()); +} + +Position RenderBlock::positionForRenderer(RenderObject* renderer, bool start) const +{ + if (!renderer) + return Position(element(), 0); + + Node* node = renderer->element() ? renderer->element() : element(); + if (!node) + return Position(); + + ASSERT(renderer == node->renderer()); + + int offset = start ? renderer->caretMinOffset() : renderer->caretMaxOffset(); + + // FIXME: This was a runtime check that seemingly couldn't fail; changed it to an assertion for now. + ASSERT(!node->isCharacterDataNode() || renderer->isText()); + + return Position(node, offset); +} + +VisiblePosition RenderBlock::positionForCoordinates(int x, int y) +{ + if (isTable()) + return RenderFlow::positionForCoordinates(x, y); + + int top = borderTop(); + int bottom = top + borderTopExtra() + paddingTop() + contentHeight() + paddingBottom() + borderBottomExtra(); + + int left = borderLeft(); + int right = left + paddingLeft() + contentWidth() + paddingRight(); + + Node* n = element(); + + int contentsX = x; + int contentsY = y; + offsetForContents(contentsX, contentsY); + + if (isReplaced()) { + if (y < 0 || y < height() && x < 0) + return VisiblePosition(n, caretMinOffset(), DOWNSTREAM); + if (y >= height() || y >= 0 && x >= width()) + return VisiblePosition(n, caretMaxOffset(), DOWNSTREAM); + } + + // If we start inside the shadow tree, we will stay inside (even if the point is above or below). + if (!(n && n->isShadowNode()) && !childrenInline()) { + // Don't return positions inside editable roots for coordinates outside those roots, except for coordinates outside + // a document that is entirely editable. + bool isEditableRoot = n && n->rootEditableElement() == n && !n->hasTagName(bodyTag) && !n->hasTagName(htmlTag); + + if (y < top || (isEditableRoot && (y < bottom && x < left))) { + if (!isEditableRoot) + if (RenderObject* c = firstChild()) { // FIXME: This code doesn't make any sense. This child could be an inline or a positioned element or a float or a compact, etc. + VisiblePosition p = c->positionForCoordinates(contentsX - c->xPos(), contentsY - c->yPos()); + if (p.isNotNull()) + return p; + } + if (n) { + if (Node* sp = n->shadowParentNode()) + n = sp; + if (Node* p = n->parent()) + return VisiblePosition(p, n->nodeIndex(), DOWNSTREAM); + } + return VisiblePosition(n, 0, DOWNSTREAM); + } + + if (y >= bottom || (isEditableRoot && (y >= top && x >= right))) { + if (!isEditableRoot) + if (RenderObject* c = lastChild()) { // FIXME: This code doesn't make any sense. This child could be an inline or a positioned element or a float or a compact, ect. + VisiblePosition p = c->positionForCoordinates(contentsX - c->xPos(), contentsY - c->yPos()); + if (p.isNotNull()) + return p; + } + if (n) { + if (Node* sp = n->shadowParentNode()) + n = sp; + if (Node* p = n->parent()) + return VisiblePosition(p, n->nodeIndex() + 1, DOWNSTREAM); + } + return VisiblePosition(n, 0, DOWNSTREAM); + } + } + + if (childrenInline()) { + if (!firstRootBox()) + return VisiblePosition(n, 0, DOWNSTREAM); + + if (contentsY < firstRootBox()->topOverflow() - verticalLineClickFudgeFactor) + // y coordinate is above first root line box + return VisiblePosition(positionForBox(firstRootBox()->firstLeafChild(), true), DOWNSTREAM); + + // look for the closest line box in the root box which is at the passed-in y coordinate + for (RootInlineBox* root = firstRootBox(); root; root = root->nextRootBox()) { + // set the bottom based on whether there is a next root box + if (root->nextRootBox()) + // FIXME: make the break point halfway between the bottom of the previous root box and the top of the next root box + bottom = root->nextRootBox()->topOverflow(); + else + bottom = root->bottomOverflow() + verticalLineClickFudgeFactor; + // check if this root line box is located at this y coordinate + if (contentsY < bottom && root->firstChild()) { + InlineBox* closestBox = root->closestLeafChildForXPos(x); + if (closestBox) + // pass the box a y position that is inside it + return closestBox->object()->positionForCoordinates(contentsX, closestBox->m_y); + } + } + + if (lastRootBox()) + // y coordinate is below last root line box + return VisiblePosition(positionForBox(lastRootBox()->lastLeafChild(), false), DOWNSTREAM); + + return VisiblePosition(n, 0, DOWNSTREAM); + } + + // See if any child blocks exist at this y coordinate. + if (firstChild() && contentsY < firstChild()->yPos()) + return VisiblePosition(n, 0, DOWNSTREAM); + for (RenderObject* renderer = firstChild(); renderer; renderer = renderer->nextSibling()) { + if (renderer->height() == 0 || renderer->style()->visibility() != VISIBLE || renderer->isFloatingOrPositioned()) + continue; + RenderObject* next = renderer->nextSibling(); + while (next && next->isFloatingOrPositioned()) + next = next->nextSibling(); + if (next) + bottom = next->yPos(); + else + bottom = top + scrollHeight(); + if (contentsY >= renderer->yPos() && contentsY < bottom) + return renderer->positionForCoordinates(contentsX - renderer->xPos(), contentsY - renderer->yPos()); + } + + return RenderFlow::positionForCoordinates(x, y); +} + +void RenderBlock::offsetForContents(int& tx, int& ty) const +{ + ty -= borderTopExtra(); + + if (hasOverflowClip()) + m_layer->addScrolledContentOffset(tx, ty); + + if (m_hasColumns) { + IntPoint contentsPoint(tx, ty); + adjustPointToColumnContents(contentsPoint); + tx = contentsPoint.x(); + ty = contentsPoint.y(); + } +} + +int RenderBlock::availableWidth() const +{ + // If we have multiple columns, then the available width is reduced to our column width. + if (m_hasColumns) + return desiredColumnWidth(); + return contentWidth(); +} + +int RenderBlock::columnGap() const +{ + if (style()->hasNormalColumnGap()) + return style()->fontDescription().computedPixelSize(); // "1em" is recommended as the normal gap setting. Matches <p> margins. + return static_cast<int>(style()->columnGap()); +} + +void RenderBlock::calcColumnWidth() +{ + // Calculate our column width and column count. + unsigned desiredColumnCount = 1; + int desiredColumnWidth = contentWidth(); + + // For now, we don't support multi-column layouts when printing, since we have to do a lot of work for proper pagination. + if (document()->printing() || (style()->hasAutoColumnCount() && style()->hasAutoColumnWidth())) { + setDesiredColumnCountAndWidth(desiredColumnCount, desiredColumnWidth); + return; + } + + int availWidth = desiredColumnWidth; + int colGap = columnGap(); + int colWidth = max(1, static_cast<int>(style()->columnWidth())); + int colCount = max(1, static_cast<int>(style()->columnCount())); + + if (style()->hasAutoColumnWidth()) { + if ((colCount - 1) * colGap < availWidth) { + desiredColumnCount = colCount; + desiredColumnWidth = (availWidth - (desiredColumnCount - 1) * colGap) / desiredColumnCount; + } else if (colGap < availWidth) { + desiredColumnCount = availWidth / colGap; + desiredColumnWidth = (availWidth - (desiredColumnCount - 1) * colGap) / desiredColumnCount; + } + } else if (style()->hasAutoColumnCount()) { + if (colWidth < availWidth) { + desiredColumnCount = (availWidth + colGap) / (colWidth + colGap); + desiredColumnWidth = (availWidth - (desiredColumnCount - 1) * colGap) / desiredColumnCount; + } + } else { + // Both are set. + if (colCount * colWidth + (colCount - 1) * colGap <= availWidth) { + desiredColumnCount = colCount; + desiredColumnWidth = colWidth; + } else if (colWidth < availWidth) { + desiredColumnCount = (availWidth + colGap) / (colWidth + colGap); + desiredColumnWidth = (availWidth - (desiredColumnCount - 1) * colGap) / desiredColumnCount; + } + } + setDesiredColumnCountAndWidth(desiredColumnCount, desiredColumnWidth); +} + +void RenderBlock::setDesiredColumnCountAndWidth(int count, int width) +{ + if (count == 1) { + if (m_hasColumns) { + delete gColumnInfoMap->take(this); + m_hasColumns = false; + } + } else { + ColumnInfo* info; + if (m_hasColumns) + info = gColumnInfoMap->get(this); + else { + if (!gColumnInfoMap) + gColumnInfoMap = new ColumnInfoMap; + info = new ColumnInfo; + gColumnInfoMap->add(this, info); + m_hasColumns = true; + } + info->m_desiredColumnCount = count; + info->m_desiredColumnWidth = width; + } +} + +int RenderBlock::desiredColumnWidth() const +{ + if (!m_hasColumns) + return contentWidth(); + return gColumnInfoMap->get(this)->m_desiredColumnWidth; +} + +unsigned RenderBlock::desiredColumnCount() const +{ + if (!m_hasColumns) + return 1; + return gColumnInfoMap->get(this)->m_desiredColumnCount; +} + +Vector<IntRect>* RenderBlock::columnRects() const +{ + if (!m_hasColumns) + return 0; + return &gColumnInfoMap->get(this)->m_columnRects; +} + +int RenderBlock::layoutColumns(int endOfContent) +{ + // Don't do anything if we have no columns + if (!m_hasColumns) + return -1; + + ColumnInfo* info = gColumnInfoMap->get(this); + int desiredColumnWidth = info->m_desiredColumnWidth; + int desiredColumnCount = info->m_desiredColumnCount; + Vector<IntRect>* columnRects = &info->m_columnRects; + + bool computeIntrinsicHeight = (endOfContent == -1); + + // Fill the columns in to the available height. Attempt to balance the height of the columns + int availableHeight = contentHeight(); + int colHeight = computeIntrinsicHeight ? availableHeight / desiredColumnCount : availableHeight; + + // Add in half our line-height to help with best-guess initial balancing. + int columnSlop = lineHeight(false) / 2; + int remainingSlopSpace = columnSlop * desiredColumnCount; + + if (computeIntrinsicHeight) + colHeight += columnSlop; + + int colGap = columnGap(); + + // Compute a collection of column rects. + columnRects->clear(); + + // Then we do a simulated "paint" into the column slices and allow the content to slightly adjust our individual column rects. + // FIXME: We need to take into account layers that are affected by the columns as well here so that they can have an opportunity + // to adjust column rects also. + RenderView* v = view(); + int left = borderLeft() + paddingLeft(); + int top = borderTop() + paddingTop(); + int currX = style()->direction() == LTR ? borderLeft() + paddingLeft() : borderLeft() + paddingLeft() + contentWidth() - desiredColumnWidth; + int currY = top; + unsigned colCount = desiredColumnCount; + int maxColBottom = borderTop() + paddingTop(); + int contentBottom = top + availableHeight; + for (unsigned i = 0; i < colCount; i++) { + // If we aren't constrained, then the last column can just get all the remaining space. + if (computeIntrinsicHeight && i == colCount - 1) + colHeight = availableHeight; + + // This represents the real column position. + IntRect colRect(currX, top, desiredColumnWidth, colHeight); + + // For the simulated paint, we pretend like everything is in one long strip. + IntRect pageRect(left, currY, desiredColumnWidth, colHeight); + v->setPrintRect(pageRect); + v->setTruncatedAt(currY + colHeight); + GraphicsContext context((PlatformGraphicsContext*)0); + RenderObject::PaintInfo paintInfo(&context, pageRect, PaintPhaseForeground, false, 0, 0); + + m_hasColumns = false; + paintObject(paintInfo, 0, 0); + m_hasColumns = true; + + int adjustedBottom = v->bestTruncatedAt(); + if (adjustedBottom <= currY) + adjustedBottom = currY + colHeight; + + colRect.setHeight(adjustedBottom - currY); + + // Add in the lost space to the subsequent columns. + // FIXME: This will create a "staircase" effect if there are enough columns, but the effect should be pretty subtle. + if (computeIntrinsicHeight) { + int lostSpace = colHeight - colRect.height(); + if (lostSpace > remainingSlopSpace) { + // Redestribute the space among the remaining columns. + int spaceToRedistribute = lostSpace - remainingSlopSpace; + int remainingColumns = colCount - i + 1; + colHeight += spaceToRedistribute / remainingColumns; + } + remainingSlopSpace = max(0, remainingSlopSpace - lostSpace); + } + + if (style()->direction() == LTR) + currX += desiredColumnWidth + colGap; + else + currX -= (desiredColumnWidth + colGap); + + currY += colRect.height(); + availableHeight -= colRect.height(); + + maxColBottom = max(colRect.bottom(), maxColBottom); + + columnRects->append(colRect); + + // Start adding in more columns as long as there's still content left. + if (currY < endOfContent && i == colCount - 1 && (computeIntrinsicHeight || contentHeight())) + colCount++; + } + + m_overflowWidth = max(m_width, currX - colGap); + m_overflowLeft = min(0, currX + desiredColumnWidth + colGap); + + m_overflowHeight = maxColBottom; + int toAdd = borderBottom() + paddingBottom() + horizontalScrollbarHeight(); + + if (computeIntrinsicHeight) + m_height = m_overflowHeight + toAdd; + + v->setPrintRect(IntRect()); + v->setTruncatedAt(0); + + ASSERT(colCount == columnRects->size()); + + return contentBottom; +} + +void RenderBlock::adjustPointToColumnContents(IntPoint& point) const +{ + // Just bail if we have no columns. + if (!m_hasColumns) + return; + + Vector<IntRect>* colRects = columnRects(); + + // Determine which columns we intersect. + int colGap = columnGap(); + int leftGap = colGap / 2; + IntPoint columnPoint(colRects->at(0).location()); + int yOffset = 0; + for (unsigned i = 0; i < colRects->size(); i++) { + // Add in half the column gap to the left and right of the rect. + IntRect colRect = colRects->at(i); + IntRect gapAndColumnRect(colRect.x() - leftGap, colRect.y(), colRect.width() + colGap, colRect.height()); + + if (gapAndColumnRect.contains(point)) { + // We're inside the column. Translate the x and y into our column coordinate space. + point.move(columnPoint.x() - colRect.x(), yOffset); + return; + } + + // Move to the next position. + yOffset += colRect.height(); + } +} + +void RenderBlock::adjustRectForColumns(IntRect& r) const +{ + // Just bail if we have no columns. + if (!m_hasColumns) + return; + + Vector<IntRect>* colRects = columnRects(); + + // Begin with a result rect that is empty. + IntRect result; + + // Determine which columns we intersect. + int currXOffset = 0; + int currYOffset = 0; + int colGap = columnGap(); + for (unsigned i = 0; i < colRects->size(); i++) { + IntRect colRect = colRects->at(i); + + IntRect repaintRect = r; + repaintRect.move(currXOffset, currYOffset); + + repaintRect.intersect(colRect); + + result.unite(repaintRect); + + // Move to the next position. + if (style()->direction() == LTR) + currXOffset += colRect.width() + colGap; + else + currXOffset -= (colRect.width() + colGap); + + currYOffset -= colRect.height(); + } + + r = result; +} + +void RenderBlock::calcPrefWidths() +{ + ASSERT(prefWidthsDirty()); + + updateFirstLetter(); + + if (!isTableCell() && style()->width().isFixed() && style()->width().value() > 0) + m_minPrefWidth = m_maxPrefWidth = calcContentBoxWidth(style()->width().value()); + else { + m_minPrefWidth = 0; + m_maxPrefWidth = 0; + + if (childrenInline()) + calcInlinePrefWidths(); + else + calcBlockPrefWidths(); + + m_maxPrefWidth = max(m_minPrefWidth, m_maxPrefWidth); + + if (!style()->autoWrap() && childrenInline()) { + m_minPrefWidth = m_maxPrefWidth; + + // A horizontal marquee with inline children has no minimum width. + if (m_layer && m_layer->marquee() && m_layer->marquee()->isHorizontal()) + m_minPrefWidth = 0; + } + + if (isTableCell()) { + Length w = static_cast<const RenderTableCell*>(this)->styleOrColWidth(); + if (w.isFixed() && w.value() > 0) + m_maxPrefWidth = max(m_minPrefWidth, calcContentBoxWidth(w.value())); + } + } + + if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) { + m_maxPrefWidth = max(m_maxPrefWidth, calcContentBoxWidth(style()->minWidth().value())); + m_minPrefWidth = max(m_minPrefWidth, calcContentBoxWidth(style()->minWidth().value())); + } + + if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) { + m_maxPrefWidth = min(m_maxPrefWidth, calcContentBoxWidth(style()->maxWidth().value())); + m_minPrefWidth = min(m_minPrefWidth, calcContentBoxWidth(style()->maxWidth().value())); + } + + int toAdd = 0; + toAdd = borderLeft() + borderRight() + paddingLeft() + paddingRight(); + + m_minPrefWidth += toAdd; + m_maxPrefWidth += toAdd; + + setPrefWidthsDirty(false); +} + +struct InlineMinMaxIterator +{ +/* InlineMinMaxIterator is a class that will iterate over all render objects that contribute to + inline min/max width calculations. Note the following about the way it walks: + (1) Positioned content is skipped (since it does not contribute to min/max width of a block) + (2) We do not drill into the children of floats or replaced elements, since you can't break + in the middle of such an element. + (3) Inline flows (e.g., <a>, <span>, <i>) are walked twice, since each side can have + distinct borders/margin/padding that contribute to the min/max width. +*/ + RenderObject* parent; + RenderObject* current; + bool endOfInline; + + InlineMinMaxIterator(RenderObject* p, bool end = false) + :parent(p), current(p), endOfInline(end) {} + + RenderObject* next(); +}; + +RenderObject* InlineMinMaxIterator::next() +{ + RenderObject* result = 0; + bool oldEndOfInline = endOfInline; + endOfInline = false; + while (current || current == parent) { + if (!oldEndOfInline && + (current == parent || + (!current->isFloating() && !current->isReplaced() && !current->isPositioned()))) + result = current->firstChild(); + if (!result) { + // We hit the end of our inline. (It was empty, e.g., <span></span>.) + if (!oldEndOfInline && current->isInlineFlow()) { + result = current; + endOfInline = true; + break; + } + + while (current && current != parent) { + result = current->nextSibling(); + if (result) break; + current = current->parent(); + if (current && current != parent && current->isInlineFlow()) { + result = current; + endOfInline = true; + break; + } + } + } + + if (!result) + break; + + if (!result->isPositioned() && (result->isText() || result->isFloating() || result->isReplaced() || result->isInlineFlow())) + break; + + current = result; + result = 0; + } + + // Update our position. + current = result; + return current; +} + +static int getBPMWidth(int childValue, Length cssUnit) +{ + if (cssUnit.type() != Auto) + return (cssUnit.isFixed() ? cssUnit.value() : childValue); + return 0; +} + +static int getBorderPaddingMargin(const RenderObject* child, bool endOfInline) +{ + RenderStyle* cstyle = child->style(); + int result = 0; + bool leftSide = (cstyle->direction() == LTR) ? !endOfInline : endOfInline; + result += getBPMWidth((leftSide ? child->marginLeft() : child->marginRight()), + (leftSide ? cstyle->marginLeft() : + cstyle->marginRight())); + result += getBPMWidth((leftSide ? child->paddingLeft() : child->paddingRight()), + (leftSide ? cstyle->paddingLeft() : + cstyle->paddingRight())); + result += leftSide ? child->borderLeft() : child->borderRight(); + return result; +} + +static inline void stripTrailingSpace(int& inlineMax, int& inlineMin, + RenderObject* trailingSpaceChild) +{ + if (trailingSpaceChild && trailingSpaceChild->isText()) { + // Collapse away the trailing space at the end of a block. + RenderText* t = static_cast<RenderText*>(trailingSpaceChild); + const UChar space = ' '; + const Font& font = t->style()->font(); // FIXME: This ignores first-line. + int spaceWidth = font.width(TextRun(&space, 1)); + inlineMax -= spaceWidth + font.wordSpacing(); + if (inlineMin > inlineMax) + inlineMin = inlineMax; + } +} + +void RenderBlock::calcInlinePrefWidths() +{ + int inlineMax = 0; + int inlineMin = 0; + + int cw = containingBlock()->contentWidth(); + + // If we are at the start of a line, we want to ignore all white-space. + // Also strip spaces if we previously had text that ended in a trailing space. + bool stripFrontSpaces = true; + RenderObject* trailingSpaceChild = 0; + + // Firefox and Opera will allow a table cell to grow to fit an image inside it under + // very specific cirucumstances (in order to match common WinIE renderings). + // Not supporting the quirk has caused us to mis-render some real sites. (See Bugzilla 10517.) + bool allowImagesToBreak = !style()->htmlHacks() || !isTableCell() || !style()->width().isIntrinsicOrAuto(); + + bool autoWrap, oldAutoWrap; + autoWrap = oldAutoWrap = style()->autoWrap(); + + InlineMinMaxIterator childIterator(this); + bool addedTextIndent = false; // Only gets added in once. + RenderObject* prevFloat = 0; + RenderObject* previousLeaf = 0; + while (RenderObject* child = childIterator.next()) { + autoWrap = child->isReplaced() ? child->parent()->style()->autoWrap() : + child->style()->autoWrap(); + + if (!child->isBR()) { + // Step One: determine whether or not we need to go ahead and + // terminate our current line. Each discrete chunk can become + // the new min-width, if it is the widest chunk seen so far, and + // it can also become the max-width. + + // Children fall into three categories: + // (1) An inline flow object. These objects always have a min/max of 0, + // and are included in the iteration solely so that their margins can + // be added in. + // + // (2) An inline non-text non-flow object, e.g., an inline replaced element. + // These objects can always be on a line by themselves, so in this situation + // we need to go ahead and break the current line, and then add in our own + // margins and min/max width on its own line, and then terminate the line. + // + // (3) A text object. Text runs can have breakable characters at the start, + // the middle or the end. They may also lose whitespace off the front if + // we're already ignoring whitespace. In order to compute accurate min-width + // information, we need three pieces of information. + // (a) the min-width of the first non-breakable run. Should be 0 if the text string + // starts with whitespace. + // (b) the min-width of the last non-breakable run. Should be 0 if the text string + // ends with whitespace. + // (c) the min/max width of the string (trimmed for whitespace). + // + // If the text string starts with whitespace, then we need to go ahead and + // terminate our current line (unless we're already in a whitespace stripping + // mode. + // + // If the text string has a breakable character in the middle, but didn't start + // with whitespace, then we add the width of the first non-breakable run and + // then end the current line. We then need to use the intermediate min/max width + // values (if any of them are larger than our current min/max). We then look at + // the width of the last non-breakable run and use that to start a new line + // (unless we end in whitespace). + RenderStyle* cstyle = child->style(); + int childMin = 0; + int childMax = 0; + + if (!child->isText()) { + // Case (1) and (2). Inline replaced and inline flow elements. + if (child->isInlineFlow()) { + // Add in padding/border/margin from the appropriate side of + // the element. + int bpm = getBorderPaddingMargin(child, childIterator.endOfInline); + childMin += bpm; + childMax += bpm; + + inlineMin += childMin; + inlineMax += childMax; + + child->setPrefWidthsDirty(false); + } else { + // Inline replaced elts add in their margins to their min/max values. + int margins = 0; + Length leftMargin = cstyle->marginLeft(); + Length rightMargin = cstyle->marginRight(); + if (leftMargin.isFixed()) + margins += leftMargin.value(); + if (rightMargin.isFixed()) + margins += rightMargin.value(); + childMin += margins; + childMax += margins; + } + } + + if (!child->isRenderInline() && !child->isText()) { + // Case (2). Inline replaced elements and floats. + // Go ahead and terminate the current line as far as + // minwidth is concerned. + childMin += child->minPrefWidth(); + childMax += child->maxPrefWidth(); + + bool clearPreviousFloat; + if (child->isFloating()) { + clearPreviousFloat = (prevFloat + && (prevFloat->style()->floating() == FLEFT && (child->style()->clear() & CLEFT) + || prevFloat->style()->floating() == FRIGHT && (child->style()->clear() & CRIGHT))); + prevFloat = child; + } else + clearPreviousFloat = false; + + bool canBreakReplacedElement = !child->isImage() || allowImagesToBreak; + if (canBreakReplacedElement && (autoWrap || oldAutoWrap) || clearPreviousFloat) { + m_minPrefWidth = max(inlineMin, m_minPrefWidth); + inlineMin = 0; + } + + // If we're supposed to clear the previous float, then terminate maxwidth as well. + if (clearPreviousFloat) { + m_maxPrefWidth = max(inlineMax, m_maxPrefWidth); + inlineMax = 0; + } + + // Add in text-indent. This is added in only once. + int ti = 0; + if (!addedTextIndent) { + addedTextIndent = true; + ti = style()->textIndent().calcMinValue(cw); + childMin+=ti; + childMax+=ti; + } + + // Add our width to the max. + inlineMax += childMax; + + if (!autoWrap || !canBreakReplacedElement) { + if (child->isFloating()) + m_minPrefWidth = max(childMin, m_minPrefWidth); + else + inlineMin += childMin; + } else { + // Now check our line. + m_minPrefWidth = max(childMin, m_minPrefWidth); + + // Now start a new line. + inlineMin = 0; + } + + // We are no longer stripping whitespace at the start of + // a line. + if (!child->isFloating()) { + stripFrontSpaces = false; + trailingSpaceChild = 0; + } + } else if (child->isText()) { + // Case (3). Text. + RenderText* t = static_cast<RenderText *>(child); + + if (t->isWordBreak()) { + m_minPrefWidth = max(inlineMin, m_minPrefWidth); + inlineMin = 0; + continue; + } + + // Determine if we have a breakable character. Pass in + // whether or not we should ignore any spaces at the front + // of the string. If those are going to be stripped out, + // then they shouldn't be considered in the breakable char + // check. + bool hasBreakableChar, hasBreak; + int beginMin, endMin; + bool beginWS, endWS; + int beginMax, endMax; + t->trimmedPrefWidths(inlineMax, beginMin, beginWS, endMin, endWS, + hasBreakableChar, hasBreak, beginMax, endMax, + childMin, childMax, stripFrontSpaces); + + // This text object will not be rendered, but it may still provide a breaking opportunity. + if (!hasBreak && childMax == 0) { + if (autoWrap && (beginWS || endWS)) { + m_minPrefWidth = max(inlineMin, m_minPrefWidth); + inlineMin = 0; + } + continue; + } + + if (stripFrontSpaces) + trailingSpaceChild = child; + else + trailingSpaceChild = 0; + + // Add in text-indent. This is added in only once. + int ti = 0; + if (!addedTextIndent) { + addedTextIndent = true; + ti = style()->textIndent().calcMinValue(cw); + childMin+=ti; beginMin += ti; + childMax+=ti; beginMax += ti; + } + + // If we have no breakable characters at all, + // then this is the easy case. We add ourselves to the current + // min and max and continue. + if (!hasBreakableChar) { + inlineMin += childMin; + } else { + // We have a breakable character. Now we need to know if + // we start and end with whitespace. + if (beginWS) + // Go ahead and end the current line. + m_minPrefWidth = max(inlineMin, m_minPrefWidth); + else { + inlineMin += beginMin; + m_minPrefWidth = max(inlineMin, m_minPrefWidth); + childMin -= ti; + } + + inlineMin = childMin; + + if (endWS) { + // We end in whitespace, which means we can go ahead + // and end our current line. + m_minPrefWidth = max(inlineMin, m_minPrefWidth); + inlineMin = 0; + } else { + m_minPrefWidth = max(inlineMin, m_minPrefWidth); + inlineMin = endMin; + } + } + + if (hasBreak) { + inlineMax += beginMax; + m_maxPrefWidth = max(inlineMax, m_maxPrefWidth); + m_maxPrefWidth = max(childMax, m_maxPrefWidth); + inlineMax = endMax; + } else + inlineMax += childMax; + } + } else { + m_minPrefWidth = max(inlineMin, m_minPrefWidth); + m_maxPrefWidth = max(inlineMax, m_maxPrefWidth); + inlineMin = inlineMax = 0; + stripFrontSpaces = true; + trailingSpaceChild = 0; + } + + oldAutoWrap = autoWrap; + if (!child->isInlineFlow()) + previousLeaf = child; + } + + if (style()->collapseWhiteSpace()) + stripTrailingSpace(inlineMax, inlineMin, trailingSpaceChild); + + m_minPrefWidth = max(inlineMin, m_minPrefWidth); + m_maxPrefWidth = max(inlineMax, m_maxPrefWidth); +} + +// Use a very large value (in effect infinite). +#define BLOCK_MAX_WIDTH 15000 + +void RenderBlock::calcBlockPrefWidths() +{ + bool nowrap = style()->whiteSpace() == NOWRAP; + + RenderObject *child = firstChild(); + int floatLeftWidth = 0, floatRightWidth = 0; + while (child) { + // Positioned children don't affect the min/max width + if (child->isPositioned()) { + child = child->nextSibling(); + continue; + } + + if (child->isFloating() || child->avoidsFloats()) { + int floatTotalWidth = floatLeftWidth + floatRightWidth; + if (child->style()->clear() & CLEFT) { + m_maxPrefWidth = max(floatTotalWidth, m_maxPrefWidth); + floatLeftWidth = 0; + } + if (child->style()->clear() & CRIGHT) { + m_maxPrefWidth = max(floatTotalWidth, m_maxPrefWidth); + floatRightWidth = 0; + } + } + + // A margin basically has three types: fixed, percentage, and auto (variable). + // Auto and percentage margins simply become 0 when computing min/max width. + // Fixed margins can be added in as is. + Length ml = child->style()->marginLeft(); + Length mr = child->style()->marginRight(); + int margin = 0, marginLeft = 0, marginRight = 0; + if (ml.isFixed()) + marginLeft += ml.value(); + if (mr.isFixed()) + marginRight += mr.value(); + margin = marginLeft + marginRight; + + int w = child->minPrefWidth() + margin; + m_minPrefWidth = max(w, m_minPrefWidth); + + // IE ignores tables for calculation of nowrap. Makes some sense. + if (nowrap && !child->isTable()) + m_maxPrefWidth = max(w, m_maxPrefWidth); + + w = child->maxPrefWidth() + margin; + + if (!child->isFloating()) { + if (child->avoidsFloats()) { + // Determine a left and right max value based off whether or not the floats can fit in the + // margins of the object. For negative margins, we will attempt to overlap the float if the negative margin + // is smaller than the float width. + int maxLeft = marginLeft > 0 ? max(floatLeftWidth, marginLeft) : floatLeftWidth + marginLeft; + int maxRight = marginRight > 0 ? max(floatRightWidth, marginRight) : floatRightWidth + marginRight; + w = child->maxPrefWidth() + maxLeft + maxRight; + w = max(w, floatLeftWidth + floatRightWidth); + } + else + m_maxPrefWidth = max(floatLeftWidth + floatRightWidth, m_maxPrefWidth); + floatLeftWidth = floatRightWidth = 0; + } + + if (child->isFloating()) { + if (style()->floating() == FLEFT) + floatLeftWidth += w; + else + floatRightWidth += w; + } else + m_maxPrefWidth = max(w, m_maxPrefWidth); + + // A very specific WinIE quirk. + // Example: + /* + <div style="position:absolute; width:100px; top:50px;"> + <div style="position:absolute;left:0px;top:50px;height:50px;background-color:green"> + <table style="width:100%"><tr><td></table> + </div> + </div> + */ + // In the above example, the inner absolute positioned block should have a computed width + // of 100px because of the table. + // We can achieve this effect by making the maxwidth of blocks that contain tables + // with percentage widths be infinite (as long as they are not inside a table cell). + if (style()->htmlHacks() && child->style()->width().isPercent() && + !isTableCell() && child->isTable() && m_maxPrefWidth < BLOCK_MAX_WIDTH) { + RenderBlock* cb = containingBlock(); + while (!cb->isRenderView() && !cb->isTableCell()) + cb = cb->containingBlock(); + if (!cb->isTableCell()) + m_maxPrefWidth = BLOCK_MAX_WIDTH; + } + + child = child->nextSibling(); + } + + // Always make sure these values are non-negative. + m_minPrefWidth = max(0, m_minPrefWidth); + m_maxPrefWidth = max(0, m_maxPrefWidth); + + m_maxPrefWidth = max(floatLeftWidth + floatRightWidth, m_maxPrefWidth); +} + +bool RenderBlock::hasLineIfEmpty() const +{ + return element() && (element()->isContentEditable() && element()->rootEditableElement() == element() || + element()->isShadowNode() && element()->shadowParentNode()->hasTagName(inputTag)); +} + +int RenderBlock::lineHeight(bool b, bool isRootLineBox) const +{ + // Inline blocks are replaced elements. Otherwise, just pass off to + // the base class. If we're being queried as though we're the root line + // box, then the fact that we're an inline-block is irrelevant, and we behave + // just like a block. + if (isReplaced() && !isRootLineBox) + return height() + marginTop() + marginBottom(); + return RenderFlow::lineHeight(b, isRootLineBox); +} + +int RenderBlock::baselinePosition(bool b, bool isRootLineBox) const +{ + // Inline blocks are replaced elements. Otherwise, just pass off to + // the base class. If we're being queried as though we're the root line + // box, then the fact that we're an inline-block is irrelevant, and we behave + // just like a block. + if (isReplaced() && !isRootLineBox) { + // For "leaf" theme objects, let the theme decide what the baseline position is. + // FIXME: Might be better to have a custom CSS property instead, so that if the theme + // is turned off, checkboxes/radios will still have decent baselines. + if (style()->hasAppearance() && !theme()->isControlContainer(style()->appearance())) + return theme()->baselinePosition(this); + + // CSS2.1 states that the baseline of an inline block is the baseline of the last line box in + // the normal flow. We make an exception for marquees, since their baselines are meaningless + // (the content inside them moves). This matches WinIE as well, which just bottom-aligns them. + // We also give up on finding a baseline if we have a vertical scrollbar, or if we are scrolled + // vertically (e.g., an overflow:hidden block that has had scrollTop moved) or if the baseline is outside + // of our content box. + int baselinePos = (m_layer && (m_layer->marquee() || m_layer->verticalScrollbar() || m_layer->scrollYOffset() != 0)) ? -1 : getBaselineOfLastLineBox(); + if (baselinePos != -1 && baselinePos <= borderTop() + paddingTop() + contentHeight()) + return marginTop() + baselinePos; + return height() + marginTop() + marginBottom(); + } + return RenderFlow::baselinePosition(b, isRootLineBox); +} + +int RenderBlock::getBaselineOfFirstLineBox() const +{ + if (!isBlockFlow()) + return RenderFlow::getBaselineOfFirstLineBox(); + + if (childrenInline()) { + if (firstLineBox()) + return firstLineBox()->yPos() + firstLineBox()->baseline(); + else + return -1; + } + else { + for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { + if (!curr->isFloatingOrPositioned()) { + int result = curr->getBaselineOfFirstLineBox(); + if (result != -1) + return curr->yPos() + result; // Translate to our coordinate space. + } + } + } + + return -1; +} + +int RenderBlock::getBaselineOfLastLineBox() const +{ + if (!isBlockFlow()) + return RenderFlow::getBaselineOfLastLineBox(); + + if (childrenInline()) { + if (!firstLineBox() && hasLineIfEmpty()) + return RenderFlow::baselinePosition(true, true) + borderTop() + paddingTop(); + if (lastLineBox()) + return lastLineBox()->yPos() + lastLineBox()->baseline(); + return -1; + } + else { + bool haveNormalFlowChild = false; + for (RenderObject* curr = lastChild(); curr; curr = curr->previousSibling()) { + if (!curr->isFloatingOrPositioned()) { + haveNormalFlowChild = true; + int result = curr->getBaselineOfLastLineBox(); + if (result != -1) + return curr->yPos() + result; // Translate to our coordinate space. + } + } + if (!haveNormalFlowChild && hasLineIfEmpty()) + return RenderFlow::baselinePosition(true, true) + borderTop() + paddingTop(); + } + + return -1; +} + +bool RenderBlock::containsNonZeroBidiLevel() const +{ + for (RootInlineBox* root = firstRootBox(); root; root = root->nextRootBox()) { + for (InlineBox* box = root->firstLeafChild(); box; box = box->nextLeafChild()) { + if (box->bidiLevel()) + return true; + } + } + return false; +} + +RenderBlock* RenderBlock::firstLineBlock() const +{ + const RenderObject* firstLineBlock = this; + bool hasPseudo = false; + while (true) { + hasPseudo = firstLineBlock->style()->hasPseudoStyle(RenderStyle::FIRST_LINE); + if (hasPseudo) + break; + RenderObject* parentBlock = firstLineBlock->parent(); + if (firstLineBlock->isReplaced() || firstLineBlock->isFloating() || + !parentBlock || parentBlock->firstChild() != firstLineBlock || !parentBlock->isBlockFlow()) + break; + firstLineBlock = parentBlock; + } + + if (!hasPseudo) + return 0; + + return (RenderBlock*)(firstLineBlock); +} + +void RenderBlock::updateFirstLetter() +{ + if (!document()->usesFirstLetterRules()) + return; + // Don't recurse + if (style()->styleType() == RenderStyle::FIRST_LETTER) + return; + + // FIXME: We need to destroy the first-letter object if it is no longer the first child. Need to find + // an efficient way to check for that situation though before implementing anything. + RenderObject* firstLetterBlock = this; + bool hasPseudoStyle = false; + while (true) { + // We only honor first-letter if the firstLetterBlock can have children in the DOM. This correctly + // prevents form controls from honoring first-letter. + hasPseudoStyle = firstLetterBlock->style()->hasPseudoStyle(RenderStyle::FIRST_LETTER) + && firstLetterBlock->canHaveChildren(); + if (hasPseudoStyle) + break; + RenderObject* parentBlock = firstLetterBlock->parent(); + if (firstLetterBlock->isReplaced() || !parentBlock || parentBlock->firstChild() != firstLetterBlock || + !parentBlock->isBlockFlow()) + break; + firstLetterBlock = parentBlock; + } + + if (!hasPseudoStyle) + return; + + // Drill into inlines looking for our first text child. + RenderObject* currChild = firstLetterBlock->firstChild(); + while (currChild && currChild->needsLayout() && (!currChild->isReplaced() || currChild->isFloatingOrPositioned()) && !currChild->isText()) { + if (currChild->isFloatingOrPositioned()) { + if (currChild->style()->styleType() == RenderStyle::FIRST_LETTER) + break; + currChild = currChild->nextSibling(); + } else + currChild = currChild->firstChild(); + } + + // Get list markers out of the way. + while (currChild && currChild->isListMarker()) + currChild = currChild->nextSibling(); + + if (!currChild) + return; + + RenderObject* firstLetterContainer = currChild->parent(); + + // If the child already has style, then it has already been created, so we just want + // to update it. + if (currChild->style()->styleType() == RenderStyle::FIRST_LETTER) { + RenderStyle* pseudo = firstLetterBlock->getCachedPseudoStyle(RenderStyle::FIRST_LETTER, + firstLetterContainer->firstLineStyle()); + currChild->setStyle(pseudo); + for (RenderObject* genChild = currChild->firstChild(); genChild; genChild = genChild->nextSibling()) { + if (genChild->isText()) + genChild->setStyle(pseudo); + } + return; + } + + // If the child does not already have style, we create it here. + if (currChild->isText() && !currChild->isBR() && currChild->parent()->style()->styleType() != RenderStyle::FIRST_LETTER) { + // Our layout state is not valid for the repaints we are going to trigger by + // adding and removing children of firstLetterContainer. + view()->disableLayoutState(); + + RenderText* textObj = static_cast<RenderText*>(currChild); + + // Create our pseudo style now that we have our firstLetterContainer determined. + RenderStyle* pseudoStyle = firstLetterBlock->getCachedPseudoStyle(RenderStyle::FIRST_LETTER, + firstLetterContainer->firstLineStyle()); + + // Force inline display (except for floating first-letters) + pseudoStyle->setDisplay( pseudoStyle->isFloating() ? BLOCK : INLINE); + pseudoStyle->setPosition( StaticPosition ); // CSS2 says first-letter can't be positioned. + + RenderObject* firstLetter = RenderFlow::createAnonymousFlow(document(), pseudoStyle); // anonymous box + firstLetterContainer->addChild(firstLetter, currChild); + + // The original string is going to be either a generated content string or a DOM node's + // string. We want the original string before it got transformed in case first-letter has + // no text-transform or a different text-transform applied to it. + RefPtr<StringImpl> oldText = textObj->originalText(); + ASSERT(oldText); + + if (oldText && oldText->length() > 0) { + unsigned int length = 0; + + // account for leading spaces and punctuation + while (length < oldText->length() && (isSpaceOrNewline((*oldText)[length]) || Unicode::isPunct((*oldText)[length]))) + length++; + + // account for first letter + length++; + + // construct text fragment for the text after the first letter + // NOTE: this might empty + RenderTextFragment* remainingText = + new (renderArena()) RenderTextFragment(textObj->node(), oldText.get(), length, oldText->length() - length); + remainingText->setStyle(textObj->style()); + if (remainingText->element()) + remainingText->element()->setRenderer(remainingText); + + RenderObject* nextObj = textObj->nextSibling(); + firstLetterContainer->removeChild(textObj); + firstLetterContainer->addChild(remainingText, nextObj); + remainingText->setFirstLetter(firstLetter); + + // construct text fragment for the first letter + RenderTextFragment* letter = + new (renderArena()) RenderTextFragment(remainingText->node(), oldText.get(), 0, length); + RefPtr<RenderStyle> newStyle = RenderStyle::create(); + newStyle->inheritFrom(pseudoStyle); + letter->setStyle(newStyle.release()); + firstLetter->addChild(letter); + + textObj->destroy(); + } + view()->enableLayoutState(); + } +} + +bool RenderBlock::inRootBlockContext() const +{ + if (isTableCell() || isFloatingOrPositioned() || hasOverflowClip()) + return false; + + if (isRoot() || isRenderView()) + return true; + + return containingBlock()->inRootBlockContext(); +} + +// Helper methods for obtaining the last line, computing line counts and heights for line counts +// (crawling into blocks). +static bool shouldCheckLines(RenderObject* obj) +{ + return !obj->isFloatingOrPositioned() && !obj->isCompact() && !obj->isRunIn() && + obj->isBlockFlow() && obj->style()->height().isAuto() && + (!obj->isFlexibleBox() || obj->style()->boxOrient() == VERTICAL); +} + +static RootInlineBox* getLineAtIndex(RenderBlock* block, int i, int& count) +{ + if (block->style()->visibility() == VISIBLE) { + if (block->childrenInline()) { + for (RootInlineBox* box = block->firstRootBox(); box; box = box->nextRootBox()) { + if (count++ == i) + return box; + } + } + else { + for (RenderObject* obj = block->firstChild(); obj; obj = obj->nextSibling()) { + if (shouldCheckLines(obj)) { + RootInlineBox *box = getLineAtIndex(static_cast<RenderBlock*>(obj), i, count); + if (box) + return box; + } + } + } + } + return 0; +} + +int getHeightForLineCount(RenderBlock* block, int l, bool includeBottom, int& count) +{ + if (block->style()->visibility() == VISIBLE) { + if (block->childrenInline()) { + for (RootInlineBox* box = block->firstRootBox(); box; box = box->nextRootBox()) { + if (++count == l) + return box->bottomOverflow() + (includeBottom ? (block->borderBottom() + block->paddingBottom()) : 0); + } + } + else { + RenderObject* normalFlowChildWithoutLines = 0; + for (RenderObject* obj = block->firstChild(); obj; obj = obj->nextSibling()) { + if (shouldCheckLines(obj)) { + int result = getHeightForLineCount(static_cast<RenderBlock*>(obj), l, false, count); + if (result != -1) + return result + obj->yPos() + (includeBottom ? (block->borderBottom() + block->paddingBottom()) : 0); + } + else if (!obj->isFloatingOrPositioned() && !obj->isCompact() && !obj->isRunIn()) + normalFlowChildWithoutLines = obj; + } + if (normalFlowChildWithoutLines && l == 0) + return normalFlowChildWithoutLines->yPos() + normalFlowChildWithoutLines->height(); + } + } + + return -1; +} + +RootInlineBox* RenderBlock::lineAtIndex(int i) +{ + int count = 0; + return getLineAtIndex(this, i, count); +} + +int RenderBlock::lineCount() +{ + int count = 0; + if (style()->visibility() == VISIBLE) { + if (childrenInline()) + for (RootInlineBox* box = firstRootBox(); box; box = box->nextRootBox()) + count++; + else + for (RenderObject* obj = firstChild(); obj; obj = obj->nextSibling()) + if (shouldCheckLines(obj)) + count += static_cast<RenderBlock*>(obj)->lineCount(); + } + return count; +} + +int RenderBlock::heightForLineCount(int l) +{ + int count = 0; + return getHeightForLineCount(this, l, true, count); +} + +void RenderBlock::adjustForBorderFit(int x, int& left, int& right) const +{ + // We don't deal with relative positioning. Our assumption is that you shrink to fit the lines without accounting + // for either overflow or translations via relative positioning. + if (style()->visibility() == VISIBLE) { + if (childrenInline()) { + for (RootInlineBox* box = firstRootBox(); box; box = box->nextRootBox()) { + if (box->firstChild()) + left = min(left, x + box->firstChild()->xPos()); + if (box->lastChild()) + right = max(right, x + box->lastChild()->xPos() + box->lastChild()->width()); + } + } + else { + for (RenderObject* obj = firstChild(); obj; obj = obj->nextSibling()) { + if (!obj->isFloatingOrPositioned()) { + if (obj->isBlockFlow() && !obj->hasOverflowClip()) + static_cast<RenderBlock*>(obj)->adjustForBorderFit(x + obj->xPos(), left, right); + else if (obj->style()->visibility() == VISIBLE) { + // We are a replaced element or some kind of non-block-flow object. + left = min(left, x + obj->xPos()); + right = max(right, x + obj->xPos() + obj->width()); + } + } + } + } + + if (m_floatingObjects) { + FloatingObject* r; + DeprecatedPtrListIterator<FloatingObject> it(*m_floatingObjects); + for (; (r = it.current()); ++it) { + // Only examine the object if our m_shouldPaint flag is set. + if (r->m_shouldPaint) { + int floatLeft = r->m_left - r->m_renderer->xPos() + r->m_renderer->marginLeft(); + int floatRight = floatLeft + r->m_renderer->width(); + left = min(left, floatLeft); + right = max(right, floatRight); + } + } + } + } +} + +void RenderBlock::borderFitAdjust(int& x, int& w) const +{ + if (style()->borderFit() == BorderFitBorder) + return; + + // Walk any normal flow lines to snugly fit. + int left = INT_MAX; + int right = INT_MIN; + int oldWidth = w; + adjustForBorderFit(0, left, right); + if (left != INT_MAX) { + left -= (borderLeft() + paddingLeft()); + if (left > 0) { + x += left; + w -= left; + } + } + if (right != INT_MIN) { + right += (borderRight() + paddingRight()); + if (right < oldWidth) + w -= (oldWidth - right); + } +} + +void RenderBlock::clearTruncation() +{ + if (style()->visibility() == VISIBLE) { + if (childrenInline() && hasMarkupTruncation()) { + setHasMarkupTruncation(false); + for (RootInlineBox* box = firstRootBox(); box; box = box->nextRootBox()) + box->clearTruncation(); + } + else + for (RenderObject* obj = firstChild(); obj; obj = obj->nextSibling()) + if (shouldCheckLines(obj)) + static_cast<RenderBlock*>(obj)->clearTruncation(); + } +} + +void RenderBlock::setMaxTopMargins(int pos, int neg) +{ + if (!m_maxMargin) { + if (pos == MaxMargin::topPosDefault(this) && neg == MaxMargin::topNegDefault(this)) + return; + m_maxMargin = new MaxMargin(this); + } + m_maxMargin->m_topPos = pos; + m_maxMargin->m_topNeg = neg; +} + +void RenderBlock::setMaxBottomMargins(int pos, int neg) +{ + if (!m_maxMargin) { + if (pos == MaxMargin::bottomPosDefault(this) && neg == MaxMargin::bottomNegDefault(this)) + return; + m_maxMargin = new MaxMargin(this); + } + m_maxMargin->m_bottomPos = pos; + m_maxMargin->m_bottomNeg = neg; +} + +const char* RenderBlock::renderName() const +{ + if (isBody()) + return "RenderBody"; // FIXME: Temporary hack until we know that the regression tests pass. + + if (isFloating()) + return "RenderBlock (floating)"; + if (isPositioned()) + return "RenderBlock (positioned)"; + if (isAnonymousBlock()) + return "RenderBlock (anonymous)"; + else if (isAnonymous()) + return "RenderBlock (generated)"; + if (isRelPositioned()) + return "RenderBlock (relative positioned)"; + if (isCompact()) + return "RenderBlock (compact)"; + if (isRunIn()) + return "RenderBlock (run-in)"; + return "RenderBlock"; +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderBlock.h b/src/3rdparty/webkit/WebCore/rendering/RenderBlock.h new file mode 100644 index 0000000..902460d --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderBlock.h @@ -0,0 +1,504 @@ +/* + * This file is part of the render object implementation for KHTML. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2007 David Smith (catfish.man@gmail.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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. + */ + +#ifndef RenderBlock_h +#define RenderBlock_h + +#include "DeprecatedPtrList.h" +#include "GapRects.h" +#include "RenderFlow.h" +#include "RootInlineBox.h" +#include <wtf/ListHashSet.h> + +namespace WebCore { + +class InlineIterator; +class BidiRun; +class Position; +class RootInlineBox; + +template <class Iterator, class Run> class BidiResolver; +typedef BidiResolver<InlineIterator, BidiRun> InlineBidiResolver; + +enum CaretType { CursorCaret, DragCaret }; + +class RenderBlock : public RenderFlow { +public: + RenderBlock(Node*); + virtual ~RenderBlock(); + + virtual const char* renderName() const; + + // These two functions are overridden for inline-block. + virtual int lineHeight(bool firstLine, bool isRootLineBox = false) const; + virtual int baselinePosition(bool firstLine, bool isRootLineBox = false) const; + + virtual bool isRenderBlock() const { return true; } + virtual bool isBlockFlow() const { return (!isInline() || isReplaced()) && !isTable(); } + virtual bool isInlineFlow() const { return isInline() && !isReplaced(); } + virtual bool isInlineBlockOrInlineTable() const { return isInline() && isReplaced(); } + + virtual bool childrenInline() const { return m_childrenInline; } + virtual void setChildrenInline(bool b) { m_childrenInline = b; } + void makeChildrenNonInline(RenderObject* insertionPoint = 0); + void deleteLineBoxTree(); + + // The height (and width) of a block when you include overflow spillage out of the bottom + // of the block (e.g., a <div style="height:25px"> that has a 100px tall image inside + // it would have an overflow height of borderTop() + paddingTop() + 100px. + virtual int overflowHeight(bool includeInterior = true) const; + virtual int overflowWidth(bool includeInterior = true) const; + virtual int overflowLeft(bool includeInterior = true) const; + virtual int overflowTop(bool includeInterior = true) const; + virtual IntRect overflowRect(bool includeInterior = true) const; + virtual void setOverflowHeight(int h) { m_overflowHeight = h; } + virtual void setOverflowWidth(int w) { m_overflowWidth = w; } + + void addVisualOverflow(const IntRect&); + + virtual bool isSelfCollapsingBlock() const; + virtual bool isTopMarginQuirk() const { return m_topMarginQuirk; } + virtual bool isBottomMarginQuirk() const { return m_bottomMarginQuirk; } + + virtual int maxTopMargin(bool positive) const { return positive ? maxTopPosMargin() : maxTopNegMargin(); } + virtual int maxBottomMargin(bool positive) const { return positive ? maxBottomPosMargin() : maxBottomNegMargin(); } + + int maxTopPosMargin() const { return m_maxMargin ? m_maxMargin->m_topPos : MaxMargin::topPosDefault(this); } + int maxTopNegMargin() const { return m_maxMargin ? m_maxMargin->m_topNeg : MaxMargin::topNegDefault(this); } + int maxBottomPosMargin() const { return m_maxMargin ? m_maxMargin->m_bottomPos : MaxMargin::bottomPosDefault(this); } + int maxBottomNegMargin() const { return m_maxMargin ? m_maxMargin->m_bottomNeg : MaxMargin::bottomNegDefault(this); } + void setMaxTopMargins(int pos, int neg); + void setMaxBottomMargins(int pos, int neg); + + void initMaxMarginValues() + { + if (m_maxMargin) { + m_maxMargin->m_topPos = MaxMargin::topPosDefault(this); + m_maxMargin->m_topNeg = MaxMargin::topNegDefault(this); + m_maxMargin->m_bottomPos = MaxMargin::bottomPosDefault(this); + m_maxMargin->m_bottomNeg = MaxMargin::bottomNegDefault(this); + } + } + + virtual void addChildToFlow(RenderObject* newChild, RenderObject* beforeChild); + virtual void removeChild(RenderObject*); + + virtual void repaintOverhangingFloats(bool paintAllDescendants); + + virtual void layout(); + virtual void layoutBlock(bool relayoutChildren); + void layoutBlockChildren(bool relayoutChildren, int& maxFloatBottom); + void layoutInlineChildren(bool relayoutChildren, int& repaintTop, int& repaintBottom); + + void layoutPositionedObjects(bool relayoutChildren); + void insertPositionedObject(RenderObject*); + void removePositionedObject(RenderObject*); + virtual void removePositionedObjects(RenderBlock*); + + void addPercentHeightDescendant(RenderBox*); + static void removePercentHeightDescendant(RenderBox*); + + virtual void positionListMarker() { } + + virtual void borderFitAdjust(int& x, int& w) const; // Shrink the box in which the border paints if border-fit is set. + + // Called to lay out the legend for a fieldset. + virtual RenderObject* layoutLegend(bool /*relayoutChildren*/) { return 0; } + + // the implementation of the following functions is in bidi.cpp + struct FloatWithRect { + FloatWithRect(RenderObject* f) + : object(f) + , rect(IntRect(f->xPos() - f->marginLeft(), f->yPos() - f->marginTop(), f->width() + f->marginLeft() + f->marginRight(), f->height() + f->marginTop() + f->marginBottom())) + { + } + + RenderObject* object; + IntRect rect; + }; + + void bidiReorderLine(InlineBidiResolver&, const InlineIterator& end); + RootInlineBox* determineStartPosition(bool& fullLayout, InlineBidiResolver&, Vector<FloatWithRect>& floats, unsigned& numCleanFloats); + RootInlineBox* determineEndPosition(RootInlineBox* startBox, InlineIterator& cleanLineStart, + BidiStatus& cleanLineBidiStatus, + int& yPos); + bool matchedEndLine(const InlineBidiResolver&, const InlineIterator& endLineStart, const BidiStatus& endLineStatus, + RootInlineBox*& endLine, int& endYPos, int& repaintBottom, int& repaintTop); + bool generatesLineBoxesForInlineChild(RenderObject*); + void skipTrailingWhitespace(InlineIterator&); + int skipLeadingWhitespace(InlineBidiResolver&); + void fitBelowFloats(int widthToFit, int& availableWidth); + InlineIterator findNextLineBreak(InlineBidiResolver&, EClear* clear = 0); + RootInlineBox* constructLine(unsigned runCount, BidiRun* firstRun, BidiRun* lastRun, bool lastLine, RenderObject* endObject); + InlineFlowBox* createLineBoxes(RenderObject*); + void computeHorizontalPositionsForLine(RootInlineBox*, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd); + void computeVerticalPositionsForLine(RootInlineBox*, BidiRun*); + void checkLinesForOverflow(); + void deleteEllipsisLineBoxes(); + void checkLinesForTextOverflow(); + // end bidi.cpp functions + + virtual void paint(PaintInfo&, int tx, int ty); + virtual void paintObject(PaintInfo&, int tx, int ty); + void paintFloats(PaintInfo&, int tx, int ty, bool preservePhase = false); + void paintContents(PaintInfo&, int tx, int ty); + void paintColumns(PaintInfo&, int tx, int ty, bool paintFloats = false); + void paintChildren(PaintInfo&, int tx, int ty); + void paintEllipsisBoxes(PaintInfo&, int tx, int ty); + void paintSelection(PaintInfo&, int tx, int ty); + void paintCaret(PaintInfo&, int tx, int ty, CaretType); + + void insertFloatingObject(RenderObject*); + void removeFloatingObject(RenderObject*); + + // Called from lineWidth, to position the floats added in the last line. + // Returns ture if and only if it has positioned any floats. + bool positionNewFloats(); + void clearFloats(); + int getClearDelta(RenderObject* child); + virtual void markAllDescendantsWithFloatsForLayout(RenderObject* floatToRemove = 0); + void markPositionedObjectsForLayout(); + + virtual bool containsFloats() { return m_floatingObjects && !m_floatingObjects->isEmpty(); } + virtual bool containsFloat(RenderObject*); + + virtual bool avoidsFloats() const; + + virtual bool hasOverhangingFloats() { return !hasColumns() && floatBottom() > m_height; } + void addIntrudingFloats(RenderBlock* prev, int xoffset, int yoffset); + int addOverhangingFloats(RenderBlock* child, int xoffset, int yoffset, bool makeChildPaintOtherFloats); + + int nextFloatBottomBelow(int) const; + int floatBottom() const; + inline int leftBottom(); + inline int rightBottom(); + IntRect floatRect() const; + + virtual int lineWidth(int) const; + virtual int lowestPosition(bool includeOverflowInterior = true, bool includeSelf = true) const; + virtual int rightmostPosition(bool includeOverflowInterior = true, bool includeSelf = true) const; + virtual int leftmostPosition(bool includeOverflowInterior = true, bool includeSelf = true) const; + + int rightOffset() const; + int rightRelOffset(int y, int fixedOffset, bool applyTextIndent = true, int* heightRemaining = 0) const; + int rightOffset(int y) const { return rightRelOffset(y, rightOffset(), true); } + + int leftOffset() const; + int leftRelOffset(int y, int fixedOffset, bool applyTextIndent = true, int* heightRemaining = 0) const; + int leftOffset(int y) const { return leftRelOffset(y, leftOffset(), true); } + + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); + virtual bool hitTestColumns(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); + virtual bool hitTestContents(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); + + virtual bool isPointInOverflowControl(HitTestResult&, int x, int y, int tx, int ty); + + virtual VisiblePosition positionForCoordinates(int x, int y); + + // Block flows subclass availableWidth to handle multi column layout (shrinking the width available to children when laying out.) + virtual int availableWidth() const; + + virtual void calcPrefWidths(); + void calcInlinePrefWidths(); + void calcBlockPrefWidths(); + + virtual int getBaselineOfFirstLineBox() const; + virtual int getBaselineOfLastLineBox() const; + + RootInlineBox* firstRootBox() const { return static_cast<RootInlineBox*>(firstLineBox()); } + RootInlineBox* lastRootBox() const { return static_cast<RootInlineBox*>(lastLineBox()); } + + bool containsNonZeroBidiLevel() const; + + // Obtains the nearest enclosing block (including this block) that contributes a first-line style to our inline + // children. + virtual RenderBlock* firstLineBlock() const; + virtual void updateFirstLetter(); + + bool inRootBlockContext() const; + + void setHasMarkupTruncation(bool b = true) { m_hasMarkupTruncation = b; } + bool hasMarkupTruncation() const { return m_hasMarkupTruncation; } + + virtual bool hasSelectedChildren() const { return m_selectionState != SelectionNone; } + virtual SelectionState selectionState() const { return static_cast<SelectionState>(m_selectionState); } + virtual void setSelectionState(SelectionState s); + + struct BlockSelectionInfo { + RenderBlock* m_block; + GapRects m_rects; + SelectionState m_state; + + BlockSelectionInfo() + : m_block(0) + , m_state(SelectionNone) + { + } + + BlockSelectionInfo(RenderBlock* b) + : m_block(b) + , m_rects(b->needsLayout() ? GapRects() : b->selectionGapRects()) + , m_state(b->selectionState()) + { + } + + RenderBlock* block() const { return m_block; } + GapRects rects() const { return m_rects; } + SelectionState state() const { return m_state; } + }; + + virtual IntRect selectionRect(bool) { return selectionGapRects(); } + GapRects selectionGapRects(); + virtual bool shouldPaintSelectionGaps() const; + bool isSelectionRoot() const; + GapRects fillSelectionGaps(RenderBlock* rootBlock, int blockX, int blockY, int tx, int ty, + int& lastTop, int& lastLeft, int& lastRight, const PaintInfo* = 0); + GapRects fillInlineSelectionGaps(RenderBlock* rootBlock, int blockX, int blockY, int tx, int ty, + int& lastTop, int& lastLeft, int& lastRight, const PaintInfo*); + GapRects fillBlockSelectionGaps(RenderBlock* rootBlock, int blockX, int blockY, int tx, int ty, + int& lastTop, int& lastLeft, int& lastRight, const PaintInfo*); + IntRect fillVerticalSelectionGap(int lastTop, int lastLeft, int lastRight, int bottomY, RenderBlock* rootBlock, + int blockX, int blockY, const PaintInfo*); + IntRect fillLeftSelectionGap(RenderObject* selObj, int xPos, int yPos, int height, RenderBlock* rootBlock, + int blockX, int blockY, int tx, int ty, const PaintInfo*); + IntRect fillRightSelectionGap(RenderObject* selObj, int xPos, int yPos, int height, RenderBlock* rootBlock, + int blockX, int blockY, int tx, int ty, const PaintInfo*); + IntRect fillHorizontalSelectionGap(RenderObject* selObj, int xPos, int yPos, int width, int height, const PaintInfo*); + + void getHorizontalSelectionGapInfo(SelectionState, bool& leftGap, bool& rightGap); + int leftSelectionOffset(RenderBlock* rootBlock, int y); + int rightSelectionOffset(RenderBlock* rootBlock, int y); + + // Helper methods for computing line counts and heights for line counts. + RootInlineBox* lineAtIndex(int); + int lineCount(); + int heightForLineCount(int); + void clearTruncation(); + + int desiredColumnWidth() const; + unsigned desiredColumnCount() const; + Vector<IntRect>* columnRects() const; + void setDesiredColumnCountAndWidth(int count, int width); + + void adjustRectForColumns(IntRect&) const; + + void addContinuationWithOutline(RenderFlow*); + void paintContinuationOutlines(PaintInfo&, int tx, int ty); + +private: + void adjustPointToColumnContents(IntPoint&) const; + void adjustForBorderFit(int x, int& left, int& right) const; // Helper function for borderFitAdjust + + void markLinesDirtyInVerticalRange(int top, int bottom); + +protected: + virtual void styleWillChange(RenderStyle::Diff, const RenderStyle* newStyle); + virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + + void newLine(EClear); + virtual bool hasLineIfEmpty() const; + bool layoutOnlyPositionedObjects(); + +private: + Position positionForBox(InlineBox*, bool start = true) const; + Position positionForRenderer(RenderObject*, bool start = true) const; + + // Adjust tx and ty from painting offsets to the local coords of this renderer + void offsetForContents(int& tx, int& ty) const; + + int columnGap() const; + void calcColumnWidth(); + int layoutColumns(int endOfContent = -1); + +protected: + struct FloatingObject { + enum Type { + FloatLeft, + FloatRight + }; + + FloatingObject(Type type) + : m_renderer(0) + , m_top(0) + , m_bottom(0) + , m_left(0) + , m_width(0) + , m_type(type) + , m_shouldPaint(true) + , m_isDescendant(false) + { + } + + Type type() { return static_cast<Type>(m_type); } + + RenderObject* m_renderer; + int m_top; + int m_bottom; + int m_left; + int m_width; + unsigned m_type : 1; // Type (left or right aligned) + bool m_shouldPaint : 1; + bool m_isDescendant : 1; + }; + + // The following helper functions and structs are used by layoutBlockChildren. + class CompactInfo { + // A compact child that needs to be collapsed into the margin of the following block. + RenderObject* m_compact; + + // The block with the open margin that the compact child is going to place itself within. + RenderObject* m_block; + + public: + RenderObject* compact() const { return m_compact; } + RenderObject* block() const { return m_block; } + bool matches(RenderObject* child) const { return m_compact && m_block == child; } + + void clear() { set(0, 0); } + void set(RenderObject* c, RenderObject* b) { m_compact = c; m_block = b; } + + CompactInfo() { clear(); } + }; + + class MarginInfo { + // Collapsing flags for whether we can collapse our margins with our children's margins. + bool m_canCollapseWithChildren : 1; + bool m_canCollapseTopWithChildren : 1; + bool m_canCollapseBottomWithChildren : 1; + + // Whether or not we are a quirky container, i.e., do we collapse away top and bottom + // margins in our container. Table cells and the body are the common examples. We + // also have a custom style property for Safari RSS to deal with TypePad blog articles. + bool m_quirkContainer : 1; + + // This flag tracks whether we are still looking at child margins that can all collapse together at the beginning of a block. + // They may or may not collapse with the top margin of the block (|m_canCollapseTopWithChildren| tells us that), but they will + // always be collapsing with one another. This variable can remain set to true through multiple iterations + // as long as we keep encountering self-collapsing blocks. + bool m_atTopOfBlock : 1; + + // This flag is set when we know we're examining bottom margins and we know we're at the bottom of the block. + bool m_atBottomOfBlock : 1; + + // If our last normal flow child was a self-collapsing block that cleared a float, + // we track it in this variable. + bool m_selfCollapsingBlockClearedFloat : 1; + + // These variables are used to detect quirky margins that we need to collapse away (in table cells + // and in the body element). + bool m_topQuirk : 1; + bool m_bottomQuirk : 1; + bool m_determinedTopQuirk : 1; + + // These flags track the previous maximal positive and negative margins. + int m_posMargin; + int m_negMargin; + + public: + MarginInfo(RenderBlock* b, int top, int bottom); + + void setAtTopOfBlock(bool b) { m_atTopOfBlock = b; } + void setAtBottomOfBlock(bool b) { m_atBottomOfBlock = b; } + void clearMargin() { m_posMargin = m_negMargin = 0; } + void setSelfCollapsingBlockClearedFloat(bool b) { m_selfCollapsingBlockClearedFloat = b; } + void setTopQuirk(bool b) { m_topQuirk = b; } + void setBottomQuirk(bool b) { m_bottomQuirk = b; } + void setDeterminedTopQuirk(bool b) { m_determinedTopQuirk = b; } + void setPosMargin(int p) { m_posMargin = p; } + void setNegMargin(int n) { m_negMargin = n; } + void setPosMarginIfLarger(int p) { if (p > m_posMargin) m_posMargin = p; } + void setNegMarginIfLarger(int n) { if (n > m_negMargin) m_negMargin = n; } + + void setMargin(int p, int n) { m_posMargin = p; m_negMargin = n; } + + bool atTopOfBlock() const { return m_atTopOfBlock; } + bool canCollapseWithTop() const { return m_atTopOfBlock && m_canCollapseTopWithChildren; } + bool canCollapseWithBottom() const { return m_atBottomOfBlock && m_canCollapseBottomWithChildren; } + bool canCollapseTopWithChildren() const { return m_canCollapseTopWithChildren; } + bool canCollapseBottomWithChildren() const { return m_canCollapseBottomWithChildren; } + bool selfCollapsingBlockClearedFloat() const { return m_selfCollapsingBlockClearedFloat; } + bool quirkContainer() const { return m_quirkContainer; } + bool determinedTopQuirk() const { return m_determinedTopQuirk; } + bool topQuirk() const { return m_topQuirk; } + bool bottomQuirk() const { return m_bottomQuirk; } + int posMargin() const { return m_posMargin; } + int negMargin() const { return m_negMargin; } + int margin() const { return m_posMargin - m_negMargin; } + }; + + void adjustPositionedBlock(RenderObject* child, const MarginInfo&); + void adjustFloatingBlock(const MarginInfo&); + RenderObject* handleSpecialChild(RenderObject* child, const MarginInfo&, CompactInfo&, bool& handled); + RenderObject* handleFloatingChild(RenderObject* child, const MarginInfo&, bool& handled); + RenderObject* handlePositionedChild(RenderObject* child, const MarginInfo&, bool& handled); + RenderObject* handleCompactChild(RenderObject* child, CompactInfo&, bool& handled); + RenderObject* handleRunInChild(RenderObject* child, bool& handled); + void collapseMargins(RenderObject* child, MarginInfo&, int yPosEstimate); + void clearFloatsIfNeeded(RenderObject* child, MarginInfo&, int oldTopPosMargin, int oldTopNegMargin); + void insertCompactIfNeeded(RenderObject* child, CompactInfo&); + int estimateVerticalPosition(RenderObject* child, const MarginInfo&); + void determineHorizontalPosition(RenderObject* child); + void handleBottomOfBlock(int top, int bottom, MarginInfo&); + void setCollapsedBottomMargin(const MarginInfo&); + // End helper functions and structs used by layoutBlockChildren. + +private: + typedef ListHashSet<RenderObject*>::const_iterator Iterator; + DeprecatedPtrList<FloatingObject>* m_floatingObjects; + ListHashSet<RenderObject*>* m_positionedObjects; + + // Allocated only when some of these fields have non-default values + struct MaxMargin { + MaxMargin(const RenderBlock* o) + : m_topPos(topPosDefault(o)) + , m_topNeg(topNegDefault(o)) + , m_bottomPos(bottomPosDefault(o)) + , m_bottomNeg(bottomNegDefault(o)) + { + } + + static int topPosDefault(const RenderBlock* o) { return o->marginTop() > 0 ? o->marginTop() : 0; } + static int topNegDefault(const RenderBlock* o) { return o->marginTop() < 0 ? -o->marginTop() : 0; } + static int bottomPosDefault(const RenderBlock* o) { return o->marginBottom() > 0 ? o->marginBottom() : 0; } + static int bottomNegDefault(const RenderBlock* o) { return o->marginBottom() < 0 ? -o->marginBottom() : 0; } + + int m_topPos; + int m_topNeg; + int m_bottomPos; + int m_bottomNeg; + }; + + MaxMargin* m_maxMargin; + +protected: + // How much content overflows out of our block vertically or horizontally. + int m_overflowHeight; + int m_overflowWidth; + int m_overflowLeft; + int m_overflowTop; +}; + +} // namespace WebCore + +#endif // RenderBlock_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderBox.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderBox.cpp new file mode 100644 index 0000000..78691de --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderBox.cpp @@ -0,0 +1,2768 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com) + * (C) 2005, 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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" +#include "RenderBox.h" + +#include "CachedImage.h" +#include "ChromeClient.h" +#include "Document.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "HTMLElement.h" +#include "HTMLNames.h" +#include "ImageBuffer.h" +#include "Frame.h" +#include "Page.h" +#include "RenderArena.h" +#include "RenderFlexibleBox.h" +#include "RenderLayer.h" +#include "RenderReplica.h" +#include "RenderTableCell.h" +#include "RenderTheme.h" +#include "RenderView.h" +#include <algorithm> +#include <math.h> + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +// Used by flexible boxes when flexing this element. +typedef WTF::HashMap<const RenderBox*, int> OverrideSizeMap; +static OverrideSizeMap* gOverrideSizeMap = 0; + +bool RenderBox::s_wasFloating = false; +bool RenderBox::s_hadOverflowClip = false; + +RenderBox::RenderBox(Node* node) + : RenderObject(node) + , m_width(0) + , m_height(0) + , m_x(0) + , m_y(0) + , m_marginLeft(0) + , m_marginRight(0) + , m_marginTop(0) + , m_marginBottom(0) + , m_minPrefWidth(-1) + , m_maxPrefWidth(-1) + , m_layer(0) + , m_inlineBoxWrapper(0) +{ +} + +RenderBox::~RenderBox() +{ +} + +void RenderBox::destroy() +{ + // A lot of the code in this function is just pasted into + // RenderWidget::destroy. If anything in this function changes, + // be sure to fix RenderWidget::destroy() as well. + if (hasOverrideSize()) + gOverrideSizeMap->remove(this); + + // This must be done before we destroy the RenderObject. + if (m_layer) + m_layer->clearClipRects(); + + if (style() && (style()->height().isPercent() || style()->minHeight().isPercent() || style()->maxHeight().isPercent())) + RenderBlock::removePercentHeightDescendant(this); + + RenderObject::destroy(); +} + +void RenderBox::styleWillChange(RenderStyle::Diff diff, const RenderStyle* newStyle) +{ + s_wasFloating = isFloating(); + s_hadOverflowClip = hasOverflowClip(); + + RenderObject::styleWillChange(diff, newStyle); +} + +void RenderBox::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +{ + // We need to ensure that view->maximalOutlineSize() is valid for any repaints that happen + // during the style change (it's used by absoluteClippedOverflowRect()). + if (style()->outlineWidth() > 0 && style()->outlineSize() > maximalOutlineSize(PaintPhaseOutline)) + static_cast<RenderView*>(document()->renderer())->setMaximalOutlineSize(style()->outlineSize()); + + RenderObject::styleDidChange(diff, oldStyle); + + if (needsLayout() && oldStyle && (oldStyle->height().isPercent() || oldStyle->minHeight().isPercent() || oldStyle->maxHeight().isPercent())) + RenderBlock::removePercentHeightDescendant(this); + + // The root and the RenderView always paint their backgrounds/borders. + if (isRoot() || isRenderView()) + setHasBoxDecorations(true); + + setInline(style()->isDisplayInlineType()); + + switch (style()->position()) { + case AbsolutePosition: + case FixedPosition: + setPositioned(true); + break; + default: + setPositioned(false); + + if (style()->isFloating()) + setFloating(true); + + if (style()->position() == RelativePosition) + setRelPositioned(true); + break; + } + + // We also handle <body> and <html>, whose overflow applies to the viewport. + if (!isRoot() && (isRenderBlock() || isTableRow() || isTableSection()) && style()->overflowX() != OVISIBLE) { + bool boxHasOverflowClip = true; + if (isBody()) { + // Overflow on the body can propagate to the viewport under the following conditions. + // (1) The root element is <html>. + // (2) We are the primary <body> (can be checked by looking at document.body). + // (3) The root element has visible overflow. + if (document()->documentElement()->hasTagName(htmlTag) && + document()->body() == element() && + document()->documentElement()->renderer()->style()->overflowX() == OVISIBLE) + boxHasOverflowClip = false; + } + + // Check for overflow clip. + // It's sufficient to just check one direction, since it's illegal to have visible on only one overflow value. + if (boxHasOverflowClip) { + if (!s_hadOverflowClip) + // Erase the overflow + repaint(); + setHasOverflowClip(); + } + } + + setHasTransform(style()->hasTransform()); + setHasReflection(style()->boxReflect()); + + if (requiresLayer()) { + if (!m_layer) { + if (s_wasFloating && isFloating()) + setChildNeedsLayout(true); + m_layer = new (renderArena()) RenderLayer(this); + setHasLayer(true); + m_layer->insertOnlyThisLayer(); + if (parent() && !needsLayout() && containingBlock()) + m_layer->updateLayerPositions(); + } + } else if (m_layer && !isRoot() && !isRenderView()) { + ASSERT(m_layer->parent()); + RenderLayer* layer = m_layer; + m_layer = 0; + setHasLayer(false); + setHasTransform(false); // Either a transform wasn't specified or the object doesn't support transforms, so just null out the bit. + setHasReflection(false); + layer->removeOnlyThisLayer(); + if (s_wasFloating && isFloating()) + setChildNeedsLayout(true); + } + + // If our zoom factor changes and we have a defined scrollLeft/Top, we need to adjust that value into the + // new zoomed coordinate space. + if (hasOverflowClip() && oldStyle && style() && oldStyle->effectiveZoom() != style()->effectiveZoom()) { + int left = scrollLeft(); + if (left) { + left = (left / oldStyle->effectiveZoom()) * style()->effectiveZoom(); + setScrollLeft(left); + } + int top = scrollTop(); + if (top) { + top = (top / oldStyle->effectiveZoom()) * style()->effectiveZoom(); + setScrollTop(top); + } + } + + if (m_layer) + m_layer->styleChanged(diff, oldStyle); + + // Set the text color if we're the body. + if (isBody()) + document()->setTextColor(style()->color()); +} + +int RenderBox::minPrefWidth() const +{ + if (prefWidthsDirty()) + const_cast<RenderBox*>(this)->calcPrefWidths(); + + return m_minPrefWidth; +} + +int RenderBox::maxPrefWidth() const +{ + if (prefWidthsDirty()) + const_cast<RenderBox*>(this)->calcPrefWidths(); + + return m_maxPrefWidth; +} + +int RenderBox::overrideSize() const +{ + if (!hasOverrideSize()) + return -1; + return gOverrideSizeMap->get(this); +} + +void RenderBox::setOverrideSize(int s) +{ + if (s == -1) { + if (hasOverrideSize()) { + setHasOverrideSize(false); + gOverrideSizeMap->remove(this); + } + } else { + if (!gOverrideSizeMap) + gOverrideSizeMap = new OverrideSizeMap(); + setHasOverrideSize(true); + gOverrideSizeMap->set(this, s); + } +} + +int RenderBox::overrideWidth() const +{ + return hasOverrideSize() ? overrideSize() : m_width; +} + +int RenderBox::overrideHeight() const +{ + return hasOverrideSize() ? overrideSize() : m_height; +} + +void RenderBox::setPos(int xPos, int yPos) +{ + // Optimize for the case where we don't move at all. + if (xPos == m_x && yPos == m_y) + return; + + m_x = xPos; + m_y = yPos; +} + +int RenderBox::calcBorderBoxWidth(int width) const +{ + int bordersPlusPadding = borderLeft() + borderRight() + paddingLeft() + paddingRight(); + if (style()->boxSizing() == CONTENT_BOX) + return width + bordersPlusPadding; + return max(width, bordersPlusPadding); +} + +int RenderBox::calcBorderBoxHeight(int height) const +{ + int bordersPlusPadding = borderTop() + borderBottom() + paddingTop() + paddingBottom(); + if (style()->boxSizing() == CONTENT_BOX) + return height + bordersPlusPadding; + return max(height, bordersPlusPadding); +} + +int RenderBox::calcContentBoxWidth(int width) const +{ + if (style()->boxSizing() == BORDER_BOX) + width -= (borderLeft() + borderRight() + paddingLeft() + paddingRight()); + return max(0, width); +} + +int RenderBox::calcContentBoxHeight(int height) const +{ + if (style()->boxSizing() == BORDER_BOX) + height -= (borderTop() + borderBottom() + paddingTop() + paddingBottom()); + return max(0, height); +} + +// Hit Testing +bool RenderBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction action) +{ + tx += m_x; + ty += m_y; + + // Check kids first. + for (RenderObject* child = lastChild(); child; child = child->previousSibling()) { + // FIXME: We have to skip over inline flows, since they can show up inside table rows + // at the moment (a demoted inline <form> for example). If we ever implement a + // table-specific hit-test method (which we should do for performance reasons anyway), + // then we can remove this check. + if (!child->hasLayer() && !child->isInlineFlow() && child->nodeAtPoint(request, result, x, y, tx, ty, action)) { + updateHitTestResult(result, IntPoint(x - tx, y - ty)); + return true; + } + } + + // Check our bounds next. For this purpose always assume that we can only be hit in the + // foreground phase (which is true for replaced elements like images). + if (visibleToHitTesting() && action == HitTestForeground && IntRect(tx, ty, m_width, m_height).contains(x, y)) { + updateHitTestResult(result, IntPoint(x - tx, y - ty)); + return true; + } + + return false; +} + +// --------------------- painting stuff ------------------------------- + +void RenderBox::paint(PaintInfo& paintInfo, int tx, int ty) +{ + tx += m_x; + ty += m_y; + + // default implementation. Just pass paint through to the children + PaintInfo childInfo(paintInfo); + childInfo.paintingRoot = paintingRootForChildren(paintInfo); + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) + child->paint(childInfo, tx, ty); +} + +void RenderBox::paintRootBoxDecorations(PaintInfo& paintInfo, int tx, int ty) +{ + const FillLayer* bgLayer = style()->backgroundLayers(); + Color bgColor = style()->backgroundColor(); + if (!style()->hasBackground() && element() && element()->hasTagName(HTMLNames::htmlTag)) { + // Locate the <body> element using the DOM. This is easier than trying + // to crawl around a render tree with potential :before/:after content and + // anonymous blocks created by inline <body> tags etc. We can locate the <body> + // render object very easily via the DOM. + HTMLElement* body = document()->body(); + RenderObject* bodyObject = (body && body->hasLocalName(bodyTag)) ? body->renderer() : 0; + if (bodyObject) { + bgLayer = bodyObject->style()->backgroundLayers(); + bgColor = bodyObject->style()->backgroundColor(); + } + } + + int w = width(); + int h = height(); + + int rw; + int rh; + if (view()->frameView()) { + rw = view()->frameView()->contentsWidth(); + rh = view()->frameView()->contentsHeight(); + } else { + rw = view()->width(); + rh = view()->height(); + } + + // CSS2 14.2: + // The background of the box generated by the root element covers the entire canvas including + // its margins. + int bx = tx - marginLeft(); + int by = ty - marginTop(); + int bw = max(w + marginLeft() + marginRight() + borderLeft() + borderRight(), rw); + int bh = max(h + marginTop() + marginBottom() + borderTop() + borderBottom(), rh); + + int my = max(by, paintInfo.rect.y()); + + paintFillLayers(paintInfo, bgColor, bgLayer, my, paintInfo.rect.height(), bx, by, bw, bh); + + if (style()->hasBorder() && style()->display() != INLINE) + paintBorder(paintInfo.context, tx, ty, w, h, style()); +} + +void RenderBox::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty) +{ + if (!shouldPaintWithinRoot(paintInfo)) + return; + + if (isRoot()) { + paintRootBoxDecorations(paintInfo, tx, ty); + return; + } + + int w = width(); + int h = height() + borderTopExtra() + borderBottomExtra(); + ty -= borderTopExtra(); + + // border-fit can adjust where we paint our border and background. If set, we snugly fit our line box descendants. (The iChat + // balloon layout is an example of this). + borderFitAdjust(tx, w); + + int my = max(ty, paintInfo.rect.y()); + int mh; + if (ty < paintInfo.rect.y()) + mh = max(0, h - (paintInfo.rect.y() - ty)); + else + mh = min(paintInfo.rect.height(), h); + + // FIXME: Should eventually give the theme control over whether the box shadow should paint, since controls could have + // custom shadows of their own. + paintBoxShadow(paintInfo.context, tx, ty, w, h, style()); + + // If we have a native theme appearance, paint that before painting our background. + // The theme will tell us whether or not we should also paint the CSS background. + bool themePainted = style()->hasAppearance() && !theme()->paint(this, paintInfo, IntRect(tx, ty, w, h)); + if (!themePainted) { + // The <body> only paints its background if the root element has defined a background + // independent of the body. Go through the DOM to get to the root element's render object, + // since the root could be inline and wrapped in an anonymous block. + if (!isBody() || document()->documentElement()->renderer()->style()->hasBackground()) + paintFillLayers(paintInfo, style()->backgroundColor(), style()->backgroundLayers(), my, mh, tx, ty, w, h); + if (style()->hasAppearance()) + theme()->paintDecorations(this, paintInfo, IntRect(tx, ty, w, h)); + } + + // The theme will tell us whether or not we should also paint the CSS border. + if ((!style()->hasAppearance() || (!themePainted && theme()->paintBorderOnly(this, paintInfo, IntRect(tx, ty, w, h)))) && style()->hasBorder()) + paintBorder(paintInfo.context, tx, ty, w, h, style()); +} + +void RenderBox::paintMask(PaintInfo& paintInfo, int tx, int ty) +{ + if (!shouldPaintWithinRoot(paintInfo) || style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) + return; + + int w = width(); + int h = height() + borderTopExtra() + borderBottomExtra(); + ty -= borderTopExtra(); + + // border-fit can adjust where we paint our border and background. If set, we snugly fit our line box descendants. (The iChat + // balloon layout is an example of this). + borderFitAdjust(tx, w); + + int my = max(ty, paintInfo.rect.y()); + int mh; + if (ty < paintInfo.rect.y()) + mh = max(0, h - (paintInfo.rect.y() - ty)); + else + mh = min(paintInfo.rect.height(), h); + + paintMaskImages(paintInfo, my, mh, tx, ty, w, h); +} + +void RenderBox::paintMaskImages(const PaintInfo& paintInfo, int my, int mh, int tx, int ty, int w, int h) +{ + // Figure out if we need to push a transparency layer to render our mask. + bool pushTransparencyLayer = false; + StyleImage* maskBoxImage = style()->maskBoxImage().image(); + if ((maskBoxImage && style()->maskLayers()->hasImage()) || style()->maskLayers()->next()) + // We have to use an extra image buffer to hold the mask. Multiple mask images need + // to composite together using source-over so that they can then combine into a single unified mask that + // can be composited with the content using destination-in. SVG images need to be able to set compositing modes + // as they draw images contained inside their sub-document, so we paint all our images into a separate buffer + // and composite that buffer as the mask. + pushTransparencyLayer = true; + + CompositeOperator compositeOp = CompositeDestinationIn; + if (pushTransparencyLayer) { + paintInfo.context->setCompositeOperation(CompositeDestinationIn); + paintInfo.context->beginTransparencyLayer(1.0f); + compositeOp = CompositeSourceOver; + } + + paintFillLayers(paintInfo, Color(), style()->maskLayers(), my, mh, tx, ty, w, h, compositeOp); + paintNinePieceImage(paintInfo.context, tx, ty, w, h, style(), style()->maskBoxImage(), compositeOp); + + if (pushTransparencyLayer) + paintInfo.context->endTransparencyLayer(); +} + +IntRect RenderBox::maskClipRect() +{ + IntRect bbox = borderBox(); + if (style()->maskBoxImage().image()) + return bbox; + + IntRect result; + for (const FillLayer* maskLayer = style()->maskLayers(); maskLayer; maskLayer = maskLayer->next()) { + if (maskLayer->image()) { + IntRect maskRect; + IntPoint phase; + IntSize tileSize; + calculateBackgroundImageGeometry(maskLayer, bbox.x(), bbox.y(), bbox.width(), bbox.height(), maskRect, phase, tileSize); + result.unite(maskRect); + } + } + return result; +} + +void RenderBox::paintFillLayers(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, + int clipY, int clipH, int tx, int ty, int width, int height, CompositeOperator op) +{ + if (!fillLayer) + return; + + paintFillLayers(paintInfo, c, fillLayer->next(), clipY, clipH, tx, ty, width, height, op); + paintFillLayer(paintInfo, c, fillLayer, clipY, clipH, tx, ty, width, height, op); +} + +void RenderBox::paintFillLayer(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, + int clipY, int clipH, int tx, int ty, int width, int height, CompositeOperator op) +{ + paintFillLayerExtended(paintInfo, c, fillLayer, clipY, clipH, tx, ty, width, height, 0, op); +} + +IntSize RenderBox::calculateBackgroundSize(const FillLayer* bgLayer, int scaledWidth, int scaledHeight) const +{ + StyleImage* bg = bgLayer->image(); + bg->setImageContainerSize(IntSize(scaledWidth, scaledHeight)); // Use the box established by background-origin. + + if (bgLayer->isSizeSet()) { + int w = scaledWidth; + int h = scaledHeight; + Length bgWidth = bgLayer->size().width(); + Length bgHeight = bgLayer->size().height(); + + if (bgWidth.isFixed()) + w = bgWidth.value(); + else if (bgWidth.isPercent()) + w = bgWidth.calcValue(scaledWidth); + + if (bgHeight.isFixed()) + h = bgHeight.value(); + else if (bgHeight.isPercent()) + h = bgHeight.calcValue(scaledHeight); + + // If one of the values is auto we have to use the appropriate + // scale to maintain our aspect ratio. + if (bgWidth.isAuto() && !bgHeight.isAuto()) + w = bg->imageSize(this, style()->effectiveZoom()).width() * h / bg->imageSize(this, style()->effectiveZoom()).height(); + else if (!bgWidth.isAuto() && bgHeight.isAuto()) + h = bg->imageSize(this, style()->effectiveZoom()).height() * w / bg->imageSize(this, style()->effectiveZoom()).width(); + else if (bgWidth.isAuto() && bgHeight.isAuto()) { + // If both width and height are auto, we just want to use the image's + // intrinsic size. + w = bg->imageSize(this, style()->effectiveZoom()).width(); + h = bg->imageSize(this, style()->effectiveZoom()).height(); + } + + return IntSize(max(1, w), max(1, h)); + } else + return bg->imageSize(this, style()->effectiveZoom()); +} + +void RenderBox::imageChanged(WrappedImagePtr image, const IntRect*) +{ + if (isInlineFlow() || + style()->borderImage().image() && style()->borderImage().image()->data() == image || + style()->maskBoxImage().image() && style()->maskBoxImage().image()->data() == image) { + repaint(); + return; + } + + bool didFullRepaint = repaintLayerRectsForImage(image, style()->backgroundLayers(), true); + if (!didFullRepaint) { + repaintLayerRectsForImage(image, style()->maskLayers(), false); + } +} + +bool RenderBox::repaintLayerRectsForImage(WrappedImagePtr image, const FillLayer* layers, bool drawingBackground) +{ + IntRect absoluteRect; + RenderBox* layerRenderer = 0; + + for (const FillLayer* curLayer = layers; curLayer; curLayer = curLayer->next()) { + if (curLayer->image() && image == curLayer->image()->data() && curLayer->image()->canRender(style()->effectiveZoom())) { + // Now that we know this image is being used, compute the renderer and the rect + // if we haven't already + if (!layerRenderer) { + bool drawingRootBackground = drawingBackground && (isRoot() || (isBody() && !document()->documentElement()->renderer()->style()->hasBackground())); + if (drawingRootBackground) { + layerRenderer = view(); + + int rw; + int rh; + + if (FrameView* frameView = static_cast<RenderView*>(layerRenderer)->frameView()) { + rw = frameView->contentsWidth(); + rh = frameView->contentsHeight(); + } else { + rw = layerRenderer->width(); + rh = layerRenderer->height(); + } + absoluteRect = IntRect(-layerRenderer->marginLeft(), + -layerRenderer->marginTop(), + max(layerRenderer->width() + layerRenderer->marginLeft() + layerRenderer->marginRight() + layerRenderer->borderLeft() + layerRenderer->borderRight(), rw), + max(layerRenderer->height() + layerRenderer->marginTop() + layerRenderer->marginBottom() + layerRenderer->borderTop() + layerRenderer->borderBottom(), rh)); + } else { + layerRenderer = this; + absoluteRect = borderBox(); + } + + layerRenderer->computeAbsoluteRepaintRect(absoluteRect); + } + + IntRect repaintRect; + IntPoint phase; + IntSize tileSize; + layerRenderer->calculateBackgroundImageGeometry(curLayer, absoluteRect.x(), absoluteRect.y(), absoluteRect.width(), absoluteRect.height(), repaintRect, phase, tileSize); + view()->repaintViewRectangle(repaintRect); + if (repaintRect == absoluteRect) + return true; + } + } + return false; +} + +void RenderBox::calculateBackgroundImageGeometry(const FillLayer* bgLayer, int tx, int ty, int w, int h, IntRect& destRect, IntPoint& phase, IntSize& tileSize) +{ + int pw; + int ph; + int left = 0; + int right = 0; + int top = 0; + int bottom = 0; + int cx; + int cy; + int rw = 0; + int rh = 0; + + // CSS2 chapter 14.2.1 + + if (bgLayer->attachment()) { + // Scroll + if (bgLayer->origin() != BorderFillBox) { + left = borderLeft(); + right = borderRight(); + top = borderTop(); + bottom = borderBottom(); + if (bgLayer->origin() == ContentFillBox) { + left += paddingLeft(); + right += paddingRight(); + top += paddingTop(); + bottom += paddingBottom(); + } + } + + // The background of the box generated by the root element covers the entire canvas including + // its margins. Since those were added in already, we have to factor them out when computing the + // box used by background-origin/size/position. + if (isRoot()) { + rw = width() - left - right; + rh = height() - top - bottom; + left += marginLeft(); + right += marginRight(); + top += marginTop(); + bottom += marginBottom(); + } + cx = tx; + cy = ty; + pw = w - left - right; + ph = h - top - bottom; + } else { + // Fixed + IntRect vr = viewRect(); + cx = vr.x(); + cy = vr.y(); + pw = vr.width(); + ph = vr.height(); + } + + int sx = 0; + int sy = 0; + int cw; + int ch; + + IntSize scaledImageSize; + if (isRoot() && bgLayer->attachment()) + scaledImageSize = calculateBackgroundSize(bgLayer, rw, rh); + else + scaledImageSize = calculateBackgroundSize(bgLayer, pw, ph); + + int scaledImageWidth = scaledImageSize.width(); + int scaledImageHeight = scaledImageSize.height(); + + EFillRepeat backgroundRepeat = bgLayer->repeat(); + + int xPosition; + if (isRoot() && bgLayer->attachment()) + xPosition = bgLayer->xPosition().calcMinValue(rw - scaledImageWidth, true); + else + xPosition = bgLayer->xPosition().calcMinValue(pw - scaledImageWidth, true); + if (backgroundRepeat == RepeatFill || backgroundRepeat == RepeatXFill) { + cw = pw + left + right; + sx = scaledImageWidth ? scaledImageWidth - (xPosition + left) % scaledImageWidth : 0; + } else { + cx += max(xPosition + left, 0); + sx = -min(xPosition + left, 0); + cw = scaledImageWidth + min(xPosition + left, 0); + } + + int yPosition; + if (isRoot() && bgLayer->attachment()) + yPosition = bgLayer->yPosition().calcMinValue(rh - scaledImageHeight, true); + else + yPosition = bgLayer->yPosition().calcMinValue(ph - scaledImageHeight, true); + if (backgroundRepeat == RepeatFill || backgroundRepeat == RepeatYFill) { + ch = ph + top + bottom; + sy = scaledImageHeight ? scaledImageHeight - (yPosition + top) % scaledImageHeight : 0; + } else { + cy += max(yPosition + top, 0); + sy = -min(yPosition + top, 0); + ch = scaledImageHeight + min(yPosition + top, 0); + } + + if (!bgLayer->attachment()) { + sx += max(tx - cx, 0); + sy += max(ty - cy, 0); + } + + destRect = IntRect(cx, cy, cw, ch); + destRect.intersect(IntRect(tx, ty, w, h)); + phase = IntPoint(sx, sy); + tileSize = IntSize(scaledImageWidth, scaledImageHeight); +} + +void RenderBox::paintFillLayerExtended(const PaintInfo& paintInfo, const Color& c, const FillLayer* bgLayer, int clipY, int clipH, + int tx, int ty, int w, int h, InlineFlowBox* box, CompositeOperator op) +{ + GraphicsContext* context = paintInfo.context; + bool includeLeftEdge = box ? box->includeLeftEdge() : true; + bool includeRightEdge = box ? box->includeRightEdge() : true; + int bLeft = includeLeftEdge ? borderLeft() : 0; + int bRight = includeRightEdge ? borderRight() : 0; + int pLeft = includeLeftEdge ? paddingLeft() : 0; + int pRight = includeRightEdge ? paddingRight() : 0; + + bool clippedToBorderRadius = false; + if (style()->hasBorderRadius() && (includeLeftEdge || includeRightEdge)) { + context->save(); + context->addRoundedRectClip(IntRect(tx, ty, w, h), + includeLeftEdge ? style()->borderTopLeftRadius() : IntSize(), + includeRightEdge ? style()->borderTopRightRadius() : IntSize(), + includeLeftEdge ? style()->borderBottomLeftRadius() : IntSize(), + includeRightEdge ? style()->borderBottomRightRadius() : IntSize()); + clippedToBorderRadius = true; + } + + if (bgLayer->clip() == PaddingFillBox || bgLayer->clip() == ContentFillBox) { + // Clip to the padding or content boxes as necessary. + bool includePadding = bgLayer->clip() == ContentFillBox; + int x = tx + bLeft + (includePadding ? pLeft : 0); + int y = ty + borderTop() + (includePadding ? paddingTop() : 0); + int width = w - bLeft - bRight - (includePadding ? pLeft + pRight : 0); + int height = h - borderTop() - borderBottom() - (includePadding ? paddingTop() + paddingBottom() : 0); + context->save(); + context->clip(IntRect(x, y, width, height)); + } else if (bgLayer->clip() == TextFillBox) { + // We have to draw our text into a mask that can then be used to clip background drawing. + // First figure out how big the mask has to be. It should be no bigger than what we need + // to actually render, so we should intersect the dirty rect with the border box of the background. + IntRect maskRect(tx, ty, w, h); + maskRect.intersect(paintInfo.rect); + + // Now create the mask. + auto_ptr<ImageBuffer> maskImage = ImageBuffer::create(maskRect.size(), false); + if (!maskImage.get()) + return; + + GraphicsContext* maskImageContext = maskImage->context(); + maskImageContext->translate(-maskRect.x(), -maskRect.y()); + + // Now add the text to the clip. We do this by painting using a special paint phase that signals to + // InlineTextBoxes that they should just add their contents to the clip. + PaintInfo info(maskImageContext, maskRect, PaintPhaseTextClip, true, 0, 0); + if (box) + box->paint(info, tx - box->xPos(), ty - box->yPos()); + else + paint(info, tx, ty); + + // The mask has been created. Now we just need to clip to it. + context->save(); + context->clipToImageBuffer(maskRect, maskImage.get()); + } + + StyleImage* bg = bgLayer->image(); + bool shouldPaintBackgroundImage = bg && bg->canRender(style()->effectiveZoom()); + Color bgColor = c; + + // When this style flag is set, change existing background colors and images to a solid white background. + // If there's no bg color or image, leave it untouched to avoid affecting transparency. + // We don't try to avoid loading the background images, because this style flag is only set + // when printing, and at that point we've already loaded the background images anyway. (To avoid + // loading the background images we'd have to do this check when applying styles rather than + // while rendering.) + if (style()->forceBackgroundsToWhite()) { + // Note that we can't reuse this variable below because the bgColor might be changed + bool shouldPaintBackgroundColor = !bgLayer->next() && bgColor.isValid() && bgColor.alpha() > 0; + if (shouldPaintBackgroundImage || shouldPaintBackgroundColor) { + bgColor = Color::white; + shouldPaintBackgroundImage = false; + } + } + + // Only fill with a base color (e.g., white) if we're the root document, since iframes/frames with + // no background in the child document should show the parent's background. + bool isTransparent = false; + if (!bgLayer->next() && isRoot() && !(bgColor.isValid() && bgColor.alpha() > 0) && view()->frameView()) { + Node* elt = document()->ownerElement(); + if (elt) { + if (!elt->hasTagName(frameTag)) { + // Locate the <body> element using the DOM. This is easier than trying + // to crawl around a render tree with potential :before/:after content and + // anonymous blocks created by inline <body> tags etc. We can locate the <body> + // render object very easily via the DOM. + HTMLElement* body = document()->body(); + isTransparent = !body || !body->hasLocalName(framesetTag); // Can't scroll a frameset document anyway. + } + } else + isTransparent = view()->frameView()->isTransparent(); + + // FIXME: This needs to be dynamic. We should be able to go back to blitting if we ever stop being transparent. + if (isTransparent) + view()->frameView()->setUseSlowRepaints(); // The parent must show behind the child. + } + + // Paint the color first underneath all images. + if (!bgLayer->next()) { + IntRect rect(tx, clipY, w, clipH); + // If we have an alpha and we are painting the root element, go ahead and blend with the base background color. + if (isRoot() && (!bgColor.isValid() || bgColor.alpha() < 0xFF) && !isTransparent) { + Color baseColor = view()->frameView()->baseBackgroundColor(); + if (baseColor.alpha() > 0) { + context->save(); + context->setCompositeOperation(CompositeCopy); + context->fillRect(rect, baseColor); + context->restore(); + } else + context->clearRect(rect); + } + + if (bgColor.isValid() && bgColor.alpha() > 0) + context->fillRect(rect, bgColor); + } + + // no progressive loading of the background image + if (shouldPaintBackgroundImage) { + IntRect destRect; + IntPoint phase; + IntSize tileSize; + + calculateBackgroundImageGeometry(bgLayer, tx, ty, w, h, destRect, phase, tileSize); + if (!destRect.isEmpty()) { + CompositeOperator compositeOp = op == CompositeSourceOver ? bgLayer->composite() : op; + context->drawTiledImage(bg->image(this, tileSize), destRect, phase, tileSize, compositeOp); + } + } + + if (bgLayer->clip() != BorderFillBox) + // Undo the background clip + context->restore(); + + if (clippedToBorderRadius) + // Undo the border radius clip + context->restore(); +} + +#if PLATFORM(MAC) + +void RenderBox::paintCustomHighlight(int tx, int ty, const AtomicString& type, bool behindText) +{ + Frame* frame = document()->frame(); + if (!frame) + return; + Page* page = frame->page(); + if (!page) + return; + + InlineBox* boxWrap = inlineBoxWrapper(); + RootInlineBox* r = boxWrap ? boxWrap->root() : 0; + if (r) { + FloatRect rootRect(tx + r->xPos(), ty + r->selectionTop(), r->width(), r->selectionHeight()); + FloatRect imageRect(tx + m_x, rootRect.y(), width(), rootRect.height()); + page->chrome()->client()->paintCustomHighlight(node(), type, imageRect, rootRect, behindText, false); + } else { + FloatRect imageRect(tx + m_x, ty + m_y, width(), height()); + page->chrome()->client()->paintCustomHighlight(node(), type, imageRect, imageRect, behindText, false); + } +} + +#endif + +IntRect RenderBox::getOverflowClipRect(int tx, int ty) +{ + // FIXME: When overflow-clip (CSS3) is implemented, we'll obtain the property + // here. + + int bLeft = borderLeft(); + int bTop = borderTop(); + + int clipX = tx + bLeft; + int clipY = ty + bTop; + int clipWidth = m_width - bLeft - borderRight(); + int clipHeight = m_height - bTop - borderBottom() + borderTopExtra() + borderBottomExtra(); + + // Subtract out scrollbars if we have them. + if (m_layer) { + clipWidth -= m_layer->verticalScrollbarWidth(); + clipHeight -= m_layer->horizontalScrollbarHeight(); + } + + return IntRect(clipX, clipY, clipWidth, clipHeight); +} + +IntRect RenderBox::getClipRect(int tx, int ty) +{ + int clipX = tx; + int clipY = ty; + int clipWidth = m_width; + int clipHeight = m_height; + + if (!style()->clipLeft().isAuto()) { + int c = style()->clipLeft().calcValue(m_width); + clipX += c; + clipWidth -= c; + } + + if (!style()->clipRight().isAuto()) + clipWidth -= m_width - style()->clipRight().calcValue(m_width); + + if (!style()->clipTop().isAuto()) { + int c = style()->clipTop().calcValue(m_height); + clipY += c; + clipHeight -= c; + } + + if (!style()->clipBottom().isAuto()) + clipHeight -= m_height - style()->clipBottom().calcValue(m_height); + + return IntRect(clipX, clipY, clipWidth, clipHeight); +} + +int RenderBox::containingBlockWidth() const +{ + RenderBlock* cb = containingBlock(); + if (!cb) + return 0; + if (shrinkToAvoidFloats()) + return cb->lineWidth(m_y); + return cb->availableWidth(); +} + +IntSize RenderBox::offsetForPositionedInContainer(RenderObject* container) const +{ + if (!container->isRelPositioned() || !container->isInlineFlow()) + return IntSize(); + + // When we have an enclosing relpositioned inline, we need to add in the offset of the first line + // box from the rest of the content, but only in the cases where we know we're positioned + // relative to the inline itself. + + IntSize offset; + RenderFlow* flow = static_cast<RenderFlow*>(container); + int sx; + int sy; + if (flow->firstLineBox()) { + sx = flow->firstLineBox()->xPos(); + sy = flow->firstLineBox()->yPos(); + } else { + sx = flow->staticX(); + sy = flow->staticY(); + } + + if (!hasStaticX()) + offset.setWidth(sx); + // This is not terribly intuitive, but we have to match other browsers. Despite being a block display type inside + // an inline, we still keep our x locked to the left of the relative positioned inline. Arguably the correct + // behavior would be to go flush left to the block that contains the inline, but that isn't what other browsers + // do. + else if (!style()->isOriginalDisplayInlineType()) + // Avoid adding in the left border/padding of the containing block twice. Subtract it out. + offset.setWidth(sx - (containingBlock()->borderLeft() + containingBlock()->paddingLeft())); + + if (!hasStaticY()) + offset.setHeight(sy); + + return offset; +} + +FloatPoint RenderBox::localToAbsolute(FloatPoint localPoint, bool fixed, bool useTransforms) const +{ + if (RenderView* v = view()) { + if (LayoutState* layoutState = v->layoutState()) { + IntSize offset = layoutState->m_offset; + offset.expand(m_x, m_y); + localPoint += offset; + if (style()->position() == RelativePosition && m_layer) + localPoint += m_layer->relativePositionOffset(); + return localPoint; + } + } + + if (style()->position() == FixedPosition) + fixed = true; + + RenderObject* o = container(); + if (o) { + if (useTransforms && m_layer && m_layer->transform()) { + fixed = false; // Elements with transforms act as a containing block for fixed position descendants + localPoint = m_layer->transform()->mapPoint(localPoint); + } + + localPoint += offsetFromContainer(o); + + return o->localToAbsoluteForContent(localPoint, fixed, useTransforms); + } + + return FloatPoint(); +} + +FloatPoint RenderBox::absoluteToLocal(FloatPoint containerPoint, bool fixed, bool useTransforms) const +{ + // We don't expect absoluteToLocal() to be called during layout (yet) + ASSERT(!view() || !view()->layoutState()); + + if (style()->position() == FixedPosition) + fixed = true; + + if (useTransforms && m_layer && m_layer->transform()) + fixed = false; + + RenderObject* o = container(); + if (o) { + FloatPoint localPoint = o->absoluteToLocal(containerPoint, fixed, useTransforms); + + // Take into account space above a vertically aligned table cell + // (see localToAbsoluteForContent()) + localPoint.move(0.0f, -static_cast<float>(borderTopExtra())); + + localPoint -= offsetFromContainer(o); + + if (useTransforms && m_layer && m_layer->transform()) + localPoint = m_layer->transform()->inverse().mapPoint(localPoint); + + return localPoint; + } + + return FloatPoint(); +} + +FloatQuad RenderBox::localToAbsoluteQuad(const FloatQuad& localQuad, bool fixed) const +{ + if (style()->position() == FixedPosition) + fixed = true; + + RenderObject* o = container(); + if (o) { + FloatQuad quad = localQuad; + if (m_layer && m_layer->transform()) { + fixed = false; // Elements with transforms act as a containing block for fixed position descendants + quad = m_layer->transform()->mapQuad(quad); + } + + quad += offsetFromContainer(o); + + // Take into account space above a vertically aligned table cell + // (see localToAbsoluteForContent()) + quad.move(0.0f, static_cast<float>(o->borderTopExtra())); + + return o->localToAbsoluteQuad(quad, fixed); + } + + return FloatQuad(); +} + +IntSize RenderBox::offsetFromContainer(RenderObject* o) const +{ + ASSERT(o == container()); + + IntSize offset; + if (isRelPositioned()) + offset += relativePositionOffset(); + + if (!isInline() || isReplaced()) { + RenderBlock* cb; + if (o->isBlockFlow() && style()->position() != AbsolutePosition && style()->position() != FixedPosition + && (cb = static_cast<RenderBlock*>(o))->hasColumns()) { + IntRect rect(m_x, m_y, 1, 1); + cb->adjustRectForColumns(rect); + offset.expand(rect.x(), rect.y()); + } else + offset.expand(m_x, m_y); + } + + if (o->hasOverflowClip()) + offset -= o->layer()->scrolledContentOffset(); + + if (style()->position() == AbsolutePosition) + offset += offsetForPositionedInContainer(o); + + return offset; +} + +void RenderBox::dirtyLineBoxes(bool fullLayout, bool /*isRootLineBox*/) +{ + if (m_inlineBoxWrapper) { + if (fullLayout) { + m_inlineBoxWrapper->destroy(renderArena()); + m_inlineBoxWrapper = 0; + } else + m_inlineBoxWrapper->dirtyLineBoxes(); + } +} + +void RenderBox::position(InlineBox* box) +{ + if (isPositioned()) { + // Cache the x position only if we were an INLINE type originally. + bool wasInline = style()->isOriginalDisplayInlineType(); + if (wasInline && hasStaticX()) { + // The value is cached in the xPos of the box. We only need this value if + // our object was inline originally, since otherwise it would have ended up underneath + // the inlines. + setStaticX(box->xPos()); + setChildNeedsLayout(true, false); // Just go ahead and mark the positioned object as needing layout, so it will update its position properly. + } else if (!wasInline && hasStaticY()) { + // Our object was a block originally, so we make our normal flow position be + // just below the line box (as though all the inlines that came before us got + // wrapped in an anonymous block, which is what would have happened had we been + // in flow). This value was cached in the yPos() of the box. + setStaticY(box->yPos()); + setChildNeedsLayout(true, false); // Just go ahead and mark the positioned object as needing layout, so it will update its position properly. + } + + // Nuke the box. + box->remove(); + box->destroy(renderArena()); + } else if (isReplaced()) { + m_x = box->xPos(); + m_y = box->yPos(); + m_inlineBoxWrapper = box; + } +} + +void RenderBox::deleteLineBoxWrapper() +{ + if (m_inlineBoxWrapper) { + if (!documentBeingDestroyed()) + m_inlineBoxWrapper->remove(); + m_inlineBoxWrapper->destroy(renderArena()); + m_inlineBoxWrapper = 0; + } +} + +IntRect RenderBox::absoluteClippedOverflowRect() +{ + if (style()->visibility() != VISIBLE && !enclosingLayer()->hasVisibleContent()) + return IntRect(); + + IntRect r = overflowRect(false); + + RenderView* v = view(); + if (v) + r.move(v->layoutDelta()); + + if (style()) { + if (style()->hasAppearance()) + // The theme may wish to inflate the rect used when repainting. + theme()->adjustRepaintRect(this, r); + + // We have to use maximalOutlineSize() because a child might have an outline + // that projects outside of our overflowRect. + if (v) { + ASSERT(style()->outlineSize() <= v->maximalOutlineSize()); + r.inflate(v->maximalOutlineSize()); + } + } + computeAbsoluteRepaintRect(r); + return r; +} + +void RenderBox::computeAbsoluteRepaintRect(IntRect& rect, bool fixed) +{ + if (RenderView* v = view()) { + if (LayoutState* layoutState = v->layoutState()) { + if (style()->position() == RelativePosition && m_layer) + rect.move(m_layer->relativePositionOffset()); + + rect.move(m_x, m_y); + rect.move(layoutState->m_offset); + if (layoutState->m_clipped) + rect.intersect(layoutState->m_clipRect); + return; + } + } + + if (hasReflection()) + rect.unite(reflectedRect(rect)); + + RenderObject* o = container(); + if (!o) + return; + + IntPoint topLeft = rect.location(); + topLeft.move(m_x, m_y); + + if (style()->position() == FixedPosition) + fixed = true; + + if (o->isBlockFlow() && style()->position() != AbsolutePosition && style()->position() != FixedPosition) { + RenderBlock* cb = static_cast<RenderBlock*>(o); + if (cb->hasColumns()) { + IntRect repaintRect(topLeft, rect.size()); + cb->adjustRectForColumns(repaintRect); + topLeft = repaintRect.location(); + rect = repaintRect; + } + } + + // We are now in our parent container's coordinate space. Apply our transform to obtain a bounding box + // in the parent's coordinate space that encloses us. + if (m_layer && m_layer->transform()) { + fixed = false; + rect = m_layer->transform()->mapRect(rect); + // FIXME: this clobbers topLeft adjustment done for multicol above + topLeft = rect.location(); + topLeft.move(m_x, m_y); + } + + if (style()->position() == AbsolutePosition) + topLeft += offsetForPositionedInContainer(o); + else if (style()->position() == RelativePosition && m_layer) { + // Apply the relative position offset when invalidating a rectangle. The layer + // is translated, but the render box isn't, so we need to do this to get the + // right dirty rect. Since this is called from RenderObject::setStyle, the relative position + // flag on the RenderObject has been cleared, so use the one on the style(). + topLeft += m_layer->relativePositionOffset(); + } + + // FIXME: We ignore the lightweight clipping rect that controls use, since if |o| is in mid-layout, + // its controlClipRect will be wrong. For overflow clip we use the values cached by the layer. + if (o->hasOverflowClip()) { + // o->height() is inaccurate if we're in the middle of a layout of |o|, so use the + // layer's size instead. Even if the layer's size is wrong, the layer itself will repaint + // anyway if its size does change. + topLeft -= o->layer()->scrolledContentOffset(); // For overflow:auto/scroll/hidden. + + IntRect repaintRect(topLeft, rect.size()); + IntRect boxRect(0, 0, o->layer()->width(), o->layer()->height()); + rect = intersection(repaintRect, boxRect); + if (rect.isEmpty()) + return; + } else + rect.setLocation(topLeft); + + o->computeAbsoluteRepaintRect(rect, fixed); +} + +void RenderBox::repaintDuringLayoutIfMoved(const IntRect& rect) +{ + int newX = m_x; + int newY = m_y; + int newWidth = m_width; + int newHeight = m_height; + if (rect.x() != newX || rect.y() != newY) { + // The child moved. Invalidate the object's old and new positions. We have to do this + // since the object may not have gotten a layout. + m_x = rect.x(); + m_y = rect.y(); + m_width = rect.width(); + m_height = rect.height(); + repaint(); + repaintOverhangingFloats(true); + + m_x = newX; + m_y = newY; + m_width = newWidth; + m_height = newHeight; + repaint(); + repaintOverhangingFloats(true); + } +} + +int RenderBox::relativePositionOffsetX() const +{ + if (!style()->left().isAuto()) { + if (!style()->right().isAuto() && containingBlock()->style()->direction() == RTL) + return -style()->right().calcValue(containingBlockWidth()); + return style()->left().calcValue(containingBlockWidth()); + } + if (!style()->right().isAuto()) + return -style()->right().calcValue(containingBlockWidth()); + return 0; +} + +int RenderBox::relativePositionOffsetY() const +{ + if (!style()->top().isAuto()) { + if (!style()->top().isPercent() || containingBlock()->style()->height().isFixed()) + return style()->top().calcValue(containingBlockHeight()); + } else if (!style()->bottom().isAuto()) { + if (!style()->bottom().isPercent() || containingBlock()->style()->height().isFixed()) + return -style()->bottom().calcValue(containingBlockHeight()); + } + return 0; +} + +void RenderBox::calcWidth() +{ + if (isPositioned()) { + calcAbsoluteHorizontal(); + return; + } + + // If layout is limited to a subtree, the subtree root's width does not change. + if (node() && view()->frameView() && view()->frameView()->layoutRoot(true) == this) + return; + + // The parent box is flexing us, so it has increased or decreased our + // width. Use the width from the style context. + if (hasOverrideSize() && parent()->style()->boxOrient() == HORIZONTAL + && parent()->isFlexibleBox() && parent()->isFlexingChildren()) { + m_width = overrideSize(); + return; + } + + bool inVerticalBox = parent()->isFlexibleBox() && (parent()->style()->boxOrient() == VERTICAL); + bool stretching = (parent()->style()->boxAlign() == BSTRETCH); + bool treatAsReplaced = shouldCalculateSizeAsReplaced() && (!inVerticalBox || !stretching); + + Length width = (treatAsReplaced) ? Length(calcReplacedWidth(), Fixed) : style()->width(); + + RenderBlock* cb = containingBlock(); + int containerWidth = max(0, containingBlockWidth()); + + Length marginLeft = style()->marginLeft(); + Length marginRight = style()->marginRight(); + + if (isInline() && !isInlineBlockOrInlineTable()) { + // just calculate margins + m_marginLeft = marginLeft.calcMinValue(containerWidth); + m_marginRight = marginRight.calcMinValue(containerWidth); + if (treatAsReplaced) + m_width = max(width.value() + borderLeft() + borderRight() + paddingLeft() + paddingRight(), minPrefWidth()); + + return; + } + + // Width calculations + if (treatAsReplaced) + m_width = width.value() + borderLeft() + borderRight() + paddingLeft() + paddingRight(); + else { + // Calculate Width + m_width = calcWidthUsing(Width, containerWidth); + + // Calculate MaxWidth + if (!style()->maxWidth().isUndefined()) { + int maxW = calcWidthUsing(MaxWidth, containerWidth); + if (m_width > maxW) { + m_width = maxW; + width = style()->maxWidth(); + } + } + + // Calculate MinWidth + int minW = calcWidthUsing(MinWidth, containerWidth); + if (m_width < minW) { + m_width = minW; + width = style()->minWidth(); + } + } + + if (stretchesToMinIntrinsicWidth()) { + m_width = max(m_width, minPrefWidth()); + width = Length(m_width, Fixed); + } + + // Margin calculations + if (width.isAuto()) { + m_marginLeft = marginLeft.calcMinValue(containerWidth); + m_marginRight = marginRight.calcMinValue(containerWidth); + } else { + m_marginLeft = 0; + m_marginRight = 0; + calcHorizontalMargins(marginLeft, marginRight, containerWidth); + } + + if (containerWidth && containerWidth != (m_width + m_marginLeft + m_marginRight) + && !isFloating() && !isInline() && !cb->isFlexibleBox()) { + if (cb->style()->direction() == LTR) + m_marginRight = containerWidth - m_width - m_marginLeft; + else + m_marginLeft = containerWidth - m_width - m_marginRight; + } +} + +int RenderBox::calcWidthUsing(WidthType widthType, int cw) +{ + int width = m_width; + Length w; + if (widthType == Width) + w = style()->width(); + else if (widthType == MinWidth) + w = style()->minWidth(); + else + w = style()->maxWidth(); + + if (w.isIntrinsicOrAuto()) { + int marginLeft = style()->marginLeft().calcMinValue(cw); + int marginRight = style()->marginRight().calcMinValue(cw); + if (cw) + width = cw - marginLeft - marginRight; + + if (sizesToIntrinsicWidth(widthType)) { + width = max(width, minPrefWidth()); + width = min(width, maxPrefWidth()); + } + } else + width = calcBorderBoxWidth(w.calcValue(cw)); + + return width; +} + +bool RenderBox::sizesToIntrinsicWidth(WidthType widthType) const +{ + // Marquees in WinIE are like a mixture of blocks and inline-blocks. They size as though they're blocks, + // but they allow text to sit on the same line as the marquee. + if (isFloating() || (isCompact() && isInline()) + || (isInlineBlockOrInlineTable() && !isHTMLMarquee())) + return true; + + // This code may look a bit strange. Basically width:intrinsic should clamp the size when testing both + // min-width and width. max-width is only clamped if it is also intrinsic. + Length width = (widthType == MaxWidth) ? style()->maxWidth() : style()->width(); + if (width.type() == Intrinsic) + return true; + + // Children of a horizontal marquee do not fill the container by default. + // FIXME: Need to deal with MAUTO value properly. It could be vertical. + if (parent()->style()->overflowX() == OMARQUEE) { + EMarqueeDirection dir = parent()->style()->marqueeDirection(); + if (dir == MAUTO || dir == MFORWARD || dir == MBACKWARD || dir == MLEFT || dir == MRIGHT) + return true; + } + + // Flexible horizontal boxes lay out children at their intrinsic widths. Also vertical boxes + // that don't stretch their kids lay out their children at their intrinsic widths. + if (parent()->isFlexibleBox() + && (parent()->style()->boxOrient() == HORIZONTAL || parent()->style()->boxAlign() != BSTRETCH)) + return true; + + return false; +} + +void RenderBox::calcHorizontalMargins(const Length& marginLeft, const Length& marginRight, int containerWidth) +{ + if (isFloating() || isInline()) { + // Inline blocks/tables and floats don't have their margins increased. + m_marginLeft = marginLeft.calcMinValue(containerWidth); + m_marginRight = marginRight.calcMinValue(containerWidth); + return; + } + + if ((marginLeft.isAuto() && marginRight.isAuto() && m_width < containerWidth) + || (!marginLeft.isAuto() && !marginRight.isAuto() && containingBlock()->style()->textAlign() == WEBKIT_CENTER)) { + m_marginLeft = max(0, (containerWidth - m_width) / 2); + m_marginRight = containerWidth - m_width - m_marginLeft; + } else if ((marginRight.isAuto() && m_width < containerWidth) + || (!marginLeft.isAuto() && containingBlock()->style()->direction() == RTL && containingBlock()->style()->textAlign() == WEBKIT_LEFT)) { + m_marginLeft = marginLeft.calcValue(containerWidth); + m_marginRight = containerWidth - m_width - m_marginLeft; + } else if ((marginLeft.isAuto() && m_width < containerWidth) + || (!marginRight.isAuto() && containingBlock()->style()->direction() == LTR && containingBlock()->style()->textAlign() == WEBKIT_RIGHT)) { + m_marginRight = marginRight.calcValue(containerWidth); + m_marginLeft = containerWidth - m_width - m_marginRight; + } else { + // This makes auto margins 0 if we failed a m_width < containerWidth test above (css2.1, 10.3.3). + m_marginLeft = marginLeft.calcMinValue(containerWidth); + m_marginRight = marginRight.calcMinValue(containerWidth); + } +} + +void RenderBox::calcHeight() +{ + // Cell height is managed by the table and inline non-replaced elements do not support a height property. + if (isTableCell() || (isInline() && !isReplaced())) + return; + + if (isPositioned()) + calcAbsoluteVertical(); + else { + calcVerticalMargins(); + + // For tables, calculate margins only. + if (isTable()) + return; + + Length h; + bool inHorizontalBox = parent()->isFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL; + bool stretching = parent()->style()->boxAlign() == BSTRETCH; + bool treatAsReplaced = shouldCalculateSizeAsReplaced() && (!inHorizontalBox || !stretching); + bool checkMinMaxHeight = false; + + // The parent box is flexing us, so it has increased or decreased our height. We have to + // grab our cached flexible height. + if (hasOverrideSize() && parent()->isFlexibleBox() && parent()->style()->boxOrient() == VERTICAL + && parent()->isFlexingChildren()) + h = Length(overrideSize() - borderTop() - borderBottom() - paddingTop() - paddingBottom(), Fixed); + else if (treatAsReplaced) + h = Length(calcReplacedHeight(), Fixed); + else { + h = style()->height(); + checkMinMaxHeight = true; + } + + // Block children of horizontal flexible boxes fill the height of the box. + if (h.isAuto() && parent()->isFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL + && parent()->isStretchingChildren()) { + h = Length(parent()->contentHeight() - marginTop() - marginBottom() - + borderTop() - paddingTop() - borderBottom() - paddingBottom(), Fixed); + checkMinMaxHeight = false; + } + + int height; + if (checkMinMaxHeight) { + height = calcHeightUsing(style()->height()); + if (height == -1) + height = m_height; + int minH = calcHeightUsing(style()->minHeight()); // Leave as -1 if unset. + int maxH = style()->maxHeight().isUndefined() ? height : calcHeightUsing(style()->maxHeight()); + if (maxH == -1) + maxH = height; + height = min(maxH, height); + height = max(minH, height); + } else + // The only times we don't check min/max height are when a fixed length has + // been given as an override. Just use that. The value has already been adjusted + // for box-sizing. + height = h.value() + borderTop() + borderBottom() + paddingTop() + paddingBottom(); + + m_height = height; + } + + // WinIE quirk: The <html> block always fills the entire canvas in quirks mode. The <body> always fills the + // <html> block in quirks mode. Only apply this quirk if the block is normal flow and no height + // is specified. + if (stretchesToViewHeight() && !document()->printing()) { + int margins = collapsedMarginTop() + collapsedMarginBottom(); + int visHeight = view()->viewHeight(); + if (isRoot()) + m_height = max(m_height, visHeight - margins); + else { + int marginsBordersPadding = margins + parent()->marginTop() + parent()->marginBottom() + + parent()->borderTop() + parent()->borderBottom() + + parent()->paddingTop() + parent()->paddingBottom(); + m_height = max(m_height, visHeight - marginsBordersPadding); + } + } +} + +int RenderBox::calcHeightUsing(const Length& h) +{ + int height = -1; + if (!h.isAuto()) { + if (h.isFixed()) + height = h.value(); + else if (h.isPercent()) + height = calcPercentageHeight(h); + if (height != -1) { + height = calcBorderBoxHeight(height); + return height; + } + } + return height; +} + +int RenderBox::calcPercentageHeight(const Length& height) +{ + int result = -1; + bool includeBorderPadding = isTable(); + RenderBlock* cb = containingBlock(); + if (style()->htmlHacks()) { + // In quirks mode, blocks with auto height are skipped, and we keep looking for an enclosing + // block that may have a specified height and then use it. In strict mode, this violates the + // specification, which states that percentage heights just revert to auto if the containing + // block has an auto height. + while (!cb->isRenderView() && !cb->isBody() && !cb->isTableCell() && !cb->isPositioned() && cb->style()->height().isAuto()) { + cb = cb->containingBlock(); + cb->addPercentHeightDescendant(this); + } + } + + // A positioned element that specified both top/bottom or that specifies height should be treated as though it has a height + // explicitly specified that can be used for any percentage computations. + bool isPositionedWithSpecifiedHeight = cb->isPositioned() && (!cb->style()->height().isAuto() || (!cb->style()->top().isAuto() && !cb->style()->bottom().isAuto())); + + // Table cells violate what the CSS spec says to do with heights. Basically we + // don't care if the cell specified a height or not. We just always make ourselves + // be a percentage of the cell's current content height. + if (cb->isTableCell()) { + result = cb->overrideSize(); + if (result == -1) { + // Normally we would let the cell size intrinsically, but scrolling overflow has to be + // treated differently, since WinIE lets scrolled overflow regions shrink as needed. + // While we can't get all cases right, we can at least detect when the cell has a specified + // height or when the table has a specified height. In these cases we want to initially have + // no size and allow the flexing of the table or the cell to its specified height to cause us + // to grow to fill the space. This could end up being wrong in some cases, but it is + // preferable to the alternative (sizing intrinsically and making the row end up too big). + RenderTableCell* cell = static_cast<RenderTableCell*>(cb); + if (scrollsOverflowY() && (!cell->style()->height().isAuto() || !cell->table()->style()->height().isAuto())) + return 0; + return -1; + } + includeBorderPadding = true; + } + // Otherwise we only use our percentage height if our containing block had a specified + // height. + else if (cb->style()->height().isFixed()) + result = cb->calcContentBoxHeight(cb->style()->height().value()); + else if (cb->style()->height().isPercent() && !isPositionedWithSpecifiedHeight) { + // We need to recur and compute the percentage height for our containing block. + result = cb->calcPercentageHeight(cb->style()->height()); + if (result != -1) + result = cb->calcContentBoxHeight(result); + } else if (cb->isRenderView() || (cb->isBody() && style()->htmlHacks()) || isPositionedWithSpecifiedHeight) { + // Don't allow this to affect the block' m_height member variable, since this + // can get called while the block is still laying out its kids. + int oldHeight = cb->height(); + cb->calcHeight(); + result = cb->contentHeight(); + cb->setHeight(oldHeight); + } else if (cb->isRoot() && isPositioned()) + // Match the positioned objects behavior, which is that positioned objects will fill their viewport + // always. Note we could only hit this case by recurring into calcPercentageHeight on a positioned containing block. + result = cb->calcContentBoxHeight(cb->availableHeight()); + + if (result != -1) { + result = height.calcValue(result); + if (includeBorderPadding) { + // It is necessary to use the border-box to match WinIE's broken + // box model. This is essential for sizing inside + // table cells using percentage heights. + result -= (borderTop() + paddingTop() + borderBottom() + paddingBottom()); + result = max(0, result); + } + } + return result; +} + +int RenderBox::calcReplacedWidth(bool includeMaxWidth) const +{ + int width = calcReplacedWidthUsing(style()->width()); + int minW = calcReplacedWidthUsing(style()->minWidth()); + int maxW = !includeMaxWidth || style()->maxWidth().isUndefined() ? width : calcReplacedWidthUsing(style()->maxWidth()); + + return max(minW, min(width, maxW)); +} + +int RenderBox::calcReplacedWidthUsing(Length width) const +{ + switch (width.type()) { + case Fixed: + return calcContentBoxWidth(width.value()); + case Percent: { + const int cw = isPositioned() ? containingBlockWidthForPositioned(container()) : containingBlockWidth(); + if (cw > 0) + return calcContentBoxWidth(width.calcMinValue(cw)); + } + // fall through + default: + return intrinsicSize().width(); + } + } + +int RenderBox::calcReplacedHeight() const +{ + int height = calcReplacedHeightUsing(style()->height()); + int minH = calcReplacedHeightUsing(style()->minHeight()); + int maxH = style()->maxHeight().isUndefined() ? height : calcReplacedHeightUsing(style()->maxHeight()); + + return max(minH, min(height, maxH)); +} + +int RenderBox::calcReplacedHeightUsing(Length height) const +{ + switch (height.type()) { + case Fixed: + return calcContentBoxHeight(height.value()); + case Percent: + { + RenderObject* cb = isPositioned() ? container() : containingBlock(); + while (cb->isAnonymous()) { + cb = cb->containingBlock(); + static_cast<RenderBlock*>(cb)->addPercentHeightDescendant(const_cast<RenderBox*>(this)); + } + + if (cb->isPositioned() && cb->style()->height().isAuto() && !(cb->style()->top().isAuto() || cb->style()->bottom().isAuto())) { + ASSERT(cb->isRenderBlock()); + RenderBlock* block = static_cast<RenderBlock*>(cb); + int oldHeight = block->height(); + block->calcHeight(); + int newHeight = block->calcContentBoxHeight(block->contentHeight()); + block->setHeight(oldHeight); + return calcContentBoxHeight(height.calcValue(newHeight)); + } + + int availableHeight = isPositioned() ? containingBlockHeightForPositioned(cb) : cb->availableHeight(); + + // It is necessary to use the border-box to match WinIE's broken + // box model. This is essential for sizing inside + // table cells using percentage heights. + if (cb->isTableCell() && (cb->style()->height().isAuto() || cb->style()->height().isPercent())) { + // Don't let table cells squeeze percent-height replaced elements + // <http://bugs.webkit.org/show_bug.cgi?id=15359> + availableHeight = max(availableHeight, intrinsicSize().height()); + return height.calcValue(availableHeight - (borderTop() + borderBottom() + + paddingTop() + paddingBottom())); + } + + return calcContentBoxHeight(height.calcValue(availableHeight)); + } + default: + return intrinsicSize().height(); + } +} + +int RenderBox::availableHeight() const +{ + return availableHeightUsing(style()->height()); +} + +int RenderBox::availableHeightUsing(const Length& h) const +{ + if (h.isFixed()) + return calcContentBoxHeight(h.value()); + + if (isRenderView()) + return static_cast<const RenderView*>(this)->frameView()->visibleHeight(); + + // We need to stop here, since we don't want to increase the height of the table + // artificially. We're going to rely on this cell getting expanded to some new + // height, and then when we lay out again we'll use the calculation below. + if (isTableCell() && (h.isAuto() || h.isPercent())) + return overrideSize() - (borderLeft() + borderRight() + paddingLeft() + paddingRight()); + + if (h.isPercent()) + return calcContentBoxHeight(h.calcValue(containingBlock()->availableHeight())); + + if (isRenderBlock() && isPositioned() && style()->height().isAuto() && !(style()->top().isAuto() || style()->bottom().isAuto())) { + RenderBlock* block = const_cast<RenderBlock*>(static_cast<const RenderBlock*>(this)); + int oldHeight = block->height(); + block->calcHeight(); + int newHeight = block->calcContentBoxHeight(block->contentHeight()); + block->setHeight(oldHeight); + return calcContentBoxHeight(newHeight); + } + + return containingBlock()->availableHeight(); +} + +void RenderBox::calcVerticalMargins() +{ + if (isTableCell()) { + m_marginTop = 0; + m_marginBottom = 0; + return; + } + + // margins are calculated with respect to the _width_ of + // the containing block (8.3) + int cw = containingBlock()->contentWidth(); + + m_marginTop = style()->marginTop().calcMinValue(cw); + m_marginBottom = style()->marginBottom().calcMinValue(cw); +} + +int RenderBox::staticX() const +{ + return m_layer ? m_layer->staticX() : 0; +} + +int RenderBox::staticY() const +{ + return m_layer ? m_layer->staticY() : 0; +} + +void RenderBox::setStaticX(int staticX) +{ + ASSERT(isPositioned() || isRelPositioned()); + m_layer->setStaticX(staticX); +} + +void RenderBox::setStaticY(int staticY) +{ + ASSERT(isPositioned() || isRelPositioned()); + + if (staticY == m_layer->staticY()) + return; + + m_layer->setStaticY(staticY); + setChildNeedsLayout(true, false); +} + +int RenderBox::containingBlockWidthForPositioned(const RenderObject* containingBlock) const +{ + if (containingBlock->isInlineFlow()) { + ASSERT(containingBlock->isRelPositioned()); + + const RenderFlow* flow = static_cast<const RenderFlow*>(containingBlock); + InlineFlowBox* first = flow->firstLineBox(); + InlineFlowBox* last = flow->lastLineBox(); + + // If the containing block is empty, return a width of 0. + if (!first || !last) + return 0; + + int fromLeft; + int fromRight; + if (containingBlock->style()->direction() == LTR) { + fromLeft = first->xPos() + first->borderLeft(); + fromRight = last->xPos() + last->width() - last->borderRight(); + } else { + fromRight = first->xPos() + first->width() - first->borderRight(); + fromLeft = last->xPos() + last->borderLeft(); + } + + return max(0, (fromRight - fromLeft)); + } + + return containingBlock->width() - containingBlock->borderLeft() - containingBlock->borderRight() - containingBlock->verticalScrollbarWidth(); +} + +int RenderBox::containingBlockHeightForPositioned(const RenderObject* containingBlock) const +{ + return containingBlock->height() - containingBlock->borderTop() - containingBlock->borderBottom(); +} + +void RenderBox::calcAbsoluteHorizontal() +{ + if (isReplaced()) { + calcAbsoluteHorizontalReplaced(); + return; + } + + // QUESTIONS + // FIXME 1: Which RenderObject's 'direction' property should used: the + // containing block (cb) as the spec seems to imply, the parent (parent()) as + // was previously done in calculating the static distances, or ourself, which + // was also previously done for deciding what to override when you had + // over-constrained margins? Also note that the container block is used + // in similar situations in other parts of the RenderBox class (see calcWidth() + // and calcHorizontalMargins()). For now we are using the parent for quirks + // mode and the containing block for strict mode. + + // FIXME 2: Should we still deal with these the cases of 'left' or 'right' having + // the type 'static' in determining whether to calculate the static distance? + // NOTE: 'static' is not a legal value for 'left' or 'right' as of CSS 2.1. + + // FIXME 3: Can perhaps optimize out cases when max-width/min-width are greater + // than or less than the computed m_width. Be careful of box-sizing and + // percentage issues. + + // The following is based off of the W3C Working Draft from April 11, 2006 of + // CSS 2.1: Section 10.3.7 "Absolutely positioned, non-replaced elements" + // <http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width> + // (block-style-comments in this function and in calcAbsoluteHorizontalValues() + // correspond to text from the spec) + + + // We don't use containingBlock(), since we may be positioned by an enclosing + // relative positioned inline. + const RenderObject* containerBlock = container(); + + const int containerWidth = containingBlockWidthForPositioned(containerBlock); + + // To match WinIE, in quirks mode use the parent's 'direction' property + // instead of the the container block's. + TextDirection containerDirection = (style()->htmlHacks()) ? parent()->style()->direction() : containerBlock->style()->direction(); + + const int bordersPlusPadding = borderLeft() + borderRight() + paddingLeft() + paddingRight(); + const Length marginLeft = style()->marginLeft(); + const Length marginRight = style()->marginRight(); + Length left = style()->left(); + Length right = style()->right(); + + /*---------------------------------------------------------------------------*\ + * For the purposes of this section and the next, the term "static position" + * (of an element) refers, roughly, to the position an element would have had + * in the normal flow. More precisely: + * + * * The static position for 'left' is the distance from the left edge of the + * containing block to the left margin edge of a hypothetical box that would + * have been the first box of the element if its 'position' property had + * been 'static' and 'float' had been 'none'. The value is negative if the + * hypothetical box is to the left of the containing block. + * * The static position for 'right' is the distance from the right edge of the + * containing block to the right margin edge of the same hypothetical box as + * above. The value is positive if the hypothetical box is to the left of the + * containing block's edge. + * + * But rather than actually calculating the dimensions of that hypothetical box, + * user agents are free to make a guess at its probable position. + * + * For the purposes of calculating the static position, the containing block of + * fixed positioned elements is the initial containing block instead of the + * viewport, and all scrollable boxes should be assumed to be scrolled to their + * origin. + \*---------------------------------------------------------------------------*/ + + // see FIXME 2 + // Calculate the static distance if needed. + if (left.isAuto() && right.isAuto()) { + if (containerDirection == LTR) { + // 'staticX' should already have been set through layout of the parent. + int staticPosition = staticX() - containerBlock->borderLeft(); + for (RenderObject* po = parent(); po && po != containerBlock; po = po->parent()) + staticPosition += po->xPos(); + left.setValue(Fixed, staticPosition); + } else { + RenderObject* po = parent(); + // 'staticX' should already have been set through layout of the parent. + int staticPosition = staticX() + containerWidth + containerBlock->borderRight() - po->width(); + for (; po && po != containerBlock; po = po->parent()) + staticPosition -= po->xPos(); + right.setValue(Fixed, staticPosition); + } + } + + // Calculate constraint equation values for 'width' case. + calcAbsoluteHorizontalValues(style()->width(), containerBlock, containerDirection, + containerWidth, bordersPlusPadding, + left, right, marginLeft, marginRight, + m_width, m_marginLeft, m_marginRight, m_x); + + // Calculate constraint equation values for 'max-width' case. + if (!style()->maxWidth().isUndefined()) { + int maxWidth; + int maxMarginLeft; + int maxMarginRight; + int maxXPos; + + calcAbsoluteHorizontalValues(style()->maxWidth(), containerBlock, containerDirection, + containerWidth, bordersPlusPadding, + left, right, marginLeft, marginRight, + maxWidth, maxMarginLeft, maxMarginRight, maxXPos); + + if (m_width > maxWidth) { + m_width = maxWidth; + m_marginLeft = maxMarginLeft; + m_marginRight = maxMarginRight; + m_x = maxXPos; + } + } + + // Calculate constraint equation values for 'min-width' case. + if (!style()->minWidth().isZero()) { + int minWidth; + int minMarginLeft; + int minMarginRight; + int minXPos; + + calcAbsoluteHorizontalValues(style()->minWidth(), containerBlock, containerDirection, + containerWidth, bordersPlusPadding, + left, right, marginLeft, marginRight, + minWidth, minMarginLeft, minMarginRight, minXPos); + + if (m_width < minWidth) { + m_width = minWidth; + m_marginLeft = minMarginLeft; + m_marginRight = minMarginRight; + m_x = minXPos; + } + } + + if (stretchesToMinIntrinsicWidth() && m_width < minPrefWidth() - bordersPlusPadding) + calcAbsoluteHorizontalValues(Length(minPrefWidth() - bordersPlusPadding, Fixed), containerBlock, containerDirection, + containerWidth, bordersPlusPadding, + left, right, marginLeft, marginRight, + m_width, m_marginLeft, m_marginRight, m_x); + + // Put m_width into correct form. + m_width += bordersPlusPadding; +} + +void RenderBox::calcAbsoluteHorizontalValues(Length width, const RenderObject* containerBlock, TextDirection containerDirection, + const int containerWidth, const int bordersPlusPadding, + const Length left, const Length right, const Length marginLeft, const Length marginRight, + int& widthValue, int& marginLeftValue, int& marginRightValue, int& xPos) +{ + // 'left' and 'right' cannot both be 'auto' because one would of been + // converted to the static postion already + ASSERT(!(left.isAuto() && right.isAuto())); + + int leftValue = 0; + + bool widthIsAuto = width.isIntrinsicOrAuto(); + bool leftIsAuto = left.isAuto(); + bool rightIsAuto = right.isAuto(); + + if (!leftIsAuto && !widthIsAuto && !rightIsAuto) { + /*-----------------------------------------------------------------------*\ + * If none of the three is 'auto': If both 'margin-left' and 'margin- + * right' are 'auto', solve the equation under the extra constraint that + * the two margins get equal values, unless this would make them negative, + * in which case when direction of the containing block is 'ltr' ('rtl'), + * set 'margin-left' ('margin-right') to zero and solve for 'margin-right' + * ('margin-left'). If one of 'margin-left' or 'margin-right' is 'auto', + * solve the equation for that value. If the values are over-constrained, + * ignore the value for 'left' (in case the 'direction' property of the + * containing block is 'rtl') or 'right' (in case 'direction' is 'ltr') + * and solve for that value. + \*-----------------------------------------------------------------------*/ + // NOTE: It is not necessary to solve for 'right' in the over constrained + // case because the value is not used for any further calculations. + + leftValue = left.calcValue(containerWidth); + widthValue = calcContentBoxWidth(width.calcValue(containerWidth)); + + const int availableSpace = containerWidth - (leftValue + widthValue + right.calcValue(containerWidth) + bordersPlusPadding); + + // Margins are now the only unknown + if (marginLeft.isAuto() && marginRight.isAuto()) { + // Both margins auto, solve for equality + if (availableSpace >= 0) { + marginLeftValue = availableSpace / 2; // split the diference + marginRightValue = availableSpace - marginLeftValue; // account for odd valued differences + } else { + // see FIXME 1 + if (containerDirection == LTR) { + marginLeftValue = 0; + marginRightValue = availableSpace; // will be negative + } else { + marginLeftValue = availableSpace; // will be negative + marginRightValue = 0; + } + } + } else if (marginLeft.isAuto()) { + // Solve for left margin + marginRightValue = marginRight.calcValue(containerWidth); + marginLeftValue = availableSpace - marginRightValue; + } else if (marginRight.isAuto()) { + // Solve for right margin + marginLeftValue = marginLeft.calcValue(containerWidth); + marginRightValue = availableSpace - marginLeftValue; + } else { + // Over-constrained, solve for left if direction is RTL + marginLeftValue = marginLeft.calcValue(containerWidth); + marginRightValue = marginRight.calcValue(containerWidth); + + // see FIXME 1 -- used to be "this->style()->direction()" + if (containerDirection == RTL) + leftValue = (availableSpace + leftValue) - marginLeftValue - marginRightValue; + } + } else { + /*--------------------------------------------------------------------*\ + * Otherwise, set 'auto' values for 'margin-left' and 'margin-right' + * to 0, and pick the one of the following six rules that applies. + * + * 1. 'left' and 'width' are 'auto' and 'right' is not 'auto', then the + * width is shrink-to-fit. Then solve for 'left' + * + * OMIT RULE 2 AS IT SHOULD NEVER BE HIT + * ------------------------------------------------------------------ + * 2. 'left' and 'right' are 'auto' and 'width' is not 'auto', then if + * the 'direction' property of the containing block is 'ltr' set + * 'left' to the static position, otherwise set 'right' to the + * static position. Then solve for 'left' (if 'direction is 'rtl') + * or 'right' (if 'direction' is 'ltr'). + * ------------------------------------------------------------------ + * + * 3. 'width' and 'right' are 'auto' and 'left' is not 'auto', then the + * width is shrink-to-fit . Then solve for 'right' + * 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve + * for 'left' + * 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve + * for 'width' + * 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve + * for 'right' + * + * Calculation of the shrink-to-fit width is similar to calculating the + * width of a table cell using the automatic table layout algorithm. + * Roughly: calculate the preferred width by formatting the content + * without breaking lines other than where explicit line breaks occur, + * and also calculate the preferred minimum width, e.g., by trying all + * possible line breaks. CSS 2.1 does not define the exact algorithm. + * Thirdly, calculate the available width: this is found by solving + * for 'width' after setting 'left' (in case 1) or 'right' (in case 3) + * to 0. + * + * Then the shrink-to-fit width is: + * min(max(preferred minimum width, available width), preferred width). + \*--------------------------------------------------------------------*/ + // NOTE: For rules 3 and 6 it is not necessary to solve for 'right' + // because the value is not used for any further calculations. + + // Calculate margins, 'auto' margins are ignored. + marginLeftValue = marginLeft.calcMinValue(containerWidth); + marginRightValue = marginRight.calcMinValue(containerWidth); + + const int availableSpace = containerWidth - (marginLeftValue + marginRightValue + bordersPlusPadding); + + // FIXME: Is there a faster way to find the correct case? + // Use rule/case that applies. + if (leftIsAuto && widthIsAuto && !rightIsAuto) { + // RULE 1: (use shrink-to-fit for width, and solve of left) + int rightValue = right.calcValue(containerWidth); + + // FIXME: would it be better to have shrink-to-fit in one step? + int preferredWidth = maxPrefWidth() - bordersPlusPadding; + int preferredMinWidth = minPrefWidth() - bordersPlusPadding; + int availableWidth = availableSpace - rightValue; + widthValue = min(max(preferredMinWidth, availableWidth), preferredWidth); + leftValue = availableSpace - (widthValue + rightValue); + } else if (!leftIsAuto && widthIsAuto && rightIsAuto) { + // RULE 3: (use shrink-to-fit for width, and no need solve of right) + leftValue = left.calcValue(containerWidth); + + // FIXME: would it be better to have shrink-to-fit in one step? + int preferredWidth = maxPrefWidth() - bordersPlusPadding; + int preferredMinWidth = minPrefWidth() - bordersPlusPadding; + int availableWidth = availableSpace - leftValue; + widthValue = min(max(preferredMinWidth, availableWidth), preferredWidth); + } else if (leftIsAuto && !width.isAuto() && !rightIsAuto) { + // RULE 4: (solve for left) + widthValue = calcContentBoxWidth(width.calcValue(containerWidth)); + leftValue = availableSpace - (widthValue + right.calcValue(containerWidth)); + } else if (!leftIsAuto && widthIsAuto && !rightIsAuto) { + // RULE 5: (solve for width) + leftValue = left.calcValue(containerWidth); + widthValue = availableSpace - (leftValue + right.calcValue(containerWidth)); + } else if (!leftIsAuto&& !widthIsAuto && rightIsAuto) { + // RULE 6: (no need solve for right) + leftValue = left.calcValue(containerWidth); + widthValue = calcContentBoxWidth(width.calcValue(containerWidth)); + } + } + + // Use computed values to calculate the horizontal position. + + // FIXME: This hack is needed to calculate the xPos for a 'rtl' relatively + // positioned, inline containing block because right now, it is using the xPos + // of the first line box when really it should use the last line box. When + // this is fixed elsewhere, this block should be removed. + if (containerBlock->isInline() && containerBlock->style()->direction() == RTL) { + const RenderFlow* flow = static_cast<const RenderFlow*>(containerBlock); + InlineFlowBox* firstLine = flow->firstLineBox(); + InlineFlowBox* lastLine = flow->lastLineBox(); + if (firstLine && lastLine && firstLine != lastLine) { + xPos = leftValue + marginLeftValue + lastLine->borderLeft() + (lastLine->xPos() - firstLine->xPos()); + return; + } + } + + xPos = leftValue + marginLeftValue + containerBlock->borderLeft(); +} + +void RenderBox::calcAbsoluteVertical() +{ + if (isReplaced()) { + calcAbsoluteVerticalReplaced(); + return; + } + + // The following is based off of the W3C Working Draft from April 11, 2006 of + // CSS 2.1: Section 10.6.4 "Absolutely positioned, non-replaced elements" + // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-non-replaced-height> + // (block-style-comments in this function and in calcAbsoluteVerticalValues() + // correspond to text from the spec) + + + // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline. + const RenderObject* containerBlock = container(); + + const int containerHeight = containingBlockHeightForPositioned(containerBlock); + + const int bordersPlusPadding = borderTop() + borderBottom() + paddingTop() + paddingBottom(); + const Length marginTop = style()->marginTop(); + const Length marginBottom = style()->marginBottom(); + Length top = style()->top(); + Length bottom = style()->bottom(); + + /*---------------------------------------------------------------------------*\ + * For the purposes of this section and the next, the term "static position" + * (of an element) refers, roughly, to the position an element would have had + * in the normal flow. More precisely, the static position for 'top' is the + * distance from the top edge of the containing block to the top margin edge + * of a hypothetical box that would have been the first box of the element if + * its 'position' property had been 'static' and 'float' had been 'none'. The + * value is negative if the hypothetical box is above the containing block. + * + * But rather than actually calculating the dimensions of that hypothetical + * box, user agents are free to make a guess at its probable position. + * + * For the purposes of calculating the static position, the containing block + * of fixed positioned elements is the initial containing block instead of + * the viewport. + \*---------------------------------------------------------------------------*/ + + // see FIXME 2 + // Calculate the static distance if needed. + if (top.isAuto() && bottom.isAuto()) { + // staticY should already have been set through layout of the parent() + int staticTop = staticY() - containerBlock->borderTop(); + for (RenderObject* po = parent(); po && po != containerBlock; po = po->parent()) { + if (!po->isTableRow()) + staticTop += po->yPos(); + } + top.setValue(Fixed, staticTop); + } + + + int height; // Needed to compute overflow. + + // Calculate constraint equation values for 'height' case. + calcAbsoluteVerticalValues(style()->height(), containerBlock, containerHeight, bordersPlusPadding, + top, bottom, marginTop, marginBottom, + height, m_marginTop, m_marginBottom, m_y); + + // Avoid doing any work in the common case (where the values of min-height and max-height are their defaults). + // see FIXME 3 + + // Calculate constraint equation values for 'max-height' case. + if (!style()->maxHeight().isUndefined()) { + int maxHeight; + int maxMarginTop; + int maxMarginBottom; + int maxYPos; + + calcAbsoluteVerticalValues(style()->maxHeight(), containerBlock, containerHeight, bordersPlusPadding, + top, bottom, marginTop, marginBottom, + maxHeight, maxMarginTop, maxMarginBottom, maxYPos); + + if (height > maxHeight) { + height = maxHeight; + m_marginTop = maxMarginTop; + m_marginBottom = maxMarginBottom; + m_y = maxYPos; + } + } + + // Calculate constraint equation values for 'min-height' case. + if (!style()->minHeight().isZero()) { + int minHeight; + int minMarginTop; + int minMarginBottom; + int minYPos; + + calcAbsoluteVerticalValues(style()->minHeight(), containerBlock, containerHeight, bordersPlusPadding, + top, bottom, marginTop, marginBottom, + minHeight, minMarginTop, minMarginBottom, minYPos); + + if (height < minHeight) { + height = minHeight; + m_marginTop = minMarginTop; + m_marginBottom = minMarginBottom; + m_y = minYPos; + } + } + + // Set final height value. + m_height = height + bordersPlusPadding; +} + +void RenderBox::calcAbsoluteVerticalValues(Length height, const RenderObject* containerBlock, + const int containerHeight, const int bordersPlusPadding, + const Length top, const Length bottom, const Length marginTop, const Length marginBottom, + int& heightValue, int& marginTopValue, int& marginBottomValue, int& yPos) +{ + // 'top' and 'bottom' cannot both be 'auto' because 'top would of been + // converted to the static position in calcAbsoluteVertical() + ASSERT(!(top.isAuto() && bottom.isAuto())); + + int contentHeight = m_height - bordersPlusPadding; + + int topValue = 0; + + bool heightIsAuto = height.isAuto(); + bool topIsAuto = top.isAuto(); + bool bottomIsAuto = bottom.isAuto(); + + // Height is never unsolved for tables. + if (isTable()) { + height.setValue(Fixed, contentHeight); + heightIsAuto = false; + } + + if (!topIsAuto && !heightIsAuto && !bottomIsAuto) { + /*-----------------------------------------------------------------------*\ + * If none of the three are 'auto': If both 'margin-top' and 'margin- + * bottom' are 'auto', solve the equation under the extra constraint that + * the two margins get equal values. If one of 'margin-top' or 'margin- + * bottom' is 'auto', solve the equation for that value. If the values + * are over-constrained, ignore the value for 'bottom' and solve for that + * value. + \*-----------------------------------------------------------------------*/ + // NOTE: It is not necessary to solve for 'bottom' in the over constrained + // case because the value is not used for any further calculations. + + heightValue = calcContentBoxHeight(height.calcValue(containerHeight)); + topValue = top.calcValue(containerHeight); + + const int availableSpace = containerHeight - (topValue + heightValue + bottom.calcValue(containerHeight) + bordersPlusPadding); + + // Margins are now the only unknown + if (marginTop.isAuto() && marginBottom.isAuto()) { + // Both margins auto, solve for equality + // NOTE: This may result in negative values. + marginTopValue = availableSpace / 2; // split the diference + marginBottomValue = availableSpace - marginTopValue; // account for odd valued differences + } else if (marginTop.isAuto()) { + // Solve for top margin + marginBottomValue = marginBottom.calcValue(containerHeight); + marginTopValue = availableSpace - marginBottomValue; + } else if (marginBottom.isAuto()) { + // Solve for bottom margin + marginTopValue = marginTop.calcValue(containerHeight); + marginBottomValue = availableSpace - marginTopValue; + } else { + // Over-constrained, (no need solve for bottom) + marginTopValue = marginTop.calcValue(containerHeight); + marginBottomValue = marginBottom.calcValue(containerHeight); + } + } else { + /*--------------------------------------------------------------------*\ + * Otherwise, set 'auto' values for 'margin-top' and 'margin-bottom' + * to 0, and pick the one of the following six rules that applies. + * + * 1. 'top' and 'height' are 'auto' and 'bottom' is not 'auto', then + * the height is based on the content, and solve for 'top'. + * + * OMIT RULE 2 AS IT SHOULD NEVER BE HIT + * ------------------------------------------------------------------ + * 2. 'top' and 'bottom' are 'auto' and 'height' is not 'auto', then + * set 'top' to the static position, and solve for 'bottom'. + * ------------------------------------------------------------------ + * + * 3. 'height' and 'bottom' are 'auto' and 'top' is not 'auto', then + * the height is based on the content, and solve for 'bottom'. + * 4. 'top' is 'auto', 'height' and 'bottom' are not 'auto', and + * solve for 'top'. + * 5. 'height' is 'auto', 'top' and 'bottom' are not 'auto', and + * solve for 'height'. + * 6. 'bottom' is 'auto', 'top' and 'height' are not 'auto', and + * solve for 'bottom'. + \*--------------------------------------------------------------------*/ + // NOTE: For rules 3 and 6 it is not necessary to solve for 'bottom' + // because the value is not used for any further calculations. + + // Calculate margins, 'auto' margins are ignored. + marginTopValue = marginTop.calcMinValue(containerHeight); + marginBottomValue = marginBottom.calcMinValue(containerHeight); + + const int availableSpace = containerHeight - (marginTopValue + marginBottomValue + bordersPlusPadding); + + // Use rule/case that applies. + if (topIsAuto && heightIsAuto && !bottomIsAuto) { + // RULE 1: (height is content based, solve of top) + heightValue = contentHeight; + topValue = availableSpace - (heightValue + bottom.calcValue(containerHeight)); + } else if (!topIsAuto && heightIsAuto && bottomIsAuto) { + // RULE 3: (height is content based, no need solve of bottom) + topValue = top.calcValue(containerHeight); + heightValue = contentHeight; + } else if (topIsAuto && !heightIsAuto && !bottomIsAuto) { + // RULE 4: (solve of top) + heightValue = calcContentBoxHeight(height.calcValue(containerHeight)); + topValue = availableSpace - (heightValue + bottom.calcValue(containerHeight)); + } else if (!topIsAuto && heightIsAuto && !bottomIsAuto) { + // RULE 5: (solve of height) + topValue = top.calcValue(containerHeight); + heightValue = max(0, availableSpace - (topValue + bottom.calcValue(containerHeight))); + } else if (!topIsAuto && !heightIsAuto && bottomIsAuto) { + // RULE 6: (no need solve of bottom) + heightValue = calcContentBoxHeight(height.calcValue(containerHeight)); + topValue = top.calcValue(containerHeight); + } + } + + // Use computed values to calculate the vertical position. + yPos = topValue + marginTopValue + containerBlock->borderTop(); +} + +void RenderBox::calcAbsoluteHorizontalReplaced() +{ + // The following is based off of the W3C Working Draft from April 11, 2006 of + // CSS 2.1: Section 10.3.8 "Absolutly positioned, replaced elements" + // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-width> + // (block-style-comments in this function correspond to text from the spec and + // the numbers correspond to numbers in spec) + + // We don't use containingBlock(), since we may be positioned by an enclosing + // relative positioned inline. + const RenderObject* containerBlock = container(); + + const int containerWidth = containingBlockWidthForPositioned(containerBlock); + + // To match WinIE, in quirks mode use the parent's 'direction' property + // instead of the the container block's. + TextDirection containerDirection = (style()->htmlHacks()) ? parent()->style()->direction() : containerBlock->style()->direction(); + + // Variables to solve. + Length left = style()->left(); + Length right = style()->right(); + Length marginLeft = style()->marginLeft(); + Length marginRight = style()->marginRight(); + + + /*-----------------------------------------------------------------------*\ + * 1. The used value of 'width' is determined as for inline replaced + * elements. + \*-----------------------------------------------------------------------*/ + // NOTE: This value of width is FINAL in that the min/max width calculations + // are dealt with in calcReplacedWidth(). This means that the steps to produce + // correct max/min in the non-replaced version, are not necessary. + m_width = calcReplacedWidth() + borderLeft() + borderRight() + paddingLeft() + paddingRight(); + const int availableSpace = containerWidth - m_width; + + /*-----------------------------------------------------------------------*\ + * 2. If both 'left' and 'right' have the value 'auto', then if 'direction' + * of the containing block is 'ltr', set 'left' to the static position; + * else if 'direction' is 'rtl', set 'right' to the static position. + \*-----------------------------------------------------------------------*/ + // see FIXME 2 + if (left.isAuto() && right.isAuto()) { + // see FIXME 1 + if (containerDirection == LTR) { + // 'staticX' should already have been set through layout of the parent. + int staticPosition = staticX() - containerBlock->borderLeft(); + for (RenderObject* po = parent(); po && po != containerBlock; po = po->parent()) + staticPosition += po->xPos(); + left.setValue(Fixed, staticPosition); + } else { + RenderObject* po = parent(); + // 'staticX' should already have been set through layout of the parent. + int staticPosition = staticX() + containerWidth + containerBlock->borderRight() - po->width(); + for (; po && po != containerBlock; po = po->parent()) + staticPosition -= po->xPos(); + right.setValue(Fixed, staticPosition); + } + } + + /*-----------------------------------------------------------------------*\ + * 3. If 'left' or 'right' are 'auto', replace any 'auto' on 'margin-left' + * or 'margin-right' with '0'. + \*-----------------------------------------------------------------------*/ + if (left.isAuto() || right.isAuto()) { + if (marginLeft.isAuto()) + marginLeft.setValue(Fixed, 0); + if (marginRight.isAuto()) + marginRight.setValue(Fixed, 0); + } + + /*-----------------------------------------------------------------------*\ + * 4. If at this point both 'margin-left' and 'margin-right' are still + * 'auto', solve the equation under the extra constraint that the two + * margins must get equal values, unless this would make them negative, + * in which case when the direction of the containing block is 'ltr' + * ('rtl'), set 'margin-left' ('margin-right') to zero and solve for + * 'margin-right' ('margin-left'). + \*-----------------------------------------------------------------------*/ + int leftValue = 0; + int rightValue = 0; + + if (marginLeft.isAuto() && marginRight.isAuto()) { + // 'left' and 'right' cannot be 'auto' due to step 3 + ASSERT(!(left.isAuto() && right.isAuto())); + + leftValue = left.calcValue(containerWidth); + rightValue = right.calcValue(containerWidth); + + int difference = availableSpace - (leftValue + rightValue); + if (difference > 0) { + m_marginLeft = difference / 2; // split the diference + m_marginRight = difference - m_marginLeft; // account for odd valued differences + } else { + // see FIXME 1 + if (containerDirection == LTR) { + m_marginLeft = 0; + m_marginRight = difference; // will be negative + } else { + m_marginLeft = difference; // will be negative + m_marginRight = 0; + } + } + + /*-----------------------------------------------------------------------*\ + * 5. If at this point there is an 'auto' left, solve the equation for + * that value. + \*-----------------------------------------------------------------------*/ + } else if (left.isAuto()) { + m_marginLeft = marginLeft.calcValue(containerWidth); + m_marginRight = marginRight.calcValue(containerWidth); + rightValue = right.calcValue(containerWidth); + + // Solve for 'left' + leftValue = availableSpace - (rightValue + m_marginLeft + m_marginRight); + } else if (right.isAuto()) { + m_marginLeft = marginLeft.calcValue(containerWidth); + m_marginRight = marginRight.calcValue(containerWidth); + leftValue = left.calcValue(containerWidth); + + // Solve for 'right' + rightValue = availableSpace - (leftValue + m_marginLeft + m_marginRight); + } else if (marginLeft.isAuto()) { + m_marginRight = marginRight.calcValue(containerWidth); + leftValue = left.calcValue(containerWidth); + rightValue = right.calcValue(containerWidth); + + // Solve for 'margin-left' + m_marginLeft = availableSpace - (leftValue + rightValue + m_marginRight); + } else if (marginRight.isAuto()) { + m_marginLeft = marginLeft.calcValue(containerWidth); + leftValue = left.calcValue(containerWidth); + rightValue = right.calcValue(containerWidth); + + // Solve for 'margin-right' + m_marginRight = availableSpace - (leftValue + rightValue + m_marginLeft); + } else { + // Nothing is 'auto', just calculate the values. + m_marginLeft = marginLeft.calcValue(containerWidth); + m_marginRight = marginRight.calcValue(containerWidth); + rightValue = right.calcValue(containerWidth); + leftValue = left.calcValue(containerWidth); + } + + /*-----------------------------------------------------------------------*\ + * 6. If at this point the values are over-constrained, ignore the value + * for either 'left' (in case the 'direction' property of the + * containing block is 'rtl') or 'right' (in case 'direction' is + * 'ltr') and solve for that value. + \*-----------------------------------------------------------------------*/ + // NOTE: It is not necessary to solve for 'right' when the direction is + // LTR because the value is not used. + int totalWidth = m_width + leftValue + rightValue + m_marginLeft + m_marginRight; + if (totalWidth > containerWidth && (containerDirection == RTL)) + leftValue = containerWidth - (totalWidth - leftValue); + + // Use computed values to calculate the horizontal position. + + // FIXME: This hack is needed to calculate the xPos for a 'rtl' relatively + // positioned, inline containing block because right now, it is using the xPos + // of the first line box when really it should use the last line box. When + // this is fixed elsewhere, this block should be removed. + if (containerBlock->isInline() && containerBlock->style()->direction() == RTL) { + const RenderFlow* flow = static_cast<const RenderFlow*>(containerBlock); + InlineFlowBox* firstLine = flow->firstLineBox(); + InlineFlowBox* lastLine = flow->lastLineBox(); + if (firstLine && lastLine && firstLine != lastLine) { + m_x = leftValue + m_marginLeft + lastLine->borderLeft() + (lastLine->xPos() - firstLine->xPos()); + return; + } + } + + m_x = leftValue + m_marginLeft + containerBlock->borderLeft(); +} + +void RenderBox::calcAbsoluteVerticalReplaced() +{ + // The following is based off of the W3C Working Draft from April 11, 2006 of + // CSS 2.1: Section 10.6.5 "Absolutly positioned, replaced elements" + // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-height> + // (block-style-comments in this function correspond to text from the spec and + // the numbers correspond to numbers in spec) + + // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline. + const RenderObject* containerBlock = container(); + + const int containerHeight = containingBlockHeightForPositioned(containerBlock); + + // Variables to solve. + Length top = style()->top(); + Length bottom = style()->bottom(); + Length marginTop = style()->marginTop(); + Length marginBottom = style()->marginBottom(); + + + /*-----------------------------------------------------------------------*\ + * 1. The used value of 'height' is determined as for inline replaced + * elements. + \*-----------------------------------------------------------------------*/ + // NOTE: This value of height is FINAL in that the min/max height calculations + // are dealt with in calcReplacedHeight(). This means that the steps to produce + // correct max/min in the non-replaced version, are not necessary. + m_height = calcReplacedHeight() + borderTop() + borderBottom() + paddingTop() + paddingBottom(); + const int availableSpace = containerHeight - m_height; + + /*-----------------------------------------------------------------------*\ + * 2. If both 'top' and 'bottom' have the value 'auto', replace 'top' + * with the element's static position. + \*-----------------------------------------------------------------------*/ + // see FIXME 2 + if (top.isAuto() && bottom.isAuto()) { + // staticY should already have been set through layout of the parent(). + int staticTop = staticY() - containerBlock->borderTop(); + for (RenderObject* po = parent(); po && po != containerBlock; po = po->parent()) { + if (!po->isTableRow()) + staticTop += po->yPos(); + } + top.setValue(Fixed, staticTop); + } + + /*-----------------------------------------------------------------------*\ + * 3. If 'bottom' is 'auto', replace any 'auto' on 'margin-top' or + * 'margin-bottom' with '0'. + \*-----------------------------------------------------------------------*/ + // FIXME: The spec. says that this step should only be taken when bottom is + // auto, but if only top is auto, this makes step 4 impossible. + if (top.isAuto() || bottom.isAuto()) { + if (marginTop.isAuto()) + marginTop.setValue(Fixed, 0); + if (marginBottom.isAuto()) + marginBottom.setValue(Fixed, 0); + } + + /*-----------------------------------------------------------------------*\ + * 4. If at this point both 'margin-top' and 'margin-bottom' are still + * 'auto', solve the equation under the extra constraint that the two + * margins must get equal values. + \*-----------------------------------------------------------------------*/ + int topValue = 0; + int bottomValue = 0; + + if (marginTop.isAuto() && marginBottom.isAuto()) { + // 'top' and 'bottom' cannot be 'auto' due to step 2 and 3 combinded. + ASSERT(!(top.isAuto() || bottom.isAuto())); + + topValue = top.calcValue(containerHeight); + bottomValue = bottom.calcValue(containerHeight); + + int difference = availableSpace - (topValue + bottomValue); + // NOTE: This may result in negative values. + m_marginTop = difference / 2; // split the difference + m_marginBottom = difference - m_marginTop; // account for odd valued differences + + /*-----------------------------------------------------------------------*\ + * 5. If at this point there is only one 'auto' left, solve the equation + * for that value. + \*-----------------------------------------------------------------------*/ + } else if (top.isAuto()) { + m_marginTop = marginTop.calcValue(containerHeight); + m_marginBottom = marginBottom.calcValue(containerHeight); + bottomValue = bottom.calcValue(containerHeight); + + // Solve for 'top' + topValue = availableSpace - (bottomValue + m_marginTop + m_marginBottom); + } else if (bottom.isAuto()) { + m_marginTop = marginTop.calcValue(containerHeight); + m_marginBottom = marginBottom.calcValue(containerHeight); + topValue = top.calcValue(containerHeight); + + // Solve for 'bottom' + // NOTE: It is not necessary to solve for 'bottom' because we don't ever + // use the value. + } else if (marginTop.isAuto()) { + m_marginBottom = marginBottom.calcValue(containerHeight); + topValue = top.calcValue(containerHeight); + bottomValue = bottom.calcValue(containerHeight); + + // Solve for 'margin-top' + m_marginTop = availableSpace - (topValue + bottomValue + m_marginBottom); + } else if (marginBottom.isAuto()) { + m_marginTop = marginTop.calcValue(containerHeight); + topValue = top.calcValue(containerHeight); + bottomValue = bottom.calcValue(containerHeight); + + // Solve for 'margin-bottom' + m_marginBottom = availableSpace - (topValue + bottomValue + m_marginTop); + } else { + // Nothing is 'auto', just calculate the values. + m_marginTop = marginTop.calcValue(containerHeight); + m_marginBottom = marginBottom.calcValue(containerHeight); + topValue = top.calcValue(containerHeight); + // NOTE: It is not necessary to solve for 'bottom' because we don't ever + // use the value. + } + + /*-----------------------------------------------------------------------*\ + * 6. If at this point the values are over-constrained, ignore the value + * for 'bottom' and solve for that value. + \*-----------------------------------------------------------------------*/ + // NOTE: It is not necessary to do this step because we don't end up using + // the value of 'bottom' regardless of whether the values are over-constrained + // or not. + + // Use computed values to calculate the vertical position. + m_y = topValue + m_marginTop + containerBlock->borderTop(); +} + +IntRect RenderBox::localCaretRect(InlineBox* box, int caretOffset, int* extraWidthToEndOfLine) +{ + // VisiblePositions at offsets inside containers either a) refer to the positions before/after + // those containers (tables and select elements) or b) refer to the position inside an empty block. + // They never refer to children. + // FIXME: Paint the carets inside empty blocks differently than the carets before/after elements. + + // FIXME: What about border and padding? + const int caretWidth = 1; + IntRect rect(xPos(), yPos(), caretWidth, m_height); + TextDirection direction = box ? box->direction() : style()->direction(); + + if ((!caretOffset) ^ (direction == LTR)) + rect.move(IntSize(m_width - caretWidth, 0)); + + if (box) { + RootInlineBox* rootBox = box->root(); + int top = rootBox->topOverflow(); + rect.setY(top); + rect.setHeight(rootBox->bottomOverflow() - top); + } + + // If height of box is smaller than font height, use the latter one, + // otherwise the caret might become invisible. + // + // Also, if the box is not a replaced element, always use the font height. + // This prevents the "big caret" bug described in: + // <rdar://problem/3777804> Deleting all content in a document can result in giant tall-as-window insertion point + // + // FIXME: ignoring :first-line, missing good reason to take care of + int fontHeight = style()->font().height(); + if (fontHeight > rect.height() || !isReplaced() && !isTable()) + rect.setHeight(fontHeight); + + if (extraWidthToEndOfLine) + *extraWidthToEndOfLine = xPos() + m_width - rect.right(); + + // Move to local coords + rect.move(-xPos(), -yPos()); + return rect; +} + +int RenderBox::lowestPosition(bool /*includeOverflowInterior*/, bool includeSelf) const +{ + if (!includeSelf || !m_width) + return 0; + int bottom = m_height; + if (isRelPositioned()) + bottom += relativePositionOffsetY(); + return bottom; +} + +int RenderBox::rightmostPosition(bool /*includeOverflowInterior*/, bool includeSelf) const +{ + if (!includeSelf || !m_height) + return 0; + int right = m_width; + if (isRelPositioned()) + right += relativePositionOffsetX(); + return right; +} + +int RenderBox::leftmostPosition(bool /*includeOverflowInterior*/, bool includeSelf) const +{ + if (!includeSelf || !m_height) + return m_width; + int left = 0; + if (isRelPositioned()) + left += relativePositionOffsetX(); + return left; +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderBox.h b/src/3rdparty/webkit/WebCore/rendering/RenderBox.h new file mode 100644 index 0000000..54349fa --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderBox.h @@ -0,0 +1,257 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2006, 2007 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef RenderBox_h +#define RenderBox_h + +#include "RenderObject.h" + +namespace WebCore { + + enum WidthType { Width, MinWidth, MaxWidth }; + +class RenderBox : public RenderObject { +public: + RenderBox(Node*); + virtual ~RenderBox(); + + virtual const char* renderName() const { return "RenderBox"; } + + virtual void paint(PaintInfo&, int tx, int ty); + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); + + virtual void destroy(); + + virtual int minPrefWidth() const; + virtual int maxPrefWidth() const; + + virtual int overrideSize() const; + virtual int overrideWidth() const; + virtual int overrideHeight() const; + virtual void setOverrideSize(int); + + virtual FloatPoint localToAbsolute(FloatPoint localPoint = FloatPoint(), bool fixed = false, bool useTransforms = false) const; + virtual FloatPoint absoluteToLocal(FloatPoint containerPoint, bool fixed = false, bool useTransforms = false) const; + virtual FloatQuad localToAbsoluteQuad(const FloatQuad&, bool fixed = false) const; + + virtual IntSize offsetFromContainer(RenderObject*) const; + + virtual int xPos() const { return m_x; } + virtual int yPos() const { return m_y; } + virtual void setPos(int x, int y); + + virtual int width() const { return m_width; } + virtual int height() const { return m_height; } + virtual void setWidth(int width) { m_width = width; } + virtual void setHeight(int height) { m_height = height; } + + virtual int marginTop() const { return m_marginTop; } + virtual int marginBottom() const { return m_marginBottom; } + virtual int marginLeft() const { return m_marginLeft; } + virtual int marginRight() const { return m_marginRight; } + + virtual IntRect borderBox() const { return IntRect(0, -borderTopExtra(), width(), height() + borderTopExtra() + borderBottomExtra()); } + + int calcBorderBoxWidth(int width) const; + int calcBorderBoxHeight(int height) const; + int calcContentBoxWidth(int width) const; + int calcContentBoxHeight(int height) const; + + virtual void borderFitAdjust(int& /*x*/, int& /*w*/) const { } // Shrink the box in which the border paints if border-fit is set. + + // This method is now public so that centered objects like tables that are + // shifted right by left-aligned floats can recompute their left and + // right margins (so that they can remain centered after being + // shifted. -dwh + void calcHorizontalMargins(const Length& marginLeft, const Length& marginRight, int containerWidth); + + virtual void position(InlineBox*); + + virtual void dirtyLineBoxes(bool fullLayout, bool isRootLineBox = false); + + // For inline replaced elements, this function returns the inline box that owns us. Enables + // the replaced RenderObject to quickly determine what line it is contained on and to easily + // iterate over structures on the line. + virtual InlineBox* inlineBoxWrapper() const { return m_inlineBoxWrapper; } + virtual void setInlineBoxWrapper(InlineBox* boxWrapper) { m_inlineBoxWrapper = boxWrapper; } + virtual void deleteLineBoxWrapper(); + + virtual int lowestPosition(bool includeOverflowInterior = true, bool includeSelf = true) const; + virtual int rightmostPosition(bool includeOverflowInterior = true, bool includeSelf = true) const; + virtual int leftmostPosition(bool includeOverflowInterior = true, bool includeSelf = true) const; + + virtual IntRect absoluteClippedOverflowRect(); + virtual void computeAbsoluteRepaintRect(IntRect&, bool fixed = false); + IntSize offsetForPositionedInContainer(RenderObject*) const; + + virtual void repaintDuringLayoutIfMoved(const IntRect&); + + virtual int containingBlockWidth() const; + + virtual void calcWidth(); + virtual void calcHeight(); + + bool stretchesToViewHeight() const + { + return style()->htmlHacks() && style()->height().isAuto() && !isFloatingOrPositioned() && (isRoot() || isBody()); + } + + virtual IntSize intrinsicSize() const { return IntSize(); } + + // Whether or not the element shrinks to its intrinsic width (rather than filling the width + // of a containing block). HTML4 buttons, <select>s, <input>s, legends, and floating/compact elements do this. + bool sizesToIntrinsicWidth(WidthType) const; + virtual bool stretchesToMinIntrinsicWidth() const { return false; } + + int calcWidthUsing(WidthType, int containerWidth); + int calcHeightUsing(const Length& height); + int calcReplacedWidthUsing(Length width) const; + int calcReplacedHeightUsing(Length height) const; + + virtual int calcReplacedWidth(bool includeMaxWidth = true) const; + virtual int calcReplacedHeight() const; + + int calcPercentageHeight(const Length& height); + + virtual int availableHeight() const; + int availableHeightUsing(const Length&) const; + + void calcVerticalMargins(); + + int relativePositionOffsetX() const; + int relativePositionOffsetY() const; + IntSize relativePositionOffset() const { return IntSize(relativePositionOffsetX(), relativePositionOffsetY()); } + + virtual RenderLayer* layer() const { return m_layer; } + + virtual IntRect localCaretRect(InlineBox*, int caretOffset, int* extraWidthToEndOfLine = 0); + + virtual void paintFillLayerExtended(const PaintInfo&, const Color&, const FillLayer*, int clipY, int clipHeight, + int tx, int ty, int width, int height, InlineFlowBox* = 0, CompositeOperator = CompositeSourceOver); + IntSize calculateBackgroundSize(const FillLayer*, int scaledWidth, int scaledHeight) const; + + virtual int staticX() const; + virtual int staticY() const; + virtual void setStaticX(int staticX); + virtual void setStaticY(int staticY); + + virtual IntRect getOverflowClipRect(int tx, int ty); + virtual IntRect getClipRect(int tx, int ty); + + virtual void paintBoxDecorations(PaintInfo&, int tx, int ty); + virtual void paintMask(PaintInfo& paintInfo, int tx, int ty); + virtual void imageChanged(WrappedImagePtr, const IntRect* = 0); + + // Called when a positioned object moves but doesn't change size. A simplified layout is done + // that just updates the object's position. + virtual void tryLayoutDoingPositionedMovementOnly() + { + int oldWidth = m_width; + calcWidth(); + // If we shrink to fit our width may have changed, so we still need full layout. + if (oldWidth != m_width) + return; + calcHeight(); + setNeedsLayout(false); + } + + virtual IntRect maskClipRect(); + +protected: + virtual void styleWillChange(RenderStyle::Diff, const RenderStyle* newStyle); + virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + + void paintFillLayer(const PaintInfo&, const Color&, const FillLayer*, int clipY, int clipHeight, int tx, int ty, int width, int height, CompositeOperator = CompositeSourceOver); + void paintFillLayers(const PaintInfo&, const Color&, const FillLayer*, int clipY, int clipHeight, int tx, int ty, int width, int height, CompositeOperator = CompositeSourceOver); + + void paintMaskImages(const PaintInfo&, int clipY, int clipHeight, int tx, int ty, int width, int height); + +#if PLATFORM(MAC) + void paintCustomHighlight(int tx, int ty, const AtomicString& type, bool behindText); +#endif + + void calcAbsoluteHorizontal(); + + virtual bool shouldCalculateSizeAsReplaced() const { return isReplaced() && !isInlineBlockOrInlineTable(); } + +private: + void paintRootBoxDecorations(PaintInfo&, int tx, int ty); + // Returns true if we did a full repaint + bool repaintLayerRectsForImage(WrappedImagePtr image, const FillLayer* layers, bool drawingBackground); + + void calculateBackgroundImageGeometry(const FillLayer*, int tx, int ty, int w, int h, IntRect& destRect, IntPoint& phase, IntSize& tileSize); + + int containingBlockWidthForPositioned(const RenderObject* containingBlock) const; + int containingBlockHeightForPositioned(const RenderObject* containingBlock) const; + + void calcAbsoluteVertical(); + void calcAbsoluteHorizontalValues(Length width, const RenderObject* cb, TextDirection containerDirection, + int containerWidth, int bordersPlusPadding, + Length left, Length right, Length marginLeft, Length marginRight, + int& widthValue, int& marginLeftValue, int& marginRightValue, int& xPos); + void calcAbsoluteVerticalValues(Length height, const RenderObject* cb, + int containerHeight, int bordersPlusPadding, + Length top, Length bottom, Length marginTop, Length marginBottom, + int& heightValue, int& marginTopValue, int& marginBottomValue, int& yPos); + + void calcAbsoluteVerticalReplaced(); + void calcAbsoluteHorizontalReplaced(); + + // This function calculates the minimum and maximum preferred widths for an object. + // These values are used in shrink-to-fit layout systems. + // These include tables, positioned objects, floats and flexible boxes. + virtual void calcPrefWidths() = 0; + +protected: + // The width/height of the contents + borders + padding. + int m_width; + int m_height; + + int m_x; + int m_y; + + int m_marginLeft; + int m_marginRight; + int m_marginTop; + int m_marginBottom; + + // The preferred width of the element if it were to break its lines at every possible opportunity. + int m_minPrefWidth; + + // The preferred width of the element if it never breaks any lines at all. + int m_maxPrefWidth; + + // A pointer to our layer if we have one. + RenderLayer* m_layer; + + // For inline replaced elements, the inline box that owns us. + InlineBox* m_inlineBoxWrapper; + +private: + // Used to store state between styleWillChange and styleDidChange + static bool s_wasFloating; + static bool s_hadOverflowClip; +}; + +} // namespace WebCore + +#endif // RenderBox_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderButton.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderButton.cpp new file mode 100644 index 0000000..10e21c3 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderButton.cpp @@ -0,0 +1,183 @@ +/** + * This file is part of the html renderer for KDE. + * + * Copyright (C) 2005 Apple Computer, Inc. + * + * 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" +#include "RenderButton.h" + +#include "Document.h" +#include "GraphicsContext.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include "RenderTextFragment.h" +#include "RenderTheme.h" + +#if ENABLE(WML) +#include "WMLDoElement.h" +#include "WMLNames.h" +#endif + +namespace WebCore { + +using namespace HTMLNames; + +RenderButton::RenderButton(Node* node) + : RenderFlexibleBox(node) + , m_buttonText(0) + , m_inner(0) + , m_default(false) +{ +} + +void RenderButton::addChild(RenderObject* newChild, RenderObject* beforeChild) +{ + if (!m_inner) { + // Create an anonymous block. + ASSERT(!firstChild()); + m_inner = createAnonymousBlock(); + setupInnerStyle(m_inner->style()); + RenderFlexibleBox::addChild(m_inner); + } + + m_inner->addChild(newChild, beforeChild); +} + +void RenderButton::removeChild(RenderObject* oldChild) +{ + if (oldChild == m_inner || !m_inner) { + RenderFlexibleBox::removeChild(oldChild); + m_inner = 0; + } else + m_inner->removeChild(oldChild); +} + +void RenderButton::styleWillChange(RenderStyle::Diff diff, const RenderStyle* newStyle) +{ + if (m_inner) { + // RenderBlock::setStyle is going to apply a new style to the inner block, which + // will have the initial box flex value, 0. The current value is 1, because we set + // it right below. Here we change it back to 0 to avoid getting a spurious layout hint + // because of the difference. + m_inner->style()->setBoxFlex(0); + } + RenderBlock::styleWillChange(diff, newStyle); +} + +void RenderButton::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +{ + RenderBlock::styleDidChange(diff, oldStyle); + + if (m_buttonText) + m_buttonText->setStyle(style()); + if (m_inner) // RenderBlock handled updating the anonymous block's style. + setupInnerStyle(m_inner->style()); + setReplaced(isInline()); + + if (!m_default && theme()->isDefault(this)) { + if (!m_timer) + m_timer.set(new Timer<RenderButton>(this, &RenderButton::timerFired)); + m_timer->startRepeating(0.03); + m_default = true; + } else if (m_default && !theme()->isDefault(this)) { + m_default = false; + m_timer.clear(); + } +} + +void RenderButton::setupInnerStyle(RenderStyle* innerStyle) +{ + ASSERT(innerStyle->refCount() == 1); + // RenderBlock::createAnonymousBlock creates a new RenderStyle, so this is + // safe to modify. + innerStyle->setBoxFlex(1.0f); + if (style()->hasAppearance()) + theme()->adjustButtonInnerStyle(innerStyle); +} + +void RenderButton::updateFromElement() +{ + // If we're an input element, we may need to change our button text. + if (element()->hasTagName(inputTag)) { + HTMLInputElement* input = static_cast<HTMLInputElement*>(element()); + String value = input->valueWithDefault(); + setText(value); + } + + +#if ENABLE(WML) + else if (element()->hasTagName(WMLNames::doTag)) { + WMLDoElement* doElement = static_cast<WMLDoElement*>(element()); + + String value = doElement->label(); + if (value.isEmpty()) + value = doElement->name(); + + setText(value); + } +#endif +} + +bool RenderButton::canHaveChildren() const +{ + // Input elements can't have children, but button elements can. We'll + // write the code assuming any other button types that might emerge in the future + // can also have children. + return !element()->hasTagName(inputTag); +} + +void RenderButton::setText(const String& str) +{ + if (str.isEmpty()) { + if (m_buttonText) { + m_buttonText->destroy(); + m_buttonText = 0; + } + } else { + if (m_buttonText) + m_buttonText->setText(str.impl()); + else { + m_buttonText = new (renderArena()) RenderTextFragment(document(), str.impl()); + m_buttonText->setStyle(style()); + addChild(m_buttonText); + } + } +} + +void RenderButton::updateBeforeAfterContent(RenderStyle::PseudoId type) +{ + if (m_inner) + m_inner->updateBeforeAfterContentForContainer(type, this); + else + updateBeforeAfterContentForContainer(type, this); +} + +IntRect RenderButton::controlClipRect(int tx, int ty) const +{ + // Clip to the padding box to at least give content the extra padding space. + return IntRect(tx + borderLeft(), ty + borderTop(), m_width - borderLeft() - borderRight(), m_height - borderTop() - borderBottom()); +} + +void RenderButton::timerFired(Timer<RenderButton>*) +{ + repaint(); +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderButton.h b/src/3rdparty/webkit/WebCore/rendering/RenderButton.h new file mode 100644 index 0000000..24e4767 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderButton.h @@ -0,0 +1,77 @@ +/* + * This file is part of the html renderer for KDE. + * + * Copyright (C) 2005 Apple Computer + * + * 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. + * + */ + +#ifndef RenderButton_h +#define RenderButton_h + +#include "RenderFlexibleBox.h" +#include "Timer.h" +#include <wtf/OwnPtr.h> + +namespace WebCore { + +class RenderTextFragment; + +// RenderButtons are just like normal flexboxes except that they will generate an anonymous block child. +// For inputs, they will also generate an anonymous RenderText and keep its style and content up +// to date as the button changes. +class RenderButton : public RenderFlexibleBox { +public: + RenderButton(Node*); + + virtual const char* renderName() const { return "RenderButton"; } + + virtual void addChild(RenderObject* newChild, RenderObject *beforeChild = 0); + virtual void removeChild(RenderObject*); + virtual void removeLeftoverAnonymousBlock(RenderBlock*) { } + virtual bool createsAnonymousWrapper() const { return true; } + + void setupInnerStyle(RenderStyle*); + virtual void updateFromElement(); + + virtual void updateBeforeAfterContent(RenderStyle::PseudoId); + + virtual bool hasControlClip() const { return true; } + virtual IntRect controlClipRect(int /*tx*/, int /*ty*/) const; + + void setText(const String&); + + virtual bool canHaveChildren() const; + +protected: + virtual void styleWillChange(RenderStyle::Diff, const RenderStyle* newStyle); + virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + + virtual bool hasLineIfEmpty() const { return true; } + + void timerFired(Timer<RenderButton>*); + + RenderTextFragment* m_buttonText; + RenderBlock* m_inner; + + OwnPtr<Timer<RenderButton> > m_timer; + bool m_default; +}; + +} // namespace WebCore + +#endif // RenderButton_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderContainer.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderContainer.cpp new file mode 100644 index 0000000..9f6d737 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderContainer.cpp @@ -0,0 +1,701 @@ +/** + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * (C) 2004 Allan Sandfeld Jensen (kde@carewolf.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net) + * + * 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" +#include "RenderContainer.h" + +#include "AXObjectCache.h" +#include "Document.h" +#include "RenderCounter.h" +#include "RenderImageGeneratedContent.h" +#include "RenderLayer.h" +#include "RenderListItem.h" +#include "RenderTable.h" +#include "RenderTextFragment.h" +#include "RenderView.h" +#include "htmlediting.h" + +namespace WebCore { + +RenderContainer::RenderContainer(Node* node) + : RenderBox(node) + , m_firstChild(0) + , m_lastChild(0) +{ +} + +RenderContainer::~RenderContainer() +{ +} + +void RenderContainer::destroy() +{ + destroyLeftoverChildren(); + RenderBox::destroy(); +} + +void RenderContainer::destroyLeftoverChildren() +{ + while (m_firstChild) { + if (m_firstChild->isListMarker() || (m_firstChild->style()->styleType() == RenderStyle::FIRST_LETTER && !m_firstChild->isText())) + m_firstChild->remove(); // List markers are owned by their enclosing list and so don't get destroyed by this container. Similarly, first letters are destroyed by their remaining text fragment. + else { + // Destroy any anonymous children remaining in the render tree, as well as implicit (shadow) DOM elements like those used in the engine-based text fields. + if (m_firstChild->element()) + m_firstChild->element()->setRenderer(0); + m_firstChild->destroy(); + } + } +} + +bool RenderContainer::canHaveChildren() const +{ + return true; +} + +static void updateListMarkerNumbers(RenderObject* child) +{ + for (RenderObject* r = child; r; r = r->nextSibling()) + if (r->isListItem()) + static_cast<RenderListItem*>(r)->updateValue(); +} + +void RenderContainer::addChild(RenderObject* newChild, RenderObject* beforeChild) +{ + bool needsTable = false; + + if (newChild->isListItem()) + updateListMarkerNumbers(beforeChild ? beforeChild : m_lastChild); + else if (newChild->isTableCol() && newChild->style()->display() == TABLE_COLUMN_GROUP) + needsTable = !isTable(); + else if (newChild->isRenderBlock() && newChild->style()->display() == TABLE_CAPTION) + needsTable = !isTable(); + else if (newChild->isTableSection()) + needsTable = !isTable(); + else if (newChild->isTableRow()) + needsTable = !isTableSection(); + else if (newChild->isTableCell()) { + needsTable = !isTableRow(); + // I'm not 100% sure this is the best way to fix this, but without this + // change we recurse infinitely when trying to render the CSS2 test page: + // http://www.bath.ac.uk/%7Epy8ieh/internet/eviltests/htmlbodyheadrendering2.html. + // See Radar 2925291. + if (needsTable && isTableCell() && !m_firstChild && !newChild->isTableCell()) + needsTable = false; + } + + if (needsTable) { + RenderTable* table; + RenderObject* afterChild = beforeChild ? beforeChild->previousSibling() : m_lastChild; + if (afterChild && afterChild->isAnonymous() && afterChild->isTable()) + table = static_cast<RenderTable*>(afterChild); + else { + table = new (renderArena()) RenderTable(document() /* is anonymous */); + RefPtr<RenderStyle> newStyle = RenderStyle::create(); + newStyle->inheritFrom(style()); + newStyle->setDisplay(TABLE); + table->setStyle(newStyle.release()); + addChild(table, beforeChild); + } + table->addChild(newChild); + } else { + // just add it... + insertChildNode(newChild, beforeChild); + } + + if (newChild->isText() && newChild->style()->textTransform() == CAPITALIZE) { + RefPtr<StringImpl> textToTransform = static_cast<RenderText*>(newChild)->originalText(); + if (textToTransform) + static_cast<RenderText*>(newChild)->setText(textToTransform.release(), true); + } +} + +RenderObject* RenderContainer::removeChildNode(RenderObject* oldChild, bool fullRemove) +{ + ASSERT(oldChild->parent() == this); + + // So that we'll get the appropriate dirty bit set (either that a normal flow child got yanked or + // that a positioned child got yanked). We also repaint, so that the area exposed when the child + // disappears gets repainted properly. + if (!documentBeingDestroyed() && fullRemove && oldChild->m_everHadLayout) { + oldChild->setNeedsLayoutAndPrefWidthsRecalc(); + oldChild->repaint(); + } + + // If we have a line box wrapper, delete it. + oldChild->deleteLineBoxWrapper(); + + if (!documentBeingDestroyed() && fullRemove) { + // if we remove visible child from an invisible parent, we don't know the layer visibility any more + RenderLayer* layer = 0; + if (m_style->visibility() != VISIBLE && oldChild->style()->visibility() == VISIBLE && !oldChild->hasLayer()) { + layer = enclosingLayer(); + layer->dirtyVisibleContentStatus(); + } + + // Keep our layer hierarchy updated. + if (oldChild->firstChild() || oldChild->hasLayer()) { + if (!layer) layer = enclosingLayer(); + oldChild->removeLayers(layer); + } + + // renumber ordered lists + if (oldChild->isListItem()) + updateListMarkerNumbers(oldChild->nextSibling()); + + if (oldChild->isPositioned() && childrenInline()) + dirtyLinesFromChangedChild(oldChild); + } + + // If oldChild is the start or end of the selection, then clear the selection to + // avoid problems of invalid pointers. + // FIXME: The SelectionController should be responsible for this when it + // is notified of DOM mutations. + if (!documentBeingDestroyed() && oldChild->isSelectionBorder()) + view()->clearSelection(); + + // remove the child + if (oldChild->previousSibling()) + oldChild->previousSibling()->setNextSibling(oldChild->nextSibling()); + if (oldChild->nextSibling()) + oldChild->nextSibling()->setPreviousSibling(oldChild->previousSibling()); + + if (m_firstChild == oldChild) + m_firstChild = oldChild->nextSibling(); + if (m_lastChild == oldChild) + m_lastChild = oldChild->previousSibling(); + + oldChild->setPreviousSibling(0); + oldChild->setNextSibling(0); + oldChild->setParent(0); + + if (AXObjectCache::accessibilityEnabled()) + document()->axObjectCache()->childrenChanged(this); + + return oldChild; +} + +void RenderContainer::removeChild(RenderObject* oldChild) +{ + // We do this here instead of in removeChildNode, since the only extremely low-level uses of remove/appendChildNode + // cannot affect the positioned object list, and the floating object list is irrelevant (since the list gets cleared on + // layout anyway). + oldChild->removeFromObjectLists(); + + removeChildNode(oldChild); +} + +RenderObject* RenderContainer::beforeAfterContainer(RenderStyle::PseudoId type) +{ + if (type == RenderStyle::BEFORE) { + RenderObject* first = this; + do { + // Skip list markers. + first = first->firstChild(); + while (first && first->isListMarker()) + first = first->nextSibling(); + } while (first && first->isAnonymous() && first->style()->styleType() == RenderStyle::NOPSEUDO); + if (first && first->style()->styleType() != type) + return 0; + return first; + } + if (type == RenderStyle::AFTER) { + RenderObject* last = this; + do { + last = last->lastChild(); + } while (last && last->isAnonymous() && last->style()->styleType() == RenderStyle::NOPSEUDO && !last->isListMarker()); + if (last && last->style()->styleType() != type) + return 0; + return last; + } + + ASSERT_NOT_REACHED(); + return 0; +} + +void RenderContainer::updateBeforeAfterContent(RenderStyle::PseudoId type) +{ + // If this is an anonymous wrapper, then the parent applies its own pseudo-element style to it. + if (parent() && parent()->createsAnonymousWrapper()) + return; + updateBeforeAfterContentForContainer(type, this); +} + +static RenderObject* findBeforeAfterParent(RenderObject* object) +{ + // Only table parts need to search for the :before or :after parent + if (!(object->isTable() || object->isTableSection() || object->isTableRow())) + return object; + + RenderObject* beforeAfterParent = object; + while (beforeAfterParent && !(beforeAfterParent->isText() || beforeAfterParent->isImage())) + beforeAfterParent = beforeAfterParent->firstChild(); + return beforeAfterParent; +} + +void RenderContainer::updateBeforeAfterContentForContainer(RenderStyle::PseudoId type, RenderContainer* styledObject) +{ + // In CSS2, before/after pseudo-content cannot nest. Check this first. + if (style()->styleType() == RenderStyle::BEFORE || style()->styleType() == RenderStyle::AFTER) + return; + + RenderStyle* pseudoElementStyle = styledObject->getCachedPseudoStyle(type); + RenderObject* child = beforeAfterContainer(type); + + // Whether or not we currently have generated content attached. + bool oldContentPresent = child; + + // Whether or not we now want generated content. + bool newContentWanted = pseudoElementStyle && pseudoElementStyle->display() != NONE; + + // For <q><p/></q>, if this object is the inline continuation of the <q>, we only want to generate + // :after content and not :before content. + if (newContentWanted && type == RenderStyle::BEFORE && isInlineContinuation()) + newContentWanted = false; + + // Similarly, if we're the beginning of a <q>, and there's an inline continuation for our object, + // then we don't generate the :after content. + if (newContentWanted && type == RenderStyle::AFTER && isRenderInline() && continuation()) + newContentWanted = false; + + // If we don't want generated content any longer, or if we have generated content, but it's no longer + // identical to the new content data we want to build render objects for, then we nuke all + // of the old generated content. + if (!newContentWanted || (oldContentPresent && Node::diff(child->style(), pseudoElementStyle) == Node::Detach)) { + // Nuke the child. + if (child && child->style()->styleType() == type) { + oldContentPresent = false; + child->destroy(); + child = (type == RenderStyle::BEFORE) ? m_firstChild : m_lastChild; + } + } + + // If we have no pseudo-element style or if the pseudo-element style's display type is NONE, then we + // have no generated content and can now return. + if (!newContentWanted) + return; + + if (isInlineFlow() && !pseudoElementStyle->isDisplayInlineType() && pseudoElementStyle->floating() == FNONE && + !(pseudoElementStyle->position() == AbsolutePosition || pseudoElementStyle->position() == FixedPosition)) + // According to the CSS2 spec (the end of section 12.1), the only allowed + // display values for the pseudo style are NONE and INLINE for inline flows. + // FIXME: CSS2.1 lifted this restriction, but block display types will crash. + // For now we at least relax the restriction to allow all inline types like inline-block + // and inline-table. + pseudoElementStyle->setDisplay(INLINE); + + if (oldContentPresent) { + if (child && child->style()->styleType() == type) { + // We have generated content present still. We want to walk this content and update our + // style information with the new pseudo-element style. + child->setStyle(pseudoElementStyle); + + RenderObject* beforeAfterParent = findBeforeAfterParent(child); + if (!beforeAfterParent) + return; + + // Note that if we ever support additional types of generated content (which should be way off + // in the future), this code will need to be patched. + for (RenderObject* genChild = beforeAfterParent->firstChild(); genChild; genChild = genChild->nextSibling()) { + if (genChild->isText()) + // Generated text content is a child whose style also needs to be set to the pseudo-element style. + genChild->setStyle(pseudoElementStyle); + else if (genChild->isImage()) { + // Images get an empty style that inherits from the pseudo. + RefPtr<RenderStyle> style = RenderStyle::create(); + style->inheritFrom(pseudoElementStyle); + genChild->setStyle(style.release()); + } else + // Must be a first-letter container. updateFirstLetter() will take care of it. + ASSERT(genChild->style()->styleType() == RenderStyle::FIRST_LETTER); + } + } + return; // We've updated the generated content. That's all we needed to do. + } + + RenderObject* insertBefore = (type == RenderStyle::BEFORE) ? firstChild() : 0; + + // Generated content consists of a single container that houses multiple children (specified + // by the content property). This generated content container gets the pseudo-element style set on it. + RenderObject* generatedContentContainer = 0; + + // Walk our list of generated content and create render objects for each. + for (const ContentData* content = pseudoElementStyle->contentData(); content; content = content->m_next) { + RenderObject* renderer = 0; + switch (content->m_type) { + case CONTENT_NONE: + break; + case CONTENT_TEXT: + renderer = new (renderArena()) RenderTextFragment(document() /* anonymous object */, content->m_content.m_text); + renderer->setStyle(pseudoElementStyle); + break; + case CONTENT_OBJECT: { + RenderImageGeneratedContent* image = new (renderArena()) RenderImageGeneratedContent(document()); // anonymous object + RefPtr<RenderStyle> style = RenderStyle::create(); + style->inheritFrom(pseudoElementStyle); + image->setStyle(style.release()); + if (StyleImage* styleImage = content->m_content.m_image) + image->setStyleImage(styleImage); + renderer = image; + break; + } + case CONTENT_COUNTER: + renderer = new (renderArena()) RenderCounter(document(), *content->m_content.m_counter); + renderer->setStyle(pseudoElementStyle); + break; + } + + if (renderer) { + if (!generatedContentContainer) { + // Make a generated box that might be any display type now that we are able to drill down into children + // to find the original content properly. + generatedContentContainer = RenderObject::createObject(document(), pseudoElementStyle); + generatedContentContainer->setStyle(pseudoElementStyle); + addChild(generatedContentContainer, insertBefore); + } + generatedContentContainer->addChild(renderer); + } + } +} + +bool RenderContainer::isAfterContent(RenderObject* child) const +{ + if (!child) + return false; + if (child->style()->styleType() != RenderStyle::AFTER) + return false; + // Text nodes don't have their own styles, so ignore the style on a text node. + if (child->isText() && !child->isBR()) + return false; + return true; +} + +static void invalidateCountersInContainer(RenderObject* container) +{ + if (!container) + return; + container = findBeforeAfterParent(container); + if (!container) + return; + for (RenderObject* content = container->firstChild(); content; content = content->nextSibling()) { + if (content->isCounter()) + static_cast<RenderCounter*>(content)->invalidate(); + } +} + +void RenderContainer::invalidateCounters() +{ + if (documentBeingDestroyed()) + return; + + invalidateCountersInContainer(beforeAfterContainer(RenderStyle::BEFORE)); + invalidateCountersInContainer(beforeAfterContainer(RenderStyle::AFTER)); +} + +void RenderContainer::appendChildNode(RenderObject* newChild, bool fullAppend) +{ + ASSERT(newChild->parent() == 0); + ASSERT(!isBlockFlow() || (!newChild->isTableSection() && !newChild->isTableRow() && !newChild->isTableCell())); + + newChild->setParent(this); + RenderObject* lChild = m_lastChild; + + if (lChild) { + newChild->setPreviousSibling(lChild); + lChild->setNextSibling(newChild); + } else + m_firstChild = newChild; + + m_lastChild = newChild; + + if (fullAppend) { + // Keep our layer hierarchy updated. Optimize for the common case where we don't have any children + // and don't have a layer attached to ourselves. + RenderLayer* layer = 0; + if (newChild->firstChild() || newChild->hasLayer()) { + layer = enclosingLayer(); + newChild->addLayers(layer, newChild); + } + + // if the new child is visible but this object was not, tell the layer it has some visible content + // that needs to be drawn and layer visibility optimization can't be used + if (style()->visibility() != VISIBLE && newChild->style()->visibility() == VISIBLE && !newChild->hasLayer()) { + if (!layer) + layer = enclosingLayer(); + if (layer) + layer->setHasVisibleContent(true); + } + + if (!newChild->isFloatingOrPositioned() && childrenInline()) + dirtyLinesFromChangedChild(newChild); + } + + newChild->setNeedsLayoutAndPrefWidthsRecalc(); // Goes up the containing block hierarchy. + if (!normalChildNeedsLayout()) + setChildNeedsLayout(true); // We may supply the static position for an absolute positioned child. + + if (AXObjectCache::accessibilityEnabled()) + document()->axObjectCache()->childrenChanged(this); +} + +void RenderContainer::insertChildNode(RenderObject* child, RenderObject* beforeChild, bool fullInsert) +{ + if (!beforeChild) { + appendChildNode(child); + return; + } + + ASSERT(!child->parent()); + while (beforeChild->parent() != this && beforeChild->parent()->isAnonymousBlock()) + beforeChild = beforeChild->parent(); + ASSERT(beforeChild->parent() == this); + + ASSERT(!isBlockFlow() || (!child->isTableSection() && !child->isTableRow() && !child->isTableCell())); + + if (beforeChild == m_firstChild) + m_firstChild = child; + + RenderObject* prev = beforeChild->previousSibling(); + child->setNextSibling(beforeChild); + beforeChild->setPreviousSibling(child); + if(prev) prev->setNextSibling(child); + child->setPreviousSibling(prev); + + child->setParent(this); + + if (fullInsert) { + // Keep our layer hierarchy updated. Optimize for the common case where we don't have any children + // and don't have a layer attached to ourselves. + RenderLayer* layer = 0; + if (child->firstChild() || child->hasLayer()) { + layer = enclosingLayer(); + child->addLayers(layer, child); + } + + // if the new child is visible but this object was not, tell the layer it has some visible content + // that needs to be drawn and layer visibility optimization can't be used + if (style()->visibility() != VISIBLE && child->style()->visibility() == VISIBLE && !child->hasLayer()) { + if (!layer) + layer = enclosingLayer(); + if (layer) + layer->setHasVisibleContent(true); + } + + + if (!child->isFloating() && childrenInline()) + dirtyLinesFromChangedChild(child); + } + + child->setNeedsLayoutAndPrefWidthsRecalc(); + if (!normalChildNeedsLayout()) + setChildNeedsLayout(true); // We may supply the static position for an absolute positioned child. + + if (AXObjectCache::accessibilityEnabled()) + document()->axObjectCache()->childrenChanged(this); +} + +void RenderContainer::layout() +{ + ASSERT(needsLayout()); + + LayoutStateMaintainer statePusher(view(), this, IntSize(m_x, m_y)); + + RenderObject* child = m_firstChild; + while (child) { + child->layoutIfNeeded(); + ASSERT(child->isRenderInline() || !child->needsLayout()); + child = child->nextSibling(); + } + + statePusher.pop(); + setNeedsLayout(false); +} + +void RenderContainer::removeLeftoverAnonymousBlock(RenderBlock* child) +{ + ASSERT(child->isAnonymousBlock()); + ASSERT(!child->childrenInline()); + + if (child->continuation()) + return; + + RenderObject* firstAnChild = child->firstChild(); + RenderObject* lastAnChild = child->lastChild(); + if (firstAnChild) { + RenderObject* o = firstAnChild; + while(o) { + o->setParent(this); + o = o->nextSibling(); + } + firstAnChild->setPreviousSibling(child->previousSibling()); + lastAnChild->setNextSibling(child->nextSibling()); + if (child->previousSibling()) + child->previousSibling()->setNextSibling(firstAnChild); + if (child->nextSibling()) + child->nextSibling()->setPreviousSibling(lastAnChild); + } else { + if (child->previousSibling()) + child->previousSibling()->setNextSibling(child->nextSibling()); + if (child->nextSibling()) + child->nextSibling()->setPreviousSibling(child->previousSibling()); + } + if (child == m_firstChild) + m_firstChild = firstAnChild; + if (child == m_lastChild) + m_lastChild = lastAnChild; + child->setParent(0); + child->setPreviousSibling(0); + child->setNextSibling(0); + if (!child->isText()) { + RenderContainer *c = static_cast<RenderContainer*>(child); + c->m_firstChild = 0; + c->m_next = 0; + } + child->destroy(); +} + +VisiblePosition RenderContainer::positionForCoordinates(int x, int y) +{ + // no children...return this render object's element, if there is one, and offset 0 + if (!m_firstChild) + return VisiblePosition(element(), 0, DOWNSTREAM); + + if (isTable() && element()) { + int right = contentWidth() + borderRight() + paddingRight() + borderLeft() + paddingLeft(); + int bottom = contentHeight() + borderTop() + paddingTop() + borderBottom() + paddingBottom(); + + if (x < 0 || x > right || y < 0 || y > bottom) { + if (x <= right / 2) + return VisiblePosition(Position(element(), 0)); + else + return VisiblePosition(Position(element(), maxDeepOffset(element()))); + } + } + + // Pass off to the closest child. + int minDist = INT_MAX; + RenderObject* closestRenderer = 0; + int newX = x; + int newY = y; + if (isTableRow()) { + newX += xPos(); + newY += yPos(); + } + for (RenderObject* renderer = m_firstChild; renderer; renderer = renderer->nextSibling()) { + if (!renderer->firstChild() && !renderer->isInline() && !renderer->isBlockFlow() + || renderer->style()->visibility() != VISIBLE) + continue; + + int top = borderTop() + paddingTop() + (isTableRow() ? 0 : renderer->yPos()); + int bottom = top + renderer->contentHeight(); + int left = borderLeft() + paddingLeft() + (isTableRow() ? 0 : renderer->xPos()); + int right = left + renderer->contentWidth(); + + if (x <= right && x >= left && y <= top && y >= bottom) { + if (renderer->isTableRow()) + return renderer->positionForCoordinates(x + newX - renderer->xPos(), y + newY - renderer->yPos()); + return renderer->positionForCoordinates(x - renderer->xPos(), y - renderer->yPos()); + } + + // Find the distance from (x, y) to the box. Split the space around the box into 8 pieces + // and use a different compare depending on which piece (x, y) is in. + IntPoint cmp; + if (x > right) { + if (y < top) + cmp = IntPoint(right, top); + else if (y > bottom) + cmp = IntPoint(right, bottom); + else + cmp = IntPoint(right, y); + } else if (x < left) { + if (y < top) + cmp = IntPoint(left, top); + else if (y > bottom) + cmp = IntPoint(left, bottom); + else + cmp = IntPoint(left, y); + } else { + if (y < top) + cmp = IntPoint(x, top); + else + cmp = IntPoint(x, bottom); + } + + int x1minusx2 = cmp.x() - x; + int y1minusy2 = cmp.y() - y; + + int dist = x1minusx2 * x1minusx2 + y1minusy2 * y1minusy2; + if (dist < minDist) { + closestRenderer = renderer; + minDist = dist; + } + } + + if (closestRenderer) + return closestRenderer->positionForCoordinates(newX - closestRenderer->xPos(), newY - closestRenderer->yPos()); + + return VisiblePosition(element(), 0, DOWNSTREAM); +} + +void RenderContainer::addLineBoxRects(Vector<IntRect>& rects, unsigned start, unsigned end, bool) +{ + if (!m_firstChild && (isInline() || isAnonymousBlock())) { + FloatPoint absPos = localToAbsoluteForContent(FloatPoint()); + absoluteRects(rects, absPos.x(), absPos.y()); + return; + } + + if (!m_firstChild) + return; + + unsigned offset = start; + for (RenderObject* child = childAt(start); child && offset < end; child = child->nextSibling(), ++offset) { + if (child->isText() || child->isInline() || child->isAnonymousBlock()) { + FloatPoint absPos = child->localToAbsoluteForContent(FloatPoint()); + child->absoluteRects(rects, absPos.x(), absPos.y()); + } + } +} + +void RenderContainer::collectAbsoluteLineBoxQuads(Vector<FloatQuad>& quads, unsigned start, unsigned end, bool /*useSelectionHeight*/) +{ + if (!m_firstChild && (isInline() || isAnonymousBlock())) { + absoluteQuads(quads); + return; + } + + if (!m_firstChild) + return; + + unsigned offset = start; + for (RenderObject* child = childAt(start); child && offset < end; child = child->nextSibling(), ++offset) { + if (child->isText() || child->isInline() || child->isAnonymousBlock()) + child->absoluteQuads(quads); + } +} + +#undef DEBUG_LAYOUT + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderContainer.h b/src/3rdparty/webkit/WebCore/rendering/RenderContainer.h new file mode 100644 index 0000000..68aba84 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderContainer.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2001 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * 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. + */ + +#ifndef RenderContainer_h +#define RenderContainer_h + +#include "RenderBox.h" + +namespace WebCore { + +// Base class for rendering objects that can have children. +class RenderContainer : public RenderBox { +public: + RenderContainer(Node*); + virtual ~RenderContainer(); + + virtual RenderObject* firstChild() const { return m_firstChild; } + virtual RenderObject* lastChild() const { return m_lastChild; } + + virtual bool canHaveChildren() const; + virtual void addChild(RenderObject* newChild, RenderObject* beforeChild = 0); + virtual void removeChild(RenderObject*); + + virtual void destroy(); + void destroyLeftoverChildren(); + + virtual RenderObject* removeChildNode(RenderObject*, bool fullRemove = true); + virtual void appendChildNode(RenderObject*, bool fullAppend = true); + virtual void insertChildNode(RenderObject* child, RenderObject* before, bool fullInsert = true); + + // Designed for speed. Don't waste time doing a bunch of work like layer updating and repainting when we know that our + // change in parentage is not going to affect anything. + virtual void moveChildNode(RenderObject* child) { appendChildNode(child->parent()->removeChildNode(child, false), false); } + + virtual void layout(); + virtual void calcPrefWidths() { setPrefWidthsDirty(false); } + + virtual void removeLeftoverAnonymousBlock(RenderBlock* child); + + RenderObject* beforeAfterContainer(RenderStyle::PseudoId); + virtual void updateBeforeAfterContent(RenderStyle::PseudoId); + void updateBeforeAfterContentForContainer(RenderStyle::PseudoId, RenderContainer*); + bool isAfterContent(RenderObject* child) const; + virtual void invalidateCounters(); + + virtual VisiblePosition positionForCoordinates(int x, int y); + + virtual void addLineBoxRects(Vector<IntRect>&, unsigned startOffset = 0, unsigned endOffset = UINT_MAX, bool useSelectionHeight = false); + virtual void collectAbsoluteLineBoxQuads(Vector<FloatQuad>&, unsigned startOffset = 0, unsigned endOffset = UINT_MAX, bool useSelectionHeight = false); + +private: + RenderObject* m_firstChild; + RenderObject* m_lastChild; +}; + +} // namespace WebCore + +#endif // RenderContainer_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderCounter.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderCounter.cpp new file mode 100644 index 0000000..598f40d --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderCounter.cpp @@ -0,0 +1,306 @@ +/** + * Copyright (C) 2004 Allan Sandfeld Jensen (kde@carewolf.com) + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * + * 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" +#include "RenderCounter.h" + +#include "CounterNode.h" +#include "Document.h" +#include "HTMLNames.h" +#include "HTMLOListElement.h" +#include "RenderListItem.h" +#include "RenderListMarker.h" +#include "RenderStyle.h" +#include <wtf/StdLibExtras.h> + +namespace WebCore { + +using namespace HTMLNames; + +typedef HashMap<RefPtr<AtomicStringImpl>, CounterNode*> CounterMap; +typedef HashMap<const RenderObject*, CounterMap*> CounterMaps; + +static CounterNode* counter(RenderObject*, const AtomicString& counterName, bool alwaysCreateCounter); + +static CounterMaps& counterMaps() +{ + DEFINE_STATIC_LOCAL(CounterMaps, staticCounterMaps, ()); + return staticCounterMaps; +} + +static inline RenderObject* previousSiblingOrParent(RenderObject* object) +{ + if (RenderObject* sibling = object->previousSibling()) + return sibling; + return object->parent(); +} + +static CounterNode* lastDescendant(CounterNode* node) +{ + CounterNode* last = node->lastChild(); + if (!last) + return 0; + + while (CounterNode* lastChild = last->lastChild()) + last = lastChild; + + return last; +} + +static CounterNode* previousInPreOrder(CounterNode* node) +{ + CounterNode* previous = node->previousSibling(); + if (!previous) + return node->parent(); + + while (CounterNode* lastChild = previous->lastChild()) + previous = lastChild; + + return previous; +} + +static bool planCounter(RenderObject* object, const AtomicString& counterName, bool& isReset, int& value) +{ + ASSERT(object); + + // Real text nodes don't have their own style so they can't have counters. + // We can't even look at their styles or we'll see extra resets and increments! + if (object->isText() && !object->isBR()) + return false; + + RenderStyle* style = object->style(); + ASSERT(style); + + if (const CounterDirectiveMap* directivesMap = style->counterDirectives()) { + CounterDirectives directives = directivesMap->get(counterName.impl()); + if (directives.m_reset) { + value = directives.m_resetValue; + if (directives.m_increment) + value += directives.m_incrementValue; + isReset = true; + return true; + } + if (directives.m_increment) { + value = directives.m_incrementValue; + isReset = false; + return true; + } + } + + if (counterName == "list-item") { + if (object->isListItem()) { + if (static_cast<RenderListItem*>(object)->hasExplicitValue()) { + value = static_cast<RenderListItem*>(object)->explicitValue(); + isReset = true; + return true; + } + value = 1; + isReset = false; + return true; + } + if (Node* e = object->element()) { + if (e->hasTagName(olTag)) { + value = static_cast<HTMLOListElement*>(e)->start(); + isReset = true; + return true; + } + if (e->hasTagName(ulTag) || e->hasTagName(menuTag) || e->hasTagName(dirTag)) { + value = 0; + isReset = true; + return true; + } + } + } + + return false; +} + +static bool findPlaceForCounter(RenderObject* object, const AtomicString& counterName, + bool isReset, CounterNode*& parent, CounterNode*& previousSibling) +{ + // Find the appropriate previous sibling for insertion into the parent node + // by searching in render tree order for a child of the counter. + parent = 0; + previousSibling = 0; + RenderObject* resetCandidate = isReset ? object->parent() : previousSiblingOrParent(object); + RenderObject* prevCounterCandidate = object; + CounterNode* candidateCounter = 0; + while ((prevCounterCandidate = prevCounterCandidate->previousInPreOrder())) { + CounterNode* c = counter(prevCounterCandidate, counterName, false); + if (prevCounterCandidate == resetCandidate) { + if (!candidateCounter) + candidateCounter = c; + if (candidateCounter) { + if (candidateCounter->isReset()) { + parent = candidateCounter; + previousSibling = 0; + } else { + parent = candidateCounter->parent(); + previousSibling = candidateCounter; + } + return true; + } + resetCandidate = previousSiblingOrParent(resetCandidate); + } else if (c) { + if (c->isReset()) + candidateCounter = 0; + else if (!candidateCounter) + candidateCounter = c; + } + } + + return false; +} + +static CounterNode* counter(RenderObject* object, const AtomicString& counterName, bool alwaysCreateCounter) +{ + ASSERT(object); + + if (object->m_hasCounterNodeMap) + if (CounterMap* nodeMap = counterMaps().get(object)) + if (CounterNode* node = nodeMap->get(counterName.impl())) + return node; + + bool isReset = false; + int value = 0; + if (!planCounter(object, counterName, isReset, value) && !alwaysCreateCounter) + return 0; + + CounterNode* newParent = 0; + CounterNode* newPreviousSibling = 0; + CounterNode* newNode; + if (findPlaceForCounter(object, counterName, isReset, newParent, newPreviousSibling)) { + newNode = new CounterNode(object, isReset, value); + newParent->insertAfter(newNode, newPreviousSibling); + } else { + // Make a reset node for counters that aren't inside an existing reset node. + newNode = new CounterNode(object, true, value); + } + + CounterMap* nodeMap; + if (object->m_hasCounterNodeMap) + nodeMap = counterMaps().get(object); + else { + nodeMap = new CounterMap; + counterMaps().set(object, nodeMap); + object->m_hasCounterNodeMap = true; + } + nodeMap->set(counterName.impl(), newNode); + + return newNode; +} + +RenderCounter::RenderCounter(Document* node, const CounterContent& counter) + : RenderText(node, StringImpl::empty()) + , m_counter(counter) + , m_counterNode(0) +{ +} + +const char* RenderCounter::renderName() const +{ + return "RenderCounter"; +} + +bool RenderCounter::isCounter() const +{ + return true; +} + +PassRefPtr<StringImpl> RenderCounter::originalText() const +{ + if (!parent()) + return 0; + + if (!m_counterNode) + m_counterNode = counter(parent(), m_counter.identifier(), true); + + CounterNode* child = m_counterNode; + int value = child->isReset() ? child->value() : child->countInParent(); + + String text = listMarkerText(m_counter.listStyle(), value); + + if (!m_counter.separator().isNull()) { + if (!child->isReset()) + child = child->parent(); + while (CounterNode* parent = child->parent()) { + text = listMarkerText(m_counter.listStyle(), child->countInParent()) + + m_counter.separator() + text; + child = parent; + } + } + + return text.impl(); +} + +void RenderCounter::dirtyLineBoxes(bool fullLayout, bool dummy) +{ + if (prefWidthsDirty()) + calcPrefWidths(0); + RenderText::dirtyLineBoxes(fullLayout, dummy); +} + +void RenderCounter::calcPrefWidths(int lead) +{ + setTextInternal(originalText()); + RenderText::calcPrefWidths(lead); +} + +void RenderCounter::invalidate() +{ + m_counterNode = 0; + setNeedsLayoutAndPrefWidthsRecalc(); +} + +static void destroyCounterNodeChildren(AtomicStringImpl* identifier, CounterNode* node) +{ + CounterNode* previous; + for (CounterNode* child = lastDescendant(node); child && child != node; child = previous) { + previous = previousInPreOrder(child); + child->parent()->removeChild(child); + ASSERT(counterMaps().get(child->renderer())->get(identifier) == child); + counterMaps().get(child->renderer())->remove(identifier); + child->renderer()->invalidateCounters(); + delete child; + } +} + +void RenderCounter::destroyCounterNodes(RenderObject* object) +{ + CounterMaps& maps = counterMaps(); + CounterMap* map = maps.get(object); + if (!map) + return; + maps.remove(object); + + CounterMap::const_iterator end = map->end(); + for (CounterMap::const_iterator it = map->begin(); it != end; ++it) { + CounterNode* node = it->second; + destroyCounterNodeChildren(it->first.get(), node); + if (CounterNode* parent = node->parent()) + parent->removeChild(node); + delete node; + } + + delete map; +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderCounter.h b/src/3rdparty/webkit/WebCore/rendering/RenderCounter.h new file mode 100644 index 0000000..10be066 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderCounter.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2004 Allan Sandfeld Jensen (kde@carewolf.com) + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef RenderCounter_h +#define RenderCounter_h + +#include "CounterContent.h" +#include "RenderText.h" + +namespace WebCore { + +class CounterNode; + +class RenderCounter : public RenderText { +public: + RenderCounter(Document*, const CounterContent&); + + virtual const char* renderName() const; + virtual bool isCounter() const; + virtual PassRefPtr<StringImpl> originalText() const; + + virtual void dirtyLineBoxes(bool, bool); + virtual void calcPrefWidths(int leadWidth); + + void invalidate(); + + static void destroyCounterNodes(RenderObject*); + +private: + CounterContent m_counter; + mutable CounterNode* m_counterNode; +}; + +} // namespace WebCore + +#endif // RenderCounter_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderFieldset.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderFieldset.cpp new file mode 100644 index 0000000..0e944f4 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderFieldset.cpp @@ -0,0 +1,282 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. + * + * 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" +#include "RenderFieldset.h" + +#include "HTMLNames.h" +#include "GraphicsContext.h" + +#if ENABLE(WML) +#include "WMLNames.h" +#endif + +using std::min; +using std::max; + +namespace WebCore { + +using namespace HTMLNames; + +RenderFieldset::RenderFieldset(Node* element) + : RenderBlock(element) +{ +} + +void RenderFieldset::calcPrefWidths() +{ + RenderBlock::calcPrefWidths(); + if (RenderObject* legend = findLegend()) { + int legendMinWidth = legend->minPrefWidth(); + + Length legendMarginLeft = legend->style()->marginLeft(); + Length legendMarginRight = legend->style()->marginLeft(); + + if (legendMarginLeft.isFixed()) + legendMinWidth += legendMarginLeft.value(); + + if (legendMarginRight.isFixed()) + legendMinWidth += legendMarginRight.value(); + + m_minPrefWidth = max(m_minPrefWidth, legendMinWidth + paddingLeft() + paddingRight() + borderLeft() + borderRight()); + } +} + +RenderObject* RenderFieldset::layoutLegend(bool relayoutChildren) +{ + RenderObject* legend = findLegend(); + if (legend) { + if (relayoutChildren) + legend->setNeedsLayout(true); + legend->layoutIfNeeded(); + + int xPos; + if (style()->direction() == RTL) { + switch (legend->style()->textAlign()) { + case LEFT: + xPos = borderLeft() + paddingLeft(); + break; + case CENTER: + xPos = (m_width - legend->width()) / 2; + break; + default: + xPos = m_width - paddingRight() - borderRight() - legend->width() - legend->marginRight(); + } + } else { + switch (legend->style()->textAlign()) { + case RIGHT: + xPos = m_width - paddingRight() - borderRight() - legend->width(); + break; + case CENTER: + xPos = (m_width - legend->width()) / 2; + break; + default: + xPos = borderLeft() + paddingLeft() + legend->marginLeft(); + } + } + int b = borderTop(); + int h = legend->height(); + legend->setPos(xPos, max((b-h)/2, 0)); + m_height = max(b,h) + paddingTop(); + } + return legend; +} + +RenderObject* RenderFieldset::findLegend() const +{ + for (RenderObject* legend = firstChild(); legend; legend = legend->nextSibling()) { + if (!legend->isFloatingOrPositioned() && legend->element() && + legend->element()->hasTagName(legendTag) +#if ENABLE(WML) + || legend->element()->hasTagName(WMLNames::insertedLegendTag) +#endif + ) + return legend; + } + return 0; +} + +void RenderFieldset::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty) +{ + int w = width(); + int h = height() + borderTopExtra() + borderBottomExtra(); + RenderObject* legend = findLegend(); + if (!legend) + return RenderBlock::paintBoxDecorations(paintInfo, tx, ty); + + int yOff = (legend->yPos() > 0) ? 0 : (legend->height() - borderTop()) / 2; + int legendBottom = ty + legend->yPos() + legend->height(); + h -= yOff; + ty += yOff - borderTopExtra(); + + int my = max(ty, paintInfo.rect.y()); + int end = min(paintInfo.rect.bottom(), ty + h); + int mh = end - my; + + paintBoxShadow(paintInfo.context, tx, ty, w, h, style()); + + paintFillLayers(paintInfo, style()->backgroundColor(), style()->backgroundLayers(), my, mh, tx, ty, w, h); + + if (!style()->hasBorder()) + return; + + // Save time by not saving and restoring the GraphicsContext in the straight border case + if (!style()->hasBorderRadius()) + return paintBorderMinusLegend(paintInfo.context, tx, ty, w, h, style(), legend->xPos(), legend->width(), legendBottom); + + // We have rounded borders, create a clipping region + // around the legend and paint the border as normal + GraphicsContext* graphicsContext = paintInfo.context; + graphicsContext->save(); + + int clipTop = ty; + int clipHeight = max(static_cast<int>(style()->borderTopWidth()), legend->height()); + + graphicsContext->clipOut(IntRect(tx + legend->xPos(), clipTop, + legend->width(), clipHeight)); + paintBorder(paintInfo.context, tx, ty, w, h, style(), true, true); + + graphicsContext->restore(); +} + +void RenderFieldset::paintMask(PaintInfo& paintInfo, int tx, int ty) +{ + if (style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) + return; + + int w = width(); + int h = height() + borderTopExtra() + borderBottomExtra(); + RenderObject* legend = findLegend(); + if (!legend) + return RenderBlock::paintMask(paintInfo, tx, ty); + + int yOff = (legend->yPos() > 0) ? 0 : (legend->height() - borderTop()) / 2; + h -= yOff; + ty += yOff - borderTopExtra(); + + int my = max(ty, paintInfo.rect.y()); + int end = min(paintInfo.rect.bottom(), ty + h); + int mh = end - my; + + paintMaskImages(paintInfo, my, mh, tx, ty, w, h); +} + +void RenderFieldset::paintBorderMinusLegend(GraphicsContext* graphicsContext, int tx, int ty, int w, int h, + const RenderStyle* style, int lx, int lw, int lb) +{ + const Color& tc = style->borderTopColor(); + const Color& bc = style->borderBottomColor(); + + EBorderStyle ts = style->borderTopStyle(); + EBorderStyle bs = style->borderBottomStyle(); + EBorderStyle ls = style->borderLeftStyle(); + EBorderStyle rs = style->borderRightStyle(); + + bool render_t = ts > BHIDDEN; + bool render_l = ls > BHIDDEN; + bool render_r = rs > BHIDDEN; + bool render_b = bs > BHIDDEN; + + int borderLeftWidth = style->borderLeftWidth(); + int borderRightWidth = style->borderRightWidth(); + + if (render_t) { + if (lx >= borderLeftWidth) + drawBorder(graphicsContext, tx, ty, tx + min(lx, w), ty + style->borderTopWidth(), BSTop, tc, style->color(), ts, + (render_l && (ls == DOTTED || ls == DASHED || ls == DOUBLE) ? borderLeftWidth : 0), + (lx >= w && render_r && (rs == DOTTED || rs == DASHED || rs == DOUBLE) ? borderRightWidth : 0)); + if (lx + lw <= w - borderRightWidth) + drawBorder(graphicsContext, tx + max(0, lx + lw), ty, tx + w, ty + style->borderTopWidth(), BSTop, tc, style->color(), ts, + (lx + lw <= 0 && render_l && (ls == DOTTED || ls == DASHED || ls == DOUBLE) ? borderLeftWidth : 0), + (render_r && (rs == DOTTED || rs == DASHED || rs == DOUBLE) ? borderRightWidth : 0)); + } + + if (render_b) + drawBorder(graphicsContext, tx, ty + h - style->borderBottomWidth(), tx + w, ty + h, BSBottom, bc, style->color(), bs, + (render_l && (ls == DOTTED || ls == DASHED || ls == DOUBLE) ? style->borderLeftWidth() : 0), + (render_r && (rs == DOTTED || rs == DASHED || rs == DOUBLE) ? style->borderRightWidth() : 0)); + + if (render_l) { + const Color& lc = style->borderLeftColor(); + int startY = ty; + + bool ignore_top = + (tc == lc) && + (ls >= OUTSET) && + (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET); + + bool ignore_bottom = + (bc == lc) && + (ls >= OUTSET) && + (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET); + + if (lx < borderLeftWidth && lx + lw > 0) { + // The legend intersects the border. + ignore_top = true; + startY = lb; + } + + drawBorder(graphicsContext, tx, startY, tx + borderLeftWidth, ty + h, BSLeft, lc, style->color(), ls, + ignore_top ? 0 : style->borderTopWidth(), ignore_bottom ? 0 : style->borderBottomWidth()); + } + + if (render_r) { + const Color& rc = style->borderRightColor(); + int startY = ty; + + bool ignore_top = + (tc == rc) && + (rs >= DOTTED || rs == INSET) && + (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET); + + bool ignore_bottom = + (bc == rc) && + (rs >= DOTTED || rs == INSET) && + (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET); + + if (lx < w && lx + lw > w - borderRightWidth) { + // The legend intersects the border. + ignore_top = true; + startY = lb; + } + + drawBorder(graphicsContext, tx + w - borderRightWidth, startY, tx + w, ty + h, BSRight, rc, style->color(), rs, + ignore_top ? 0 : style->borderTopWidth(), ignore_bottom ? 0 : style->borderBottomWidth()); + } +} + +void RenderFieldset::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +{ + RenderBlock::styleDidChange(diff, oldStyle); + + // WinIE renders fieldsets with display:inline like they're inline-blocks. For us, + // an inline-block is just a block element with replaced set to true and inline set + // to true. Ensure that if we ended up being inline that we set our replaced flag + // so that we're treated like an inline-block. + if (isInline()) + setReplaced(true); +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderFieldset.h b/src/3rdparty/webkit/WebCore/rendering/RenderFieldset.h new file mode 100644 index 0000000..47d1a91 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderFieldset.h @@ -0,0 +1,60 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef RenderFieldset_h +#define RenderFieldset_h + +#include "RenderBlock.h" + +namespace WebCore { + +class RenderFieldset : public RenderBlock { +public: + RenderFieldset(Node*); + + virtual const char* renderName() const { return "RenderFieldSet"; } + virtual bool isFieldset() const { return true; } + + virtual RenderObject* layoutLegend(bool relayoutChildren); + + virtual void calcPrefWidths(); + virtual bool avoidsFloats() const { return true; } + virtual bool expandsToEncloseOverhangingFloats() const { return style()->height().isAuto(); } + virtual bool stretchesToMinIntrinsicWidth() const { return true; } + + RenderObject* findLegend() const; + +protected: + virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + +private: + virtual void paintBoxDecorations(PaintInfo&, int tx, int ty); + virtual void paintMask(PaintInfo& paintInfo, int tx, int ty); + void paintBorderMinusLegend(GraphicsContext*, int tx, int ty, int w, int h, const RenderStyle*, int lx, int lw, int lb); +}; + +} // namespace WebCore + +#endif // RenderFieldset_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderFileUploadControl.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderFileUploadControl.cpp new file mode 100644 index 0000000..6a4bb54 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderFileUploadControl.cpp @@ -0,0 +1,298 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * + * 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" +#include "RenderFileUploadControl.h" + +#include "FileList.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include "Icon.h" +#include "LocalizedStrings.h" +#include "Page.h" +#include "RenderButton.h" +#include "RenderText.h" +#include "RenderTheme.h" +#include "RenderView.h" +#include <math.h> + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +const int afterButtonSpacing = 4; +const int iconHeight = 16; +const int iconWidth = 16; +const int iconFilenameSpacing = 2; +const int defaultWidthNumChars = 34; +const int buttonShadowHeight = 2; + +class HTMLFileUploadInnerButtonElement : public HTMLInputElement { +public: + HTMLFileUploadInnerButtonElement(Document*, Node* shadowParent); + + virtual bool isShadowNode() const { return true; } + virtual Node* shadowParentNode() { return m_shadowParent; } + +private: + Node* m_shadowParent; +}; + +RenderFileUploadControl::RenderFileUploadControl(HTMLInputElement* input) + : RenderBlock(input) + , m_button(0) + , m_fileChooser(FileChooser::create(this, input->value())) +{ +} + +RenderFileUploadControl::~RenderFileUploadControl() +{ + if (m_button) + m_button->detach(); + m_fileChooser->disconnectClient(); +} + +void RenderFileUploadControl::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +{ + RenderBlock::styleDidChange(diff, oldStyle); + if (m_button) + m_button->renderer()->setStyle(createButtonStyle(style())); + + setReplaced(isInline()); +} + +void RenderFileUploadControl::valueChanged() +{ + // onChange may destroy this renderer + RefPtr<FileChooser> fileChooser = m_fileChooser; + + HTMLInputElement* inputElement = static_cast<HTMLInputElement*>(node()); + inputElement->setFileListFromRenderer(fileChooser->filenames()); + inputElement->onChange(); + + // only repaint if it doesn't seem we have been destroyed + if (!fileChooser->disconnected()) + repaint(); +} + +bool RenderFileUploadControl::allowsMultipleFiles() +{ + HTMLInputElement* input = static_cast<HTMLInputElement*>(node()); + return !input->getAttribute(multipleAttr).isNull(); +} + +void RenderFileUploadControl::click() +{ + Frame* frame = node()->document()->frame(); + if (!frame) + return; + Page* page = frame->page(); + if (!page) + return; + page->chrome()->runOpenPanel(frame, m_fileChooser); +} + +void RenderFileUploadControl::updateFromElement() +{ + HTMLInputElement* inputElement = static_cast<HTMLInputElement*>(node()); + ASSERT(inputElement->inputType() == HTMLInputElement::FILE); + + if (!m_button) { + m_button = new HTMLFileUploadInnerButtonElement(document(), inputElement); + m_button->setInputType("button"); + m_button->setValue(fileButtonChooseFileLabel()); + RefPtr<RenderStyle> buttonStyle = createButtonStyle(style()); + RenderObject* renderer = m_button->createRenderer(renderArena(), buttonStyle.get()); + m_button->setRenderer(renderer); + renderer->setStyle(buttonStyle.release()); + renderer->updateFromElement(); + m_button->setAttached(); + m_button->setInDocument(true); + + addChild(renderer); + } + + m_button->setDisabled(!theme()->isEnabled(this)); + + // This only supports clearing out the files, but that's OK because for + // security reasons that's the only change the DOM is allowed to make. + FileList* files = inputElement->files(); + ASSERT(files); + if (files && files->isEmpty() && !m_fileChooser->filenames().isEmpty()) { + m_fileChooser->clear(); + repaint(); + } +} + +int RenderFileUploadControl::maxFilenameWidth() const +{ + return max(0, contentWidth() - m_button->renderer()->width() - afterButtonSpacing + - (m_fileChooser->icon() ? iconWidth + iconFilenameSpacing : 0)); +} + +PassRefPtr<RenderStyle> RenderFileUploadControl::createButtonStyle(const RenderStyle* parentStyle) const +{ + RefPtr<RenderStyle> style = getCachedPseudoStyle(RenderStyle::FILE_UPLOAD_BUTTON); + if (!style) { + style = RenderStyle::create(); + if (parentStyle) + style->inheritFrom(parentStyle); + } + + // Button text will wrap on file upload controls with widths smaller than the intrinsic button width + // without this setWhiteSpace. + style->setWhiteSpace(NOWRAP); + + return style.release(); +} + +void RenderFileUploadControl::paintObject(PaintInfo& paintInfo, int tx, int ty) +{ + if (style()->visibility() != VISIBLE) + return; + + // Push a clip. + if (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseChildBlockBackgrounds) { + IntRect clipRect(tx + borderLeft(), ty + borderTop(), + width() - borderLeft() - borderRight(), height() - borderBottom() - borderTop() + buttonShadowHeight); + if (clipRect.isEmpty()) + return; + paintInfo.context->save(); + paintInfo.context->clip(clipRect); + } + + if (paintInfo.phase == PaintPhaseForeground) { + const String& displayedFilename = m_fileChooser->basenameForWidth(style()->font(), maxFilenameWidth()); + unsigned length = displayedFilename.length(); + const UChar* string = displayedFilename.characters(); + TextRun textRun(string, length, false, 0, 0, style()->direction() == RTL, style()->unicodeBidi() == Override); + + // Determine where the filename should be placed + int contentLeft = tx + borderLeft() + paddingLeft(); + int buttonAndIconWidth = m_button->renderer()->width() + afterButtonSpacing + + (m_fileChooser->icon() ? iconWidth + iconFilenameSpacing : 0); + int textX; + if (style()->direction() == LTR) + textX = contentLeft + buttonAndIconWidth; + else + textX = contentLeft + contentWidth() - buttonAndIconWidth - style()->font().width(textRun); + // We want to match the button's baseline + RenderButton* buttonRenderer = static_cast<RenderButton*>(m_button->renderer()); + int textY = buttonRenderer->absoluteBoundingBoxRect().y() + + buttonRenderer->marginTop() + buttonRenderer->borderTop() + buttonRenderer->paddingTop() + + buttonRenderer->baselinePosition(true, false); + + paintInfo.context->setFont(style()->font()); + paintInfo.context->setFillColor(style()->color()); + + // Draw the filename + paintInfo.context->drawBidiText(textRun, IntPoint(textX, textY)); + + if (m_fileChooser->icon()) { + // Determine where the icon should be placed + int iconY = ty + borderTop() + paddingTop() + (contentHeight() - iconHeight) / 2; + int iconX; + if (style()->direction() == LTR) + iconX = contentLeft + m_button->renderer()->width() + afterButtonSpacing; + else + iconX = contentLeft + contentWidth() - m_button->renderer()->width() - afterButtonSpacing - iconWidth; + + // Draw the file icon + m_fileChooser->icon()->paint(paintInfo.context, IntRect(iconX, iconY, iconWidth, iconHeight)); + } + } + + // Paint the children. + RenderBlock::paintObject(paintInfo, tx, ty); + + // Pop the clip. + if (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseChildBlockBackgrounds) + paintInfo.context->restore(); +} + +void RenderFileUploadControl::calcPrefWidths() +{ + ASSERT(prefWidthsDirty()); + + m_minPrefWidth = 0; + m_maxPrefWidth = 0; + + if (style()->width().isFixed() && style()->width().value() > 0) + m_minPrefWidth = m_maxPrefWidth = calcContentBoxWidth(style()->width().value()); + else { + // Figure out how big the filename space needs to be for a given number of characters + // (using "0" as the nominal character). + const UChar ch = '0'; + float charWidth = style()->font().floatWidth(TextRun(&ch, 1, false, 0, 0, false, false, false)); + m_maxPrefWidth = (int)ceilf(charWidth * defaultWidthNumChars); + } + + if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) { + m_maxPrefWidth = max(m_maxPrefWidth, calcContentBoxWidth(style()->minWidth().value())); + m_minPrefWidth = max(m_minPrefWidth, calcContentBoxWidth(style()->minWidth().value())); + } else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent())) + m_minPrefWidth = 0; + else + m_minPrefWidth = m_maxPrefWidth; + + if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) { + m_maxPrefWidth = min(m_maxPrefWidth, calcContentBoxWidth(style()->maxWidth().value())); + m_minPrefWidth = min(m_minPrefWidth, calcContentBoxWidth(style()->maxWidth().value())); + } + + int toAdd = paddingLeft() + paddingRight() + borderLeft() + borderRight(); + m_minPrefWidth += toAdd; + m_maxPrefWidth += toAdd; + + setPrefWidthsDirty(false); +} + +void RenderFileUploadControl::receiveDroppedFiles(const Vector<String>& paths) +{ + if (allowsMultipleFiles()) + m_fileChooser->chooseFiles(paths); + else + m_fileChooser->chooseFile(paths[0]); +} + +String RenderFileUploadControl::buttonValue() +{ + if (!m_button) + return String(); + + return m_button->value(); +} + +String RenderFileUploadControl::fileTextValue() +{ + return m_fileChooser->basenameForWidth(style()->font(), maxFilenameWidth()); +} + +HTMLFileUploadInnerButtonElement::HTMLFileUploadInnerButtonElement(Document* doc, Node* shadowParent) + : HTMLInputElement(inputTag, doc) + , m_shadowParent(shadowParent) +{ +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderFileUploadControl.h b/src/3rdparty/webkit/WebCore/rendering/RenderFileUploadControl.h new file mode 100644 index 0000000..60e7a7b --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderFileUploadControl.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. + * + * 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. + * + */ + +#ifndef RenderFileUploadControl_h +#define RenderFileUploadControl_h + +#include "FileChooser.h" +#include "RenderBlock.h" + +namespace WebCore { + +class HTMLInputElement; + +// Each RenderFileUploadControl contains a RenderButton (for opening the file chooser), and +// sufficient space to draw a file icon and filename. The RenderButton has a shadow node +// associated with it to receive click/hover events. + +class RenderFileUploadControl : public RenderBlock, private FileChooserClient { +public: + RenderFileUploadControl(HTMLInputElement*); + ~RenderFileUploadControl(); + + virtual const char* renderName() const { return "RenderFileUploadControl"; } + + virtual void updateFromElement(); + virtual void calcPrefWidths(); + virtual void paintObject(PaintInfo&, int tx, int ty); + + void click(); + + void valueChanged(); + + void receiveDroppedFiles(const Vector<String>&); + + String buttonValue(); + String fileTextValue(); + + bool allowsMultipleFiles(); + +protected: + virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + +private: + int maxFilenameWidth() const; + PassRefPtr<RenderStyle> createButtonStyle(const RenderStyle* parentStyle) const; + + RefPtr<HTMLInputElement> m_button; + RefPtr<FileChooser> m_fileChooser; +}; + +} // namespace WebCore + +#endif // RenderFileUploadControl_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderFlexibleBox.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderFlexibleBox.cpp new file mode 100644 index 0000000..08bc567 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderFlexibleBox.cpp @@ -0,0 +1,1157 @@ +/* + * This file is part of the render object implementation for KHTML. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003 Apple Computer, Inc. + * + * 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" +#include "RenderFlexibleBox.h" + +#include "CharacterNames.h" +#include "RenderLayer.h" +#include "RenderView.h" +#include <wtf/StdLibExtras.h> + +using namespace std; + +namespace WebCore { + +class FlexBoxIterator { +public: + FlexBoxIterator(RenderFlexibleBox* parent) { + box = parent; + if (box->style()->boxOrient() == HORIZONTAL && box->style()->direction() == RTL) + forward = box->style()->boxDirection() != BNORMAL; + else + forward = box->style()->boxDirection() == BNORMAL; + lastOrdinal = 1; + if (!forward) { + // No choice, since we're going backwards, we have to find out the highest ordinal up front. + RenderObject* child = box->firstChild(); + while (child) { + if (child->style()->boxOrdinalGroup() > lastOrdinal) + lastOrdinal = child->style()->boxOrdinalGroup(); + child = child->nextSibling(); + } + } + + reset(); + } + + void reset() { + current = 0; + currentOrdinal = forward ? 0 : lastOrdinal+1; + } + + RenderObject* first() { + reset(); + return next(); + } + + RenderObject* next() { + + do { + if (!current) { + if (forward) { + currentOrdinal++; + if (currentOrdinal > lastOrdinal) + return 0; + current = box->firstChild(); + } else { + currentOrdinal--; + if (currentOrdinal == 0) + return 0; + current = box->lastChild(); + } + } + else + current = forward ? current->nextSibling() : current->previousSibling(); + if (current && current->style()->boxOrdinalGroup() > lastOrdinal) + lastOrdinal = current->style()->boxOrdinalGroup(); + } while (!current || current->style()->boxOrdinalGroup() != currentOrdinal || + current->style()->visibility() == COLLAPSE); + return current; + } + +private: + RenderFlexibleBox* box; + RenderObject* current; + bool forward; + unsigned int currentOrdinal; + unsigned int lastOrdinal; +}; + +RenderFlexibleBox::RenderFlexibleBox(Node* node) +:RenderBlock(node) +{ + setChildrenInline(false); // All of our children must be block-level + m_flexingChildren = m_stretchingChildren = false; +} + +RenderFlexibleBox::~RenderFlexibleBox() +{ +} + +void RenderFlexibleBox::calcHorizontalPrefWidths() +{ + RenderObject *child = firstChild(); + while (child) { + // positioned children don't affect the minmaxwidth + if (child->isPositioned() || child->style()->visibility() == COLLAPSE) { + child = child->nextSibling(); + continue; + } + + // A margin basically has three types: fixed, percentage, and auto (variable). + // Auto and percentage margins simply become 0 when computing min/max width. + // Fixed margins can be added in as is. + Length ml = child->style()->marginLeft(); + Length mr = child->style()->marginRight(); + int margin = 0, marginLeft = 0, marginRight = 0; + if (ml.isFixed()) + marginLeft += ml.value(); + if (mr.isFixed()) + marginRight += mr.value(); + margin = marginLeft + marginRight; + + m_minPrefWidth += child->minPrefWidth() + margin; + m_maxPrefWidth += child->maxPrefWidth() + margin; + + child = child->nextSibling(); + } +} + +void RenderFlexibleBox::calcVerticalPrefWidths() +{ + RenderObject *child = firstChild(); + while(child != 0) + { + // Positioned children and collapsed children don't affect the min/max width + if (child->isPositioned() || child->style()->visibility() == COLLAPSE) { + child = child->nextSibling(); + continue; + } + + // A margin basically has three types: fixed, percentage, and auto (variable). + // Auto/percentage margins simply become 0 when computing min/max width. + // Fixed margins can be added in as is. + Length ml = child->style()->marginLeft(); + Length mr = child->style()->marginRight(); + int margin = 0; + if (ml.isFixed()) + margin += ml.value(); + if (mr.isFixed()) + margin += mr.value(); + + int w = child->minPrefWidth() + margin; + m_minPrefWidth = max(w, m_minPrefWidth); + + w = child->maxPrefWidth() + margin; + m_maxPrefWidth = max(w, m_maxPrefWidth); + + child = child->nextSibling(); + } +} + +void RenderFlexibleBox::calcPrefWidths() +{ + ASSERT(prefWidthsDirty()); + + if (style()->width().isFixed() && style()->width().value() > 0) + m_minPrefWidth = m_maxPrefWidth = calcContentBoxWidth(style()->width().value()); + else { + m_minPrefWidth = m_maxPrefWidth = 0; + + if (hasMultipleLines() || isVertical()) + calcVerticalPrefWidths(); + else + calcHorizontalPrefWidths(); + + m_maxPrefWidth = max(m_minPrefWidth, m_maxPrefWidth); + } + + if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) { + m_maxPrefWidth = max(m_maxPrefWidth, calcContentBoxWidth(style()->minWidth().value())); + m_minPrefWidth = max(m_minPrefWidth, calcContentBoxWidth(style()->minWidth().value())); + } + + if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) { + m_maxPrefWidth = min(m_maxPrefWidth, calcContentBoxWidth(style()->maxWidth().value())); + m_minPrefWidth = min(m_minPrefWidth, calcContentBoxWidth(style()->maxWidth().value())); + } + + int toAdd = borderLeft() + borderRight() + paddingLeft() + paddingRight(); + m_minPrefWidth += toAdd; + m_maxPrefWidth += toAdd; + + setPrefWidthsDirty(false); +} + +void RenderFlexibleBox::layoutBlock(bool relayoutChildren) +{ + ASSERT(needsLayout()); + + if (!relayoutChildren && layoutOnlyPositionedObjects()) + return; + + IntRect oldBounds; + IntRect oldOutlineBox; + bool checkForRepaint = checkForRepaintDuringLayout(); + if (checkForRepaint) { + oldBounds = absoluteClippedOverflowRect(); + oldOutlineBox = absoluteOutlineBounds(); + } + + LayoutStateMaintainer statePusher(view(), this, IntSize(m_x, m_y), !hasReflection()); + + int previousWidth = m_width; + int previousHeight = m_height; + + calcWidth(); + calcHeight(); + m_overflowWidth = m_width; + + if (previousWidth != m_width || previousHeight != m_height || + (parent()->isFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL && + parent()->style()->boxAlign() == BSTRETCH)) + relayoutChildren = true; + + m_height = 0; + m_overflowHeight = 0; + m_flexingChildren = m_stretchingChildren = false; + + initMaxMarginValues(); + + // For overflow:scroll blocks, ensure we have both scrollbars in place always. + if (scrollsOverflow()) { + if (style()->overflowX() == OSCROLL) + m_layer->setHasHorizontalScrollbar(true); + if (style()->overflowY() == OSCROLL) + m_layer->setHasVerticalScrollbar(true); + } + + if (isHorizontal()) + layoutHorizontalBox(relayoutChildren); + else + layoutVerticalBox(relayoutChildren); + + int oldHeight = m_height; + calcHeight(); + if (oldHeight != m_height) { + // If the block got expanded in size, then increase our overflowheight to match. + if (m_overflowHeight > m_height) + m_overflowHeight -= (borderBottom() + paddingBottom() + horizontalScrollbarHeight()); + if (m_overflowHeight < m_height) + m_overflowHeight = m_height; + } + if (previousHeight != m_height) + relayoutChildren = true; + + layoutPositionedObjects(relayoutChildren || isRoot()); + + if (!isFloatingOrPositioned() && m_height == 0) { + // We are a block with no border and padding and a computed height + // of 0. The CSS spec states that zero-height blocks collapse their margins + // together. + // When blocks are self-collapsing, we just use the top margin values and set the + // bottom margin max values to 0. This way we don't factor in the values + // twice when we collapse with our previous vertically adjacent and + // following vertically adjacent blocks. + int pos = maxTopPosMargin(); + int neg = maxTopNegMargin(); + if (maxBottomPosMargin() > pos) + pos = maxBottomPosMargin(); + if (maxBottomNegMargin() > neg) + neg = maxBottomNegMargin(); + setMaxTopMargins(pos, neg); + setMaxBottomMargins(0, 0); + } + + // Always ensure our overflow width is at least as large as our width. + if (m_overflowWidth < m_width) + m_overflowWidth = m_width; + + if (!hasOverflowClip()) { + for (ShadowData* boxShadow = style()->boxShadow(); boxShadow; boxShadow = boxShadow->next) { + m_overflowLeft = min(m_overflowLeft, boxShadow->x - boxShadow->blur); + m_overflowWidth = max(m_overflowWidth, m_width + boxShadow->x + boxShadow->blur); + m_overflowTop = min(m_overflowTop, boxShadow->y - boxShadow->blur); + m_overflowHeight = max(m_overflowHeight, m_height + boxShadow->y + boxShadow->blur); + } + + if (hasReflection()) { + IntRect reflection(reflectionBox()); + m_overflowTop = min(m_overflowTop, reflection.y()); + m_overflowHeight = max(m_overflowHeight, reflection.bottom()); + m_overflowLeft = min(m_overflowLeft, reflection.x()); + m_overflowHeight = max(m_overflowWidth, reflection.right()); + } + } + + statePusher.pop(); + + // Update our scrollbars if we're overflow:auto/scroll/hidden now that we know if + // we overflow or not. + if (hasOverflowClip()) + m_layer->updateScrollInfoAfterLayout(); + + // Repaint with our new bounds if they are different from our old bounds. + if (checkForRepaint) + repaintAfterLayoutIfNeeded(oldBounds, oldOutlineBox); + + setNeedsLayout(false); +} + +void RenderFlexibleBox::layoutHorizontalBox(bool relayoutChildren) +{ + int toAdd = borderBottom() + paddingBottom() + horizontalScrollbarHeight(); + int yPos = borderTop() + paddingTop(); + int xPos = borderLeft() + paddingLeft(); + bool heightSpecified = false; + int oldHeight = 0; + + unsigned int highestFlexGroup = 0; + unsigned int lowestFlexGroup = 0; + bool haveFlex = false; + int remainingSpace = 0; + m_overflowHeight = m_height; + + // The first walk over our kids is to find out if we have any flexible children. + FlexBoxIterator iterator(this); + RenderObject* child = iterator.next(); + while (child) { + // Check to see if this child flexes. + if (!child->isPositioned() && child->style()->boxFlex() > 0.0f) { + // We always have to lay out flexible objects again, since the flex distribution + // may have changed, and we need to reallocate space. + child->setOverrideSize(-1); + if (!relayoutChildren) + child->setChildNeedsLayout(true, false); + haveFlex = true; + unsigned int flexGroup = child->style()->boxFlexGroup(); + if (lowestFlexGroup == 0) + lowestFlexGroup = flexGroup; + if (flexGroup < lowestFlexGroup) + lowestFlexGroup = flexGroup; + if (flexGroup > highestFlexGroup) + highestFlexGroup = flexGroup; + } + child = iterator.next(); + } + + // We do 2 passes. The first pass is simply to lay everyone out at + // their preferred widths. The second pass handles flexing the children. + do { + // Reset our height. + m_height = yPos; + m_overflowHeight = m_height; + xPos = borderLeft() + paddingLeft(); + + // Our first pass is done without flexing. We simply lay the children + // out within the box. We have to do a layout first in order to determine + // our box's intrinsic height. + int maxAscent = 0, maxDescent = 0; + child = iterator.first(); + while (child) { + // make sure we relayout children if we need it. + if (relayoutChildren || (child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent()))) + child->setChildNeedsLayout(true, false); + + if (child->isPositioned()) { + child = iterator.next(); + continue; + } + + // Compute the child's vertical margins. + child->calcVerticalMargins(); + + // Now do the layout. + child->layoutIfNeeded(); + + // Update our height and overflow height. + if (style()->boxAlign() == BBASELINE) { + int ascent = child->marginTop() + child->getBaselineOfFirstLineBox(); + if (ascent == -1) + ascent = child->marginTop() + child->height() + child->marginBottom(); + int descent = (child->marginTop() + child->height() + child->marginBottom()) - ascent; + + // Update our maximum ascent. + maxAscent = max(maxAscent, ascent); + + // Update our maximum descent. + maxDescent = max(maxDescent, descent); + + // Now update our height. + m_height = max(yPos + maxAscent + maxDescent, m_height); + } + else + m_height = max(m_height, yPos + child->marginTop() + child->height() + child->marginBottom()); + + child = iterator.next(); + } + + if (!iterator.first() && hasLineIfEmpty()) + m_height += lineHeight(true, true); + + m_height += toAdd; + + // Always make sure our overflowheight is at least our height. + if (m_overflowHeight < m_height) + m_overflowHeight = m_height; + + oldHeight = m_height; + calcHeight(); + + relayoutChildren = false; + if (oldHeight != m_height) + heightSpecified = true; + + // Now that our height is actually known, we can place our boxes. + m_stretchingChildren = (style()->boxAlign() == BSTRETCH); + child = iterator.first(); + while (child) { + if (child->isPositioned()) { + child->containingBlock()->insertPositionedObject(child); + if (child->hasStaticX()) { + if (style()->direction() == LTR) + child->setStaticX(xPos); + else child->setStaticX(width() - xPos); + } + if (child->hasStaticY()) + child->setStaticY(yPos); + child = iterator.next(); + continue; + } + + // We need to see if this child's height has changed, since we make block elements + // fill the height of a containing box by default. + // Now do a layout. + int oldChildHeight = child->height(); + static_cast<RenderBox*>(child)->calcHeight(); + if (oldChildHeight != child->height()) + child->setChildNeedsLayout(true, false); + child->layoutIfNeeded(); + + // We can place the child now, using our value of box-align. + xPos += child->marginLeft(); + int childY = yPos; + switch (style()->boxAlign()) { + case BCENTER: + childY += child->marginTop() + max(0, (contentHeight() - (child->height() + child->marginTop() + child->marginBottom()))/2); + break; + case BBASELINE: { + int ascent = child->marginTop() + child->getBaselineOfFirstLineBox(); + if (ascent == -1) + ascent = child->marginTop() + child->height() + child->marginBottom(); + childY += child->marginTop() + (maxAscent - ascent); + break; + } + case BEND: + childY += contentHeight() - child->marginBottom() - child->height(); + break; + default: // BSTART + childY += child->marginTop(); + break; + } + + placeChild(child, xPos, childY); + + if (child->isRenderBlock()) + static_cast<RenderBlock*>(child)->addVisualOverflow(static_cast<RenderBlock*>(child)->floatRect()); + + m_overflowHeight = max(m_overflowHeight, childY + child->overflowHeight(false)); + m_overflowTop = min(m_overflowTop, child->yPos() + child->overflowTop(false)); + + xPos += child->width() + child->marginRight(); + + child = iterator.next(); + } + + remainingSpace = borderLeft() + paddingLeft() + contentWidth() - xPos; + + m_stretchingChildren = false; + if (m_flexingChildren) + haveFlex = false; // We're done. + else if (haveFlex) { + // We have some flexible objects. See if we need to grow/shrink them at all. + if (!remainingSpace) + break; + + // Allocate the remaining space among the flexible objects. If we are trying to + // grow, then we go from the lowest flex group to the highest flex group. For shrinking, + // we go from the highest flex group to the lowest group. + bool expanding = remainingSpace > 0; + unsigned int start = expanding ? lowestFlexGroup : highestFlexGroup; + unsigned int end = expanding? highestFlexGroup : lowestFlexGroup; + for (unsigned int i = start; i <= end && remainingSpace; i++) { + // Always start off by assuming the group can get all the remaining space. + int groupRemainingSpace = remainingSpace; + do { + // Flexing consists of multiple passes, since we have to change ratios every time an object hits its max/min-width + // For a given pass, we always start off by computing the totalFlex of all objects that can grow/shrink at all, and + // computing the allowed growth before an object hits its min/max width (and thus + // forces a totalFlex recomputation). + int groupRemainingSpaceAtBeginning = groupRemainingSpace; + float totalFlex = 0.0f; + child = iterator.first(); + while (child) { + if (allowedChildFlex(child, expanding, i)) + totalFlex += child->style()->boxFlex(); + child = iterator.next(); + } + child = iterator.first(); + int spaceAvailableThisPass = groupRemainingSpace; + while (child) { + int allowedFlex = allowedChildFlex(child, expanding, i); + if (allowedFlex) { + int projectedFlex = (allowedFlex == INT_MAX) ? allowedFlex : (int)(allowedFlex * (totalFlex / child->style()->boxFlex())); + spaceAvailableThisPass = expanding ? min(spaceAvailableThisPass, projectedFlex) : max(spaceAvailableThisPass, projectedFlex); + } + child = iterator.next(); + } + + // The flex groups may not have any flexible objects this time around. + if (!spaceAvailableThisPass || totalFlex == 0.0f) { + // If we just couldn't grow/shrink any more, then it's time to transition to the next flex group. + groupRemainingSpace = 0; + continue; + } + + // Now distribute the space to objects. + child = iterator.first(); + while (child && spaceAvailableThisPass && totalFlex) { + if (allowedChildFlex(child, expanding, i)) { + int spaceAdd = (int)(spaceAvailableThisPass * (child->style()->boxFlex()/totalFlex)); + if (spaceAdd) { + child->setOverrideSize(child->overrideWidth() + spaceAdd); + m_flexingChildren = true; + relayoutChildren = true; + } + + spaceAvailableThisPass -= spaceAdd; + remainingSpace -= spaceAdd; + groupRemainingSpace -= spaceAdd; + + totalFlex -= child->style()->boxFlex(); + } + child = iterator.next(); + } + if (groupRemainingSpace == groupRemainingSpaceAtBeginning) { + // this is not advancing, avoid getting stuck by distributing the remaining pixels + child = iterator.first(); + int spaceAdd = groupRemainingSpace > 0 ? 1 : -1; + while (child && groupRemainingSpace) { + if (allowedChildFlex(child, expanding, i)) { + child->setOverrideSize(child->overrideWidth() + spaceAdd); + m_flexingChildren = true; + relayoutChildren = true; + remainingSpace -= spaceAdd; + groupRemainingSpace -= spaceAdd; + } + child = iterator.next(); + } + } + } while (groupRemainingSpace); + } + + // We didn't find any children that could grow. + if (haveFlex && !m_flexingChildren) + haveFlex = false; + } + } while (haveFlex); + + m_flexingChildren = false; + + if (remainingSpace > 0 && ((style()->direction() == LTR && style()->boxPack() != BSTART) || + (style()->direction() == RTL && style()->boxPack() != BEND))) { + // Children must be repositioned. + int offset = 0; + if (style()->boxPack() == BJUSTIFY) { + // Determine the total number of children. + int totalChildren = 0; + child = iterator.first(); + while (child) { + if (child->isPositioned()) { + child = iterator.next(); + continue; + } + totalChildren++; + child = iterator.next(); + } + + // Iterate over the children and space them out according to the + // justification level. + if (totalChildren > 1) { + totalChildren--; + bool firstChild = true; + child = iterator.first(); + while (child) { + if (child->isPositioned()) { + child = iterator.next(); + continue; + } + + if (firstChild) { + firstChild = false; + child = iterator.next(); + continue; + } + + offset += remainingSpace/totalChildren; + remainingSpace -= (remainingSpace/totalChildren); + totalChildren--; + + placeChild(child, child->xPos()+offset, child->yPos()); + child = iterator.next(); + } + } + } else { + if (style()->boxPack() == BCENTER) + offset += remainingSpace/2; + else // END for LTR, START for RTL + offset += remainingSpace; + child = iterator.first(); + while (child) { + if (child->isPositioned()) { + child = iterator.next(); + continue; + } + placeChild(child, child->xPos()+offset, child->yPos()); + child = iterator.next(); + } + } + } + + child = iterator.first(); + while (child && child->isPositioned()) { + child = iterator.next(); + } + + if (child) { + m_overflowLeft = min(child->xPos() + child->overflowLeft(false), m_overflowLeft); + + RenderObject* lastChild = child; + while ((child = iterator.next())) { + if (!child->isPositioned()) + lastChild = child; + } + m_overflowWidth = max(lastChild->xPos() + lastChild->overflowWidth(false), m_overflowWidth); + } + + // So that the calcHeight in layoutBlock() knows to relayout positioned objects because of + // a height change, we revert our height back to the intrinsic height before returning. + if (heightSpecified) + m_height = oldHeight; +} + +void RenderFlexibleBox::layoutVerticalBox(bool relayoutChildren) +{ + int xPos = borderLeft() + paddingLeft(); + int yPos = borderTop() + paddingTop(); + if( style()->direction() == RTL ) + xPos = m_width - paddingRight() - borderRight(); + int toAdd = borderBottom() + paddingBottom() + horizontalScrollbarHeight(); + bool heightSpecified = false; + int oldHeight = 0; + + unsigned int highestFlexGroup = 0; + unsigned int lowestFlexGroup = 0; + bool haveFlex = false; + int remainingSpace = 0; + + // The first walk over our kids is to find out if we have any flexible children. + FlexBoxIterator iterator(this); + RenderObject *child = iterator.next(); + while (child) { + // Check to see if this child flexes. + if (!child->isPositioned() && child->style()->boxFlex() > 0.0f) { + // We always have to lay out flexible objects again, since the flex distribution + // may have changed, and we need to reallocate space. + child->setOverrideSize(-1); + if (!relayoutChildren) + child->setChildNeedsLayout(true, false); + haveFlex = true; + unsigned int flexGroup = child->style()->boxFlexGroup(); + if (lowestFlexGroup == 0) + lowestFlexGroup = flexGroup; + if (flexGroup < lowestFlexGroup) + lowestFlexGroup = flexGroup; + if (flexGroup > highestFlexGroup) + highestFlexGroup = flexGroup; + } + child = iterator.next(); + } + + // We confine the line clamp ugliness to vertical flexible boxes (thus keeping it out of + // mainstream block layout); this is not really part of the XUL box model. + bool haveLineClamp = style()->lineClamp() >= 0 && style()->lineClamp() <= 100; + if (haveLineClamp) { + int maxLineCount = 0; + child = iterator.first(); + while (child) { + if (!child->isPositioned()) { + if (relayoutChildren || (child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent())) || + (child->style()->height().isAuto() && child->isBlockFlow() && !child->needsLayout())) { + child->setChildNeedsLayout(true, false); + + // Dirty all the positioned objects. + if (child->isRenderBlock()) { + static_cast<RenderBlock*>(child)->markPositionedObjectsForLayout(); + static_cast<RenderBlock*>(child)->clearTruncation(); + } + } + child->layoutIfNeeded(); + if (child->style()->height().isAuto() && child->isBlockFlow()) + maxLineCount = max(maxLineCount, static_cast<RenderBlock*>(child)->lineCount()); + } + child = iterator.next(); + } + + // Get the # of lines and then alter all block flow children with auto height to use the + // specified height. We always try to leave room for at least one line. + int numVisibleLines = max(1, static_cast<int>((maxLineCount + 1) * style()->lineClamp() / 100.0)); + if (numVisibleLines < maxLineCount) { + for (child = iterator.first(); child; child = iterator.next()) { + if (child->isPositioned() || !child->style()->height().isAuto() || !child->isBlockFlow()) + continue; + + RenderBlock* blockChild = static_cast<RenderBlock*>(child); + int lineCount = blockChild->lineCount(); + if (lineCount <= numVisibleLines) + continue; + + int newHeight = blockChild->heightForLineCount(numVisibleLines); + if (newHeight == child->height()) + continue; + + child->setChildNeedsLayout(true, false); + child->setOverrideSize(newHeight); + m_flexingChildren = true; + child->layoutIfNeeded(); + m_flexingChildren = false; + child->setOverrideSize(-1); + + // FIXME: For now don't support RTL. + if (style()->direction() != LTR) + continue; + + // Get the last line + RootInlineBox* lastLine = blockChild->lineAtIndex(lineCount-1); + if (!lastLine) + continue; + + // See if the last item is an anchor + InlineBox* anchorBox = lastLine->lastChild(); + if (!anchorBox) + continue; + if (!anchorBox->object()->element()) + continue; + if (!anchorBox->object()->element()->isLink()) + continue; + + RootInlineBox* lastVisibleLine = blockChild->lineAtIndex(numVisibleLines-1); + if (!lastVisibleLine) + continue; + + const UChar ellipsisAndSpace[2] = { horizontalEllipsis, ' ' }; + DEFINE_STATIC_LOCAL(AtomicString, ellipsisAndSpaceStr, (ellipsisAndSpace, 2)); + + const Font& font = style(numVisibleLines == 1)->font(); + int ellipsisAndSpaceWidth = font.width(TextRun(ellipsisAndSpace, 2)); + + // Get ellipsis width + " " + anchor width + int totalWidth = ellipsisAndSpaceWidth + anchorBox->width(); + + // See if this width can be accommodated on the last visible line + RenderBlock* destBlock = static_cast<RenderBlock*>(lastVisibleLine->object()); + RenderBlock* srcBlock = static_cast<RenderBlock*>(lastLine->object()); + + // FIXME: Directions of src/destBlock could be different from our direction and from one another. + if (srcBlock->style()->direction() != LTR) + continue; + if (destBlock->style()->direction() != LTR) + continue; + + int blockEdge = destBlock->rightOffset(lastVisibleLine->yPos()); + if (!lastVisibleLine->canAccommodateEllipsis(true, blockEdge, + lastVisibleLine->xPos() + lastVisibleLine->width(), + totalWidth)) + continue; + + // Let the truncation code kick in. + lastVisibleLine->placeEllipsis(ellipsisAndSpaceStr, true, blockEdge, totalWidth, anchorBox); + destBlock->setHasMarkupTruncation(true); + } + } + } + + // We do 2 passes. The first pass is simply to lay everyone out at + // their preferred widths. The second pass handles flexing the children. + // Our first pass is done without flexing. We simply lay the children + // out within the box. + do { + m_height = borderTop() + paddingTop(); + int minHeight = m_height + toAdd; + m_overflowHeight = m_height; + + child = iterator.first(); + while (child) { + // make sure we relayout children if we need it. + if (!haveLineClamp && (relayoutChildren || (child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent())))) + child->setChildNeedsLayout(true, false); + + if (child->isPositioned()) + { + child->containingBlock()->insertPositionedObject(child); + if (child->hasStaticX()) { + if (style()->direction() == LTR) + child->setStaticX(borderLeft()+paddingLeft()); + else + child->setStaticX(borderRight()+paddingRight()); + } + if (child->hasStaticY()) + child->setStaticY(m_height); + child = iterator.next(); + continue; + } + + // Compute the child's vertical margins. + child->calcVerticalMargins(); + + // Add in the child's marginTop to our height. + m_height += child->marginTop(); + + // Now do a layout. + child->layoutIfNeeded(); + + // We can place the child now, using our value of box-align. + int childX = borderLeft() + paddingLeft(); + switch (style()->boxAlign()) { + case BCENTER: + case BBASELINE: // Baseline just maps to center for vertical boxes + childX += child->marginLeft() + max(0, (contentWidth() - (child->width() + child->marginLeft() + child->marginRight()))/2); + break; + case BEND: + if (style()->direction() == RTL) + childX += child->marginLeft(); + else + childX += contentWidth() - child->marginRight() - child->width(); + break; + default: // BSTART/BSTRETCH + if (style()->direction() == LTR) + childX += child->marginLeft(); + else + childX += contentWidth() - child->marginRight() - child->width(); + break; + } + + // Place the child. + placeChild(child, childX, m_height); + m_height += child->height() + child->marginBottom(); + + if (child->isRenderBlock()) + static_cast<RenderBlock*>(child)->addVisualOverflow(static_cast<RenderBlock*>(child)->floatRect()); + + // See if this child has made our overflow need to grow. + m_overflowWidth = max(child->xPos() + child->overflowWidth(false), m_overflowWidth); + m_overflowLeft = min(child->xPos() + child->overflowLeft(false), m_overflowLeft); + + child = iterator.next(); + } + + yPos = m_height; + + if (!iterator.first() && hasLineIfEmpty()) + m_height += lineHeight(true, true); + + m_height += toAdd; + + // Negative margins can cause our height to shrink below our minimal height (border/padding). + // If this happens, ensure that the computed height is increased to the minimal height. + if (m_height < minHeight) + m_height = minHeight; + + // Always make sure our overflowheight is at least our height. + if (m_overflowHeight < m_height) + m_overflowHeight = m_height; + + // Now we have to calc our height, so we know how much space we have remaining. + oldHeight = m_height; + calcHeight(); + if (oldHeight != m_height) + heightSpecified = true; + + remainingSpace = borderTop() + paddingTop() + contentHeight() - yPos; + + if (m_flexingChildren) + haveFlex = false; // We're done. + else if (haveFlex) { + // We have some flexible objects. See if we need to grow/shrink them at all. + if (!remainingSpace) + break; + + // Allocate the remaining space among the flexible objects. If we are trying to + // grow, then we go from the lowest flex group to the highest flex group. For shrinking, + // we go from the highest flex group to the lowest group. + bool expanding = remainingSpace > 0; + unsigned int start = expanding ? lowestFlexGroup : highestFlexGroup; + unsigned int end = expanding? highestFlexGroup : lowestFlexGroup; + for (unsigned int i = start; i <= end && remainingSpace; i++) { + // Always start off by assuming the group can get all the remaining space. + int groupRemainingSpace = remainingSpace; + do { + // Flexing consists of multiple passes, since we have to change ratios every time an object hits its max/min-width + // For a given pass, we always start off by computing the totalFlex of all objects that can grow/shrink at all, and + // computing the allowed growth before an object hits its min/max width (and thus + // forces a totalFlex recomputation). + int groupRemainingSpaceAtBeginning = groupRemainingSpace; + float totalFlex = 0.0f; + child = iterator.first(); + while (child) { + if (allowedChildFlex(child, expanding, i)) + totalFlex += child->style()->boxFlex(); + child = iterator.next(); + } + child = iterator.first(); + int spaceAvailableThisPass = groupRemainingSpace; + while (child) { + int allowedFlex = allowedChildFlex(child, expanding, i); + if (allowedFlex) { + int projectedFlex = (allowedFlex == INT_MAX) ? allowedFlex : (int)(allowedFlex * (totalFlex / child->style()->boxFlex())); + spaceAvailableThisPass = expanding ? min(spaceAvailableThisPass, projectedFlex) : max(spaceAvailableThisPass, projectedFlex); + } + child = iterator.next(); + } + + // The flex groups may not have any flexible objects this time around. + if (!spaceAvailableThisPass || totalFlex == 0.0f) { + // If we just couldn't grow/shrink any more, then it's time to transition to the next flex group. + groupRemainingSpace = 0; + continue; + } + + // Now distribute the space to objects. + child = iterator.first(); + while (child && spaceAvailableThisPass && totalFlex) { + if (allowedChildFlex(child, expanding, i)) { + int spaceAdd = (int)(spaceAvailableThisPass * (child->style()->boxFlex()/totalFlex)); + if (spaceAdd) { + child->setOverrideSize(child->overrideHeight() + spaceAdd); + m_flexingChildren = true; + relayoutChildren = true; + } + + spaceAvailableThisPass -= spaceAdd; + remainingSpace -= spaceAdd; + groupRemainingSpace -= spaceAdd; + + totalFlex -= child->style()->boxFlex(); + } + child = iterator.next(); + } + if (groupRemainingSpace == groupRemainingSpaceAtBeginning) { + // this is not advancing, avoid getting stuck by distributing the remaining pixels + child = iterator.first(); + int spaceAdd = groupRemainingSpace > 0 ? 1 : -1; + while (child && groupRemainingSpace) { + if (allowedChildFlex(child, expanding, i)) { + child->setOverrideSize(child->overrideHeight() + spaceAdd); + m_flexingChildren = true; + relayoutChildren = true; + remainingSpace -= spaceAdd; + groupRemainingSpace -= spaceAdd; + } + child = iterator.next(); + } + } + } while (groupRemainingSpace); + } + + // We didn't find any children that could grow. + if (haveFlex && !m_flexingChildren) + haveFlex = false; + } + } while (haveFlex); + + if (style()->boxPack() != BSTART && remainingSpace > 0) { + // Children must be repositioned. + int offset = 0; + if (style()->boxPack() == BJUSTIFY) { + // Determine the total number of children. + int totalChildren = 0; + child = iterator.first(); + while (child) { + if (child->isPositioned()) { + child = iterator.next(); + continue; + } + totalChildren++; + child = iterator.next(); + } + + // Iterate over the children and space them out according to the + // justification level. + if (totalChildren > 1) { + totalChildren--; + bool firstChild = true; + child = iterator.first(); + while (child) { + if (child->isPositioned()) { + child = iterator.next(); + continue; + } + + if (firstChild) { + firstChild = false; + child = iterator.next(); + continue; + } + + offset += remainingSpace/totalChildren; + remainingSpace -= (remainingSpace/totalChildren); + totalChildren--; + placeChild(child, child->xPos(), child->yPos()+offset); + child = iterator.next(); + } + } + } else { + if (style()->boxPack() == BCENTER) + offset += remainingSpace/2; + else // END + offset += remainingSpace; + child = iterator.first(); + while (child) { + if (child->isPositioned()) { + child = iterator.next(); + continue; + } + placeChild(child, child->xPos(), child->yPos()+offset); + child = iterator.next(); + } + } + } + + child = iterator.first(); + while (child && child->isPositioned()) { + child = iterator.next(); + } + + if (child) { + m_overflowTop = min(child->yPos() + child->overflowTop(false), m_overflowTop); + + RenderObject* lastChild = child; + while ((child = iterator.next())) { + if (!child->isPositioned()) + lastChild = child; + } + m_overflowHeight = max(lastChild->yPos() + lastChild->overflowHeight(false), m_overflowHeight); + } + + // So that the calcHeight in layoutBlock() knows to relayout positioned objects because of + // a height change, we revert our height back to the intrinsic height before returning. + if (heightSpecified) + m_height = oldHeight; +} + +void RenderFlexibleBox::placeChild(RenderObject* child, int x, int y) +{ + IntRect oldRect(child->xPos(), child->yPos() , child->width(), child->height()); + + // Place the child. + child->setPos(x, y); + + // If the child moved, we have to repaint it as well as any floating/positioned + // descendants. An exception is if we need a layout. In this case, we know we're going to + // repaint ourselves (and the child) anyway. + if (!selfNeedsLayout() && child->checkForRepaintDuringLayout()) + child->repaintDuringLayoutIfMoved(oldRect); +} + +int RenderFlexibleBox::allowedChildFlex(RenderObject* child, bool expanding, unsigned int group) +{ + if (child->isPositioned() || child->style()->boxFlex() == 0.0f || child->style()->boxFlexGroup() != group) + return 0; + + if (expanding) { + if (isHorizontal()) { + // FIXME: For now just handle fixed values. + int maxW = INT_MAX; + int w = child->overrideWidth() - (child->borderLeft() + child->borderRight() + child->paddingLeft() + child->paddingRight()); + if (!child->style()->maxWidth().isUndefined() && + child->style()->maxWidth().isFixed()) + maxW = child->style()->maxWidth().value(); + else if (child->style()->maxWidth().type() == Intrinsic) + maxW = child->maxPrefWidth(); + else if (child->style()->maxWidth().type() == MinIntrinsic) + maxW = child->minPrefWidth(); + if (maxW == INT_MAX) + return maxW; + return max(0, maxW - w); + } else { + // FIXME: For now just handle fixed values. + int maxH = INT_MAX; + int h = child->overrideHeight() - (child->borderTop() + child->borderBottom() + child->paddingTop() + child->paddingBottom()); + if (!child->style()->maxHeight().isUndefined() && + child->style()->maxHeight().isFixed()) + maxH = child->style()->maxHeight().value(); + if (maxH == INT_MAX) + return maxH; + return max(0, maxH - h); + } + } + + // FIXME: For now just handle fixed values. + if (isHorizontal()) { + int minW = child->minPrefWidth(); + int w = child->overrideWidth() - (child->borderLeft() + child->borderRight() + child->paddingLeft() + child->paddingRight()); + if (child->style()->minWidth().isFixed()) + minW = child->style()->minWidth().value(); + else if (child->style()->minWidth().type() == Intrinsic) + minW = child->maxPrefWidth(); + else if (child->style()->minWidth().type() == MinIntrinsic) + minW = child->minPrefWidth(); + + int allowedShrinkage = min(0, minW - w); + return allowedShrinkage; + } else { + if (child->style()->minHeight().isFixed()) { + int minH = child->style()->minHeight().value(); + int h = child->overrideHeight() - (child->borderLeft() + child->borderRight() + child->paddingLeft() + child->paddingRight()); + int allowedShrinkage = min(0, minH - h); + return allowedShrinkage; + } + } + + return 0; +} + +const char *RenderFlexibleBox::renderName() const +{ + if (isFloating()) + return "RenderFlexibleBox (floating)"; + if (isPositioned()) + return "RenderFlexibleBox (positioned)"; + if (isRelPositioned()) + return "RenderFlexibleBox (relative positioned)"; + return "RenderFlexibleBox"; +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderFlexibleBox.h b/src/3rdparty/webkit/WebCore/rendering/RenderFlexibleBox.h new file mode 100644 index 0000000..f48caf5 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderFlexibleBox.h @@ -0,0 +1,66 @@ +/* + * This file is part of the render object implementation for KHTML. + * + * Copyright (C) 2003 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef RenderFlexibleBox_h +#define RenderFlexibleBox_h + +#include "RenderBlock.h" + +namespace WebCore { + +class RenderFlexibleBox : public RenderBlock { +public: + RenderFlexibleBox(Node*); + virtual ~RenderFlexibleBox(); + + virtual const char* renderName() const; + + virtual void calcPrefWidths(); + void calcHorizontalPrefWidths(); + void calcVerticalPrefWidths(); + + virtual void layoutBlock(bool relayoutChildren); + void layoutHorizontalBox(bool relayoutChildren); + void layoutVerticalBox(bool relayoutChildren); + + virtual bool avoidsFloats() const { return true; } + + virtual bool isFlexibleBox() const { return true; } + virtual bool isFlexingChildren() const { return m_flexingChildren; } + virtual bool isStretchingChildren() const { return m_stretchingChildren; } + + void placeChild(RenderObject* child, int x, int y); + +protected: + int allowedChildFlex(RenderObject* child, bool expanding, unsigned group); + + bool hasMultipleLines() const { return style()->boxLines() == MULTIPLE; } + bool isVertical() const { return style()->boxOrient() == VERTICAL; } + bool isHorizontal() const { return style()->boxOrient() == HORIZONTAL; } + + bool m_flexingChildren : 1; + bool m_stretchingChildren : 1; +}; + +} // namespace WebCore + +#endif // RenderFlexibleBox_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderFlow.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderFlow.cpp new file mode 100644 index 0000000..86a92f3 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderFlow.cpp @@ -0,0 +1,883 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * 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" +#include "RenderFlow.h" + +#include "Document.h" +#include "GraphicsContext.h" +#include "HTMLNames.h" +#include "InlineTextBox.h" +#include "RenderArena.h" +#include "RenderInline.h" +#include "RenderLayer.h" +#include "RenderView.h" + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +#ifndef NDEBUG + +RenderFlow::~RenderFlow() +{ + ASSERT(!m_firstLineBox); + ASSERT(!m_lastLineBox); +} + +#endif + +RenderFlow* RenderFlow::createAnonymousFlow(Document* doc, PassRefPtr<RenderStyle> style) +{ + RenderFlow* result; + if (style->display() == INLINE) + result = new (doc->renderArena()) RenderInline(doc); + else + result = new (doc->renderArena()) RenderBlock(doc); + result->setStyle(style); + return result; +} + +RenderFlow* RenderFlow::continuationBefore(RenderObject* beforeChild) +{ + if (beforeChild && beforeChild->parent() == this) + return this; + + RenderFlow* curr = continuation(); + RenderFlow* nextToLast = this; + RenderFlow* last = this; + while (curr) { + if (beforeChild && beforeChild->parent() == curr) { + if (curr->firstChild() == beforeChild) + return last; + return curr; + } + + nextToLast = last; + last = curr; + curr = curr->continuation(); + } + + if (!beforeChild && !last->firstChild()) + return nextToLast; + return last; +} + +void RenderFlow::addChildWithContinuation(RenderObject* newChild, RenderObject* beforeChild) +{ + if (beforeChild && (beforeChild->parent()->isTableRow() || beforeChild->parent()->isTableSection() || beforeChild->parent()->isTable())) { + RenderObject* anonymousTablePart = beforeChild->parent(); + ASSERT(anonymousTablePart->isAnonymous()); + while (!anonymousTablePart->isTable()) { + anonymousTablePart = anonymousTablePart->parent(); + ASSERT(anonymousTablePart->isAnonymous()); + } + return anonymousTablePart->addChild(newChild, beforeChild); + } + + RenderFlow* flow = continuationBefore(beforeChild); + ASSERT(!beforeChild || beforeChild->parent()->isRenderBlock() || + beforeChild->parent()->isRenderInline()); + RenderFlow* beforeChildParent = beforeChild ? static_cast<RenderFlow*>(beforeChild->parent()) : + (flow->continuation() ? flow->continuation() : flow); + + if (newChild->isFloatingOrPositioned()) + return beforeChildParent->addChildToFlow(newChild, beforeChild); + + // A continuation always consists of two potential candidates: an inline or an anonymous + // block box holding block children. + bool childInline = newChild->isInline(); + bool bcpInline = beforeChildParent->isInline(); + bool flowInline = flow->isInline(); + + if (flow == beforeChildParent) + return flow->addChildToFlow(newChild, beforeChild); + else { + // The goal here is to match up if we can, so that we can coalesce and create the + // minimal # of continuations needed for the inline. + if (childInline == bcpInline) + return beforeChildParent->addChildToFlow(newChild, beforeChild); + else if (flowInline == childInline) + return flow->addChildToFlow(newChild, 0); // Just treat like an append. + else + return beforeChildParent->addChildToFlow(newChild, beforeChild); + } +} + +void RenderFlow::addChild(RenderObject* newChild, RenderObject* beforeChild) +{ + if (continuation()) + return addChildWithContinuation(newChild, beforeChild); + return addChildToFlow(newChild, beforeChild); +} + +void RenderFlow::extractLineBox(InlineFlowBox* box) +{ + checkConsistency(); + + m_lastLineBox = box->prevFlowBox(); + if (box == m_firstLineBox) + m_firstLineBox = 0; + if (box->prevLineBox()) + box->prevLineBox()->setNextLineBox(0); + box->setPreviousLineBox(0); + for (InlineRunBox* curr = box; curr; curr = curr->nextLineBox()) + curr->setExtracted(); + + checkConsistency(); +} + +void RenderFlow::attachLineBox(InlineFlowBox* box) +{ + checkConsistency(); + + if (m_lastLineBox) { + m_lastLineBox->setNextLineBox(box); + box->setPreviousLineBox(m_lastLineBox); + } else + m_firstLineBox = box; + InlineFlowBox* last = box; + for (InlineFlowBox* curr = box; curr; curr = curr->nextFlowBox()) { + curr->setExtracted(false); + last = curr; + } + m_lastLineBox = last; + + checkConsistency(); +} + +void RenderFlow::removeLineBox(InlineFlowBox* box) +{ + checkConsistency(); + + if (box == m_firstLineBox) + m_firstLineBox = box->nextFlowBox(); + if (box == m_lastLineBox) + m_lastLineBox = box->prevFlowBox(); + if (box->nextLineBox()) + box->nextLineBox()->setPreviousLineBox(box->prevLineBox()); + if (box->prevLineBox()) + box->prevLineBox()->setNextLineBox(box->nextLineBox()); + + checkConsistency(); +} + +void RenderFlow::deleteLineBoxes() +{ + if (m_firstLineBox) { + RenderArena* arena = renderArena(); + InlineRunBox* next; + for (InlineRunBox* curr = m_firstLineBox; curr; curr = next) { + next = curr->nextLineBox(); + curr->destroy(arena); + } + m_firstLineBox = 0; + m_lastLineBox = 0; + } +} + +void RenderFlow::destroy() +{ + // Detach our continuation first. + if (m_continuation) + m_continuation->destroy(); + m_continuation = 0; + + // Make sure to destroy anonymous children first while they are still connected to the rest of the tree, so that they will + // properly dirty line boxes that they are removed from. Effects that do :before/:after only on hover could crash otherwise. + RenderContainer::destroyLeftoverChildren(); + + if (!documentBeingDestroyed()) { + if (m_firstLineBox) { + // We can't wait for RenderContainer::destroy to clear the selection, + // because by then we will have nuked the line boxes. + // FIXME: The SelectionController should be responsible for this when it + // is notified of DOM mutations. + if (isSelectionBorder()) + view()->clearSelection(); + + // If line boxes are contained inside a root, that means we're an inline. + // In that case, we need to remove all the line boxes so that the parent + // lines aren't pointing to deleted children. If the first line box does + // not have a parent that means they are either already disconnected or + // root lines that can just be destroyed without disconnecting. + if (m_firstLineBox->parent()) { + for (InlineRunBox* box = m_firstLineBox; box; box = box->nextLineBox()) + box->remove(); + } + + // If we are an anonymous block, then our line boxes might have children + // that will outlast this block. In the non-anonymous block case those + // children will be destroyed by the time we return from this function. + if (isAnonymousBlock()) { + for (InlineFlowBox* box = m_firstLineBox; box; box = box->nextFlowBox()) { + while (InlineBox* childBox = box->firstChild()) + childBox->remove(); + } + } + } else if (isInline() && parent()) + parent()->dirtyLinesFromChangedChild(this); + } + + deleteLineBoxes(); + + RenderContainer::destroy(); +} + +void RenderFlow::dirtyLinesFromChangedChild(RenderObject* child) +{ + if (!parent() || (selfNeedsLayout() && !isInlineFlow()) || isTable()) + return; + + // If we have no first line box, then just bail early. + if (!firstLineBox()) { + // For an empty inline, go ahead and propagate the check up to our parent, unless the parent + // is already dirty. + if (isInline() && !parent()->selfNeedsLayout()) + parent()->dirtyLinesFromChangedChild(this); + return; + } + + // Try to figure out which line box we belong in. First try to find a previous + // line box by examining our siblings. If we didn't find a line box, then use our + // parent's first line box. + RootInlineBox* box = 0; + RenderObject* curr = 0; + for (curr = child->previousSibling(); curr; curr = curr->previousSibling()) { + if (curr->isFloatingOrPositioned()) + continue; + + if (curr->isReplaced()) { + InlineBox* wrapper = curr->inlineBoxWrapper(); + if (wrapper) + box = wrapper->root(); + } else if (curr->isText()) { + InlineTextBox* textBox = static_cast<RenderText*>(curr)->lastTextBox(); + if (textBox) + box = textBox->root(); + } else if (curr->isInlineFlow()) { + InlineRunBox* runBox = static_cast<RenderFlow*>(curr)->lastLineBox(); + if (runBox) + box = runBox->root(); + } + + if (box) + break; + } + if (!box) + box = firstLineBox()->root(); + + // If we found a line box, then dirty it. + if (box) { + RootInlineBox* adjacentBox; + box->markDirty(); + + // dirty the adjacent lines that might be affected + // NOTE: we dirty the previous line because RootInlineBox objects cache + // the address of the first object on the next line after a BR, which we may be + // invalidating here. For more info, see how RenderBlock::layoutInlineChildren + // calls setLineBreakInfo with the result of findNextLineBreak. findNextLineBreak, + // despite the name, actually returns the first RenderObject after the BR. + // <rdar://problem/3849947> "Typing after pasting line does not appear until after window resize." + adjacentBox = box->prevRootBox(); + if (adjacentBox) + adjacentBox->markDirty(); + if (child->isBR() || (curr && curr->isBR())) { + adjacentBox = box->nextRootBox(); + if (adjacentBox) + adjacentBox->markDirty(); + } + } +} + +int RenderFlow::lineHeight(bool firstLine, bool /*isRootLineBox*/) const +{ + if (firstLine) { + RenderStyle* s = style(firstLine); + Length lh = s->lineHeight(); + if (lh.isNegative()) { + if (s == style()) { + if (m_lineHeight == -1) + m_lineHeight = RenderObject::lineHeight(false); + return m_lineHeight; + } + return s->font().lineSpacing(); + } + if (lh.isPercent()) + return lh.calcMinValue(s->fontSize()); + return lh.value(); + } + + if (m_lineHeight == -1) + m_lineHeight = RenderObject::lineHeight(false); + return m_lineHeight; +} + +void RenderFlow::dirtyLineBoxes(bool fullLayout, bool isRootLineBox) +{ + if (!isRootLineBox && isReplaced()) + return RenderContainer::dirtyLineBoxes(fullLayout, isRootLineBox); + + if (fullLayout) + deleteLineBoxes(); + else { + for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) + curr->dirtyLineBoxes(); + } +} + +InlineBox* RenderFlow::createInlineBox(bool makePlaceHolderBox, bool isRootLineBox, bool /*isOnlyRun*/) +{ + checkConsistency(); + + if (!isRootLineBox && + (isReplaced() || makePlaceHolderBox)) // Inline tables and inline blocks + return RenderContainer::createInlineBox(false, isRootLineBox); // (or positioned element placeholders). + + InlineFlowBox* flowBox = 0; + if (isInlineFlow()) + flowBox = new (renderArena()) InlineFlowBox(this); + else + flowBox = new (renderArena()) RootInlineBox(this); + + if (!m_firstLineBox) + m_firstLineBox = m_lastLineBox = flowBox; + else { + m_lastLineBox->setNextLineBox(flowBox); + flowBox->setPreviousLineBox(m_lastLineBox); + m_lastLineBox = flowBox; + } + + checkConsistency(); + + return flowBox; +} + +void RenderFlow::paintLines(PaintInfo& paintInfo, int tx, int ty) +{ + // Only paint during the foreground/selection phases. + if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection && paintInfo.phase != PaintPhaseOutline + && paintInfo.phase != PaintPhaseSelfOutline && paintInfo.phase != PaintPhaseChildOutlines && paintInfo.phase != PaintPhaseTextClip + && paintInfo.phase != PaintPhaseMask) + return; + + bool inlineFlow = isInlineFlow(); + if (inlineFlow) + ASSERT(m_layer); // The only way a compact/run-in/inline could paint like this is if it has a layer. + + // If we have no lines then we have no work to do. + if (!firstLineBox()) + return; + + // We can check the first box and last box and avoid painting if we don't + // intersect. This is a quick short-circuit that we can take to avoid walking any lines. + // FIXME: This check is flawed in the following extremely obscure way: + // if some line in the middle has a huge overflow, it might actually extend below the last line. + int yPos = firstLineBox()->root()->topOverflow() - maximalOutlineSize(paintInfo.phase); + int h = maximalOutlineSize(paintInfo.phase) + lastLineBox()->root()->bottomOverflow() - yPos; + yPos += ty; + if (yPos >= paintInfo.rect.bottom() || yPos + h <= paintInfo.rect.y()) + return; + + PaintInfo info(paintInfo); + RenderFlowSequencedSet outlineObjects; + info.outlineObjects = &outlineObjects; + + // See if our root lines intersect with the dirty rect. If so, then we paint + // them. Note that boxes can easily overlap, so we can't make any assumptions + // based off positions of our first line box or our last line box. + RenderView* v = view(); + bool usePrintRect = !v->printRect().isEmpty(); + for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextFlowBox()) { + if (usePrintRect) { + // FIXME: This is a feeble effort to avoid splitting a line across two pages. + // It is utterly inadequate, and this should not be done at paint time at all. + // The whole way objects break across pages needs to be redone. + // Try to avoid splitting a line vertically, but only if it's less than the height + // of the entire page. + if (curr->root()->bottomOverflow() - curr->root()->topOverflow() <= v->printRect().height()) { + if (ty + curr->root()->bottomOverflow() > v->printRect().bottom()) { + if (ty + curr->root()->topOverflow() < v->truncatedAt()) + v->setBestTruncatedAt(ty + curr->root()->topOverflow(), this); + // If we were able to truncate, don't paint. + if (ty + curr->root()->topOverflow() >= v->truncatedAt()) + break; + } + } + } + + int top = min(curr->root()->topOverflow(), curr->root()->selectionTop()) - maximalOutlineSize(info.phase); + int bottom = curr->root()->bottomOverflow() + maximalOutlineSize(info.phase); + h = bottom - top; + yPos = ty + top; + if (yPos < info.rect.bottom() && yPos + h > info.rect.y()) + curr->paint(info, tx, ty); + } + + if (info.phase == PaintPhaseOutline || info.phase == PaintPhaseSelfOutline || info.phase == PaintPhaseChildOutlines) { + RenderFlowSequencedSet::iterator end = info.outlineObjects->end(); + for (RenderFlowSequencedSet::iterator it = info.outlineObjects->begin(); it != end; ++it) { + RenderFlow* flow = *it; + flow->paintOutline(info.context, tx, ty); + } + info.outlineObjects->clear(); + } +} + +bool RenderFlow::hitTestLines(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction) +{ + if (hitTestAction != HitTestForeground) + return false; + + bool inlineFlow = isInlineFlow(); + if (inlineFlow) + ASSERT(m_layer); // The only way a compact/run-in/inline could paint like this is if it has a layer. + + // If we have no lines then we have no work to do. + if (!firstLineBox()) + return false; + + // We can check the first box and last box and avoid hit testing if we don't + // contain the point. This is a quick short-circuit that we can take to avoid walking any lines. + // FIXME: This check is flawed in the following extremely obscure way: + // if some line in the middle has a huge overflow, it might actually extend below the last line. + if ((y >= ty + lastLineBox()->root()->bottomOverflow()) || (y < ty + firstLineBox()->root()->topOverflow())) + return false; + + // See if our root lines contain the point. If so, then we hit test + // them further. Note that boxes can easily overlap, so we can't make any assumptions + // based off positions of our first line box or our last line box. + for (InlineFlowBox* curr = lastLineBox(); curr; curr = curr->prevFlowBox()) { + if (y >= ty + curr->root()->topOverflow() && y < ty + curr->root()->bottomOverflow()) { + bool inside = curr->nodeAtPoint(request, result, x, y, tx, ty); + if (inside) { + updateHitTestResult(result, IntPoint(x - tx, y - ty)); + return true; + } + } + } + + return false; +} + +IntRect RenderFlow::absoluteClippedOverflowRect() +{ + if (isInlineFlow()) { + // Only compacts and run-ins are allowed in here during layout. + ASSERT(!view() || !view()->layoutState() || isCompact() || isRunIn()); + + if (!firstLineBox() && !continuation()) + return IntRect(); + + // Find our leftmost position. + int left = 0; + int top = firstLineBox() ? firstLineBox()->yPos() : 0; + for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) { + if (curr == firstLineBox() || curr->xPos() < left) + left = curr->xPos(); + } + + // Now invalidate a rectangle. + int ow = style() ? style()->outlineSize() : 0; + if (isCompact()) + left -= m_x; + + // We need to add in the relative position offsets of any inlines (including us) up to our + // containing block. + RenderBlock* cb = containingBlock(); + for (RenderObject* inlineFlow = this; inlineFlow && inlineFlow->isInlineFlow() && inlineFlow != cb; + inlineFlow = inlineFlow->parent()) { + if (inlineFlow->style()->position() == RelativePosition && inlineFlow->hasLayer()) + inlineFlow->layer()->relativePositionOffset(left, top); + } + + IntRect r(-ow + left, -ow + top, width() + ow * 2, height() + ow * 2); + if (cb->hasColumns()) + cb->adjustRectForColumns(r); + + if (cb->hasOverflowClip()) { + // cb->height() is inaccurate if we're in the middle of a layout of |cb|, so use the + // layer's size instead. Even if the layer's size is wrong, the layer itself will repaint + // anyway if its size does change. + int x = r.x(); + int y = r.y(); + IntRect boxRect(0, 0, cb->layer()->width(), cb->layer()->height()); + cb->layer()->subtractScrolledContentOffset(x, y); // For overflow:auto/scroll/hidden. + IntRect repaintRect(x, y, r.width(), r.height()); + r = intersection(repaintRect, boxRect); + } + cb->computeAbsoluteRepaintRect(r); + + if (ow) { + for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { + if (!curr->isText()) { + IntRect childRect = curr->getAbsoluteRepaintRectWithOutline(ow); + r.unite(childRect); + } + } + + if (continuation() && !continuation()->isInline()) { + IntRect contRect = continuation()->getAbsoluteRepaintRectWithOutline(ow); + r.unite(contRect); + } + } + + return r; + } + + return RenderContainer::absoluteClippedOverflowRect(); +} + +int RenderFlow::lowestPosition(bool includeOverflowInterior, bool includeSelf) const +{ + ASSERT(!isInlineFlow()); + if (!includeOverflowInterior && (hasOverflowClip() || hasControlClip())) + return includeSelf && m_width > 0 ? overflowHeight(false) : 0; + + int bottom = includeSelf && m_width > 0 ? m_height : 0; + if (!hasColumns()) { + // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids. + // For now, we have to descend into all the children, since we may have a huge abs div inside + // a tiny rel div buried somewhere deep in our child tree. In this case we have to get to + // the abs div. + for (RenderObject* c = firstChild(); c; c = c->nextSibling()) { + if (!c->isFloatingOrPositioned() && !c->isText() && !c->isInlineFlow()) + bottom = max(bottom, c->yPos() + c->lowestPosition(false)); + } + } + + if (includeSelf && isRelPositioned()) + bottom += relativePositionOffsetY(); + + return bottom; +} + +int RenderFlow::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const +{ + ASSERT(!isInlineFlow()); + if (!includeOverflowInterior && (hasOverflowClip() || hasControlClip())) + return includeSelf && m_height > 0 ? overflowWidth(false) : 0; + + int right = includeSelf && m_height > 0 ? m_width : 0; + if (!hasColumns()) { + // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids. + // For now, we have to descend into all the children, since we may have a huge abs div inside + // a tiny rel div buried somewhere deep in our child tree. In this case we have to get to + // the abs div. + for (RenderObject* c = firstChild(); c; c = c->nextSibling()) { + if (!c->isFloatingOrPositioned() && !c->isText() && !c->isInlineFlow()) + right = max(right, c->xPos() + c->rightmostPosition(false)); + } + } + + if (includeSelf && isRelPositioned()) + right += relativePositionOffsetX(); + + return right; +} + +int RenderFlow::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const +{ + ASSERT(!isInlineFlow()); + if (!includeOverflowInterior && (hasOverflowClip() || hasControlClip())) + return includeSelf && m_height > 0 ? overflowLeft(false) : m_width; + + int left = includeSelf && m_height > 0 ? 0 : m_width; + if (!hasColumns()) { + // FIXME: Come up with a way to use the layer tree to avoid visiting all the kids. + // For now, we have to descend into all the children, since we may have a huge abs div inside + // a tiny rel div buried somewhere deep in our child tree. In this case we have to get to + // the abs div. + for (RenderObject* c = firstChild(); c; c = c->nextSibling()) { + if (!c->isFloatingOrPositioned() && !c->isText() && !c->isInlineFlow()) + left = min(left, c->xPos() + c->leftmostPosition(false)); + } + } + + if (includeSelf && isRelPositioned()) + left += relativePositionOffsetX(); + + return left; +} + +IntRect RenderFlow::localCaretRect(InlineBox* inlineBox, int caretOffset, int* extraWidthToEndOfLine) +{ + // Do the normal calculation in most cases. + if (firstChild() || style()->display() == INLINE) + return RenderContainer::localCaretRect(inlineBox, caretOffset, extraWidthToEndOfLine); + + // This is a special case: + // The element is not an inline element, and it's empty. So we have to + // calculate a fake position to indicate where objects are to be inserted. + + // FIXME: This does not take into account either :first-line or :first-letter + // However, as soon as some content is entered, the line boxes will be + // constructed and this kludge is not called any more. So only the caret size + // of an empty :first-line'd block is wrong. I think we can live with that. + RenderStyle* currentStyle = firstLineStyle(); + int height = lineHeight(true); + const int caretWidth = 1; + + enum CaretAlignment { alignLeft, alignRight, alignCenter }; + + CaretAlignment alignment = alignLeft; + + switch (currentStyle->textAlign()) { + case TAAUTO: + case JUSTIFY: + if (currentStyle->direction() == RTL) + alignment = alignRight; + break; + case LEFT: + case WEBKIT_LEFT: + break; + case CENTER: + case WEBKIT_CENTER: + alignment = alignCenter; + break; + case RIGHT: + case WEBKIT_RIGHT: + alignment = alignRight; + break; + } + + int x = borderLeft() + paddingLeft(); + int w = width(); + + switch (alignment) { + case alignLeft: + break; + case alignCenter: + x = (x + w - (borderRight() + paddingRight())) / 2; + break; + case alignRight: + x = w - (borderRight() + paddingRight()); + break; + } + + if (extraWidthToEndOfLine) { + if (isRenderBlock()) { + *extraWidthToEndOfLine = w - (x + caretWidth); + } else { + // FIXME: This code looks wrong. + // myRight and containerRight are set up, but then clobbered. + // So *extraWidthToEndOfLine will always be 0 here. + + int myRight = x + caretWidth; + // FIXME: why call localToAbsoluteForContent() twice here, too? + FloatPoint absRightPoint = localToAbsoluteForContent(FloatPoint(myRight, 0)); + + int containerRight = containingBlock()->xPos() + containingBlockWidth(); + FloatPoint absContainerPoint = localToAbsoluteForContent(FloatPoint(containerRight, 0)); + + *extraWidthToEndOfLine = absContainerPoint.x() - absRightPoint.x(); + } + } + + int y = paddingTop() + borderTop(); + + return IntRect(x, y, caretWidth, height); +} + +void RenderFlow::addFocusRingRects(GraphicsContext* graphicsContext, int tx, int ty) +{ + if (isRenderBlock()) { + // Continuations should include their margins in the outline rect. + if (continuation()) { + bool nextInlineHasLineBox = continuation()->firstLineBox(); + bool prevInlineHasLineBox = static_cast<RenderFlow*>(continuation()->element()->renderer())->firstLineBox(); + int topMargin = prevInlineHasLineBox ? collapsedMarginTop() : 0; + int bottomMargin = nextInlineHasLineBox ? collapsedMarginBottom() : 0; + graphicsContext->addFocusRingRect(IntRect(tx, ty - topMargin, + width(), height() + topMargin + bottomMargin)); + } else + graphicsContext->addFocusRingRect(IntRect(tx, ty, width(), height())); + } + + if (!hasOverflowClip() && !hasControlClip()) { + for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) + graphicsContext->addFocusRingRect(IntRect(tx + curr->xPos(), ty + curr->yPos(), curr->width(), curr->height())); + + for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) + if (!curr->isText() && !curr->isListMarker()) { + FloatPoint pos; + // FIXME: This doesn't work correctly with transforms. + if (curr->layer()) + pos = curr->localToAbsolute(); + else + pos = FloatPoint(tx + curr->xPos(), ty + curr->yPos()); + curr->addFocusRingRects(graphicsContext, pos.x(), pos.y()); + } + } + + if (continuation()) { + if (isInline()) + continuation()->addFocusRingRects(graphicsContext, + tx - containingBlock()->xPos() + continuation()->xPos(), + ty - containingBlock()->yPos() + continuation()->yPos()); + else + continuation()->addFocusRingRects(graphicsContext, + tx - xPos() + continuation()->containingBlock()->xPos(), + ty - yPos() + continuation()->containingBlock()->yPos()); + } +} + +void RenderFlow::paintOutline(GraphicsContext* graphicsContext, int tx, int ty) +{ + if (!hasOutline()) + return; + + if (style()->outlineStyleIsAuto() || hasOutlineAnnotation()) { + int ow = style()->outlineWidth(); + Color oc = style()->outlineColor(); + if (!oc.isValid()) + oc = style()->color(); + + graphicsContext->initFocusRing(ow, style()->outlineOffset()); + addFocusRingRects(graphicsContext, tx, ty); + if (style()->outlineStyleIsAuto()) + graphicsContext->drawFocusRing(oc); + else + addPDFURLRect(graphicsContext, graphicsContext->focusRingBoundingRect()); + graphicsContext->clearFocusRing(); + } + + if (style()->outlineStyleIsAuto() || style()->outlineStyle() == BNONE) + return; + + Vector<IntRect> rects; + + rects.append(IntRect()); + for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) + rects.append(IntRect(curr->xPos(), curr->yPos(), curr->width(), curr->height())); + + rects.append(IntRect()); + + for (unsigned i = 1; i < rects.size() - 1; i++) + paintOutlineForLine(graphicsContext, tx, ty, rects.at(i - 1), rects.at(i), rects.at(i + 1)); +} + +void RenderFlow::paintOutlineForLine(GraphicsContext* graphicsContext, int tx, int ty, + const IntRect& lastline, const IntRect& thisline, const IntRect& nextline) +{ + int ow = style()->outlineWidth(); + EBorderStyle os = style()->outlineStyle(); + Color oc = style()->outlineColor(); + if (!oc.isValid()) + oc = style()->color(); + + int offset = style()->outlineOffset(); + + int t = ty + thisline.y() - offset; + int l = tx + thisline.x() - offset; + int b = ty + thisline.bottom() + offset; + int r = tx + thisline.right() + offset; + + // left edge + drawBorder(graphicsContext, + l - ow, + t - (lastline.isEmpty() || thisline.x() < lastline.x() || (lastline.right() - 1) <= thisline.x() ? ow : 0), + l, + b + (nextline.isEmpty() || thisline.x() <= nextline.x() || (nextline.right() - 1) <= thisline.x() ? ow : 0), + BSLeft, + oc, style()->color(), os, + (lastline.isEmpty() || thisline.x() < lastline.x() || (lastline.right() - 1) <= thisline.x() ? ow : -ow), + (nextline.isEmpty() || thisline.x() <= nextline.x() || (nextline.right() - 1) <= thisline.x() ? ow : -ow)); + + // right edge + drawBorder(graphicsContext, + r, + t - (lastline.isEmpty() || lastline.right() < thisline.right() || (thisline.right() - 1) <= lastline.x() ? ow : 0), + r + ow, + b + (nextline.isEmpty() || nextline.right() <= thisline.right() || (thisline.right() - 1) <= nextline.x() ? ow : 0), + BSRight, + oc, style()->color(), os, + (lastline.isEmpty() || lastline.right() < thisline.right() || (thisline.right() - 1) <= lastline.x() ? ow : -ow), + (nextline.isEmpty() || nextline.right() <= thisline.right() || (thisline.right() - 1) <= nextline.x() ? ow : -ow)); + // upper edge + if (thisline.x() < lastline.x()) + drawBorder(graphicsContext, + l - ow, + t - ow, + min(r+ow, (lastline.isEmpty() ? 1000000 : tx + lastline.x())), + t , + BSTop, oc, style()->color(), os, + ow, + (!lastline.isEmpty() && tx + lastline.x() + 1 < r + ow) ? -ow : ow); + + if (lastline.right() < thisline.right()) + drawBorder(graphicsContext, + max(lastline.isEmpty() ? -1000000 : tx + lastline.right(), l - ow), + t - ow, + r + ow, + t , + BSTop, oc, style()->color(), os, + (!lastline.isEmpty() && l - ow < tx + lastline.right()) ? -ow : ow, + ow); + + // lower edge + if (thisline.x() < nextline.x()) + drawBorder(graphicsContext, + l - ow, + b, + min(r + ow, !nextline.isEmpty() ? tx + nextline.x() + 1 : 1000000), + b + ow, + BSBottom, oc, style()->color(), os, + ow, + (!nextline.isEmpty() && tx + nextline.x() + 1 < r + ow) ? -ow : ow); + + if (nextline.right() < thisline.right()) + drawBorder(graphicsContext, + max(!nextline.isEmpty() ? tx + nextline.right() : -1000000, l - ow), + b, + r + ow, + b + ow, + BSBottom, oc, style()->color(), os, + (!nextline.isEmpty() && l - ow < tx + nextline.right()) ? -ow : ow, + ow); +} + +void RenderFlow::calcMargins(int containerWidth) +{ + m_marginLeft = style()->marginLeft().calcMinValue(containerWidth); + m_marginRight = style()->marginRight().calcMinValue(containerWidth); +} + +#ifndef NDEBUG + +void RenderFlow::checkConsistency() const +{ +#ifdef CHECK_CONSISTENCY + const InlineFlowBox* prev = 0; + for (const InlineFlowBox* child = m_firstLineBox; child != 0; child = child->nextFlowBox()) { + ASSERT(child->object() == this); + ASSERT(child->prevFlowBox() == prev); + prev = child; + } + ASSERT(prev == m_lastLineBox); +#endif +} + +#endif + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderFlow.h b/src/3rdparty/webkit/WebCore/rendering/RenderFlow.h new file mode 100644 index 0000000..7e3a345 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderFlow.h @@ -0,0 +1,146 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef RenderFlow_h +#define RenderFlow_h + +#include "RenderContainer.h" + +namespace WebCore { + +/** + * all geometry managing stuff is only in the block elements. + * + * Inline elements don't layout themselves, but the whole paragraph + * gets flowed by the surrounding block element. This is, because + * one needs to know the whole paragraph to calculate bidirectional + * behaviour of text, so putting the layouting routines in the inline + * elements is impossible. + */ +class RenderFlow : public RenderContainer { +public: + RenderFlow(Node* node) + : RenderContainer(node) + , m_continuation(0) + , m_firstLineBox(0) + , m_lastLineBox(0) + , m_lineHeight(-1) + , m_childrenInline(true) + , m_firstLine(false) + , m_topMarginQuirk(false) + , m_bottomMarginQuirk(false) + , m_hasMarkupTruncation(false) + , m_selectionState(SelectionNone) + , m_hasColumns(false) + , m_isContinuation(false) + { + } +#ifndef NDEBUG + virtual ~RenderFlow(); +#endif + + virtual RenderFlow* continuation() const { return m_continuation; } + void setContinuation(RenderFlow* c) { m_continuation = c; } + RenderFlow* continuationBefore(RenderObject* beforeChild); + + void addChildWithContinuation(RenderObject* newChild, RenderObject* beforeChild); + virtual void addChildToFlow(RenderObject* newChild, RenderObject* beforeChild) = 0; + virtual void addChild(RenderObject* newChild, RenderObject* beforeChild = 0); + + static RenderFlow* createAnonymousFlow(Document*, PassRefPtr<RenderStyle>); + + void extractLineBox(InlineFlowBox*); + void attachLineBox(InlineFlowBox*); + void removeLineBox(InlineFlowBox*); + void deleteLineBoxes(); + virtual void destroy(); + + virtual void dirtyLinesFromChangedChild(RenderObject* child); + + virtual int lineHeight(bool firstLine, bool isRootLineBox = false) const; + + InlineFlowBox* firstLineBox() const { return m_firstLineBox; } + InlineFlowBox* lastLineBox() const { return m_lastLineBox; } + + virtual InlineBox* createInlineBox(bool makePlaceHolderBox, bool isRootLineBox, bool isOnlyRun=false); + virtual void dirtyLineBoxes(bool fullLayout, bool isRootLineBox = false); + + void paintLines(PaintInfo&, int tx, int ty); + bool hitTestLines(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); + + virtual IntRect absoluteClippedOverflowRect(); + + virtual int lowestPosition(bool includeOverflowInterior = true, bool includeSelf = true) const; + virtual int rightmostPosition(bool includeOverflowInterior = true, bool includeSelf = true) const; + virtual int leftmostPosition(bool includeOverflowInterior = true, bool includeSelf = true) const; + + virtual IntRect localCaretRect(InlineBox*, int caretOffset, int* extraWidthToEndOfLine = 0); + + virtual void addFocusRingRects(GraphicsContext*, int tx, int ty); + void paintOutlineForLine(GraphicsContext*, int tx, int ty, const IntRect& prevLine, const IntRect& thisLine, const IntRect& nextLine); + void paintOutline(GraphicsContext*, int tx, int ty); + + virtual bool hasColumns() const { return m_hasColumns; } + + void calcMargins(int containerWidth); + + void checkConsistency() const; + +private: + // An inline can be split with blocks occurring in between the inline content. + // When this occurs we need a pointer to our next object. We can basically be + // split into a sequence of inlines and blocks. The continuation will either be + // an anonymous block (that houses other blocks) or it will be an inline flow. + RenderFlow* m_continuation; + +protected: + // For block flows, each box represents the root inline box for a line in the + // paragraph. + // For inline flows, each box represents a portion of that inline. + InlineFlowBox* m_firstLineBox; + InlineFlowBox* m_lastLineBox; + + mutable int m_lineHeight; + + // These bitfields are moved here from subclasses to pack them together + // from RenderBlock + bool m_childrenInline : 1; + bool m_firstLine : 1; + bool m_topMarginQuirk : 1; + bool m_bottomMarginQuirk : 1; + bool m_hasMarkupTruncation : 1; + unsigned m_selectionState : 3; // SelectionState + bool m_hasColumns : 1; + + // from RenderInline + bool m_isContinuation : 1; // Whether or not we're a continuation of an inline. +}; + +#ifdef NDEBUG +inline void RenderFlow::checkConsistency() const +{ +} +#endif + +} // namespace WebCore + +#endif // RenderFlow_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderForeignObject.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderForeignObject.cpp new file mode 100644 index 0000000..83401fc --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderForeignObject.cpp @@ -0,0 +1,132 @@ +/* + * This file is part of the WebKit project. + * + * Copyright (C) 2006 Apple Computer, Inc. + * + * 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(SVG) && ENABLE(SVG_FOREIGN_OBJECT) +#include "RenderForeignObject.h" + +#include "GraphicsContext.h" +#include "RenderView.h" +#include "SVGForeignObjectElement.h" +#include "SVGLength.h" +#include "SVGTransformList.h" + +namespace WebCore { + +RenderForeignObject::RenderForeignObject(SVGForeignObjectElement* node) + : RenderSVGBlock(node) +{ +} + +TransformationMatrix RenderForeignObject::translationForAttributes() +{ + SVGForeignObjectElement* foreign = static_cast<SVGForeignObjectElement*>(element()); + return TransformationMatrix().translate(foreign->x().value(foreign), foreign->y().value(foreign)); +} + +void RenderForeignObject::paint(PaintInfo& paintInfo, int parentX, int parentY) +{ + if (paintInfo.context->paintingDisabled()) + return; + + paintInfo.context->save(); + paintInfo.context->concatCTM(TransformationMatrix().translate(parentX, parentY)); + paintInfo.context->concatCTM(localTransform()); + paintInfo.context->concatCTM(translationForAttributes()); + paintInfo.context->clip(getClipRect(parentX, parentY)); + + float opacity = style()->opacity(); + if (opacity < 1.0f) + // FIXME: Possible optimization by clipping to bbox here, once relativeBBox is implemented & clip, mask and filter support added. + paintInfo.context->beginTransparencyLayer(opacity); + + PaintInfo pi(paintInfo); + pi.rect = absoluteTransform().inverse().mapRect(paintInfo.rect); + RenderBlock::paint(pi, 0, 0); + + if (opacity < 1.0f) + paintInfo.context->endTransparencyLayer(); + + paintInfo.context->restore(); +} + +void RenderForeignObject::computeAbsoluteRepaintRect(IntRect& r, bool f) +{ + TransformationMatrix transform = translationForAttributes() * localTransform(); + r = transform.mapRect(r); + + RenderBlock::computeAbsoluteRepaintRect(r, f); +} + +bool RenderForeignObject::requiresLayer() +{ + return false; +} + +bool RenderForeignObject::calculateLocalTransform() +{ + TransformationMatrix oldTransform = m_localTransform; + m_localTransform = static_cast<SVGForeignObjectElement*>(element())->animatedLocalTransform(); + return (oldTransform != m_localTransform); +} + +void RenderForeignObject::layout() +{ + ASSERT(needsLayout()); + + // Arbitrary affine transforms are incompatible with LayoutState. + view()->disableLayoutState(); + + IntRect oldBounds; + IntRect oldOutlineBox; + bool checkForRepaint = checkForRepaintDuringLayout(); + if (checkForRepaint) { + oldBounds = m_absoluteBounds; + oldOutlineBox = absoluteOutlineBounds(); + } + + calculateLocalTransform(); + + RenderBlock::layout(); + + m_absoluteBounds = absoluteClippedOverflowRect(); + + if (checkForRepaint) + repaintAfterLayoutIfNeeded(oldBounds, oldOutlineBox); + + view()->enableLayoutState(); + setNeedsLayout(false); +} + +bool RenderForeignObject::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction) +{ + TransformationMatrix totalTransform = absoluteTransform(); + totalTransform *= translationForAttributes(); + double localX, localY; + totalTransform.inverse().map(x, y, &localX, &localY); + return RenderBlock::nodeAtPoint(request, result, static_cast<int>(localX), static_cast<int>(localY), tx, ty, hitTestAction); +} + +} // namespace WebCore + +#endif // ENABLE(SVG) && ENABLE(SVG_FOREIGN_OBJECT) diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderForeignObject.h b/src/3rdparty/webkit/WebCore/rendering/RenderForeignObject.h new file mode 100644 index 0000000..c7f30e9 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderForeignObject.h @@ -0,0 +1,61 @@ +/* + * This file is part of the WebKit project. + * + * Copyright (C) 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef RenderForeignObject_h +#define RenderForeignObject_h +#if ENABLE(SVG) && ENABLE(SVG_FOREIGN_OBJECT) + +#include "TransformationMatrix.h" +#include "RenderSVGBlock.h" + +namespace WebCore { + +class SVGForeignObjectElement; + +class RenderForeignObject : public RenderSVGBlock { +public: + RenderForeignObject(SVGForeignObjectElement*); + + virtual const char* renderName() const { return "RenderForeignObject"; } + + virtual void paint(PaintInfo&, int parentX, int parentY); + + virtual TransformationMatrix localTransform() const { return m_localTransform; } + virtual bool calculateLocalTransform(); + + virtual void computeAbsoluteRepaintRect(IntRect&, bool fixed); + virtual bool requiresLayer(); + virtual void layout(); + + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); + + private: + TransformationMatrix translationForAttributes(); + + TransformationMatrix m_localTransform; + IntRect m_absoluteBounds; +}; + +} // namespace WebCore + +#endif // ENABLE(SVG) && ENABLE(SVG_FOREIGN_OBJECT) +#endif // RenderForeignObject_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderFrame.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderFrame.cpp new file mode 100644 index 0000000..15bd24e --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderFrame.cpp @@ -0,0 +1,62 @@ +/** + * This file is part of the KDE project. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 2000 Simon Hausmann <hausmann@kde.org> + * (C) 2000 Stefan Schimanski (1Stein@gmx.de) + * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. + * + * 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" +#include "RenderFrame.h" +#include "RenderFrameSet.h" +#include "FrameView.h" +#include "HTMLFrameSetElement.h" +#include "HTMLNames.h" + +namespace WebCore { + +using namespace HTMLNames; + +RenderFrame::RenderFrame(HTMLFrameElement* frame) + : RenderPart(frame) +{ + setInline(false); +} + +FrameEdgeInfo RenderFrame::edgeInfo() const +{ + return FrameEdgeInfo(element()->noResize(), element()->hasFrameBorder()); +} + +void RenderFrame::viewCleared() +{ + if (element() && m_widget && m_widget->isFrameView()) { + FrameView* view = static_cast<FrameView*>(m_widget); + int marginw = element()->getMarginWidth(); + int marginh = element()->getMarginHeight(); + + if (marginw != -1) + view->setMarginWidth(marginw); + if (marginh != -1) + view->setMarginHeight(marginh); + } +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderFrame.h b/src/3rdparty/webkit/WebCore/rendering/RenderFrame.h new file mode 100644 index 0000000..49aedd3 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderFrame.h @@ -0,0 +1,50 @@ +/* + * This file is part of the KDE project. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 2000 Simon Hausmann <hausmann@kde.org> + * Copyright (C) 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef RenderFrame_h +#define RenderFrame_h + +#include "HTMLFrameElement.h" +#include "RenderPart.h" +#include "RenderFrameSet.h" + +namespace WebCore { + +class RenderFrame : public RenderPart { +public: + RenderFrame(HTMLFrameElement*); + + virtual const char* renderName() const { return "RenderFrame"; } + virtual bool isFrame() const { return true; } + + HTMLFrameElement* element() const { return static_cast<HTMLFrameElement*>(RenderPart::element()); } + + FrameEdgeInfo edgeInfo() const; + + virtual void viewCleared(); +}; + +} // namespace WebCore + +#endif // RenderFrame_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderFrameSet.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderFrameSet.cpp new file mode 100644 index 0000000..d93fa86 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderFrameSet.cpp @@ -0,0 +1,669 @@ +/** + * This file is part of the KDE project. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 2000 Simon Hausmann <hausmann@kde.org> + * (C) 2000 Stefan Schimanski (1Stein@gmx.de) + * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. + * + * 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" +#include "RenderFrameSet.h" + +#include "Document.h" +#include "EventHandler.h" +#include "EventNames.h" +#include "Frame.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "HTMLFrameSetElement.h" +#include "HitTestRequest.h" +#include "HitTestResult.h" +#include "MouseEvent.h" +#include "RenderFrame.h" +#include "RenderView.h" + +namespace WebCore { + +RenderFrameSet::RenderFrameSet(HTMLFrameSetElement* frameSet) + : RenderContainer(frameSet) + , m_isResizing(false) + , m_isChildResizing(false) +{ + setInline(false); +} + +RenderFrameSet::~RenderFrameSet() +{ +} + +RenderFrameSet::GridAxis::GridAxis() + : m_splitBeingResized(noSplit) +{ +} + +inline HTMLFrameSetElement* RenderFrameSet::frameSet() const +{ + return static_cast<HTMLFrameSetElement*>(node()); +} + +static Color borderStartEdgeColor() +{ + return Color(170,170,170); +} + +static Color borderEndEdgeColor() +{ + return Color::black; +} + +static Color borderFillColor() +{ + return Color(208, 208, 208); +} + +void RenderFrameSet::paintColumnBorder(const PaintInfo& paintInfo, const IntRect& borderRect) +{ + if (!paintInfo.rect.intersects(borderRect)) + return; + + // FIXME: We should do something clever when borders from distinct framesets meet at a join. + + // Fill first. + GraphicsContext* context = paintInfo.context; + context->fillRect(borderRect, frameSet()->hasBorderColor() ? style()->borderLeftColor() : borderFillColor()); + + // Now stroke the edges but only if we have enough room to paint both edges with a little + // bit of the fill color showing through. + if (borderRect.width() >= 3) { + context->fillRect(IntRect(borderRect.topLeft(), IntSize(1, height())), borderStartEdgeColor()); + context->fillRect(IntRect(borderRect.topRight(), IntSize(1, height())), borderEndEdgeColor()); + } +} + +void RenderFrameSet::paintRowBorder(const PaintInfo& paintInfo, const IntRect& borderRect) +{ + if (!paintInfo.rect.intersects(borderRect)) + return; + + // FIXME: We should do something clever when borders from distinct framesets meet at a join. + + // Fill first. + GraphicsContext* context = paintInfo.context; + context->fillRect(borderRect, frameSet()->hasBorderColor() ? style()->borderLeftColor() : borderFillColor()); + + // Now stroke the edges but only if we have enough room to paint both edges with a little + // bit of the fill color showing through. + if (borderRect.height() >= 3) { + context->fillRect(IntRect(borderRect.topLeft(), IntSize(width(), 1)), borderStartEdgeColor()); + context->fillRect(IntRect(borderRect.bottomLeft(), IntSize(width(), 1)), borderEndEdgeColor()); + } +} + +void RenderFrameSet::paint(PaintInfo& paintInfo, int tx, int ty) +{ + if (paintInfo.phase != PaintPhaseForeground) + return; + + RenderObject* child = firstChild(); + if (!child) + return; + + // Add in our offsets. + tx += m_x; + ty += m_y; + + int rows = frameSet()->totalRows(); + int cols = frameSet()->totalCols(); + int borderThickness = frameSet()->border(); + + int yPos = 0; + for (int r = 0; r < rows; r++) { + int xPos = 0; + for (int c = 0; c < cols; c++) { + child->paint(paintInfo, tx, ty); + xPos += m_cols.m_sizes[c]; + if (borderThickness && m_cols.m_allowBorder[c + 1]) { + paintColumnBorder(paintInfo, IntRect(tx + xPos, ty + yPos, borderThickness, height())); + xPos += borderThickness; + } + child = child->nextSibling(); + if (!child) + return; + } + yPos += m_rows.m_sizes[r]; + if (borderThickness && m_rows.m_allowBorder[r + 1]) { + paintRowBorder(paintInfo, IntRect(tx, ty + yPos, width(), borderThickness)); + yPos += borderThickness; + } + } +} + +bool RenderFrameSet::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, + int x, int y, int tx, int ty, HitTestAction action) +{ + if (action != HitTestForeground) + return false; + + bool inside = RenderContainer::nodeAtPoint(request, result, x, y, tx, ty, action) + || m_isResizing || canResize(IntPoint(x, y)); + + if (inside && frameSet()->noResize() + && !request.readonly && !result.innerNode()) { + result.setInnerNode(node()); + result.setInnerNonSharedNode(node()); + } + + return inside || m_isChildResizing; +} + +void RenderFrameSet::GridAxis::resize(int size) +{ + m_sizes.resize(size); + m_deltas.resize(size); + m_deltas.fill(0); + + // To track edges for resizability and borders, we need to be (size + 1). This is because a parent frameset + // may ask us for information about our left/top/right/bottom edges in order to make its own decisions about + // what to do. We are capable of tainting that parent frameset's borders, so we have to cache this info. + m_preventResize.resize(size + 1); + m_allowBorder.resize(size + 1); +} + +void RenderFrameSet::layOutAxis(GridAxis& axis, const Length* grid, int availableLen) +{ + availableLen = max(availableLen, 0); + + int* gridLayout = axis.m_sizes.data(); + + if (!grid) { + gridLayout[0] = availableLen; + return; + } + + int gridLen = axis.m_sizes.size(); + ASSERT(gridLen); + + int totalRelative = 0; + int totalFixed = 0; + int totalPercent = 0; + int countRelative = 0; + int countFixed = 0; + int countPercent = 0; + + // First we need to investigate how many columns of each type we have and + // how much space these columns are going to require. + for (int i = 0; i < gridLen; ++i) { + // Count the total length of all of the fixed columns/rows -> totalFixed + // Count the number of columns/rows which are fixed -> countFixed + if (grid[i].isFixed()) { + gridLayout[i] = max(grid[i].value(), 0); + totalFixed += gridLayout[i]; + countFixed++; + } + + // Count the total percentage of all of the percentage columns/rows -> totalPercent + // Count the number of columns/rows which are percentages -> countPercent + if (grid[i].isPercent()) { + gridLayout[i] = max(grid[i].calcValue(availableLen), 0); + totalPercent += gridLayout[i]; + countPercent++; + } + + // Count the total relative of all the relative columns/rows -> totalRelative + // Count the number of columns/rows which are relative -> countRelative + if (grid[i].isRelative()) { + totalRelative += max(grid[i].value(), 1); + countRelative++; + } + } + + int remainingLen = availableLen; + + // Fixed columns/rows are our first priority. If there is not enough space to fit all fixed + // columns/rows we need to proportionally adjust their size. + if (totalFixed > remainingLen) { + int remainingFixed = remainingLen; + + for (int i = 0; i < gridLen; ++i) { + if (grid[i].isFixed()) { + gridLayout[i] = (gridLayout[i] * remainingFixed) / totalFixed; + remainingLen -= gridLayout[i]; + } + } + } else + remainingLen -= totalFixed; + + // Percentage columns/rows are our second priority. Divide the remaining space proportionally + // over all percentage columns/rows. IMPORTANT: the size of each column/row is not relative + // to 100%, but to the total percentage. For example, if there are three columns, each of 75%, + // and the available space is 300px, each column will become 100px in width. + if (totalPercent > remainingLen) { + int remainingPercent = remainingLen; + + for (int i = 0; i < gridLen; ++i) { + if (grid[i].isPercent()) { + gridLayout[i] = (gridLayout[i] * remainingPercent) / totalPercent; + remainingLen -= gridLayout[i]; + } + } + } else + remainingLen -= totalPercent; + + // Relative columns/rows are our last priority. Divide the remaining space proportionally + // over all relative columns/rows. IMPORTANT: the relative value of 0* is treated as 1*. + if (countRelative) { + int lastRelative = 0; + int remainingRelative = remainingLen; + + for (int i = 0; i < gridLen; ++i) { + if (grid[i].isRelative()) { + gridLayout[i] = (max(grid[i].value(), 1) * remainingRelative) / totalRelative; + remainingLen -= gridLayout[i]; + lastRelative = i; + } + } + + // If we could not evenly distribute the available space of all of the relative + // columns/rows, the remainder will be added to the last column/row. + // For example: if we have a space of 100px and three columns (*,*,*), the remainder will + // be 1px and will be added to the last column: 33px, 33px, 34px. + if (remainingLen) { + gridLayout[lastRelative] += remainingLen; + remainingLen = 0; + } + } + + // If we still have some left over space we need to divide it over the already existing + // columns/rows + if (remainingLen) { + // Our first priority is to spread if over the percentage columns. The remaining + // space is spread evenly, for example: if we have a space of 100px, the columns + // definition of 25%,25% used to result in two columns of 25px. After this the + // columns will each be 50px in width. + if (countPercent && totalPercent) { + int remainingPercent = remainingLen; + int changePercent = 0; + + for (int i = 0; i < gridLen; ++i) { + if (grid[i].isPercent()) { + changePercent = (remainingPercent * gridLayout[i]) / totalPercent; + gridLayout[i] += changePercent; + remainingLen -= changePercent; + } + } + } else if (totalFixed) { + // Our last priority is to spread the remaining space over the fixed columns. + // For example if we have 100px of space and two column of each 40px, both + // columns will become exactly 50px. + int remainingFixed = remainingLen; + int changeFixed = 0; + + for (int i = 0; i < gridLen; ++i) { + if (grid[i].isFixed()) { + changeFixed = (remainingFixed * gridLayout[i]) / totalFixed; + gridLayout[i] += changeFixed; + remainingLen -= changeFixed; + } + } + } + } + + // If we still have some left over space we probably ended up with a remainder of + // a division. We cannot spread it evenly anymore. If we have any percentage + // columns/rows simply spread the remainder equally over all available percentage columns, + // regardless of their size. + if (remainingLen && countPercent) { + int remainingPercent = remainingLen; + int changePercent = 0; + + for (int i = 0; i < gridLen; ++i) { + if (grid[i].isPercent()) { + changePercent = remainingPercent / countPercent; + gridLayout[i] += changePercent; + remainingLen -= changePercent; + } + } + } + + // If we don't have any percentage columns/rows we only have fixed columns. Spread + // the remainder equally over all fixed columns/rows. + else if (remainingLen && countFixed) { + int remainingFixed = remainingLen; + int changeFixed = 0; + + for (int i = 0; i < gridLen; ++i) { + if (grid[i].isFixed()) { + changeFixed = remainingFixed / countFixed; + gridLayout[i] += changeFixed; + remainingLen -= changeFixed; + } + } + } + + // Still some left over. Add it to the last column, because it is impossible + // spread it evenly or equally. + if (remainingLen) + gridLayout[gridLen - 1] += remainingLen; + + // now we have the final layout, distribute the delta over it + bool worked = true; + int* gridDelta = axis.m_deltas.data(); + for (int i = 0; i < gridLen; ++i) { + if (gridLayout[i] && gridLayout[i] + gridDelta[i] <= 0) + worked = false; + gridLayout[i] += gridDelta[i]; + } + // if the deltas broke something, undo them + if (!worked) { + for (int i = 0; i < gridLen; ++i) + gridLayout[i] -= gridDelta[i]; + axis.m_deltas.fill(0); + } +} + +void RenderFrameSet::fillFromEdgeInfo(const FrameEdgeInfo& edgeInfo, int r, int c) +{ + if (edgeInfo.allowBorder(LeftFrameEdge)) + m_cols.m_allowBorder[c] = true; + if (edgeInfo.allowBorder(RightFrameEdge)) + m_cols.m_allowBorder[c + 1] = true; + if (edgeInfo.preventResize(LeftFrameEdge)) + m_cols.m_preventResize[c] = true; + if (edgeInfo.preventResize(RightFrameEdge)) + m_cols.m_preventResize[c + 1] = true; + + if (edgeInfo.allowBorder(TopFrameEdge)) + m_rows.m_allowBorder[r] = true; + if (edgeInfo.allowBorder(BottomFrameEdge)) + m_rows.m_allowBorder[r + 1] = true; + if (edgeInfo.preventResize(TopFrameEdge)) + m_rows.m_preventResize[r] = true; + if (edgeInfo.preventResize(BottomFrameEdge)) + m_rows.m_preventResize[r + 1] = true; +} + +void RenderFrameSet::computeEdgeInfo() +{ + m_rows.m_preventResize.fill(frameSet()->noResize()); + m_rows.m_allowBorder.fill(false); + m_cols.m_preventResize.fill(frameSet()->noResize()); + m_cols.m_allowBorder.fill(false); + + RenderObject* child = firstChild(); + if (!child) + return; + + int rows = frameSet()->totalRows(); + int cols = frameSet()->totalCols(); + for (int r = 0; r < rows; ++r) { + for (int c = 0; c < cols; ++c) { + FrameEdgeInfo edgeInfo; + if (child->isFrameSet()) + edgeInfo = static_cast<RenderFrameSet*>(child)->edgeInfo(); + else + edgeInfo = static_cast<RenderFrame*>(child)->edgeInfo(); + fillFromEdgeInfo(edgeInfo, r, c); + child = child->nextSibling(); + if (!child) + return; + } + } +} + +FrameEdgeInfo RenderFrameSet::edgeInfo() const +{ + FrameEdgeInfo result(frameSet()->noResize(), true); + + int rows = frameSet()->totalRows(); + int cols = frameSet()->totalCols(); + if (rows && cols) { + result.setPreventResize(LeftFrameEdge, m_cols.m_preventResize[0]); + result.setAllowBorder(LeftFrameEdge, m_cols.m_allowBorder[0]); + result.setPreventResize(RightFrameEdge, m_cols.m_preventResize[cols]); + result.setAllowBorder(RightFrameEdge, m_cols.m_allowBorder[cols]); + result.setPreventResize(TopFrameEdge, m_rows.m_preventResize[0]); + result.setAllowBorder(TopFrameEdge, m_rows.m_allowBorder[0]); + result.setPreventResize(BottomFrameEdge, m_rows.m_preventResize[rows]); + result.setAllowBorder(BottomFrameEdge, m_rows.m_allowBorder[rows]); + } + + return result; +} + +void RenderFrameSet::layout() +{ + ASSERT(needsLayout()); + + bool doFullRepaint = selfNeedsLayout() && checkForRepaintDuringLayout(); + IntRect oldBounds; + if (doFullRepaint) + oldBounds = absoluteClippedOverflowRect(); + + if (!parent()->isFrameSet() && !document()->printing()) { + m_width = view()->viewWidth(); + m_height = view()->viewHeight(); + } + + size_t cols = frameSet()->totalCols(); + size_t rows = frameSet()->totalRows(); + + if (m_rows.m_sizes.size() != rows || m_cols.m_sizes.size() != cols) { + m_rows.resize(rows); + m_cols.resize(cols); + } + + int borderThickness = frameSet()->border(); + layOutAxis(m_rows, frameSet()->rowLengths(), m_height - (rows - 1) * borderThickness); + layOutAxis(m_cols, frameSet()->colLengths(), m_width - (cols - 1) * borderThickness); + + positionFrames(); + + RenderContainer::layout(); + + computeEdgeInfo(); + + if (doFullRepaint) { + view()->repaintViewRectangle(oldBounds); + IntRect newBounds = absoluteClippedOverflowRect(); + if (newBounds != oldBounds) + view()->repaintViewRectangle(newBounds); + } + + setNeedsLayout(false); +} + +void RenderFrameSet::positionFrames() +{ + RenderObject* child = firstChild(); + if (!child) + return; + + int rows = frameSet()->totalRows(); + int cols = frameSet()->totalCols(); + + int yPos = 0; + int borderThickness = frameSet()->border(); + for (int r = 0; r < rows; r++) { + int xPos = 0; + int height = m_rows.m_sizes[r]; + for (int c = 0; c < cols; c++) { + child->setPos(xPos, yPos); + int width = m_cols.m_sizes[c]; + + // has to be resized and itself resize its contents + if (width != child->width() || height != child->height()) { + child->setWidth(width); + child->setHeight(height); + child->setNeedsLayout(true); + child->layout(); + } + + xPos += width + borderThickness; + + child = child->nextSibling(); + if (!child) + return; + } + yPos += height + borderThickness; + } + + // all the remaining frames are hidden to avoid ugly spurious unflowed frames + for (; child; child = child->nextSibling()) { + child->setWidth(0); + child->setHeight(0); + child->setNeedsLayout(false); + } +} + +void RenderFrameSet::startResizing(GridAxis& axis, int position) +{ + int split = hitTestSplit(axis, position); + if (split == noSplit || !axis.m_allowBorder[split] || axis.m_preventResize[split]) { + axis.m_splitBeingResized = noSplit; + return; + } + axis.m_splitBeingResized = split; + axis.m_splitResizeOffset = position - splitPosition(axis, split); +} + +void RenderFrameSet::continueResizing(GridAxis& axis, int position) +{ + if (needsLayout()) + return; + if (axis.m_splitBeingResized == noSplit) + return; + int currentSplitPosition = splitPosition(axis, axis.m_splitBeingResized); + int delta = (position - currentSplitPosition) - axis.m_splitResizeOffset; + if (delta == 0) + return; + axis.m_deltas[axis.m_splitBeingResized - 1] += delta; + axis.m_deltas[axis.m_splitBeingResized] -= delta; + setNeedsLayout(true); +} + +bool RenderFrameSet::userResize(MouseEvent* evt) +{ + if (!m_isResizing) { + if (needsLayout()) + return false; + if (evt->type() == eventNames().mousedownEvent && evt->button() == LeftButton) { + startResizing(m_cols, evt->pageX() - xPos()); + startResizing(m_rows, evt->pageY() - yPos()); + if (m_cols.m_splitBeingResized != noSplit || m_rows.m_splitBeingResized != noSplit) { + setIsResizing(true); + return true; + } + } + } else { + if (evt->type() == eventNames().mousemoveEvent || (evt->type() == eventNames().mouseupEvent && evt->button() == LeftButton)) { + continueResizing(m_cols, evt->pageX() - xPos()); + continueResizing(m_rows, evt->pageY() - yPos()); + if (evt->type() == eventNames().mouseupEvent && evt->button() == LeftButton) { + setIsResizing(false); + return true; + } + } + } + + return false; +} + +void RenderFrameSet::setIsResizing(bool isResizing) +{ + m_isResizing = isResizing; + for (RenderObject* p = parent(); p; p = p->parent()) + if (p->isFrameSet()) + static_cast<RenderFrameSet*>(p)->m_isChildResizing = isResizing; + if (Frame* frame = document()->frame()) + frame->eventHandler()->setResizingFrameSet(isResizing ? frameSet() : 0); +} + +bool RenderFrameSet::isResizingRow() const +{ + return m_isResizing && m_rows.m_splitBeingResized != noSplit; +} + +bool RenderFrameSet::isResizingColumn() const +{ + return m_isResizing && m_cols.m_splitBeingResized != noSplit; +} + +bool RenderFrameSet::canResize(const IntPoint& p) const +{ + return hitTestSplit(m_cols, p.x()) != noSplit || hitTestSplit(m_rows, p.y()) != noSplit; +} + +bool RenderFrameSet::canResizeRow(const IntPoint& p) const +{ + int r = hitTestSplit(m_rows, p.y()); + return r != noSplit && m_rows.m_allowBorder[r] && !m_rows.m_preventResize[r]; +} + +bool RenderFrameSet::canResizeColumn(const IntPoint& p) const +{ + int c = hitTestSplit(m_cols, p.x()); + return c != noSplit && m_cols.m_allowBorder[c] && !m_cols.m_preventResize[c]; +} + +int RenderFrameSet::splitPosition(const GridAxis& axis, int split) const +{ + if (needsLayout()) + return 0; + + int borderThickness = frameSet()->border(); + + int size = axis.m_sizes.size(); + if (!size) + return 0; + + int position = 0; + for (int i = 0; i < split && i < size; ++i) + position += axis.m_sizes[i] + borderThickness; + return position - borderThickness; +} + +int RenderFrameSet::hitTestSplit(const GridAxis& axis, int position) const +{ + if (needsLayout()) + return noSplit; + + int borderThickness = frameSet()->border(); + if (borderThickness <= 0) + return noSplit; + + size_t size = axis.m_sizes.size(); + if (!size) + return noSplit; + + int splitPosition = axis.m_sizes[0]; + for (size_t i = 1; i < size; ++i) { + if (position >= splitPosition && position < splitPosition + borderThickness) + return i; + splitPosition += borderThickness + axis.m_sizes[i]; + } + return noSplit; +} + +bool RenderFrameSet::isChildAllowed(RenderObject* child, RenderStyle*) const +{ + return child->isFrame() || child->isFrameSet(); +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderFrameSet.h b/src/3rdparty/webkit/WebCore/rendering/RenderFrameSet.h new file mode 100644 index 0000000..5401c1b --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderFrameSet.h @@ -0,0 +1,122 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 2000 Simon Hausmann <hausmann@kde.org> + * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef RenderFrameSet_h +#define RenderFrameSet_h + +#include "RenderContainer.h" + +namespace WebCore { + +class HTMLFrameSetElement; +class MouseEvent; + +enum FrameEdge { LeftFrameEdge, RightFrameEdge, TopFrameEdge, BottomFrameEdge }; + +struct FrameEdgeInfo +{ + FrameEdgeInfo(bool preventResize = false, bool allowBorder = true) + : m_preventResize(4) + , m_allowBorder(4) + { + m_preventResize.fill(preventResize); + m_allowBorder.fill(allowBorder); + } + + bool preventResize(FrameEdge edge) const { return m_preventResize[edge]; } + bool allowBorder(FrameEdge edge) const { return m_allowBorder[edge]; } + + void setPreventResize(FrameEdge edge, bool preventResize) { m_preventResize[edge] = preventResize; } + void setAllowBorder(FrameEdge edge, bool allowBorder) { m_allowBorder[edge] = allowBorder; } + +private: + Vector<bool> m_preventResize; + Vector<bool> m_allowBorder; +}; + +class RenderFrameSet : public RenderContainer { +public: + RenderFrameSet(HTMLFrameSetElement*); + virtual ~RenderFrameSet(); + + virtual const char* renderName() const { return "RenderFrameSet"; } + virtual bool isFrameSet() const { return true; } + + virtual void layout(); + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); + virtual void paint(PaintInfo& paintInfo, int tx, int ty); + virtual bool isChildAllowed(RenderObject*, RenderStyle*) const; + + FrameEdgeInfo edgeInfo() const; + + bool userResize(MouseEvent*); + + bool isResizingRow() const; + bool isResizingColumn() const; + + bool canResizeRow(const IntPoint&) const; + bool canResizeColumn(const IntPoint&) const; + +private: + static const int noSplit = -1; + + class GridAxis : Noncopyable { + public: + GridAxis(); + void resize(int); + Vector<int> m_sizes; + Vector<int> m_deltas; + Vector<bool> m_preventResize; + Vector<bool> m_allowBorder; + int m_splitBeingResized; + int m_splitResizeOffset; + }; + + inline HTMLFrameSetElement* frameSet() const; + + bool canResize(const IntPoint&) const; + void setIsResizing(bool); + + void layOutAxis(GridAxis&, const Length*, int availableSpace); + void computeEdgeInfo(); + void fillFromEdgeInfo(const FrameEdgeInfo& edgeInfo, int r, int c); + void positionFrames(); + + int splitPosition(const GridAxis&, int split) const; + int hitTestSplit(const GridAxis&, int position) const; + + void startResizing(GridAxis&, int position); + void continueResizing(GridAxis&, int position); + + void paintRowBorder(const PaintInfo& paintInfo, const IntRect& rect); + void paintColumnBorder(const PaintInfo& paintInfo, const IntRect& rect); + + GridAxis m_rows; + GridAxis m_cols; + + bool m_isResizing; + bool m_isChildResizing; +}; + +} // namespace WebCore + +#endif // RenderFrameSet_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderHTMLCanvas.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderHTMLCanvas.cpp new file mode 100644 index 0000000..cc8c2c1 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderHTMLCanvas.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "RenderHTMLCanvas.h" + +#include "Document.h" +#include "GraphicsContext.h" +#include "HTMLCanvasElement.h" +#include "HTMLNames.h" +#include "RenderView.h" + +namespace WebCore { + +using namespace HTMLNames; + +RenderHTMLCanvas::RenderHTMLCanvas(HTMLCanvasElement* element) + : RenderReplaced(element, element->size()) +{ +} + +void RenderHTMLCanvas::paintReplaced(PaintInfo& paintInfo, int tx, int ty) +{ + IntRect rect = contentBox(); + rect.move(tx, ty); + static_cast<HTMLCanvasElement*>(node())->paint(paintInfo.context, rect); +} + +void RenderHTMLCanvas::canvasSizeChanged() +{ + IntSize size = static_cast<HTMLCanvasElement*>(node())->size(); + IntSize zoomedSize(size.width() * style()->effectiveZoom(), size.height() * style()->effectiveZoom()); + + if (size == intrinsicSize()) + return; + + setIntrinsicSize(size); + + if (!prefWidthsDirty()) + setPrefWidthsDirty(true); + + IntSize oldSize = IntSize(m_width, m_height); + calcWidth(); + calcHeight(); + if (oldSize == IntSize(m_width, m_height)) + return; + + if (!selfNeedsLayout()) + setNeedsLayout(true); +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderHTMLCanvas.h b/src/3rdparty/webkit/WebCore/rendering/RenderHTMLCanvas.h new file mode 100644 index 0000000..ad6c505 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderHTMLCanvas.h @@ -0,0 +1,52 @@ +/* + * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RenderHTMLCanvas_h +#define RenderHTMLCanvas_h + +#include "RenderReplaced.h" + +namespace WebCore { + +class HTMLCanvasElement; + +class RenderHTMLCanvas : public RenderReplaced { +public: + RenderHTMLCanvas(HTMLCanvasElement*); + + virtual const char* renderName() const { return "RenderHTMLCanvas"; } + + virtual void paintReplaced(PaintInfo& paintInfo, int tx, int ty); + + void canvasSizeChanged(); + +protected: + virtual void intrinsicSizeChanged() { canvasSizeChanged(); } + +}; + +} // namespace WebCore + +#endif // RenderHTMLCanvas_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderImage.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderImage.cpp new file mode 100644 index 0000000..fc6f19b --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderImage.cpp @@ -0,0 +1,577 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * (C) 2006 Allan Sandfeld Jensen (kde@carewolf.com) + * (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2008 Apple Inc. All rights reserved. + * + * 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" +#include "RenderImage.h" + +#include "BitmapImage.h" +#include "Document.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "HTMLImageElement.h" +#include "HTMLInputElement.h" +#include "HTMLMapElement.h" +#include "HTMLNames.h" +#include "HitTestResult.h" +#include "Page.h" +#include "RenderView.h" +#include "SystemTime.h" + +#if ENABLE(WML) +#include "WMLImageElement.h" +#include "WMLNames.h" +#endif + +using namespace std; + +namespace WebCore { + +static const double cInterpolationCutoff = 800. * 800.; +static const double cLowQualityTimeThreshold = 0.050; // 50 ms + +class RenderImageScaleData { +public: + RenderImageScaleData(RenderImage* image, const IntSize& size, double time, bool lowQualityScale) + : m_size(size) + , m_time(time) + , m_lowQualityScale(lowQualityScale) + , m_highQualityRepaintTimer(image, &RenderImage::highQualityRepaintTimerFired) + { + } + + ~RenderImageScaleData() + { + m_highQualityRepaintTimer.stop(); + } + + const IntSize& size() const { return m_size; } + double time() const { return m_time; } + bool useLowQualityScale() const { return m_lowQualityScale; } + Timer<RenderImage>& hiqhQualityRepaintTimer() { return m_highQualityRepaintTimer; } + + void setSize(const IntSize& s) { m_size = s; } + void setTime(double t) { m_time = t; } + void setUseLowQualityScale(bool b) + { + m_highQualityRepaintTimer.stop(); + m_lowQualityScale = b; + if (b) + m_highQualityRepaintTimer.startOneShot(cLowQualityTimeThreshold); + } + +private: + IntSize m_size; + double m_time; + bool m_lowQualityScale; + Timer<RenderImage> m_highQualityRepaintTimer; +}; + +class RenderImageScaleObserver +{ +public: + static bool shouldImagePaintAtLowQuality(RenderImage*, const IntSize&); + + static void imageDestroyed(RenderImage* image) + { + if (gImages) { + RenderImageScaleData* data = gImages->take(image); + delete data; + if (gImages->size() == 0) { + delete gImages; + gImages = 0; + } + } + } + + static void highQualityRepaintTimerFired(RenderImage* image) + { + RenderImageScaleObserver::imageDestroyed(image); + image->repaint(); + } + + static HashMap<RenderImage*, RenderImageScaleData*>* gImages; +}; + +bool RenderImageScaleObserver::shouldImagePaintAtLowQuality(RenderImage* image, const IntSize& size) +{ + // If the image is not a bitmap image, then none of this is relevant and we just paint at high + // quality. + if (!image->image() || !image->image()->isBitmapImage()) + return false; + + // Make sure to use the unzoomed image size, since if a full page zoom is in effect, the image + // is actually being scaled. + IntSize imageSize(image->image()->width(), image->image()->height()); + + // Look ourselves up in the hashtable. + RenderImageScaleData* data = 0; + if (gImages) + data = gImages->get(image); + + if (imageSize == size) { + // There is no scale in effect. If we had a scale in effect before, we can just delete this data. + if (data) { + gImages->remove(image); + delete data; + } + return false; + } + + // There is no need to hash scaled images that always use low quality mode when the page demands it. This is the iChat case. + if (image->document()->page()->inLowQualityImageInterpolationMode()) { + double totalPixels = static_cast<double>(image->image()->width()) * static_cast<double>(image->image()->height()); + if (totalPixels > cInterpolationCutoff) + return true; + } + + // If there is no data yet, we will paint the first scale at high quality and record the paint time in case a second scale happens + // very soon. + if (!data) { + data = new RenderImageScaleData(image, size, currentTime(), false); + if (!gImages) + gImages = new HashMap<RenderImage*, RenderImageScaleData*>; + gImages->set(image, data); + return false; + } + + // We are scaled, but we painted already at this size, so just keep using whatever mode we last painted with. + if (data->size() == size) + return data->useLowQualityScale(); + + // We have data and our size just changed. If this change happened quickly, go into low quality mode and then set a repaint + // timer to paint in high quality mode. Otherwise it is ok to just paint in high quality mode. + double newTime = currentTime(); + data->setUseLowQualityScale(newTime - data->time() < cLowQualityTimeThreshold); + data->setTime(newTime); + data->setSize(size); + return data->useLowQualityScale(); +} + +HashMap<RenderImage*, RenderImageScaleData*>* RenderImageScaleObserver::gImages = 0; + +void RenderImage::highQualityRepaintTimerFired(Timer<RenderImage>*) +{ + RenderImageScaleObserver::highQualityRepaintTimerFired(this); +} + +using namespace HTMLNames; + +RenderImage::RenderImage(Node* node) + : RenderReplaced(node, IntSize(0, 0)) + , m_cachedImage(0) +{ + updateAltText(); + + view()->frameView()->setIsVisuallyNonEmpty(); +} + +RenderImage::~RenderImage() +{ + if (m_cachedImage) + m_cachedImage->removeClient(this); + RenderImageScaleObserver::imageDestroyed(this); +} + +void RenderImage::setCachedImage(CachedImage* newImage) +{ + if (m_cachedImage == newImage) + return; + if (m_cachedImage) + m_cachedImage->removeClient(this); + m_cachedImage = newImage; + if (m_cachedImage) { + m_cachedImage->addClient(this); + if (m_cachedImage->errorOccurred()) + imageChanged(m_cachedImage.get()); + } +} + +// If we'll be displaying either alt text or an image, add some padding. +static const unsigned short paddingWidth = 4; +static const unsigned short paddingHeight = 4; + +// Alt text is restricted to this maximum size, in pixels. These are +// signed integers because they are compared with other signed values. +static const int maxAltTextWidth = 1024; +static const int maxAltTextHeight = 256; + +// Sets the image height and width to fit the alt text. Returns true if the +// image size changed. +bool RenderImage::setImageSizeForAltText(CachedImage* newImage /* = 0 */) +{ + int imageWidth = 0; + int imageHeight = 0; + + // If we'll be displaying either text or an image, add a little padding. + if (!m_altText.isEmpty() || newImage) { + imageWidth = paddingWidth; + imageHeight = paddingHeight; + } + + if (newImage) { + // imageSize() returns 0 for the error image. We need the true size of the + // error image, so we have to get it by grabbing image() directly. + imageWidth += newImage->image()->width() * style()->effectiveZoom(); + imageHeight += newImage->image()->height() * style()->effectiveZoom(); + } + + // we have an alt and the user meant it (its not a text we invented) + if (!m_altText.isEmpty()) { + const Font& font = style()->font(); + imageWidth = max(imageWidth, min(font.width(TextRun(m_altText.characters(), m_altText.length())), maxAltTextWidth)); + imageHeight = max(imageHeight, min(font.height(), maxAltTextHeight)); + } + + IntSize imageSize = IntSize(imageWidth, imageHeight); + if (imageSize == intrinsicSize()) + return false; + + setIntrinsicSize(imageSize); + return true; +} + +void RenderImage::imageChanged(WrappedImagePtr newImage, const IntRect* rect) +{ + if (documentBeingDestroyed()) + return; + + if (hasBoxDecorations() || hasMask()) + RenderReplaced::imageChanged(newImage, rect); + + if (newImage != imagePtr() || !newImage) + return; + + bool imageSizeChanged = false; + + // Set image dimensions, taking into account the size of the alt text. + if (errorOccurred()) + imageSizeChanged = setImageSizeForAltText(m_cachedImage.get()); + + bool shouldRepaint = true; + + // Image dimensions have been changed, see what needs to be done + if (imageSize(style()->effectiveZoom()) != intrinsicSize() || imageSizeChanged) { + if (!errorOccurred()) + setIntrinsicSize(imageSize(style()->effectiveZoom())); + + // In the case of generated image content using :before/:after, we might not be in the + // render tree yet. In that case, we don't need to worry about check for layout, since we'll get a + // layout when we get added in to the render tree hierarchy later. + if (containingBlock()) { + // lets see if we need to relayout at all.. + int oldwidth = m_width; + int oldheight = m_height; + if (!prefWidthsDirty()) + setPrefWidthsDirty(true); + calcWidth(); + calcHeight(); + + if (imageSizeChanged || m_width != oldwidth || m_height != oldheight) { + shouldRepaint = false; + if (!selfNeedsLayout()) + setNeedsLayout(true); + } + + m_width = oldwidth; + m_height = oldheight; + } + } + + if (shouldRepaint) { + IntRect repaintRect; + if (rect) { + // The image changed rect is in source image coordinates (pre-zooming), + // so map from the bounds of the image to the contentsBox. + repaintRect = enclosingIntRect(mapRect(*rect, FloatRect(FloatPoint(), imageSize(1.0f)), contentBox())); + // Guard against too-large changed rects. + repaintRect.intersect(contentBox()); + } else + repaintRect = contentBox(); + + repaintRectangle(repaintRect); + } +} + +void RenderImage::resetAnimation() +{ + if (m_cachedImage) { + image()->resetAnimation(); + if (!needsLayout()) + repaint(); + } +} + +void RenderImage::paintReplaced(PaintInfo& paintInfo, int tx, int ty) +{ + int cWidth = contentWidth(); + int cHeight = contentHeight(); + int leftBorder = borderLeft(); + int topBorder = borderTop(); + int leftPad = paddingLeft(); + int topPad = paddingTop(); + + if (document()->printing() && !view()->printImages()) + return; + + GraphicsContext* context = paintInfo.context; + + if (!hasImage() || errorOccurred()) { + if (paintInfo.phase == PaintPhaseSelection) + return; + + if (cWidth > 2 && cHeight > 2) { + // Draw an outline rect where the image should be. + context->setStrokeStyle(SolidStroke); + context->setStrokeColor(Color::lightGray); + context->setFillColor(Color::transparent); + context->drawRect(IntRect(tx + leftBorder + leftPad, ty + topBorder + topPad, cWidth, cHeight)); + + bool errorPictureDrawn = false; + int imageX = 0; + int imageY = 0; + // When calculating the usable dimensions, exclude the pixels of + // the ouline rect so the error image/alt text doesn't draw on it. + int usableWidth = cWidth - 2; + int usableHeight = cHeight - 2; + + if (errorOccurred() && !image()->isNull() && (usableWidth >= image()->width()) && (usableHeight >= image()->height())) { + // Center the error image, accounting for border and padding. + int centerX = (usableWidth - image()->width()) / 2; + if (centerX < 0) + centerX = 0; + int centerY = (usableHeight - image()->height()) / 2; + if (centerY < 0) + centerY = 0; + imageX = leftBorder + leftPad + centerX + 1; + imageY = topBorder + topPad + centerY + 1; + context->drawImage(image(), IntPoint(tx + imageX, ty + imageY)); + errorPictureDrawn = true; + } + + if (!m_altText.isEmpty()) { + String text = m_altText; + text.replace('\\', backslashAsCurrencySymbol()); + context->setFont(style()->font()); + context->setFillColor(style()->color()); + int ax = tx + leftBorder + leftPad; + int ay = ty + topBorder + topPad; + const Font& font = style()->font(); + int ascent = font.ascent(); + + // Only draw the alt text if it'll fit within the content box, + // and only if it fits above the error image. + TextRun textRun(text.characters(), text.length()); + int textWidth = font.width(textRun); + if (errorPictureDrawn) { + if (usableWidth >= textWidth && font.height() <= imageY) + context->drawText(textRun, IntPoint(ax, ay + ascent)); + } else if (usableWidth >= textWidth && cHeight >= font.height()) + context->drawText(textRun, IntPoint(ax, ay + ascent)); + } + } + } else if (hasImage() && cWidth > 0 && cHeight > 0) { + Image* img = image(cWidth, cHeight); + if (!img || img->isNull()) + return; + +#if PLATFORM(MAC) + if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled()) + paintCustomHighlight(tx - m_x, ty - m_y, style()->highlight(), true); +#endif + + IntSize contentSize(cWidth, cHeight); + bool useLowQualityScaling = RenderImageScaleObserver::shouldImagePaintAtLowQuality(this, contentSize); + IntRect rect(IntPoint(tx + leftBorder + leftPad, ty + topBorder + topPad), contentSize); + HTMLImageElement* imageElt = (element() && element()->hasTagName(imgTag)) ? static_cast<HTMLImageElement*>(element()) : 0; + CompositeOperator compositeOperator = imageElt ? imageElt->compositeOperator() : CompositeSourceOver; + context->drawImage(image(cWidth, cHeight), rect, compositeOperator, useLowQualityScaling); + } +} + +int RenderImage::minimumReplacedHeight() const +{ + return errorOccurred() ? intrinsicSize().height() : 0; +} + +HTMLMapElement* RenderImage::imageMap() +{ + HTMLImageElement* i = element() && element()->hasTagName(imgTag) ? static_cast<HTMLImageElement*>(element()) : 0; + return i ? i->document()->getImageMap(i->useMap()) : 0; +} + +bool RenderImage::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction) +{ + bool inside = RenderReplaced::nodeAtPoint(request, result, _x, _y, _tx, _ty, hitTestAction); + + if (inside && element()) { + int tx = _tx + m_x; + int ty = _ty + m_y; + + HTMLMapElement* map = imageMap(); + if (map) { + // we're a client side image map + inside = map->mapMouseEvent(_x - tx, _y - ty, IntSize(contentWidth(), contentHeight()), result); + result.setInnerNonSharedNode(element()); + } + } + + return inside; +} + +void RenderImage::updateAltText() +{ + if (!element()) + return; + + if (element()->hasTagName(inputTag)) + m_altText = static_cast<HTMLInputElement*>(element())->altText(); + else if (element()->hasTagName(imgTag)) + m_altText = static_cast<HTMLImageElement*>(element())->altText(); +#if ENABLE(WML) + else if (element()->hasTagName(WMLNames::imgTag)) + m_altText = static_cast<WMLImageElement*>(element())->altText(); +#endif +} + +bool RenderImage::isWidthSpecified() const +{ + switch (style()->width().type()) { + case Fixed: + case Percent: + return true; + case Auto: + case Relative: // FIXME: Shouldn't this case return true? + case Static: + case Intrinsic: + case MinIntrinsic: + return false; + } + ASSERT(false); + return false; +} + +bool RenderImage::isHeightSpecified() const +{ + switch (style()->height().type()) { + case Fixed: + case Percent: + return true; + case Auto: + case Relative: // FIXME: Shouldn't this case return true? + case Static: + case Intrinsic: + case MinIntrinsic: + return false; + } + ASSERT(false); + return false; +} + +int RenderImage::calcReplacedWidth(bool includeMaxWidth) const +{ + if (imageHasRelativeWidth()) + if (RenderObject* cb = isPositioned() ? container() : containingBlock()) + setImageContainerSize(IntSize(cb->availableWidth(), cb->availableHeight())); + + int width; + if (isWidthSpecified()) + width = calcReplacedWidthUsing(style()->width()); + else if (usesImageContainerSize()) + width = imageSize(style()->effectiveZoom()).width(); + else if (imageHasRelativeWidth()) + width = 0; // If the image is relatively-sized, set the width to 0 until there is a set container size. + else + width = calcAspectRatioWidth(); + + int minW = calcReplacedWidthUsing(style()->minWidth()); + int maxW = !includeMaxWidth || style()->maxWidth().isUndefined() ? width : calcReplacedWidthUsing(style()->maxWidth()); + + return max(minW, min(width, maxW)); +} + +int RenderImage::calcReplacedHeight() const +{ + int height; + if (isHeightSpecified()) + height = calcReplacedHeightUsing(style()->height()); + else if (usesImageContainerSize()) + height = imageSize(style()->effectiveZoom()).height(); + else if (imageHasRelativeHeight()) + height = 0; // If the image is relatively-sized, set the height to 0 until there is a set container size. + else + height = calcAspectRatioHeight(); + + int minH = calcReplacedHeightUsing(style()->minHeight()); + int maxH = style()->maxHeight().isUndefined() ? height : calcReplacedHeightUsing(style()->maxHeight()); + + return max(minH, min(height, maxH)); +} + +int RenderImage::calcAspectRatioWidth() const +{ + IntSize size = intrinsicSize(); + if (!size.height()) + return 0; + if (!hasImage() || errorOccurred()) + return size.width(); // Don't bother scaling. + return RenderReplaced::calcReplacedHeight() * size.width() / size.height(); +} + +int RenderImage::calcAspectRatioHeight() const +{ + IntSize size = intrinsicSize(); + if (!size.width()) + return 0; + if (!hasImage() || errorOccurred()) + return size.height(); // Don't bother scaling. + return RenderReplaced::calcReplacedWidth() * size.height() / size.width(); +} + +void RenderImage::calcPrefWidths() +{ + ASSERT(prefWidthsDirty()); + + int paddingAndBorders = paddingLeft() + paddingRight() + borderLeft() + borderRight(); + m_maxPrefWidth = calcReplacedWidth(false) + paddingAndBorders; + + if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) + m_maxPrefWidth = min(m_maxPrefWidth, style()->maxWidth().value() + (style()->boxSizing() == CONTENT_BOX ? paddingAndBorders : 0)); + + if (style()->width().isPercent() || style()->height().isPercent() || + style()->maxWidth().isPercent() || style()->maxHeight().isPercent() || + style()->minWidth().isPercent() || style()->minHeight().isPercent()) + m_minPrefWidth = 0; + else + m_minPrefWidth = m_maxPrefWidth; + + setPrefWidthsDirty(false); +} + +Image* RenderImage::nullImage() +{ + return Image::nullImage(); +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderImage.h b/src/3rdparty/webkit/WebCore/rendering/RenderImage.h new file mode 100644 index 0000000..71896d6 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderImage.h @@ -0,0 +1,107 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2006 Allan Sandfeld Jensen (kde@carewolf.com) + * (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef RenderImage_h +#define RenderImage_h + +#include "CachedImage.h" +#include "CachedResourceHandle.h" +#include "RenderReplaced.h" + +namespace WebCore { + +class HTMLMapElement; + +class RenderImage : public RenderReplaced { +public: + RenderImage(Node*); + virtual ~RenderImage(); + + virtual const char* renderName() const { return "RenderImage"; } + + virtual bool isImage() const { return true; } + virtual bool isRenderImage() const { return true; } + + virtual void paintReplaced(PaintInfo& paintInfo, int tx, int ty); + + virtual int minimumReplacedHeight() const; + + virtual void imageChanged(WrappedImagePtr, const IntRect* = 0); + + bool setImageSizeForAltText(CachedImage* newImage = 0); + + void updateAltText(); + + void setCachedImage(CachedImage*); + CachedImage* cachedImage() const { return m_cachedImage.get(); } + + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); + + virtual int calcReplacedWidth(bool includeMaxWidth = true) const; + virtual int calcReplacedHeight() const; + + virtual void calcPrefWidths(); + + HTMLMapElement* imageMap(); + + void resetAnimation(); + + virtual bool hasImage() const { return m_cachedImage; } + + void highQualityRepaintTimerFired(Timer<RenderImage>*); + +protected: + virtual Image* image(int /*w*/ = 0, int /*h*/ = 0) { return m_cachedImage ? m_cachedImage->image() : nullImage(); } + virtual bool errorOccurred() const { return m_cachedImage && m_cachedImage->errorOccurred(); } + virtual bool usesImageContainerSize() const { return m_cachedImage ? m_cachedImage->usesImageContainerSize() : false; } + virtual void setImageContainerSize(const IntSize& size) const { if (m_cachedImage) m_cachedImage->setImageContainerSize(size); } + virtual bool imageHasRelativeWidth() const { return m_cachedImage ? m_cachedImage->imageHasRelativeWidth() : false; } + virtual bool imageHasRelativeHeight() const { return m_cachedImage ? m_cachedImage->imageHasRelativeHeight() : false; } + virtual IntSize imageSize(float multiplier) const { return m_cachedImage ? m_cachedImage->imageSize(multiplier) : IntSize(); } + virtual WrappedImagePtr imagePtr() const { return m_cachedImage.get(); } + + virtual void intrinsicSizeChanged() { imageChanged(imagePtr()); } + +private: + int calcAspectRatioWidth() const; + int calcAspectRatioHeight() const; + + bool isWidthSpecified() const; + bool isHeightSpecified() const; + +protected: + // The image we are rendering. + CachedResourceHandle<CachedImage> m_cachedImage; + + // Text to display as long as the image isn't available. + String m_altText; + + static Image* nullImage(); + + friend class RenderImageScaleObserver; +}; + +} // namespace WebCore + +#endif // RenderImage_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderImageGeneratedContent.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderImageGeneratedContent.cpp new file mode 100644 index 0000000..29d0508 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderImageGeneratedContent.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2008 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "RenderImageGeneratedContent.h" + +#include "RenderBlock.h" +#include "RenderStyle.h" +#include "StyleCachedImage.h" + +namespace WebCore { + +RenderImageGeneratedContent::RenderImageGeneratedContent(Node* n) +: RenderImage(n) +{} + +RenderImageGeneratedContent::~RenderImageGeneratedContent() +{ + m_cachedImage = 0; + m_styleImage->removeClient(this); +} + +void RenderImageGeneratedContent::setStyleImage(StyleImage* image) +{ + if (image->isCachedImage()) + m_cachedImage = static_cast<StyleCachedImage*>(image)->cachedImage(); + m_styleImage = image; + m_styleImage->addClient(this); +} + +} diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderImageGeneratedContent.h b/src/3rdparty/webkit/WebCore/rendering/RenderImageGeneratedContent.h new file mode 100644 index 0000000..cab0192 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderImageGeneratedContent.h @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2008 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RenderImageGeneratedContent_h +#define RenderImageGeneratedContent_h + +#include "RenderImage.h" +#include <wtf/RefPtr.h> + +#include "RenderStyle.h" + +namespace WebCore { + +class StyleImage; + +class RenderImageGeneratedContent : public RenderImage +{ +public: + RenderImageGeneratedContent(Node*); + virtual ~RenderImageGeneratedContent(); + + void setStyleImage(StyleImage*); + + virtual bool hasImage() const { return true; } + +protected: + virtual Image* image(int w = 0, int h = 0) { return m_styleImage->image(this, IntSize(w, h)); } + virtual bool errorOccurred() const { return m_styleImage->errorOccurred(); } + virtual bool usesImageContainerSize() const { return m_styleImage->usesImageContainerSize(); } + virtual void setImageContainerSize(const IntSize& size) const { m_styleImage->setImageContainerSize(size); } + virtual bool imageHasRelativeWidth() const { return m_styleImage->imageHasRelativeWidth(); } + virtual bool imageHasRelativeHeight() const { return m_styleImage->imageHasRelativeHeight(); } + virtual IntSize imageSize(float multiplier) const { return m_styleImage->imageSize(this, multiplier); } + virtual WrappedImagePtr imagePtr() const { return m_styleImage->data(); } + +private: + RefPtr<StyleImage> m_styleImage; +}; + +} + +#endif diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderInline.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderInline.cpp new file mode 100644 index 0000000..40e5029 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderInline.cpp @@ -0,0 +1,399 @@ +/* + * This file is part of the render object implementation for KHTML. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. + * + * 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" +#include "RenderInline.h" + +#include "Document.h" +#include "RenderArena.h" +#include "RenderBlock.h" +#include "VisiblePosition.h" + +namespace WebCore { + +RenderInline::RenderInline(Node* node) + : RenderFlow(node) +{ +} + +RenderInline::~RenderInline() +{ +} + +void RenderInline::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +{ + RenderFlow::styleDidChange(diff, oldStyle); + + setInline(true); + setHasReflection(false); + + // Ensure that all of the split inlines pick up the new style. We + // only do this if we're an inline, since we don't want to propagate + // a block's style to the other inlines. + // e.g., <font>foo <h4>goo</h4> moo</font>. The <font> inlines before + // and after the block share the same style, but the block doesn't + // need to pass its style on to anyone else. + RenderFlow* currCont = continuation(); + while (currCont) { + if (currCont->isInline()) { + RenderFlow* nextCont = currCont->continuation(); + currCont->setContinuation(0); + currCont->setStyle(style()); + currCont->setContinuation(nextCont); + } + currCont = currCont->continuation(); + } + + m_lineHeight = -1; + + // Update pseudos for :before and :after now. + if (!isAnonymous()) { + updateBeforeAfterContent(RenderStyle::BEFORE); + updateBeforeAfterContent(RenderStyle::AFTER); + } +} + +bool RenderInline::isInlineContinuation() const +{ + return m_isContinuation; +} + +static inline bool isAfterContent(RenderObject* child) +{ + if (!child) + return false; + if (child->style()->styleType() != RenderStyle::AFTER) + return false; + // Text nodes don't have their own styles, so ignore the style on a text node. + if (child->isText() && !child->isBR()) + return false; + return true; +} + +void RenderInline::addChildToFlow(RenderObject* newChild, RenderObject* beforeChild) +{ + // Make sure we don't append things after :after-generated content if we have it. + if (!beforeChild && isAfterContent(lastChild())) + beforeChild = lastChild(); + + if (!newChild->isInline() && !newChild->isFloatingOrPositioned()) { + // We are placing a block inside an inline. We have to perform a split of this + // inline into continuations. This involves creating an anonymous block box to hold + // |newChild|. We then make that block box a continuation of this inline. We take all of + // the children after |beforeChild| and put them in a clone of this object. + RefPtr<RenderStyle> newStyle = RenderStyle::create(); + newStyle->inheritFrom(style()); + newStyle->setDisplay(BLOCK); + + RenderBlock* newBox = new (renderArena()) RenderBlock(document() /* anonymous box */); + newBox->setStyle(newStyle.release()); + RenderFlow* oldContinuation = continuation(); + setContinuation(newBox); + + // Someone may have put a <p> inside a <q>, causing a split. When this happens, the :after content + // has to move into the inline continuation. Call updateBeforeAfterContent to ensure that our :after + // content gets properly destroyed. + bool isLastChild = (beforeChild == lastChild()); + updateBeforeAfterContent(RenderStyle::AFTER); + if (isLastChild && beforeChild != lastChild()) + beforeChild = 0; // We destroyed the last child, so now we need to update our insertion + // point to be 0. It's just a straight append now. + + splitFlow(beforeChild, newBox, newChild, oldContinuation); + return; + } + + RenderContainer::addChild(newChild, beforeChild); + + newChild->setNeedsLayoutAndPrefWidthsRecalc(); +} + +RenderInline* RenderInline::cloneInline(RenderFlow* src) +{ + RenderInline* o = new (src->renderArena()) RenderInline(src->element()); + o->m_isContinuation = true; + o->setStyle(src->style()); + return o; +} + +void RenderInline::splitInlines(RenderBlock* fromBlock, RenderBlock* toBlock, + RenderBlock* middleBlock, + RenderObject* beforeChild, RenderFlow* oldCont) +{ + // Create a clone of this inline. + RenderInline* clone = cloneInline(this); + clone->setContinuation(oldCont); + + // Now take all of the children from beforeChild to the end and remove + // them from |this| and place them in the clone. + RenderObject* o = beforeChild; + while (o) { + RenderObject* tmp = o; + o = tmp->nextSibling(); + clone->addChildToFlow(removeChildNode(tmp), 0); + tmp->setNeedsLayoutAndPrefWidthsRecalc(); + } + + // Hook |clone| up as the continuation of the middle block. + middleBlock->setContinuation(clone); + + // We have been reparented and are now under the fromBlock. We need + // to walk up our inline parent chain until we hit the containing block. + // Once we hit the containing block we're done. + RenderFlow* curr = static_cast<RenderFlow*>(parent()); + RenderFlow* currChild = this; + + // FIXME: Because splitting is O(n^2) as tags nest pathologically, we cap the depth at which we're willing to clone. + // There will eventually be a better approach to this problem that will let us nest to a much + // greater depth (see bugzilla bug 13430) but for now we have a limit. This *will* result in + // incorrect rendering, but the alternative is to hang forever. + unsigned splitDepth = 1; + const unsigned cMaxSplitDepth = 200; + while (curr && curr != fromBlock) { + if (splitDepth < cMaxSplitDepth) { + // Create a new clone. + RenderInline* cloneChild = clone; + clone = cloneInline(curr); + + // Insert our child clone as the first child. + clone->addChildToFlow(cloneChild, 0); + + // Hook the clone up as a continuation of |curr|. + RenderFlow* oldCont = curr->continuation(); + curr->setContinuation(clone); + clone->setContinuation(oldCont); + + // Someone may have indirectly caused a <q> to split. When this happens, the :after content + // has to move into the inline continuation. Call updateBeforeAfterContent to ensure that the inline's :after + // content gets properly destroyed. + curr->updateBeforeAfterContent(RenderStyle::AFTER); + + // Now we need to take all of the children starting from the first child + // *after* currChild and append them all to the clone. + o = currChild->nextSibling(); + while (o) { + RenderObject* tmp = o; + o = tmp->nextSibling(); + clone->addChildToFlow(curr->removeChildNode(tmp), 0); + tmp->setNeedsLayoutAndPrefWidthsRecalc(); + } + } + + // Keep walking up the chain. + currChild = curr; + curr = static_cast<RenderFlow*>(curr->parent()); + splitDepth++; + } + + // Now we are at the block level. We need to put the clone into the toBlock. + toBlock->appendChildNode(clone); + + // Now take all the children after currChild and remove them from the fromBlock + // and put them in the toBlock. + o = currChild->nextSibling(); + while (o) { + RenderObject* tmp = o; + o = tmp->nextSibling(); + toBlock->appendChildNode(fromBlock->removeChildNode(tmp)); + } +} + +void RenderInline::splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox, + RenderObject* newChild, RenderFlow* oldCont) +{ + RenderBlock* pre = 0; + RenderBlock* block = containingBlock(); + + // Delete our line boxes before we do the inline split into continuations. + block->deleteLineBoxTree(); + + bool madeNewBeforeBlock = false; + if (block->isAnonymousBlock() && (!block->parent() || !block->parent()->createsAnonymousWrapper())) { + // We can reuse this block and make it the preBlock of the next continuation. + pre = block; + block = block->containingBlock(); + } else { + // No anonymous block available for use. Make one. + pre = block->createAnonymousBlock(); + madeNewBeforeBlock = true; + } + + RenderBlock* post = block->createAnonymousBlock(); + + RenderObject* boxFirst = madeNewBeforeBlock ? block->firstChild() : pre->nextSibling(); + if (madeNewBeforeBlock) + block->insertChildNode(pre, boxFirst); + block->insertChildNode(newBlockBox, boxFirst); + block->insertChildNode(post, boxFirst); + block->setChildrenInline(false); + + if (madeNewBeforeBlock) { + RenderObject* o = boxFirst; + while (o) { + RenderObject* no = o; + o = no->nextSibling(); + pre->appendChildNode(block->removeChildNode(no)); + no->setNeedsLayoutAndPrefWidthsRecalc(); + } + } + + splitInlines(pre, post, newBlockBox, beforeChild, oldCont); + + // We already know the newBlockBox isn't going to contain inline kids, so avoid wasting + // time in makeChildrenNonInline by just setting this explicitly up front. + newBlockBox->setChildrenInline(false); + + // We don't just call addChild, since it would pass things off to the + // continuation, so we call addChildToFlow explicitly instead. We delayed + // adding the newChild until now so that the |newBlockBox| would be fully + // connected, thus allowing newChild access to a renderArena should it need + // to wrap itself in additional boxes (e.g., table construction). + newBlockBox->addChildToFlow(newChild, 0); + + // Always just do a full layout in order to ensure that line boxes (especially wrappers for images) + // get deleted properly. Because objects moves from the pre block into the post block, we want to + // make new line boxes instead of leaving the old line boxes around. + pre->setNeedsLayoutAndPrefWidthsRecalc(); + block->setNeedsLayoutAndPrefWidthsRecalc(); + post->setNeedsLayoutAndPrefWidthsRecalc(); +} + +void RenderInline::paint(PaintInfo& paintInfo, int tx, int ty) +{ + paintLines(paintInfo, tx, ty); +} + +void RenderInline::absoluteRects(Vector<IntRect>& rects, int tx, int ty, bool topLevel) +{ + for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) + rects.append(IntRect(tx + curr->xPos(), ty + curr->yPos(), curr->width(), curr->height())); + + for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { + if (!curr->isText()) + curr->absoluteRects(rects, tx + curr->xPos(), ty + curr->yPos(), false); + } + + if (continuation() && topLevel) + continuation()->absoluteRects(rects, + tx - containingBlock()->xPos() + continuation()->xPos(), + ty - containingBlock()->yPos() + continuation()->yPos(), + topLevel); +} + +void RenderInline::absoluteQuads(Vector<FloatQuad>& quads, bool topLevel) +{ + for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) { + FloatRect localRect(curr->xPos(), curr->yPos(), curr->width(), curr->height()); + quads.append(localToAbsoluteQuad(localRect)); + } + + for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { + if (!curr->isText()) + curr->absoluteQuads(quads, false); + } + + if (continuation() && topLevel) + continuation()->absoluteQuads(quads, topLevel); +} + +bool RenderInline::requiresLayer() +{ + return isRelPositioned() || isTransparent() || hasMask(); +} + +int RenderInline::width() const +{ + // Return the width of the minimal left side and the maximal right side. + int leftSide = 0; + int rightSide = 0; + for (InlineRunBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) { + if (curr == firstLineBox() || curr->xPos() < leftSide) + leftSide = curr->xPos(); + if (curr == firstLineBox() || curr->xPos() + curr->width() > rightSide) + rightSide = curr->xPos() + curr->width(); + } + + return rightSide - leftSide; +} + +int RenderInline::height() const +{ + // See <rdar://problem/5289721>, for an unknown reason the linked list here is sometimes inconsistent, first is non-zero and last is zero. We have been + // unable to reproduce this at all (and consequently unable to figure ot why this is happening). The assert will hopefully catch the problem in debug + // builds and help us someday figure out why. We also put in a redundant check of lastLineBox() to avoid the crash for now. + ASSERT(!firstLineBox() == !lastLineBox()); // Either both are null or both exist. + if (firstLineBox() && lastLineBox()) + return lastLineBox()->yPos() + lastLineBox()->height() - firstLineBox()->yPos(); + return 0; +} + +int RenderInline::offsetLeft() const +{ + int x = RenderFlow::offsetLeft(); + if (firstLineBox()) + x += firstLineBox()->xPos(); + return x; +} + +int RenderInline::offsetTop() const +{ + int y = RenderFlow::offsetTop(); + if (firstLineBox()) + y += firstLineBox()->yPos(); + return y; +} + +const char* RenderInline::renderName() const +{ + if (isRelPositioned()) + return "RenderInline (relative positioned)"; + if (isAnonymous()) + return "RenderInline (generated)"; + return "RenderInline"; +} + +bool RenderInline::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, + int x, int y, int tx, int ty, HitTestAction hitTestAction) +{ + return hitTestLines(request, result, x, y, tx, ty, hitTestAction); +} + +VisiblePosition RenderInline::positionForCoordinates(int x, int y) +{ + // Translate the coords from the pre-anonymous block to the post-anonymous block. + RenderBlock* cb = containingBlock(); + int parentBlockX = cb->xPos() + x; + int parentBlockY = cb->yPos() + y; + for (RenderObject* c = continuation(); c; c = c->continuation()) { + RenderObject* contBlock = c; + if (c->isInline()) + contBlock = c->containingBlock(); + if (c->isInline() || c->firstChild()) + return c->positionForCoordinates(parentBlockX - contBlock->xPos(), parentBlockY - contBlock->yPos()); + } + + return RenderFlow::positionForCoordinates(x, y); +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderInline.h b/src/3rdparty/webkit/WebCore/rendering/RenderInline.h new file mode 100644 index 0000000..1c42a6f --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderInline.h @@ -0,0 +1,84 @@ +/* + * This file is part of the render object implementation for KHTML. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef RenderInline_h +#define RenderInline_h + +#include "RenderFlow.h" + +namespace WebCore { + +class Position; + +class RenderInline : public RenderFlow { +public: + RenderInline(Node*); + virtual ~RenderInline(); + + virtual const char* renderName() const; + + virtual bool isRenderInline() const { return true; } + virtual bool isInlineFlow() const { return true; } + virtual bool childrenInline() const { return true; } + + virtual bool isInlineContinuation() const; + + virtual void addChildToFlow(RenderObject* newChild, RenderObject* beforeChild); + void splitInlines(RenderBlock* fromBlock, RenderBlock* toBlock, RenderBlock* middleBlock, + RenderObject* beforeChild, RenderFlow* oldCont); + void splitFlow(RenderObject* beforeChild, RenderBlock* newBlockBox, + RenderObject* newChild, RenderFlow* oldCont); + + virtual void layout() { } // Do nothing for layout() + + virtual void paint(PaintInfo&, int tx, int ty); + + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); + + // overrides RenderObject + virtual bool requiresLayer(); + + virtual int width() const; + virtual int height() const; + + // used to calculate offsetWidth/Height. Overridden by inlines (RenderFlow) to return + // the remaining width on a given line (and the height of a single line). + virtual int offsetLeft() const; + virtual int offsetTop() const; + + void absoluteRects(Vector<IntRect>&, int tx, int ty, bool topLevel = true); + virtual void absoluteQuads(Vector<FloatQuad>&, bool topLevel = true); + + virtual VisiblePosition positionForCoordinates(int x, int y); + +protected: + virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + + static RenderInline* cloneInline(RenderFlow* src); + +}; + +} // namespace WebCore + +#endif // RenderInline_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderLayer.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderLayer.cpp new file mode 100644 index 0000000..9c9eff0 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderLayer.cpp @@ -0,0 +1,2609 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * Portions are Copyright (C) 1998 Netscape Communications Corporation. + * + * Other contributors: + * Robert O'Callahan <roc+@cs.cmu.edu> + * David Baron <dbaron@fas.harvard.edu> + * Christian Biesinger <cbiesinger@web.de> + * Randall Jesup <rjesup@wgate.com> + * Roland Mainz <roland.mainz@informatik.med.uni-giessen.de> + * Josh Soref <timeless@mac.com> + * Boris Zbarsky <bzbarsky@mit.edu> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Alternatively, the contents of this file may be used under the terms + * of either the Mozilla Public License Version 1.1, found at + * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public + * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html + * (the "GPL"), in which case the provisions of the MPL or the GPL are + * applicable instead of those above. If you wish to allow use of your + * version of this file only under the terms of one of those two + * licenses (the MPL or the GPL) and not to allow others to use your + * version of this file under the LGPL, indicate your decision by + * deletingthe provisions above and replace them with the notice and + * other provisions required by the MPL or the GPL, as the case may be. + * If you do not delete the provisions above, a recipient may use your + * version of this file under any of the LGPL, the MPL or the GPL. + */ + +#include "config.h" +#include "RenderLayer.h" + +#include "CSSPropertyNames.h" +#include "Document.h" +#include "EventHandler.h" +#include "EventNames.h" +#include "FloatRect.h" +#include "FocusController.h" +#include "Frame.h" +#include "FrameTree.h" +#include "FrameView.h" +#include "Gradient.h" +#include "GraphicsContext.h" +#include "HTMLNames.h" +#include "HitTestRequest.h" +#include "HitTestResult.h" +#include "OverflowEvent.h" +#include "Page.h" +#include "PlatformMouseEvent.h" +#include "RenderArena.h" +#include "RenderInline.h" +#include "RenderMarquee.h" +#include "RenderReplica.h" +#include "RenderScrollbar.h" +#include "RenderScrollbarPart.h" +#include "RenderTheme.h" +#include "RenderView.h" +#include "ScaleTransformOperation.h" +#include "Scrollbar.h" +#include "ScrollbarTheme.h" +#include "SelectionController.h" +#include "TranslateTransformOperation.h" +#include <wtf/StdLibExtras.h> + +#if ENABLE(SVG) +#include "SVGNames.h" +#endif + +#define MIN_INTERSECT_FOR_REVEAL 32 + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +const RenderLayer::ScrollAlignment RenderLayer::gAlignCenterIfNeeded = { RenderLayer::noScroll, RenderLayer::alignCenter, RenderLayer::alignToClosestEdge }; +const RenderLayer::ScrollAlignment RenderLayer::gAlignToEdgeIfNeeded = { RenderLayer::noScroll, RenderLayer::alignToClosestEdge, RenderLayer::alignToClosestEdge }; +const RenderLayer::ScrollAlignment RenderLayer::gAlignCenterAlways = { RenderLayer::alignCenter, RenderLayer::alignCenter, RenderLayer::alignCenter }; +const RenderLayer::ScrollAlignment RenderLayer::gAlignTopAlways = { RenderLayer::alignTop, RenderLayer::alignTop, RenderLayer::alignTop }; +const RenderLayer::ScrollAlignment RenderLayer::gAlignBottomAlways = { RenderLayer::alignBottom, RenderLayer::alignBottom, RenderLayer::alignBottom }; + +const int MinimumWidthWhileResizing = 100; +const int MinimumHeightWhileResizing = 40; + +void* ClipRects::operator new(size_t sz, RenderArena* renderArena) throw() +{ + return renderArena->allocate(sz); +} + +void ClipRects::operator delete(void* ptr, size_t sz) +{ + // Stash size where destroy can find it. + *(size_t *)ptr = sz; +} + +void ClipRects::destroy(RenderArena* renderArena) +{ + delete this; + + // Recover the size left there for us by operator delete and free the memory. + renderArena->free(*(size_t *)this, this); +} + +RenderLayer::RenderLayer(RenderObject* object) + : m_object(object) + , m_parent(0) + , m_previous(0) + , m_next(0) + , m_first(0) + , m_last(0) + , m_relX(0) + , m_relY(0) + , m_x(0) + , m_y(0) + , m_width(0) + , m_height(0) + , m_scrollX(0) + , m_scrollY(0) + , m_scrollOriginX(0) + , m_scrollLeftOverflow(0) + , m_scrollWidth(0) + , m_scrollHeight(0) + , m_inResizeMode(false) + , m_posZOrderList(0) + , m_negZOrderList(0) + , m_overflowList(0) + , m_clipRects(0) +#ifndef NDEBUG + , m_clipRectsRoot(0) +#endif + , m_scrollDimensionsDirty(true) + , m_zOrderListsDirty(true) + , m_overflowListDirty(true) + , m_isOverflowOnly(shouldBeOverflowOnly()) + , m_usedTransparency(false) + , m_paintingInsideReflection(false) + , m_inOverflowRelayout(false) + , m_needsFullRepaint(false) + , m_overflowStatusDirty(true) + , m_visibleContentStatusDirty(true) + , m_hasVisibleContent(false) + , m_visibleDescendantStatusDirty(false) + , m_hasVisibleDescendant(false) + , m_marquee(0) + , m_staticX(0) + , m_staticY(0) + , m_transform(0) + , m_reflection(0) + , m_scrollCorner(0) + , m_resizer(0) +{ + if (!object->firstChild() && object->style()) { + m_visibleContentStatusDirty = false; + m_hasVisibleContent = object->style()->visibility() == VISIBLE; + } +} + +RenderLayer::~RenderLayer() +{ + if (inResizeMode() && !renderer()->documentBeingDestroyed()) { + if (Frame* frame = renderer()->document()->frame()) + frame->eventHandler()->resizeLayerDestroyed(); + } + + destroyScrollbar(HorizontalScrollbar); + destroyScrollbar(VerticalScrollbar); + + // Child layers will be deleted by their corresponding render objects, so + // we don't need to delete them ourselves. + + delete m_posZOrderList; + delete m_negZOrderList; + delete m_overflowList; + delete m_marquee; + + // Make sure we have no lingering clip rects. + ASSERT(!m_clipRects); + + if (m_reflection) { + if (!m_reflection->documentBeingDestroyed()) + m_reflection->removeLayers(this); + m_reflection->setParent(0); + m_reflection->destroy(); + } + + if (m_scrollCorner) + m_scrollCorner->destroy(); + if (m_resizer) + m_resizer->destroy(); +} + +void RenderLayer::updateLayerPositions(bool doFullRepaint, bool checkForRepaint) +{ + if (doFullRepaint) { + m_object->repaint(); + checkForRepaint = doFullRepaint = false; + } + + updateLayerPosition(); // For relpositioned layers or non-positioned layers, + // we need to keep in sync, since we may have shifted relative + // to our parent layer. + + int x = 0; + int y = 0; + convertToLayerCoords(root(), x, y); + positionOverflowControls(x, y); + + updateVisibilityStatus(); + + updateTransform(); + + if (m_hasVisibleContent) { + RenderView* view = m_object->view(); + ASSERT(view); + // FIXME: Optimize using LayoutState and remove the disableLayoutState() call + // from updateScrollInfoAfterLayout(). + ASSERT(!view->layoutState()); + + IntRect newRect = m_object->absoluteClippedOverflowRect(); + IntRect newOutlineBox = m_object->absoluteOutlineBounds(); + if (checkForRepaint) { + if (view && !view->printing()) { + if (m_needsFullRepaint) { + view->repaintViewRectangle(m_repaintRect); + if (newRect != m_repaintRect) + view->repaintViewRectangle(newRect); + } else + m_object->repaintAfterLayoutIfNeeded(m_repaintRect, m_outlineBox); + } + } + m_repaintRect = newRect; + m_outlineBox = newOutlineBox; + } else { + m_repaintRect = IntRect(); + m_outlineBox = IntRect(); + } + + m_needsFullRepaint = false; + + // Go ahead and update the reflection's position and size. + if (m_reflection) + m_reflection->layout(); + + for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) + child->updateLayerPositions(doFullRepaint, checkForRepaint); + + // With all our children positioned, now update our marquee if we need to. + if (m_marquee) + m_marquee->updateMarqueePosition(); +} + +void RenderLayer::updateTransform() +{ + bool hasTransform = renderer()->hasTransform(); + bool hadTransform = m_transform; + if (hasTransform != hadTransform) { + if (hasTransform) + m_transform.set(new TransformationMatrix); + else + m_transform.clear(); + } + + if (hasTransform) { + m_transform->reset(); + renderer()->style()->applyTransform(*m_transform, renderer()->borderBox().size()); + } +} + +void RenderLayer::setHasVisibleContent(bool b) +{ + if (m_hasVisibleContent == b && !m_visibleContentStatusDirty) + return; + m_visibleContentStatusDirty = false; + m_hasVisibleContent = b; + if (m_hasVisibleContent) { + m_repaintRect = renderer()->absoluteClippedOverflowRect(); + m_outlineBox = renderer()->absoluteOutlineBounds(); + if (!isOverflowOnly()) + dirtyStackingContextZOrderLists(); + } + if (parent()) + parent()->childVisibilityChanged(m_hasVisibleContent); +} + +void RenderLayer::dirtyVisibleContentStatus() +{ + m_visibleContentStatusDirty = true; + if (parent()) + parent()->dirtyVisibleDescendantStatus(); +} + +void RenderLayer::childVisibilityChanged(bool newVisibility) +{ + if (m_hasVisibleDescendant == newVisibility || m_visibleDescendantStatusDirty) + return; + if (newVisibility) { + RenderLayer* l = this; + while (l && !l->m_visibleDescendantStatusDirty && !l->m_hasVisibleDescendant) { + l->m_hasVisibleDescendant = true; + l = l->parent(); + } + } else + dirtyVisibleDescendantStatus(); +} + +void RenderLayer::dirtyVisibleDescendantStatus() +{ + RenderLayer* l = this; + while (l && !l->m_visibleDescendantStatusDirty) { + l->m_visibleDescendantStatusDirty = true; + l = l->parent(); + } +} + +void RenderLayer::updateVisibilityStatus() +{ + if (m_visibleDescendantStatusDirty) { + m_hasVisibleDescendant = false; + for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) { + child->updateVisibilityStatus(); + if (child->m_hasVisibleContent || child->m_hasVisibleDescendant) { + m_hasVisibleDescendant = true; + break; + } + } + m_visibleDescendantStatusDirty = false; + } + + if (m_visibleContentStatusDirty) { + if (m_object->style()->visibility() == VISIBLE) + m_hasVisibleContent = true; + else { + // layer may be hidden but still have some visible content, check for this + m_hasVisibleContent = false; + RenderObject* r = m_object->firstChild(); + while (r) { + if (r->style()->visibility() == VISIBLE && !r->hasLayer()) { + m_hasVisibleContent = true; + break; + } + if (r->firstChild() && !r->hasLayer()) + r = r->firstChild(); + else if (r->nextSibling()) + r = r->nextSibling(); + else { + do { + r = r->parent(); + if (r==m_object) + r = 0; + } while (r && !r->nextSibling()); + if (r) + r = r->nextSibling(); + } + } + } + m_visibleContentStatusDirty = false; + } +} + +void RenderLayer::updateLayerPosition() +{ + // Clear our cached clip rect information. + clearClipRects(); + + int x = m_object->xPos(); + int y = m_object->yPos() - m_object->borderTopExtra(); + + if (!m_object->isPositioned() && m_object->parent()) { + // We must adjust our position by walking up the render tree looking for the + // nearest enclosing object with a layer. + RenderObject* curr = m_object->parent(); + while (curr && !curr->hasLayer()) { + if (!curr->isTableRow()) { + // Rows and cells share the same coordinate space (that of the section). + // Omit them when computing our xpos/ypos. + x += curr->xPos(); + y += curr->yPos(); + } + curr = curr->parent(); + } + y += curr->borderTopExtra(); + if (curr->isTableRow()) { + // Put ourselves into the row coordinate space. + x -= curr->xPos(); + y -= curr->yPos(); + } + } + + m_relX = m_relY = 0; + if (m_object->isRelPositioned()) { + m_relX = static_cast<RenderBox*>(m_object)->relativePositionOffsetX(); + m_relY = static_cast<RenderBox*>(m_object)->relativePositionOffsetY(); + x += m_relX; y += m_relY; + } + + // Subtract our parent's scroll offset. + if (m_object->isPositioned() && enclosingPositionedAncestor()) { + RenderLayer* positionedParent = enclosingPositionedAncestor(); + + // For positioned layers, we subtract out the enclosing positioned layer's scroll offset. + positionedParent->subtractScrolledContentOffset(x, y); + + if (m_object->isPositioned()) { + IntSize offset = static_cast<RenderBox*>(m_object)->offsetForPositionedInContainer(positionedParent->renderer()); + x += offset.width(); + y += offset.height(); + } + } else if (parent()) + parent()->subtractScrolledContentOffset(x, y); + + setPos(x,y); + + setWidth(m_object->width()); + setHeight(m_object->height() + m_object->borderTopExtra() + m_object->borderBottomExtra()); + + if (!m_object->hasOverflowClip()) { + if (m_object->overflowWidth() > m_object->width()) + setWidth(m_object->overflowWidth()); + if (m_object->overflowHeight() > m_object->height()) + setHeight(m_object->overflowHeight()); + } +} + +RenderLayer *RenderLayer::stackingContext() const +{ + RenderLayer* curr = parent(); + for ( ; curr && !curr->m_object->isRenderView() && !curr->m_object->isRoot() && + curr->m_object->style()->hasAutoZIndex(); + curr = curr->parent()) { } + return curr; +} + +RenderLayer* RenderLayer::enclosingPositionedAncestor() const +{ + RenderLayer* curr = parent(); + for ( ; curr && !curr->m_object->isRenderView() && !curr->m_object->isPositioned() && !curr->m_object->isRelPositioned() && !curr->hasTransform(); + curr = curr->parent()) { } + return curr; +} + +RenderLayer* RenderLayer::enclosingTransformedAncestor() const +{ + RenderLayer* curr = parent(); + for ( ; curr && !curr->m_object->isRenderView() && !curr->transform(); curr = curr->parent()) + { } + return curr; +} + +IntPoint RenderLayer::absoluteToContents(const IntPoint& absolutePoint) const +{ + // We don't use convertToLayerCoords because it doesn't know about transforms + return roundedIntPoint(renderer()->absoluteToLocal(absolutePoint, false, true)); +} + +bool RenderLayer::requiresSlowRepaints() const +{ + if (isTransparent() || hasReflection() || hasTransform()) + return true; + if (!parent()) + return false; + return parent()->requiresSlowRepaints(); +} + +bool RenderLayer::isTransparent() const +{ +#if ENABLE(SVG) + if (m_object->node()->namespaceURI() == SVGNames::svgNamespaceURI) + return false; +#endif + return m_object->isTransparent() || m_object->hasMask(); +} + +RenderLayer* +RenderLayer::transparentAncestor() +{ + RenderLayer* curr = parent(); + for ( ; curr && !curr->isTransparent(); curr = curr->parent()) { } + return curr; +} + +static IntRect transparencyClipBox(const TransformationMatrix& enclosingTransform, const RenderLayer* l, const RenderLayer* rootLayer) +{ + // FIXME: Although this function completely ignores CSS-imposed clipping, we did already intersect with the + // paintDirtyRect, and that should cut down on the amount we have to paint. Still it + // would be better to respect clips. + + TransformationMatrix* t = l->transform(); + if (t && rootLayer != l) { + // The best we can do here is to use enclosed bounding boxes to establish a "fuzzy" enough clip to encompass + // the transformed layer and all of its children. + int x = 0; + int y = 0; + l->convertToLayerCoords(rootLayer, x, y); + TransformationMatrix transform; + transform.translate(x, y); + transform = *t * transform; + transform = transform * enclosingTransform; + + // We now have a transform that will produce a rectangle in our view's space. + IntRect clipRect = transform.mapRect(l->boundingBox(l)); + + // Now shift the root layer to be us and pass down the new enclosing transform. + for (RenderLayer* curr = l->firstChild(); curr; curr = curr->nextSibling()) { + if (!l->reflection() || l->reflectionLayer() != curr) + clipRect.unite(transparencyClipBox(transform, curr, l)); + } + + return clipRect; + } + + // Note: we don't have to walk z-order lists since transparent elements always establish + // a stacking context. This means we can just walk the layer tree directly. + IntRect clipRect = l->boundingBox(rootLayer); + + // If we have a mask, then the clip is limited to the border box area (and there is + // no need to examine child layers). + if (!l->renderer()->hasMask()) { + for (RenderLayer* curr = l->firstChild(); curr; curr = curr->nextSibling()) { + if (!l->reflection() || l->reflectionLayer() != curr) + clipRect.unite(transparencyClipBox(enclosingTransform, curr, rootLayer)); + } + } + + // Now map the clipRect via the enclosing transform + return enclosingTransform.mapRect(clipRect); +} + +void RenderLayer::beginTransparencyLayers(GraphicsContext* p, const RenderLayer* rootLayer) +{ + if (p->paintingDisabled() || (isTransparent() && m_usedTransparency)) + return; + + RenderLayer* ancestor = transparentAncestor(); + if (ancestor) + ancestor->beginTransparencyLayers(p, rootLayer); + + if (isTransparent()) { + m_usedTransparency = true; + p->save(); + p->clip(transparencyClipBox(TransformationMatrix(), this, rootLayer)); + p->beginTransparencyLayer(renderer()->opacity()); + } +} + +void* RenderLayer::operator new(size_t sz, RenderArena* renderArena) throw() +{ + return renderArena->allocate(sz); +} + +void RenderLayer::operator delete(void* ptr, size_t sz) +{ + // Stash size where destroy can find it. + *(size_t *)ptr = sz; +} + +void RenderLayer::destroy(RenderArena* renderArena) +{ + delete this; + + // Recover the size left there for us by operator delete and free the memory. + renderArena->free(*(size_t *)this, this); +} + +void RenderLayer::addChild(RenderLayer* child, RenderLayer* beforeChild) +{ + RenderLayer* prevSibling = beforeChild ? beforeChild->previousSibling() : lastChild(); + if (prevSibling) { + child->setPreviousSibling(prevSibling); + prevSibling->setNextSibling(child); + } else + setFirstChild(child); + + if (beforeChild) { + beforeChild->setPreviousSibling(child); + child->setNextSibling(beforeChild); + } else + setLastChild(child); + + child->setParent(this); + + if (child->isOverflowOnly()) + dirtyOverflowList(); + + if (!child->isOverflowOnly() || child->firstChild()) { + // Dirty the z-order list in which we are contained. The stackingContext() can be null in the + // case where we're building up generated content layers. This is ok, since the lists will start + // off dirty in that case anyway. + child->dirtyStackingContextZOrderLists(); + } + + child->updateVisibilityStatus(); + if (child->m_hasVisibleContent || child->m_hasVisibleDescendant) + childVisibilityChanged(true); +} + +RenderLayer* RenderLayer::removeChild(RenderLayer* oldChild) +{ + // remove the child + if (oldChild->previousSibling()) + oldChild->previousSibling()->setNextSibling(oldChild->nextSibling()); + if (oldChild->nextSibling()) + oldChild->nextSibling()->setPreviousSibling(oldChild->previousSibling()); + + if (m_first == oldChild) + m_first = oldChild->nextSibling(); + if (m_last == oldChild) + m_last = oldChild->previousSibling(); + + if (oldChild->isOverflowOnly()) + dirtyOverflowList(); + if (!oldChild->isOverflowOnly() || oldChild->firstChild()) { + // Dirty the z-order list in which we are contained. When called via the + // reattachment process in removeOnlyThisLayer, the layer may already be disconnected + // from the main layer tree, so we need to null-check the |stackingContext| value. + oldChild->dirtyStackingContextZOrderLists(); + } + + oldChild->setPreviousSibling(0); + oldChild->setNextSibling(0); + oldChild->setParent(0); + + oldChild->updateVisibilityStatus(); + if (oldChild->m_hasVisibleContent || oldChild->m_hasVisibleDescendant) + childVisibilityChanged(false); + + return oldChild; +} + +void RenderLayer::removeOnlyThisLayer() +{ + if (!m_parent) + return; + + // Dirty the clip rects. + clearClipRectsIncludingDescendants(); + + // Remove us from the parent. + RenderLayer* parent = m_parent; + RenderLayer* nextSib = nextSibling(); + parent->removeChild(this); + + if (reflection()) + removeChild(reflectionLayer()); + + // Now walk our kids and reattach them to our parent. + RenderLayer* current = m_first; + while (current) { + RenderLayer* next = current->nextSibling(); + removeChild(current); + parent->addChild(current, nextSib); + current->updateLayerPositions(); + current = next; + } + + destroy(renderer()->renderArena()); +} + +void RenderLayer::insertOnlyThisLayer() +{ + if (!m_parent && renderer()->parent()) { + // We need to connect ourselves when our renderer() has a parent. + // Find our enclosingLayer and add ourselves. + RenderLayer* parentLayer = renderer()->parent()->enclosingLayer(); + RenderLayer* beforeChild = parentLayer->reflectionLayer() != this ? renderer()->parent()->findNextLayer(parentLayer, renderer()) : 0; + if (parentLayer) + parentLayer->addChild(this, beforeChild); + } + + // Remove all descendant layers from the hierarchy and add them to the new position. + for (RenderObject* curr = renderer()->firstChild(); curr; curr = curr->nextSibling()) + curr->moveLayers(m_parent, this); + + // Clear out all the clip rects. + clearClipRectsIncludingDescendants(); +} + +void +RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, int& x, int& y) const +{ + if (ancestorLayer == this) + return; + + if (m_object->style()->position() == FixedPosition) { + // Add in the offset of the view. We can obtain this by calling + // localToAbsolute() on the RenderView. + FloatPoint absPos = m_object->localToAbsolute(FloatPoint(), true); + x += absPos.x(); + y += absPos.y(); + return; + } + + RenderLayer* parentLayer; + if (m_object->style()->position() == AbsolutePosition) + parentLayer = enclosingPositionedAncestor(); + else + parentLayer = parent(); + + if (!parentLayer) return; + + parentLayer->convertToLayerCoords(ancestorLayer, x, y); + + x += xPos(); + y += yPos(); +} + +void RenderLayer::panScrollFromPoint(const IntPoint& sourcePoint) +{ + // We want to reduce the speed if we're close from the original point to improve the handleability of the scroll + const int shortDistanceLimit = 100; // We delimit a 200 pixels long square enclosing the original point + const int speedReducer = 2; // Within this square we divide the scrolling speed by 2 + + const int iconRadius = 10; + Frame* frame = renderer()->document()->frame(); + if (!frame) + return; + + IntPoint currentMousePosition = frame->eventHandler()->currentMousePosition(); + + // We need to check if the current mouse position is out of the window. When the mouse is out of the window, the position is incoherent + static IntPoint previousMousePosition; + if (currentMousePosition.x() < 0 || currentMousePosition.y() < 0) + currentMousePosition = previousMousePosition; + else + previousMousePosition = currentMousePosition; + + int xDelta = currentMousePosition.x() - sourcePoint.x(); + int yDelta = currentMousePosition.y() - sourcePoint.y(); + + if (abs(xDelta) < iconRadius) // at the center we let the space for the icon + xDelta = 0; + if (abs(yDelta) < iconRadius) + yDelta = 0; + + // Let's attenuate the speed for the short distances + if (abs(xDelta) < shortDistanceLimit) + xDelta /= speedReducer; + if (abs(yDelta) < shortDistanceLimit) + yDelta /= speedReducer; + + scrollByRecursively(xDelta, yDelta); +} + +void RenderLayer::scrollByRecursively(int xDelta, int yDelta) +{ + bool restrictedByLineClamp = false; + if (m_object->parent()) + restrictedByLineClamp = m_object->parent()->style()->lineClamp() >= 0; + + if (m_object->hasOverflowClip() && !restrictedByLineClamp) { + int newOffsetX = scrollXOffset() + xDelta; + int newOffsetY = scrollYOffset() + yDelta; + scrollToOffset(newOffsetX, newOffsetY); + + // If this layer can't do the scroll we ask its parent + int leftToScrollX = newOffsetX - scrollXOffset(); + int leftToScrollY = newOffsetY - scrollYOffset(); + if ((leftToScrollX || leftToScrollY) && m_object->parent()) { + m_object->parent()->enclosingLayer()->scrollByRecursively(leftToScrollX, leftToScrollY); + Frame* frame = renderer()->document()->frame(); + if (frame) + frame->eventHandler()->updateAutoscrollRenderer(); + } + } else if (m_object->view()->frameView()) + m_object->view()->frameView()->scrollBy(IntSize(xDelta, yDelta)); +} + + +void +RenderLayer::addScrolledContentOffset(int& x, int& y) const +{ + x += scrollXOffset() + m_scrollLeftOverflow; + y += scrollYOffset(); +} + +void +RenderLayer::subtractScrolledContentOffset(int& x, int& y) const +{ + x -= scrollXOffset() + m_scrollLeftOverflow; + y -= scrollYOffset(); +} + +void RenderLayer::scrollToOffset(int x, int y, bool updateScrollbars, bool repaint) +{ + if (renderer()->style()->overflowX() != OMARQUEE) { + if (x < 0) x = 0; + if (y < 0) y = 0; + + // Call the scrollWidth/Height functions so that the dimensions will be computed if they need + // to be (for overflow:hidden blocks). + int maxX = scrollWidth() - m_object->clientWidth(); + int maxY = scrollHeight() - m_object->clientHeight(); + + if (x > maxX) x = maxX; + if (y > maxY) y = maxY; + } + + // FIXME: Eventually, we will want to perform a blit. For now never + // blit, since the check for blitting is going to be very + // complicated (since it will involve testing whether our layer + // is either occluded by another layer or clipped by an enclosing + // layer or contains fixed backgrounds, etc.). + int newScrollX = x - m_scrollOriginX; + if (m_scrollY == y && m_scrollX == newScrollX) + return; + m_scrollX = newScrollX; + m_scrollY = y; + + // Update the positions of our child layers. + for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) + child->updateLayerPositions(false, false); + + RenderView* view = renderer()->view(); + + // We should have a RenderView if we're trying to scroll. + ASSERT(view); + if (view) { +#if ENABLE(DASHBOARD_SUPPORT) + // Update dashboard regions, scrolling may change the clip of a + // particular region. + view->frameView()->updateDashboardRegions(); +#endif + + view->updateWidgetPositions(); + } + + // The caret rect needs to be invalidated after scrolling + Frame* frame = renderer()->document()->frame(); + if (frame) + frame->invalidateSelection(); + + // Just schedule a full repaint of our object. + if (repaint) + m_object->repaint(); + + if (updateScrollbars) { + if (m_hBar) + m_hBar->setValue(scrollXOffset()); + if (m_vBar) + m_vBar->setValue(m_scrollY); + } + + // Schedule the scroll DOM event. + if (view) { + if (FrameView* frameView = view->frameView()) + frameView->scheduleEvent(Event::create(eventNames().scrollEvent, false, false), EventTargetNodeCast(renderer()->element())); + } +} + +void RenderLayer::scrollRectToVisible(const IntRect &rect, bool scrollToAnchor, const ScrollAlignment& alignX, const ScrollAlignment& alignY) +{ + RenderLayer* parentLayer = 0; + IntRect newRect = rect; + int xOffset = 0, yOffset = 0; + + // We may end up propagating a scroll event. It is important that we suspend events until + // the end of the function since they could delete the layer or the layer's m_object. + FrameView* frameView = m_object->document()->view(); + if (frameView) + frameView->pauseScheduledEvents(); + + bool restrictedByLineClamp = false; + if (m_object->parent()) { + parentLayer = m_object->parent()->enclosingLayer(); + restrictedByLineClamp = m_object->parent()->style()->lineClamp() >= 0; + } + + if (m_object->hasOverflowClip() && !restrictedByLineClamp) { + // Don't scroll to reveal an overflow layer that is restricted by the -webkit-line-clamp property. + // This will prevent us from revealing text hidden by the slider in Safari RSS. + FloatPoint absPos = m_object->localToAbsolute(); + absPos.move(m_object->borderLeft(), m_object->borderTop()); + + IntRect layerBounds = IntRect(absPos.x() + scrollXOffset(), absPos.y() + scrollYOffset(), m_object->clientWidth(), m_object->clientHeight()); + IntRect exposeRect = IntRect(rect.x() + scrollXOffset(), rect.y() + scrollYOffset(), rect.width(), rect.height()); + IntRect r = getRectToExpose(layerBounds, exposeRect, alignX, alignY); + + xOffset = r.x() - absPos.x(); + yOffset = r.y() - absPos.y(); + // Adjust offsets if they're outside of the allowable range. + xOffset = max(0, min(scrollWidth() - layerBounds.width(), xOffset)); + yOffset = max(0, min(scrollHeight() - layerBounds.height(), yOffset)); + + if (xOffset != scrollXOffset() || yOffset != scrollYOffset()) { + int diffX = scrollXOffset(); + int diffY = scrollYOffset(); + scrollToOffset(xOffset, yOffset); + diffX = scrollXOffset() - diffX; + diffY = scrollYOffset() - diffY; + newRect.setX(rect.x() - diffX); + newRect.setY(rect.y() - diffY); + } + } else if (!parentLayer && renderer()->canBeProgramaticallyScrolled(scrollToAnchor)) { + if (frameView) { + if (m_object->document() && m_object->document()->ownerElement() && m_object->document()->ownerElement()->renderer()) { + IntRect viewRect = frameView->visibleContentRect(); + IntRect r = getRectToExpose(viewRect, rect, alignX, alignY); + + xOffset = r.x(); + yOffset = r.y(); + // Adjust offsets if they're outside of the allowable range. + xOffset = max(0, min(frameView->contentsWidth(), xOffset)); + yOffset = max(0, min(frameView->contentsHeight(), yOffset)); + + frameView->setScrollPosition(IntPoint(xOffset, yOffset)); + parentLayer = m_object->document()->ownerElement()->renderer()->enclosingLayer(); + newRect.setX(rect.x() - frameView->scrollX() + frameView->x()); + newRect.setY(rect.y() - frameView->scrollY() + frameView->y()); + } else { + IntRect viewRect = frameView->visibleContentRect(true); + IntRect r = getRectToExpose(viewRect, rect, alignX, alignY); + + // If this is the outermost view that RenderLayer needs to scroll, then we should scroll the view recursively + // Other apps, like Mail, rely on this feature. + frameView->scrollRectIntoViewRecursively(r); + } + } + } + + if (parentLayer) + parentLayer->scrollRectToVisible(newRect, scrollToAnchor, alignX, alignY); + + if (frameView) + frameView->resumeScheduledEvents(); +} + +IntRect RenderLayer::getRectToExpose(const IntRect &visibleRect, const IntRect &exposeRect, const ScrollAlignment& alignX, const ScrollAlignment& alignY) +{ + // Determine the appropriate X behavior. + ScrollBehavior scrollX; + IntRect exposeRectX(exposeRect.x(), visibleRect.y(), exposeRect.width(), visibleRect.height()); + int intersectWidth = intersection(visibleRect, exposeRectX).width(); + if (intersectWidth == exposeRect.width() || intersectWidth >= MIN_INTERSECT_FOR_REVEAL) + // If the rectangle is fully visible, use the specified visible behavior. + // If the rectangle is partially visible, but over a certain threshold, + // then treat it as fully visible to avoid unnecessary horizontal scrolling + scrollX = getVisibleBehavior(alignX); + else if (intersectWidth == visibleRect.width()) { + // If the rect is bigger than the visible area, don't bother trying to center. Other alignments will work. + scrollX = getVisibleBehavior(alignX); + if (scrollX == alignCenter) + scrollX = noScroll; + } else if (intersectWidth > 0) + // If the rectangle is partially visible, but not above the minimum threshold, use the specified partial behavior + scrollX = getPartialBehavior(alignX); + else + scrollX = getHiddenBehavior(alignX); + // If we're trying to align to the closest edge, and the exposeRect is further right + // than the visibleRect, and not bigger than the visible area, then align with the right. + if (scrollX == alignToClosestEdge && exposeRect.right() > visibleRect.right() && exposeRect.width() < visibleRect.width()) + scrollX = alignRight; + + // Given the X behavior, compute the X coordinate. + int x; + if (scrollX == noScroll) + x = visibleRect.x(); + else if (scrollX == alignRight) + x = exposeRect.right() - visibleRect.width(); + else if (scrollX == alignCenter) + x = exposeRect.x() + (exposeRect.width() - visibleRect.width()) / 2; + else + x = exposeRect.x(); + + // Determine the appropriate Y behavior. + ScrollBehavior scrollY; + IntRect exposeRectY(visibleRect.x(), exposeRect.y(), visibleRect.width(), exposeRect.height()); + int intersectHeight = intersection(visibleRect, exposeRectY).height(); + if (intersectHeight == exposeRect.height()) + // If the rectangle is fully visible, use the specified visible behavior. + scrollY = getVisibleBehavior(alignY); + else if (intersectHeight == visibleRect.height()) { + // If the rect is bigger than the visible area, don't bother trying to center. Other alignments will work. + scrollY = getVisibleBehavior(alignY); + if (scrollY == alignCenter) + scrollY = noScroll; + } else if (intersectHeight > 0) + // If the rectangle is partially visible, use the specified partial behavior + scrollY = getPartialBehavior(alignY); + else + scrollY = getHiddenBehavior(alignY); + // If we're trying to align to the closest edge, and the exposeRect is further down + // than the visibleRect, and not bigger than the visible area, then align with the bottom. + if (scrollY == alignToClosestEdge && exposeRect.bottom() > visibleRect.bottom() && exposeRect.height() < visibleRect.height()) + scrollY = alignBottom; + + // Given the Y behavior, compute the Y coordinate. + int y; + if (scrollY == noScroll) + y = visibleRect.y(); + else if (scrollY == alignBottom) + y = exposeRect.bottom() - visibleRect.height(); + else if (scrollY == alignCenter) + y = exposeRect.y() + (exposeRect.height() - visibleRect.height()) / 2; + else + y = exposeRect.y(); + + return IntRect(IntPoint(x, y), visibleRect.size()); +} + +void RenderLayer::autoscroll() +{ + Frame* frame = renderer()->document()->frame(); + if (!frame) + return; + + FrameView* frameView = frame->view(); + if (!frameView) + return; + + frame->eventHandler()->updateSelectionForMouseDrag(); + + IntPoint currentDocumentPosition = frameView->windowToContents(frame->eventHandler()->currentMousePosition()); + scrollRectToVisible(IntRect(currentDocumentPosition, IntSize(1, 1)), false, gAlignToEdgeIfNeeded, gAlignToEdgeIfNeeded); +} + +void RenderLayer::resize(const PlatformMouseEvent& evt, const IntSize& oldOffset) +{ + if (!inResizeMode() || !m_object->hasOverflowClip()) + return; + + // Set the width and height of the shadow ancestor node if there is one. + // This is necessary for textarea elements since the resizable layer is in the shadow content. + Element* element = static_cast<Element*>(m_object->node()->shadowAncestorNode()); + RenderBox* renderer = static_cast<RenderBox*>(element->renderer()); + + EResize resize = renderer->style()->resize(); + if (resize == RESIZE_NONE) + return; + + Document* document = element->document(); + if (!document->frame()->eventHandler()->mousePressed()) + return; + + float zoomFactor = renderer->style()->effectiveZoom(); + + IntSize newOffset = offsetFromResizeCorner(document->view()->windowToContents(evt.pos())); + newOffset.setWidth(newOffset.width() / zoomFactor); + newOffset.setHeight(newOffset.height() / zoomFactor); + + IntSize currentSize = IntSize(renderer->width() / zoomFactor, renderer->height() / zoomFactor); + IntSize minimumSize = element->minimumSizeForResizing().shrunkTo(currentSize); + element->setMinimumSizeForResizing(minimumSize); + + IntSize adjustedOldOffset = IntSize(oldOffset.width() / zoomFactor, oldOffset.height() / zoomFactor); + + IntSize difference = (currentSize + newOffset - adjustedOldOffset).expandedTo(minimumSize) - currentSize; + + CSSStyleDeclaration* style = element->style(); + bool isBoxSizingBorder = renderer->style()->boxSizing() == BORDER_BOX; + + ExceptionCode ec; + + if (difference.width()) { + if (element && element->isControl()) { + // Make implicit margins from the theme explicit (see <http://bugs.webkit.org/show_bug.cgi?id=9547>). + style->setProperty(CSSPropertyMarginLeft, String::number(renderer->marginLeft() / zoomFactor) + "px", false, ec); + style->setProperty(CSSPropertyMarginRight, String::number(renderer->marginRight() / zoomFactor) + "px", false, ec); + } + int baseWidth = renderer->width() - (isBoxSizingBorder ? 0 + : renderer->borderLeft() + renderer->paddingLeft() + renderer->borderRight() + renderer->paddingRight()); + baseWidth = baseWidth / zoomFactor; + style->setProperty(CSSPropertyWidth, String::number(baseWidth + difference.width()) + "px", false, ec); + } + + if (difference.height()) { + if (element && element->isControl()) { + // Make implicit margins from the theme explicit (see <http://bugs.webkit.org/show_bug.cgi?id=9547>). + style->setProperty(CSSPropertyMarginTop, String::number(renderer->marginTop() / zoomFactor) + "px", false, ec); + style->setProperty(CSSPropertyMarginBottom, String::number(renderer->marginBottom() / zoomFactor) + "px", false, ec); + } + int baseHeight = renderer->height() - (isBoxSizingBorder ? 0 + : renderer->borderTop() + renderer->paddingTop() + renderer->borderBottom() + renderer->paddingBottom()); + baseHeight = baseHeight / zoomFactor; + style->setProperty(CSSPropertyHeight, String::number(baseHeight + difference.height()) + "px", false, ec); + } + + document->updateLayout(); + + // FIXME (Radar 4118564): We should also autoscroll the window as necessary to keep the point under the cursor in view. +} + +void RenderLayer::valueChanged(Scrollbar*) +{ + // Update scroll position from scrollbars. + + bool needUpdate = false; + int newX = scrollXOffset(); + int newY = m_scrollY; + + if (m_hBar) { + newX = m_hBar->value(); + if (newX != scrollXOffset()) + needUpdate = true; + } + + if (m_vBar) { + newY = m_vBar->value(); + if (newY != m_scrollY) + needUpdate = true; + } + + if (needUpdate) + scrollToOffset(newX, newY, false); +} + +bool RenderLayer::isActive() const +{ + Page* page = renderer()->document()->frame()->page(); + return page && page->focusController()->isActive(); +} + + +static IntRect cornerRect(const RenderLayer* layer, const IntRect& bounds) +{ + int horizontalThickness; + int verticalThickness; + if (!layer->verticalScrollbar() && !layer->horizontalScrollbar()) { + // FIXME: This isn't right. We need to know the thickness of custom scrollbars + // even when they don't exist in order to set the resizer square size properly. + horizontalThickness = ScrollbarTheme::nativeTheme()->scrollbarThickness(); + verticalThickness = horizontalThickness; + } else if (layer->verticalScrollbar() && !layer->horizontalScrollbar()) { + horizontalThickness = layer->verticalScrollbar()->width(); + verticalThickness = horizontalThickness; + } else if (layer->horizontalScrollbar() && !layer->verticalScrollbar()) { + verticalThickness = layer->horizontalScrollbar()->height(); + horizontalThickness = verticalThickness; + } else { + horizontalThickness = layer->verticalScrollbar()->width(); + verticalThickness = layer->horizontalScrollbar()->height(); + } + return IntRect(bounds.right() - horizontalThickness - layer->renderer()->style()->borderRightWidth(), + bounds.bottom() - verticalThickness - layer->renderer()->style()->borderBottomWidth(), + horizontalThickness, verticalThickness); +} + +static IntRect scrollCornerRect(const RenderLayer* layer, const IntRect& bounds) +{ + // We have a scrollbar corner when a scrollbar is visible and not filling the entire length of the box. + // This happens when: + // (a) A resizer is present and at least one scrollbar is present + // (b) Both scrollbars are present. + bool hasHorizontalBar = layer->horizontalScrollbar(); + bool hasVerticalBar = layer->verticalScrollbar(); + bool hasResizer = layer->renderer()->style()->resize() != RESIZE_NONE; + if ((hasHorizontalBar && hasVerticalBar) || (hasResizer && (hasHorizontalBar || hasVerticalBar))) + return cornerRect(layer, bounds); + return IntRect(); +} + +static IntRect resizerCornerRect(const RenderLayer* layer, const IntRect& bounds) +{ + if (layer->renderer()->style()->resize() == RESIZE_NONE) + return IntRect(); + return cornerRect(layer, bounds); +} + +bool RenderLayer::scrollbarCornerPresent() const +{ + return !scrollCornerRect(this, m_object->borderBox()).isEmpty(); +} + +void RenderLayer::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect) +{ + IntRect scrollRect = rect; + if (scrollbar == m_vBar.get()) + scrollRect.move(renderer()->width() - renderer()->borderRight() - scrollbar->width(), renderer()->borderTop()); + else + scrollRect.move(renderer()->borderLeft(), renderer()->height() - renderer()->borderBottom() - scrollbar->height()); + renderer()->repaintRectangle(scrollRect); +} + +PassRefPtr<Scrollbar> RenderLayer::createScrollbar(ScrollbarOrientation orientation) +{ + RefPtr<Scrollbar> widget; + bool hasCustomScrollbarStyle = m_object->node()->shadowAncestorNode()->renderer()->style()->hasPseudoStyle(RenderStyle::SCROLLBAR); + if (hasCustomScrollbarStyle) + widget = RenderScrollbar::createCustomScrollbar(this, orientation, m_object->node()->shadowAncestorNode()->renderer()); + else + widget = Scrollbar::createNativeScrollbar(this, orientation, RegularScrollbar); + m_object->document()->view()->addChild(widget.get()); + return widget.release(); +} + +void RenderLayer::destroyScrollbar(ScrollbarOrientation orientation) +{ + RefPtr<Scrollbar>& scrollbar = orientation == HorizontalScrollbar ? m_hBar : m_vBar; + if (scrollbar) { + scrollbar->removeFromParent(); + scrollbar->setClient(0); + scrollbar = 0; + } +} + +void RenderLayer::setHasHorizontalScrollbar(bool hasScrollbar) +{ + if (hasScrollbar == (m_hBar != 0)) + return; + + if (hasScrollbar) + m_hBar = createScrollbar(HorizontalScrollbar); + else + destroyScrollbar(HorizontalScrollbar); + + // Destroying or creating one bar can cause our scrollbar corner to come and go. We need to update the opposite scrollbar's style. + if (m_hBar) + m_hBar->styleChanged(); + if (m_vBar) + m_vBar->styleChanged(); + +#if ENABLE(DASHBOARD_SUPPORT) + // Force an update since we know the scrollbars have changed things. + if (m_object->document()->hasDashboardRegions()) + m_object->document()->setDashboardRegionsDirty(true); +#endif +} + +void RenderLayer::setHasVerticalScrollbar(bool hasScrollbar) +{ + if (hasScrollbar == (m_vBar != 0)) + return; + + if (hasScrollbar) + m_vBar = createScrollbar(VerticalScrollbar); + else + destroyScrollbar(VerticalScrollbar); + + // Destroying or creating one bar can cause our scrollbar corner to come and go. We need to update the opposite scrollbar's style. + if (m_hBar) + m_hBar->styleChanged(); + if (m_vBar) + m_vBar->styleChanged(); + +#if ENABLE(DASHBOARD_SUPPORT) + // Force an update since we know the scrollbars have changed things. + if (m_object->document()->hasDashboardRegions()) + m_object->document()->setDashboardRegionsDirty(true); +#endif +} + +int RenderLayer::verticalScrollbarWidth() const +{ + if (!m_vBar) + return 0; + return m_vBar->width(); +} + +int RenderLayer::horizontalScrollbarHeight() const +{ + if (!m_hBar) + return 0; + return m_hBar->height(); +} + +IntSize RenderLayer::offsetFromResizeCorner(const IntPoint& absolutePoint) const +{ + // Currently the resize corner is always the bottom right corner + IntPoint bottomRight(width(), height()); + IntPoint localPoint = absoluteToContents(absolutePoint); + return localPoint - bottomRight; +} + +void RenderLayer::positionOverflowControls(int tx, int ty) +{ + if (!m_hBar && !m_vBar && (!m_object->hasOverflowClip() || m_object->style()->resize() == RESIZE_NONE)) + return; + + IntRect borderBox = m_object->borderBox(); + IntRect scrollCorner(scrollCornerRect(this, borderBox)); + IntRect absBounds(borderBox.x() + tx, borderBox.y() + ty, borderBox.width(), borderBox.height()); + if (m_vBar) + m_vBar->setFrameRect(IntRect(absBounds.right() - m_object->borderRight() - m_vBar->width(), + absBounds.y() + m_object->borderTop(), + m_vBar->width(), + absBounds.height() - (m_object->borderTop() + m_object->borderBottom()) - scrollCorner.height())); + + if (m_hBar) + m_hBar->setFrameRect(IntRect(absBounds.x() + m_object->borderLeft(), + absBounds.bottom() - m_object->borderBottom() - m_hBar->height(), + absBounds.width() - (m_object->borderLeft() + m_object->borderRight()) - scrollCorner.width(), + m_hBar->height())); + + if (m_scrollCorner) + m_scrollCorner->setRect(scrollCorner); + if (m_resizer) + m_resizer->setRect(resizerCornerRect(this, borderBox)); +} + +int RenderLayer::scrollWidth() +{ + if (m_scrollDimensionsDirty) + computeScrollDimensions(); + return m_scrollWidth; +} + +int RenderLayer::scrollHeight() +{ + if (m_scrollDimensionsDirty) + computeScrollDimensions(); + return m_scrollHeight; +} + +void RenderLayer::computeScrollDimensions(bool* needHBar, bool* needVBar) +{ + m_scrollDimensionsDirty = false; + + bool ltr = m_object->style()->direction() == LTR; + + int clientWidth = m_object->clientWidth(); + int clientHeight = m_object->clientHeight(); + + m_scrollLeftOverflow = ltr ? 0 : min(0, m_object->leftmostPosition(true, false) - m_object->borderLeft()); + + int rightPos = ltr ? + m_object->rightmostPosition(true, false) - m_object->borderLeft() : + clientWidth - m_scrollLeftOverflow; + int bottomPos = m_object->lowestPosition(true, false) - m_object->borderTop(); + + m_scrollWidth = max(rightPos, clientWidth); + m_scrollHeight = max(bottomPos, clientHeight); + + m_scrollOriginX = ltr ? 0 : m_scrollWidth - clientWidth; + + if (needHBar) + *needHBar = rightPos > clientWidth; + if (needVBar) + *needVBar = bottomPos > clientHeight; +} + +void RenderLayer::updateOverflowStatus(bool horizontalOverflow, bool verticalOverflow) +{ + if (m_overflowStatusDirty) { + m_horizontalOverflow = horizontalOverflow; + m_verticalOverflow = verticalOverflow; + m_overflowStatusDirty = false; + + return; + } + + bool horizontalOverflowChanged = (m_horizontalOverflow != horizontalOverflow); + bool verticalOverflowChanged = (m_verticalOverflow != verticalOverflow); + + if (horizontalOverflowChanged || verticalOverflowChanged) { + m_horizontalOverflow = horizontalOverflow; + m_verticalOverflow = verticalOverflow; + + if (FrameView* frameView = m_object->document()->view()) { + frameView->scheduleEvent(OverflowEvent::create(horizontalOverflowChanged, horizontalOverflow, verticalOverflowChanged, verticalOverflow), + EventTargetNodeCast(m_object->element())); + } + } +} + +void +RenderLayer::updateScrollInfoAfterLayout() +{ + m_scrollDimensionsDirty = true; + + bool horizontalOverflow, verticalOverflow; + computeScrollDimensions(&horizontalOverflow, &verticalOverflow); + + if (m_object->style()->overflowX() != OMARQUEE) { + // Layout may cause us to be in an invalid scroll position. In this case we need + // to pull our scroll offsets back to the max (or push them up to the min). + int newX = max(0, min(scrollXOffset(), scrollWidth() - m_object->clientWidth())); + int newY = max(0, min(m_scrollY, scrollHeight() - m_object->clientHeight())); + if (newX != scrollXOffset() || newY != m_scrollY) { + RenderView* view = m_object->view(); + ASSERT(view); + // scrollToOffset() may call updateLayerPositions(), which doesn't work + // with LayoutState. + // FIXME: Remove the disableLayoutState/enableLayoutState if the above changes. + if (view) + view->disableLayoutState(); + scrollToOffset(newX, newY); + if (view) + view->enableLayoutState(); + } + } + + bool haveHorizontalBar = m_hBar; + bool haveVerticalBar = m_vBar; + + // overflow:scroll should just enable/disable. + if (m_object->style()->overflowX() == OSCROLL) + m_hBar->setEnabled(horizontalOverflow); + if (m_object->style()->overflowY() == OSCROLL) + m_vBar->setEnabled(verticalOverflow); + + // A dynamic change from a scrolling overflow to overflow:hidden means we need to get rid of any + // scrollbars that may be present. + if (m_object->style()->overflowX() == OHIDDEN && haveHorizontalBar) + setHasHorizontalScrollbar(false); + if (m_object->style()->overflowY() == OHIDDEN && haveVerticalBar) + setHasVerticalScrollbar(false); + + // overflow:auto may need to lay out again if scrollbars got added/removed. + bool scrollbarsChanged = (m_object->hasAutoHorizontalScrollbar() && haveHorizontalBar != horizontalOverflow) || + (m_object->hasAutoVerticalScrollbar() && haveVerticalBar != verticalOverflow); + if (scrollbarsChanged) { + if (m_object->hasAutoHorizontalScrollbar()) + setHasHorizontalScrollbar(horizontalOverflow); + if (m_object->hasAutoVerticalScrollbar()) + setHasVerticalScrollbar(verticalOverflow); + +#if ENABLE(DASHBOARD_SUPPORT) + // Force an update since we know the scrollbars have changed things. + if (m_object->document()->hasDashboardRegions()) + m_object->document()->setDashboardRegionsDirty(true); +#endif + + m_object->repaint(); + + if (m_object->style()->overflowX() == OAUTO || m_object->style()->overflowY() == OAUTO) { + if (!m_inOverflowRelayout) { + // Our proprietary overflow: overlay value doesn't trigger a layout. + m_inOverflowRelayout = true; + m_object->setNeedsLayout(true); + if (m_object->isRenderBlock()) + static_cast<RenderBlock*>(m_object)->layoutBlock(true); + else + m_object->layout(); + m_inOverflowRelayout = false; + } + } + } + + // If overflow:scroll is turned into overflow:auto a bar might still be disabled (Bug 11985). + if (m_hBar && m_object->hasAutoHorizontalScrollbar()) + m_hBar->setEnabled(true); + if (m_vBar && m_object->hasAutoVerticalScrollbar()) + m_vBar->setEnabled(true); + + // Set up the range (and page step/line step). + if (m_hBar) { + int clientWidth = m_object->clientWidth(); + int pageStep = (clientWidth - cAmountToKeepWhenPaging); + if (pageStep < 0) pageStep = clientWidth; + m_hBar->setSteps(cScrollbarPixelsPerLineStep, pageStep); + m_hBar->setProportion(clientWidth, m_scrollWidth); + m_hBar->setValue(scrollXOffset()); + } + if (m_vBar) { + int clientHeight = m_object->clientHeight(); + int pageStep = (clientHeight - cAmountToKeepWhenPaging); + if (pageStep < 0) pageStep = clientHeight; + m_vBar->setSteps(cScrollbarPixelsPerLineStep, pageStep); + m_vBar->setProportion(clientHeight, m_scrollHeight); + } + + if (m_object->element() && m_object->document()->hasListenerType(Document::OVERFLOWCHANGED_LISTENER)) + updateOverflowStatus(horizontalOverflow, verticalOverflow); +} + +void RenderLayer::paintOverflowControls(GraphicsContext* context, int tx, int ty, const IntRect& damageRect) +{ + // Don't do anything if we have no overflow. + if (!m_object->hasOverflowClip()) + return; + + // Move the scrollbar widgets if necessary. We normally move and resize widgets during layout, but sometimes + // widgets can move without layout occurring (most notably when you scroll a document that + // contains fixed positioned elements). + positionOverflowControls(tx, ty); + + // Now that we're sure the scrollbars are in the right place, paint them. + if (m_hBar) + m_hBar->paint(context, damageRect); + if (m_vBar) + m_vBar->paint(context, damageRect); + + // We fill our scroll corner with white if we have a scrollbar that doesn't run all the way up to the + // edge of the box. + paintScrollCorner(context, tx, ty, damageRect); + + // Paint our resizer last, since it sits on top of the scroll corner. + paintResizer(context, tx, ty, damageRect); +} + +void RenderLayer::paintScrollCorner(GraphicsContext* context, int tx, int ty, const IntRect& damageRect) +{ + IntRect cornerRect = scrollCornerRect(this, m_object->borderBox()); + IntRect absRect = IntRect(cornerRect.x() + tx, cornerRect.y() + ty, cornerRect.width(), cornerRect.height()); + if (!absRect.intersects(damageRect)) + return; + + if (context->updatingControlTints()) { + updateScrollCornerStyle(); + return; + } + + if (m_scrollCorner) { + m_scrollCorner->paintIntoRect(context, tx, ty, absRect); + return; + } + + context->fillRect(absRect, Color::white); +} + +void RenderLayer::paintResizer(GraphicsContext* context, int tx, int ty, const IntRect& damageRect) +{ + if (m_object->style()->resize() == RESIZE_NONE) + return; + + IntRect cornerRect = resizerCornerRect(this, m_object->borderBox()); + IntRect absRect = IntRect(cornerRect.x() + tx, cornerRect.y() + ty, cornerRect.width(), cornerRect.height()); + if (!absRect.intersects(damageRect)) + return; + + if (context->updatingControlTints()) { + updateResizerStyle(); + return; + } + + if (m_resizer) { + m_resizer->paintIntoRect(context, tx, ty, absRect); + return; + } + + // Paint the resizer control. + DEFINE_STATIC_LOCAL(RefPtr<Image>, resizeCornerImage, (Image::loadPlatformResource("textAreaResizeCorner"))); + IntPoint imagePoint(absRect.right() - resizeCornerImage->width(), absRect.bottom() - resizeCornerImage->height()); + context->drawImage(resizeCornerImage.get(), imagePoint); + + // Draw a frame around the resizer (1px grey line) if there are any scrollbars present. + // Clipping will exclude the right and bottom edges of this frame. + if (m_hBar || m_vBar) { + context->save(); + IntRect largerCorner = absRect; + largerCorner.setSize(IntSize(largerCorner.width() + 1, largerCorner.height() + 1)); + context->setStrokeColor(Color(makeRGB(217, 217, 217))); + context->setStrokeThickness(1.0f); + context->setFillColor(Color::transparent); + context->drawRect(largerCorner); + context->restore(); + } +} + +bool RenderLayer::isPointInResizeControl(const IntPoint& absolutePoint) const +{ + if (!m_object->hasOverflowClip() || m_object->style()->resize() == RESIZE_NONE) + return false; + + IntPoint localPoint = absoluteToContents(absolutePoint); + + IntRect localBounds(0, 0, m_object->width(), m_object->height()); + return resizerCornerRect(this, localBounds).contains(localPoint); +} + +bool RenderLayer::hitTestOverflowControls(HitTestResult& result) +{ + if (!m_hBar && !m_vBar && (!renderer()->hasOverflowClip() || renderer()->style()->resize() == RESIZE_NONE)) + return false; + + int x = 0; + int y = 0; + convertToLayerCoords(root(), x, y); + IntRect absBounds(x, y, renderer()->width(), renderer()->height()); + + IntRect resizeControlRect; + if (renderer()->style()->resize() != RESIZE_NONE) { + resizeControlRect = resizerCornerRect(this, absBounds); + if (resizeControlRect.contains(result.point())) + return true; + } + + int resizeControlSize = max(resizeControlRect.height(), 0); + + if (m_vBar) { + IntRect vBarRect(absBounds.right() - renderer()->borderRight() - m_vBar->width(), absBounds.y() + renderer()->borderTop(), m_vBar->width(), absBounds.height() - (renderer()->borderTop() + renderer()->borderBottom()) - (m_hBar ? m_hBar->height() : resizeControlSize)); + if (vBarRect.contains(result.point())) { + result.setScrollbar(m_vBar.get()); + return true; + } + } + + resizeControlSize = max(resizeControlRect.width(), 0); + if (m_hBar) { + IntRect hBarRect(absBounds.x() + renderer()->borderLeft(), absBounds.bottom() - renderer()->borderBottom() - m_hBar->height(), absBounds.width() - (renderer()->borderLeft() + renderer()->borderRight()) - (m_vBar ? m_vBar->width() : resizeControlSize), m_hBar->height()); + if (hBarRect.contains(result.point())) { + result.setScrollbar(m_hBar.get()); + return true; + } + } + + return false; +} + +bool RenderLayer::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier) +{ + bool didHorizontalScroll = false; + bool didVerticalScroll = false; + + if (m_hBar) { + if (granularity == ScrollByDocument) { + // Special-case for the ScrollByDocument granularity. A document scroll can only be up + // or down and in both cases the horizontal bar goes all the way to the left. + didHorizontalScroll = m_hBar->scroll(ScrollLeft, ScrollByDocument, multiplier); + } else + didHorizontalScroll = m_hBar->scroll(direction, granularity, multiplier); + } + + if (m_vBar) + didVerticalScroll = m_vBar->scroll(direction, granularity, multiplier); + + return (didHorizontalScroll || didVerticalScroll); +} + +void +RenderLayer::paint(GraphicsContext* p, const IntRect& damageRect, PaintRestriction paintRestriction, RenderObject *paintingRoot) +{ + paintLayer(this, p, damageRect, false, paintRestriction, paintingRoot); +} + +static void setClip(GraphicsContext* p, const IntRect& paintDirtyRect, const IntRect& clipRect) +{ + if (paintDirtyRect == clipRect) + return; + p->save(); + p->clip(clipRect); +} + +static void restoreClip(GraphicsContext* p, const IntRect& paintDirtyRect, const IntRect& clipRect) +{ + if (paintDirtyRect == clipRect) + return; + p->restore(); +} + +void +RenderLayer::paintLayer(RenderLayer* rootLayer, GraphicsContext* p, + const IntRect& paintDirtyRect, bool haveTransparency, PaintRestriction paintRestriction, + RenderObject* paintingRoot, bool appliedTransform, bool temporaryClipRects) +{ + // Avoid painting layers when stylesheets haven't loaded. This eliminates FOUC. + // It's ok not to draw, because later on, when all the stylesheets do load, updateStyleSelector on the Document + // will do a full repaint(). + if (renderer()->document()->didLayoutWithPendingStylesheets() && !renderer()->isRenderView() && !renderer()->isRoot()) + return; + + // If this layer is totally invisible then there is nothing to paint. + if (!m_object->opacity()) + return; + + if (isTransparent()) + haveTransparency = true; + + // Apply a transform if we have one. A reflection is considered to be a transform, since it is a flip and a translate. + if (m_transform && !appliedTransform) { + // If the transform can't be inverted, then don't paint anything. + if (!m_transform->isInvertible()) + return; + + // If we have a transparency layer enclosing us and we are the root of a transform, then we need to establish the transparency + // layer from the parent now. + if (haveTransparency) + parent()->beginTransparencyLayers(p, rootLayer); + + // Make sure the parent's clip rects have been calculated. + IntRect clipRect = paintDirtyRect; + if (parent()) { + if (temporaryClipRects) { + ClipRects parentClipRects; + parent()->calculateClipRects(rootLayer, parentClipRects); + clipRect = parentClipRects.overflowClipRect(); + } else { + parent()->updateClipRects(rootLayer); + clipRect = parent()->clipRects()->overflowClipRect(); + } + clipRect.intersect(paintDirtyRect); + } + + // Push the parent coordinate space's clip. + setClip(p, paintDirtyRect, clipRect); + + // Adjust the transform such that the renderer's upper left corner will paint at (0,0) in user space. + // This involves subtracting out the position of the layer in our current coordinate space. + int x = 0; + int y = 0; + convertToLayerCoords(rootLayer, x, y); + TransformationMatrix transform; + transform.translate(x, y); + transform = *m_transform * transform; + + // Apply the transform. + p->save(); + p->concatCTM(transform); + + // Now do a paint with the root layer shifted to be us. + paintLayer(this, p, transform.inverse().mapRect(paintDirtyRect), haveTransparency, paintRestriction, paintingRoot, true, temporaryClipRects); + + p->restore(); + + // Restore the clip. + restoreClip(p, paintDirtyRect, clipRect); + + return; + } + + // Paint the reflection first if we have one. + if (m_reflection && !m_paintingInsideReflection && (!m_transform || appliedTransform)) { + // Mark that we are now inside replica painting. + m_paintingInsideReflection = true; + reflectionLayer()->paintLayer(rootLayer, p, paintDirtyRect, haveTransparency, paintRestriction, paintingRoot, false, temporaryClipRects); + m_paintingInsideReflection = false; + } + + // Calculate the clip rects we should use. + IntRect layerBounds, damageRect, clipRectToApply, outlineRect; + calculateRects(rootLayer, paintDirtyRect, layerBounds, damageRect, clipRectToApply, outlineRect, temporaryClipRects); + int x = layerBounds.x(); + int y = layerBounds.y(); + int tx = x - renderer()->xPos(); + int ty = y - renderer()->yPos() + renderer()->borderTopExtra(); + + // Ensure our lists are up-to-date. + updateZOrderLists(); + updateOverflowList(); + + bool selectionOnly = paintRestriction == PaintRestrictionSelectionOnly || paintRestriction == PaintRestrictionSelectionOnlyBlackText; + bool forceBlackText = paintRestriction == PaintRestrictionSelectionOnlyBlackText; + + // If this layer's renderer is a child of the paintingRoot, we render unconditionally, which + // is done by passing a nil paintingRoot down to our renderer (as if no paintingRoot was ever set). + // Else, our renderer tree may or may not contain the painting root, so we pass that root along + // so it will be tested against as we decend through the renderers. + RenderObject* paintingRootForRenderer = 0; + if (paintingRoot && !m_object->isDescendantOf(paintingRoot)) + paintingRootForRenderer = paintingRoot; + + // We want to paint our layer, but only if we intersect the damage rect. + bool shouldPaint = intersectsDamageRect(layerBounds, damageRect, rootLayer) && m_hasVisibleContent; + if (shouldPaint && !selectionOnly && !damageRect.isEmpty()) { + // Begin transparency layers lazily now that we know we have to paint something. + if (haveTransparency) + beginTransparencyLayers(p, rootLayer); + + // Paint our background first, before painting any child layers. + // Establish the clip used to paint our background. + setClip(p, paintDirtyRect, damageRect); + + // Paint the background. + RenderObject::PaintInfo paintInfo(p, damageRect, PaintPhaseBlockBackground, false, paintingRootForRenderer, 0); + renderer()->paint(paintInfo, tx, ty); + + // Our scrollbar widgets paint exactly when we tell them to, so that they work properly with + // z-index. We paint after we painted the background/border, so that the scrollbars will + // sit above the background/border. + paintOverflowControls(p, x, y, damageRect); + + // Restore the clip. + restoreClip(p, paintDirtyRect, damageRect); + } + + // Now walk the sorted list of children with negative z-indices. + if (m_negZOrderList) + for (Vector<RenderLayer*>::iterator it = m_negZOrderList->begin(); it != m_negZOrderList->end(); ++it) + it[0]->paintLayer(rootLayer, p, paintDirtyRect, haveTransparency, paintRestriction, paintingRoot, false, temporaryClipRects); + + // Now establish the appropriate clip and paint our child RenderObjects. + if (shouldPaint && !clipRectToApply.isEmpty()) { + // Begin transparency layers lazily now that we know we have to paint something. + if (haveTransparency) + beginTransparencyLayers(p, rootLayer); + + // Set up the clip used when painting our children. + setClip(p, paintDirtyRect, clipRectToApply); + RenderObject::PaintInfo paintInfo(p, clipRectToApply, + selectionOnly ? PaintPhaseSelection : PaintPhaseChildBlockBackgrounds, + forceBlackText, paintingRootForRenderer, 0); + renderer()->paint(paintInfo, tx, ty); + if (!selectionOnly) { + paintInfo.phase = PaintPhaseFloat; + renderer()->paint(paintInfo, tx, ty); + paintInfo.phase = PaintPhaseForeground; + renderer()->paint(paintInfo, tx, ty); + paintInfo.phase = PaintPhaseChildOutlines; + renderer()->paint(paintInfo, tx, ty); + } + + // Now restore our clip. + restoreClip(p, paintDirtyRect, clipRectToApply); + } + + if (!outlineRect.isEmpty()) { + // Paint our own outline + RenderObject::PaintInfo paintInfo(p, outlineRect, PaintPhaseSelfOutline, false, paintingRootForRenderer, 0); + setClip(p, paintDirtyRect, outlineRect); + renderer()->paint(paintInfo, tx, ty); + restoreClip(p, paintDirtyRect, outlineRect); + } + + // Paint any child layers that have overflow. + if (m_overflowList) + for (Vector<RenderLayer*>::iterator it = m_overflowList->begin(); it != m_overflowList->end(); ++it) + it[0]->paintLayer(rootLayer, p, paintDirtyRect, haveTransparency, paintRestriction, paintingRoot, false, temporaryClipRects); + + // Now walk the sorted list of children with positive z-indices. + if (m_posZOrderList) + for (Vector<RenderLayer*>::iterator it = m_posZOrderList->begin(); it != m_posZOrderList->end(); ++it) + it[0]->paintLayer(rootLayer, p, paintDirtyRect, haveTransparency, paintRestriction, paintingRoot, false, temporaryClipRects); + + if (renderer()->hasMask() && shouldPaint && !selectionOnly && !damageRect.isEmpty()) { + setClip(p, paintDirtyRect, damageRect); + + // Paint the mask. + RenderObject::PaintInfo paintInfo(p, damageRect, PaintPhaseMask, false, paintingRootForRenderer, 0); + renderer()->paint(paintInfo, tx, ty); + + // Restore the clip. + restoreClip(p, paintDirtyRect, damageRect); + } + + // End our transparency layer + if (isTransparent() && m_usedTransparency) { + p->endTransparencyLayer(); + p->restore(); + m_usedTransparency = false; + } +} + +static inline IntRect frameVisibleRect(RenderObject* renderer) +{ + FrameView* frameView = renderer->document()->view(); + if (!frameView) + return IntRect(); + + return frameView->visibleContentRect(); +} + +bool RenderLayer::hitTest(const HitTestRequest& request, HitTestResult& result) +{ + renderer()->document()->updateLayout(); + + IntRect boundsRect(m_x, m_y, width(), height()); + boundsRect.intersect(frameVisibleRect(renderer())); + + RenderLayer* insideLayer = hitTestLayer(this, request, result, boundsRect, result.point()); + + // Now determine if the result is inside an anchor; make sure an image map wins if + // it already set URLElement and only use the innermost. + Node* node = result.innerNode(); + while (node) { + // for imagemaps, URLElement is the associated area element not the image itself + if (node->isLink() && !result.URLElement() && !node->hasTagName(imgTag)) + result.setURLElement(static_cast<Element*>(node)); + node = node->eventParentNode(); + } + + // Next set up the correct :hover/:active state along the new chain. + updateHoverActiveState(request, result); + + // Now return whether we were inside this layer (this will always be true for the root + // layer). + return insideLayer; +} + +Node* RenderLayer::enclosingElement() const +{ + for (RenderObject* r = renderer(); r; r = r->parent()) { + if (Node* e = r->element()) + return e; + } + ASSERT_NOT_REACHED(); + return 0; +} + +RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, const HitTestRequest& request, HitTestResult& result, + const IntRect& hitTestRect, const IntPoint& hitTestPoint, bool appliedTransform) +{ + // Apply a transform if we have one. + if (m_transform && !appliedTransform) { + // If the transform can't be inverted, then don't hit test this layer at all. + if (!m_transform->isInvertible()) + return 0; + + // Make sure the parent's clip rects have been calculated. + if (parent()) { + parent()->updateClipRects(rootLayer); + + // Go ahead and test the enclosing clip now. + IntRect clipRect = parent()->clipRects()->overflowClipRect(); + if (!clipRect.contains(hitTestPoint)) + return 0; + } + + // Adjust the transform such that the renderer's upper left corner is at (0,0) in user space. + // This involves subtracting out the position of the layer in our current coordinate space. + int x = 0; + int y = 0; + convertToLayerCoords(rootLayer, x, y); + TransformationMatrix transform; + transform.translate(x, y); + transform = *m_transform * transform; + + // Map the hit test point into the transformed space and then do a hit test with the root layer shifted to be us. + return hitTestLayer(this, request, result, transform.inverse().mapRect(hitTestRect), transform.inverse().mapPoint(hitTestPoint), true); + } + + // Calculate the clip rects we should use. + IntRect layerBounds; + IntRect bgRect; + IntRect fgRect; + IntRect outlineRect; + calculateRects(rootLayer, hitTestRect, layerBounds, bgRect, fgRect, outlineRect); + + // Ensure our lists are up-to-date. + updateZOrderLists(); + updateOverflowList(); + + // This variable tracks which layer the mouse ends up being inside. The minute we find an insideLayer, + // we are done and can return it. + RenderLayer* insideLayer = 0; + + // Begin by walking our list of positive layers from highest z-index down to the lowest + // z-index. + if (m_posZOrderList) { + for (int i = m_posZOrderList->size() - 1; i >= 0; --i) { + insideLayer = m_posZOrderList->at(i)->hitTestLayer(rootLayer, request, result, hitTestRect, hitTestPoint); + if (insideLayer) + return insideLayer; + } + } + + // Now check our overflow objects. + if (m_overflowList) { + for (int i = m_overflowList->size() - 1; i >= 0; --i) { + insideLayer = m_overflowList->at(i)->hitTestLayer(rootLayer, request, result, hitTestRect, hitTestPoint); + if (insideLayer) + return insideLayer; + } + } + + // Next we want to see if the mouse pos is inside the child RenderObjects of the layer. + if (fgRect.contains(hitTestPoint) && + renderer()->hitTest(request, result, hitTestPoint, + layerBounds.x() - renderer()->xPos(), + layerBounds.y() - renderer()->yPos() + m_object->borderTopExtra(), + HitTestDescendants)) { + // For positioned generated content, we might still not have a + // node by the time we get to the layer level, since none of + // the content in the layer has an element. So just walk up + // the tree. + if (!result.innerNode() || !result.innerNonSharedNode()) { + Node* e = enclosingElement(); + if (!result.innerNode()) + result.setInnerNode(e); + if (!result.innerNonSharedNode()) + result.setInnerNonSharedNode(e); + } + + return this; + } + + // Now check our negative z-index children. + if (m_negZOrderList) { + for (int i = m_negZOrderList->size() - 1; i >= 0; --i) { + insideLayer = m_negZOrderList->at(i)->hitTestLayer(rootLayer, request, result, hitTestRect, hitTestPoint); + if (insideLayer) + return insideLayer; + } + } + + // Next we want to see if the mouse is inside this layer but not any of its children. + if (bgRect.contains(hitTestPoint) && + renderer()->hitTest(request, result, hitTestPoint, + layerBounds.x() - renderer()->xPos(), + layerBounds.y() - renderer()->yPos() + m_object->borderTopExtra(), + HitTestSelf)) { + if (!result.innerNode() || !result.innerNonSharedNode()) { + Node* e = enclosingElement(); + if (!result.innerNode()) + result.setInnerNode(e); + if (!result.innerNonSharedNode()) + result.setInnerNonSharedNode(e); + } + + return this; + } + + // We didn't hit any layer. If we are the root layer and the mouse is -- or just was -- down, + // return ourselves. We do this so mouse events continue getting delivered after a drag has + // exited the WebView, and so hit testing over a scrollbar hits the content document. + if ((request.active || request.mouseUp) && renderer()->isRenderView()) { + renderer()->updateHitTestResult(result, hitTestPoint); + return this; + } + + return 0; +} + +void RenderLayer::updateClipRects(const RenderLayer* rootLayer) +{ + if (m_clipRects) { + ASSERT(rootLayer == m_clipRectsRoot); + return; // We have the correct cached value. + } + + // For transformed layers, the root layer was shifted to be us, so there is no need to + // examine the parent. We want to cache clip rects with us as the root. + RenderLayer* parentLayer = rootLayer != this ? parent() : 0; + if (parentLayer) + parentLayer->updateClipRects(rootLayer); + + ClipRects clipRects; + calculateClipRects(rootLayer, clipRects, true); + + if (parentLayer && parentLayer->clipRects() && clipRects == *parentLayer->clipRects()) + m_clipRects = parentLayer->clipRects(); + else + m_clipRects = new (m_object->renderArena()) ClipRects(clipRects); + m_clipRects->ref(); +#ifndef NDEBUG + m_clipRectsRoot = rootLayer; +#endif +} + +void RenderLayer::calculateClipRects(const RenderLayer* rootLayer, ClipRects& clipRects, bool useCached) const +{ + IntRect infiniteRect(INT_MIN/2, INT_MIN/2, INT_MAX, INT_MAX); + if (!parent()) { + // The root layer's clip rect is always infinite. + clipRects.reset(infiniteRect); + return; + } + + // For transformed layers, the root layer was shifted to be us, so there is no need to + // examine the parent. We want to cache clip rects with us as the root. + RenderLayer* parentLayer = rootLayer != this ? parent() : 0; + + // Ensure that our parent's clip has been calculated so that we can examine the values. + if (parentLayer) { + if (useCached && parentLayer->clipRects()) + clipRects = *parentLayer->clipRects(); + else + parentLayer->calculateClipRects(rootLayer, clipRects); + } + else + clipRects.reset(infiniteRect); + + // A fixed object is essentially the root of its containing block hierarchy, so when + // we encounter such an object, we reset our clip rects to the fixedClipRect. + if (m_object->style()->position() == FixedPosition) { + clipRects.setPosClipRect(clipRects.fixedClipRect()); + clipRects.setOverflowClipRect(clipRects.fixedClipRect()); + clipRects.setFixed(true); + } + else if (m_object->style()->position() == RelativePosition) + clipRects.setPosClipRect(clipRects.overflowClipRect()); + else if (m_object->style()->position() == AbsolutePosition) + clipRects.setOverflowClipRect(clipRects.posClipRect()); + + // Update the clip rects that will be passed to child layers. + if (m_object->hasOverflowClip() || m_object->hasClip()) { + // This layer establishes a clip of some kind. + int x = 0; + int y = 0; + convertToLayerCoords(rootLayer, x, y); + RenderView* view = renderer()->view(); + ASSERT(view); + if (view && clipRects.fixed() && rootLayer->renderer() == view) { + x -= view->frameView()->scrollX(); + y -= view->frameView()->scrollY(); + } + + if (m_object->hasOverflowClip()) { + IntRect newOverflowClip = m_object->getOverflowClipRect(x,y); + clipRects.setOverflowClipRect(intersection(newOverflowClip, clipRects.overflowClipRect())); + if (m_object->isPositioned() || m_object->isRelPositioned()) + clipRects.setPosClipRect(intersection(newOverflowClip, clipRects.posClipRect())); + } + if (m_object->hasClip()) { + IntRect newPosClip = m_object->getClipRect(x,y); + clipRects.setPosClipRect(intersection(newPosClip, clipRects.posClipRect())); + clipRects.setOverflowClipRect(intersection(newPosClip, clipRects.overflowClipRect())); + clipRects.setFixedClipRect(intersection(newPosClip, clipRects.fixedClipRect())); + } + } +} + +void RenderLayer::calculateRects(const RenderLayer* rootLayer, const IntRect& paintDirtyRect, IntRect& layerBounds, + IntRect& backgroundRect, IntRect& foregroundRect, IntRect& outlineRect, bool temporaryClipRects) const +{ + if (rootLayer != this && parent()) { + ClipRects parentClipRects; + if (temporaryClipRects) + parent()->calculateClipRects(rootLayer, parentClipRects); + else { + parent()->updateClipRects(rootLayer); + parentClipRects = *parent()->clipRects(); + } + + backgroundRect = m_object->style()->position() == FixedPosition ? parentClipRects.fixedClipRect() : + (m_object->isPositioned() ? parentClipRects.posClipRect() : + parentClipRects.overflowClipRect()); + RenderView* view = renderer()->view(); + ASSERT(view); + if (view && parentClipRects.fixed() && rootLayer->renderer() == view) + backgroundRect.move(view->frameView()->scrollX(), view->frameView()->scrollY()); + + backgroundRect.intersect(paintDirtyRect); + } else + backgroundRect = paintDirtyRect; + + foregroundRect = backgroundRect; + outlineRect = backgroundRect; + + int x = 0; + int y = 0; + convertToLayerCoords(rootLayer, x, y); + layerBounds = IntRect(x,y,width(),height()); + + // Update the clip rects that will be passed to child layers. + if (m_object->hasOverflowClip() || m_object->hasClip()) { + // This layer establishes a clip of some kind. + if (m_object->hasOverflowClip()) + foregroundRect.intersect(m_object->getOverflowClipRect(x,y)); + if (m_object->hasClip()) { + // Clip applies to *us* as well, so go ahead and update the damageRect. + IntRect newPosClip = m_object->getClipRect(x,y); + backgroundRect.intersect(newPosClip); + foregroundRect.intersect(newPosClip); + outlineRect.intersect(newPosClip); + } + + // If we establish a clip at all, then go ahead and make sure our background + // rect is intersected with our layer's bounds. + if (ShadowData* boxShadow = renderer()->style()->boxShadow()) { + IntRect overflow = layerBounds; + do { + IntRect shadowRect = layerBounds; + shadowRect.move(boxShadow->x, boxShadow->y); + shadowRect.inflate(boxShadow->blur); + overflow.unite(shadowRect); + boxShadow = boxShadow->next; + } while (boxShadow); + backgroundRect.intersect(overflow); + } else + backgroundRect.intersect(layerBounds); + } +} + +IntRect RenderLayer::childrenClipRect() const +{ + RenderLayer* rootLayer = renderer()->document()->renderer()->layer(); + IntRect layerBounds, backgroundRect, foregroundRect, outlineRect; + calculateRects(rootLayer, rootLayer->boundingBox(rootLayer), layerBounds, backgroundRect, foregroundRect, outlineRect); + return foregroundRect; +} + +IntRect RenderLayer::selfClipRect() const +{ + RenderLayer* rootLayer = renderer()->document()->renderer()->layer(); + IntRect layerBounds, backgroundRect, foregroundRect, outlineRect; + calculateRects(rootLayer, rootLayer->boundingBox(rootLayer), layerBounds, backgroundRect, foregroundRect, outlineRect); + return backgroundRect; +} + +bool RenderLayer::intersectsDamageRect(const IntRect& layerBounds, const IntRect& damageRect, const RenderLayer* rootLayer) const +{ + // Always examine the canvas and the root. + // FIXME: Could eliminate the isRoot() check if we fix background painting so that the RenderView + // paints the root's background. + if (renderer()->isRenderView() || renderer()->isRoot()) + return true; + + // If we aren't an inline flow, and our layer bounds do intersect the damage rect, then we + // can go ahead and return true. + RenderView* view = renderer()->view(); + ASSERT(view); + if (view && !renderer()->isInlineFlow()) { + IntRect b = layerBounds; + b.inflate(view->maximalOutlineSize()); + if (b.intersects(damageRect)) + return true; + } + + // Otherwise we need to compute the bounding box of this single layer and see if it intersects + // the damage rect. + return boundingBox(rootLayer).intersects(damageRect); +} + +IntRect RenderLayer::boundingBox(const RenderLayer* rootLayer) const +{ + // There are three special cases we need to consider. + // (1) Inline Flows. For inline flows we will create a bounding box that fully encompasses all of the lines occupied by the + // inline. In other words, if some <span> wraps to three lines, we'll create a bounding box that fully encloses the root + // line boxes of all three lines (including overflow on those lines). + // (2) Left/Top Overflow. The width/height of layers already includes right/bottom overflow. However, in the case of left/top + // overflow, we have to create a bounding box that will extend to include this overflow. + // (3) Floats. When a layer has overhanging floats that it paints, we need to make sure to include these overhanging floats + // as part of our bounding box. We do this because we are the responsible layer for both hit testing and painting those + // floats. + IntRect result; + if (renderer()->isInlineFlow()) { + // Go from our first line box to our last line box. + RenderInline* inlineFlow = static_cast<RenderInline*>(renderer()); + InlineFlowBox* firstBox = inlineFlow->firstLineBox(); + if (!firstBox) + return result; + int top = firstBox->root()->topOverflow(); + int bottom = inlineFlow->lastLineBox()->root()->bottomOverflow(); + int left = firstBox->xPos(); + for (InlineRunBox* curr = firstBox->nextLineBox(); curr; curr = curr->nextLineBox()) + left = min(left, curr->xPos()); + result = IntRect(m_x + left, m_y + (top - renderer()->yPos()), width(), bottom - top); + } else if (renderer()->isTableRow()) { + // Our bounding box is just the union of all of our cells' border/overflow rects. + for (RenderObject* child = renderer()->firstChild(); child; child = child->nextSibling()) { + if (child->isTableCell()) { + IntRect bbox = child->borderBox(); + bbox.move(0, child->borderTopExtra()); + result.unite(bbox); + IntRect overflowRect = renderer()->overflowRect(false); + overflowRect.move(0, child->borderTopExtra()); + if (bbox != overflowRect) + result.unite(overflowRect); + } + } + result.move(m_x, m_y); + } else { + if (renderer()->hasMask()) + result = renderer()->maskClipRect(); + else { + IntRect bbox = renderer()->borderBox(); + result = bbox; + IntRect overflowRect = renderer()->overflowRect(false); + if (bbox != overflowRect) + result.unite(overflowRect); + } + + // We have to adjust the x/y of this result so that it is in the coordinate space of the layer. + // We also have to add in borderTopExtra here, since borderBox(), in order to play well with methods like + // floatRect that deal with child content, uses an origin of (0,0) that is at the child content box (so + // border box returns a y coord of -borderTopExtra(). The layer, however, uses the outer box. This is all + // really confusing. + result.move(m_x, m_y + renderer()->borderTopExtra()); + } + + // Convert the bounding box to an absolute position. We can do this easily by looking at the delta + // between the bounding box's xpos and our layer's xpos and then applying that to the absolute layerBounds + // passed in. + int absX = 0, absY = 0; + convertToLayerCoords(rootLayer, absX, absY); + result.move(absX - m_x, absY - m_y); + RenderView* view = renderer()->view(); + ASSERT(view); + if (view) + result.inflate(view->maximalOutlineSize()); + return result; +} + +void RenderLayer::clearClipRectsIncludingDescendants() +{ + if (!m_clipRects) + return; + + clearClipRects(); + + for (RenderLayer* l = firstChild(); l; l = l->nextSibling()) + l->clearClipRectsIncludingDescendants(); +} + +void RenderLayer::clearClipRects() +{ + if (m_clipRects) { + m_clipRects->deref(m_object->renderArena()); + m_clipRects = 0; +#ifndef NDEBUG + m_clipRectsRoot = 0; +#endif + } +} + +static RenderObject* commonAncestor(RenderObject* obj1, RenderObject* obj2) +{ + if (!obj1 || !obj2) + return 0; + + for (RenderObject* currObj1 = obj1; currObj1; currObj1 = currObj1->hoverAncestor()) + for (RenderObject* currObj2 = obj2; currObj2; currObj2 = currObj2->hoverAncestor()) + if (currObj1 == currObj2) + return currObj1; + + return 0; +} + +void RenderLayer::updateHoverActiveState(const HitTestRequest& request, HitTestResult& result) +{ + // We don't update :hover/:active state when the result is marked as readonly. + if (request.readonly) + return; + + Document* doc = renderer()->document(); + + Node* activeNode = doc->activeNode(); + if (activeNode && !request.active) { + // We are clearing the :active chain because the mouse has been released. + for (RenderObject* curr = activeNode->renderer(); curr; curr = curr->parent()) { + if (curr->element() && !curr->isText()) + curr->element()->setInActiveChain(false); + } + doc->setActiveNode(0); + } else { + Node* newActiveNode = result.innerNode(); + if (!activeNode && newActiveNode && request.active) { + // We are setting the :active chain and freezing it. If future moves happen, they + // will need to reference this chain. + for (RenderObject* curr = newActiveNode->renderer(); curr; curr = curr->parent()) { + if (curr->element() && !curr->isText()) { + curr->element()->setInActiveChain(true); + } + } + doc->setActiveNode(newActiveNode); + } + } + + // If the mouse is down and if this is a mouse move event, we want to restrict changes in + // :hover/:active to only apply to elements that are in the :active chain that we froze + // at the time the mouse went down. + bool mustBeInActiveChain = request.active && request.mouseMove; + + // Check to see if the hovered node has changed. If not, then we don't need to + // do anything. + RefPtr<Node> oldHoverNode = doc->hoverNode(); + Node* newHoverNode = result.innerNode(); + + // Update our current hover node. + doc->setHoverNode(newHoverNode); + + // We have two different objects. Fetch their renderers. + RenderObject* oldHoverObj = oldHoverNode ? oldHoverNode->renderer() : 0; + RenderObject* newHoverObj = newHoverNode ? newHoverNode->renderer() : 0; + + // Locate the common ancestor render object for the two renderers. + RenderObject* ancestor = commonAncestor(oldHoverObj, newHoverObj); + + if (oldHoverObj != newHoverObj) { + // The old hover path only needs to be cleared up to (and not including) the common ancestor; + for (RenderObject* curr = oldHoverObj; curr && curr != ancestor; curr = curr->hoverAncestor()) { + if (curr->element() && !curr->isText() && (!mustBeInActiveChain || curr->element()->inActiveChain())) { + curr->element()->setActive(false); + curr->element()->setHovered(false); + } + } + } + + // Now set the hover state for our new object up to the root. + for (RenderObject* curr = newHoverObj; curr; curr = curr->hoverAncestor()) { + if (curr->element() && !curr->isText() && (!mustBeInActiveChain || curr->element()->inActiveChain())) { + curr->element()->setActive(request.active); + curr->element()->setHovered(true); + } + } +} + +// Helper for the sorting of layers by z-index. +static inline bool compareZIndex(RenderLayer* first, RenderLayer* second) +{ + return first->zIndex() < second->zIndex(); +} + +void RenderLayer::dirtyZOrderLists() +{ + if (m_posZOrderList) + m_posZOrderList->clear(); + if (m_negZOrderList) + m_negZOrderList->clear(); + m_zOrderListsDirty = true; +} + +void RenderLayer::dirtyStackingContextZOrderLists() +{ + RenderLayer* sc = stackingContext(); + if (sc) + sc->dirtyZOrderLists(); +} + +void RenderLayer::dirtyOverflowList() +{ + if (m_overflowList) + m_overflowList->clear(); + m_overflowListDirty = true; +} + +void RenderLayer::updateZOrderLists() +{ + if (!isStackingContext() || !m_zOrderListsDirty) + return; + + for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) + if (!m_reflection || reflectionLayer() != child) + child->collectLayers(m_posZOrderList, m_negZOrderList); + + // Sort the two lists. + if (m_posZOrderList) + std::stable_sort(m_posZOrderList->begin(), m_posZOrderList->end(), compareZIndex); + if (m_negZOrderList) + std::stable_sort(m_negZOrderList->begin(), m_negZOrderList->end(), compareZIndex); + + m_zOrderListsDirty = false; +} + +void RenderLayer::updateOverflowList() +{ + if (!m_overflowListDirty) + return; + + for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) { + // Ignore non-overflow layers and reflections. + if (child->isOverflowOnly() && (!m_reflection || reflectionLayer() != child)) { + if (!m_overflowList) + m_overflowList = new Vector<RenderLayer*>; + m_overflowList->append(child); + } + } + + m_overflowListDirty = false; +} + +void RenderLayer::collectLayers(Vector<RenderLayer*>*& posBuffer, Vector<RenderLayer*>*& negBuffer) +{ + updateVisibilityStatus(); + + // Overflow layers are just painted by their enclosing layers, so they don't get put in zorder lists. + if ((m_hasVisibleContent || (m_hasVisibleDescendant && isStackingContext())) && !isOverflowOnly()) { + // Determine which buffer the child should be in. + Vector<RenderLayer*>*& buffer = (zIndex() >= 0) ? posBuffer : negBuffer; + + // Create the buffer if it doesn't exist yet. + if (!buffer) + buffer = new Vector<RenderLayer*>; + + // Append ourselves at the end of the appropriate buffer. + buffer->append(this); + } + + // Recur into our children to collect more layers, but only if we don't establish + // a stacking context. + if (m_hasVisibleDescendant && !isStackingContext()) { + for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) { + // Ignore reflections. + if (!m_reflection || reflectionLayer() != child) + child->collectLayers(posBuffer, negBuffer); + } + } +} + +void RenderLayer::repaintIncludingDescendants() +{ + m_object->repaint(); + for (RenderLayer* curr = firstChild(); curr; curr = curr->nextSibling()) + curr->repaintIncludingDescendants(); +} + +bool RenderLayer::shouldBeOverflowOnly() const +{ + return (renderer()->hasOverflowClip() || renderer()->hasReflection()) && + !renderer()->isPositioned() && + !renderer()->isRelPositioned() && + !renderer()->hasTransform() && + !isTransparent(); +} + +void RenderLayer::styleChanged(RenderStyle::Diff, const RenderStyle*) +{ + bool isOverflowOnly = shouldBeOverflowOnly(); + if (isOverflowOnly != m_isOverflowOnly) { + m_isOverflowOnly = isOverflowOnly; + RenderLayer* p = parent(); + if (p) + p->dirtyOverflowList(); + dirtyStackingContextZOrderLists(); + } + + if (m_object->style()->overflowX() == OMARQUEE && m_object->style()->marqueeBehavior() != MNONE) { + if (!m_marquee) + m_marquee = new RenderMarquee(this); + m_marquee->updateMarqueeStyle(); + } + else if (m_marquee) { + delete m_marquee; + m_marquee = 0; + } + + if (!hasReflection() && m_reflection) { + m_reflection->destroy(); + m_reflection = 0; + } else if (hasReflection()) { + if (!m_reflection) + createReflection(); + updateReflectionStyle(); + } + + // FIXME: Need to detect a swap from custom to native scrollbars (and vice versa). + if (m_hBar) + m_hBar->styleChanged(); + if (m_vBar) + m_vBar->styleChanged(); + + updateScrollCornerStyle(); + updateResizerStyle(); +} + +void RenderLayer::updateScrollCornerStyle() +{ + RenderObject* actualRenderer = m_object->node()->isElementNode() ? m_object->node()->shadowAncestorNode()->renderer() : m_object; + RefPtr<RenderStyle> corner = m_object->hasOverflowClip() ? actualRenderer->getUncachedPseudoStyle(RenderStyle::SCROLLBAR_CORNER, actualRenderer->style()) : 0; + if (corner) { + if (!m_scrollCorner) { + m_scrollCorner = new (m_object->renderArena()) RenderScrollbarPart(m_object->document()); + m_scrollCorner->setParent(m_object); + } + m_scrollCorner->setStyle(corner.release()); + } else if (m_scrollCorner) { + m_scrollCorner->destroy(); + m_scrollCorner = 0; + } +} + +void RenderLayer::updateResizerStyle() +{ + RenderObject* actualRenderer = m_object->node()->isElementNode() ? m_object->node()->shadowAncestorNode()->renderer() : m_object; + RefPtr<RenderStyle> resizer = m_object->hasOverflowClip() ? actualRenderer->getUncachedPseudoStyle(RenderStyle::RESIZER, actualRenderer->style()) : 0; + if (resizer) { + if (!m_resizer) { + m_resizer = new (m_object->renderArena()) RenderScrollbarPart(m_object->document()); + m_resizer->setParent(m_object); + } + m_resizer->setStyle(resizer.release()); + } else if (m_resizer) { + m_resizer->destroy(); + m_resizer = 0; + } +} + +RenderLayer* RenderLayer::reflectionLayer() const +{ + return m_reflection ? m_reflection->layer() : 0; +} + +void RenderLayer::createReflection() +{ + ASSERT(!m_reflection); + m_reflection = new (renderer()->renderArena()) RenderReplica(renderer()->document()); + m_reflection->setParent(renderer()); // We create a 1-way connection. +} + +void RenderLayer::updateReflectionStyle() +{ + RefPtr<RenderStyle> newStyle = RenderStyle::create(); + newStyle->inheritFrom(renderer()->style()); + + // Map in our transform. + TransformOperations transform; + switch (renderer()->style()->boxReflect()->direction()) { + case ReflectionBelow: + transform.operations().append(TranslateTransformOperation::create(Length(0, Fixed), Length(100., Percent), TransformOperation::TRANSLATE)); + transform.operations().append(TranslateTransformOperation::create(Length(0, Fixed), renderer()->style()->boxReflect()->offset(), TransformOperation::TRANSLATE)); + transform.operations().append(ScaleTransformOperation::create(1.0, -1.0, ScaleTransformOperation::SCALE)); + break; + case ReflectionAbove: + transform.operations().append(ScaleTransformOperation::create(1.0, -1.0, ScaleTransformOperation::SCALE)); + transform.operations().append(TranslateTransformOperation::create(Length(0, Fixed), Length(100., Percent), TransformOperation::TRANSLATE)); + transform.operations().append(TranslateTransformOperation::create(Length(0, Fixed), renderer()->style()->boxReflect()->offset(), TransformOperation::TRANSLATE)); + break; + case ReflectionRight: + transform.operations().append(TranslateTransformOperation::create(Length(100., Percent), Length(0, Fixed), TransformOperation::TRANSLATE)); + transform.operations().append(TranslateTransformOperation::create(renderer()->style()->boxReflect()->offset(), Length(0, Fixed), TransformOperation::TRANSLATE)); + transform.operations().append(ScaleTransformOperation::create(-1.0, 1.0, ScaleTransformOperation::SCALE)); + break; + case ReflectionLeft: + transform.operations().append(ScaleTransformOperation::create(-1.0, 1.0, ScaleTransformOperation::SCALE)); + transform.operations().append(TranslateTransformOperation::create(Length(100., Percent), Length(0, Fixed), TransformOperation::TRANSLATE)); + transform.operations().append(TranslateTransformOperation::create(renderer()->style()->boxReflect()->offset(), Length(0, Fixed), TransformOperation::TRANSLATE)); + break; + } + newStyle->setTransform(transform); + + // Map in our mask. + newStyle->setMaskBoxImage(renderer()->style()->boxReflect()->mask()); + + m_reflection->setStyle(newStyle.release()); +} + +void RenderLayer::suspendMarquees() +{ + if (m_marquee) + m_marquee->suspend(); + + for (RenderLayer* curr = firstChild(); curr; curr = curr->nextSibling()) + curr->suspendMarquees(); +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderLayer.h b/src/3rdparty/webkit/WebCore/rendering/RenderLayer.h new file mode 100644 index 0000000..b2ba48c --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderLayer.h @@ -0,0 +1,523 @@ +/* + * Copyright (C) 2003 Apple Computer, Inc. + * + * Portions are Copyright (C) 1998 Netscape Communications Corporation. + * + * Other contributors: + * Robert O'Callahan <roc+@cs.cmu.edu> + * David Baron <dbaron@fas.harvard.edu> + * Christian Biesinger <cbiesinger@web.de> + * Randall Jesup <rjesup@wgate.com> + * Roland Mainz <roland.mainz@informatik.med.uni-giessen.de> + * Josh Soref <timeless@mac.com> + * Boris Zbarsky <bzbarsky@mit.edu> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Alternatively, the contents of this file may be used under the terms + * of either the Mozilla Public License Version 1.1, found at + * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public + * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html + * (the "GPL"), in which case the provisions of the MPL or the GPL are + * applicable instead of those above. If you wish to allow use of your + * version of this file only under the terms of one of those two + * licenses (the MPL or the GPL) and not to allow others to use your + * version of this file under the LGPL, indicate your decision by + * deletingthe provisions above and replace them with the notice and + * other provisions required by the MPL or the GPL, as the case may be. + * If you do not delete the provisions above, a recipient may use your + * version of this file under any of the LGPL, the MPL or the GPL. + */ + +#ifndef RenderLayer_h +#define RenderLayer_h + +#include "ScrollbarClient.h" +#include "RenderObject.h" +#include "Timer.h" +#include <wtf/OwnPtr.h> + +namespace WebCore { + +class TransformationMatrix; +class CachedResource; +class HitTestResult; +class RenderFrameSet; +class RenderMarquee; +class RenderObject; +class RenderReplica; +class RenderScrollbarPart; +class RenderStyle; +class RenderTable; +class RenderText; +class RenderView; +class Scrollbar; + +struct HitTestRequest; + +class ClipRects { +public: + ClipRects() + : m_refCnt(0) + , m_fixed(false) + { + } + + ClipRects(const IntRect& r) + : m_overflowClipRect(r) + , m_fixedClipRect(r) + , m_posClipRect(r) + , m_refCnt(0) + , m_fixed(false) + { + } + + ClipRects(const ClipRects& other) + : m_overflowClipRect(other.overflowClipRect()) + , m_fixedClipRect(other.fixedClipRect()) + , m_posClipRect(other.posClipRect()) + , m_refCnt(0) + , m_fixed(other.fixed()) + { + } + + void reset(const IntRect& r) + { + m_overflowClipRect = r; + m_fixedClipRect = r; + m_posClipRect = r; + m_fixed = false; + } + + const IntRect& overflowClipRect() const { return m_overflowClipRect; } + void setOverflowClipRect(const IntRect& r) { m_overflowClipRect = r; } + + const IntRect& fixedClipRect() const { return m_fixedClipRect; } + void setFixedClipRect(const IntRect&r) { m_fixedClipRect = r; } + + const IntRect& posClipRect() const { return m_posClipRect; } + void setPosClipRect(const IntRect& r) { m_posClipRect = r; } + + bool fixed() const { return m_fixed; } + void setFixed(bool fixed) { m_fixed = fixed; } + + void ref() { m_refCnt++; } + void deref(RenderArena* renderArena) { if (--m_refCnt == 0) destroy(renderArena); } + + void destroy(RenderArena*); + + // Overloaded new operator. + void* operator new(size_t, RenderArena*) throw(); + + // Overridden to prevent the normal delete from being called. + void operator delete(void*, size_t); + + bool operator==(const ClipRects& other) const + { + return m_overflowClipRect == other.overflowClipRect() && + m_fixedClipRect == other.fixedClipRect() && + m_posClipRect == other.posClipRect() && + m_fixed == other.fixed(); + } + + ClipRects& operator=(const ClipRects& other) + { + m_overflowClipRect = other.overflowClipRect(); + m_fixedClipRect = other.fixedClipRect(); + m_posClipRect = other.posClipRect(); + m_fixed = other.fixed(); + return *this; + } + +private: + // The normal operator new is disallowed on all render objects. + void* operator new(size_t) throw(); + +private: + IntRect m_overflowClipRect; + IntRect m_fixedClipRect; + IntRect m_posClipRect; + unsigned m_refCnt : 31; + bool m_fixed : 1; +}; + +class RenderLayer : public ScrollbarClient { +public: + enum ScrollBehavior { + noScroll, + alignCenter, + alignTop, + alignBottom, + alignLeft, + alignRight, + alignToClosestEdge + }; + + struct ScrollAlignment { + ScrollBehavior m_rectVisible; + ScrollBehavior m_rectHidden; + ScrollBehavior m_rectPartial; + }; + + friend class RenderReplica; + + static const ScrollAlignment gAlignCenterIfNeeded; + static const ScrollAlignment gAlignToEdgeIfNeeded; + static const ScrollAlignment gAlignCenterAlways; + static const ScrollAlignment gAlignTopAlways; + static const ScrollAlignment gAlignBottomAlways; + + static ScrollBehavior getVisibleBehavior(const ScrollAlignment& s) { return s.m_rectVisible; } + static ScrollBehavior getPartialBehavior(const ScrollAlignment& s) { return s.m_rectPartial; } + static ScrollBehavior getHiddenBehavior(const ScrollAlignment& s) { return s.m_rectHidden; } + + RenderLayer(RenderObject*); + ~RenderLayer(); + + RenderObject* renderer() const { return m_object; } + RenderLayer* parent() const { return m_parent; } + RenderLayer* previousSibling() const { return m_previous; } + RenderLayer* nextSibling() const { return m_next; } + RenderLayer* firstChild() const { return m_first; } + RenderLayer* lastChild() const { return m_last; } + + void addChild(RenderLayer* newChild, RenderLayer* beforeChild = 0); + RenderLayer* removeChild(RenderLayer*); + + void removeOnlyThisLayer(); + void insertOnlyThisLayer(); + + void repaintIncludingDescendants(); + + void styleChanged(RenderStyle::Diff, const RenderStyle*); + + RenderMarquee* marquee() const { return m_marquee; } + void suspendMarquees(); + + bool isOverflowOnly() const { return m_isOverflowOnly; } + + bool requiresSlowRepaints() const; + + bool isTransparent() const; + RenderLayer* transparentAncestor(); + void beginTransparencyLayers(GraphicsContext*, const RenderLayer* rootLayer); + + bool hasReflection() const { return m_object->hasReflection(); } + RenderReplica* reflection() const { return m_reflection; } + RenderLayer* reflectionLayer() const; + + const RenderLayer* root() const + { + const RenderLayer* curr = this; + while (curr->parent()) + curr = curr->parent(); + return curr; + } + + int xPos() const { return m_x; } + int yPos() const { return m_y; } + void setPos(int xPos, int yPos) + { + m_x = xPos; + m_y = yPos; + } + + int width() const { return m_width; } + int height() const { return m_height; } + void setWidth(int w) { m_width = w; } + void setHeight(int h) { m_height = h; } + + int scrollWidth(); + int scrollHeight(); + + void panScrollFromPoint(const IntPoint&); + + // Scrolling methods for layers that can scroll their overflow. + void scrollByRecursively(int xDelta, int yDelta); + void addScrolledContentOffset(int& x, int& y) const; + void subtractScrolledContentOffset(int& x, int& y) const; + IntSize scrolledContentOffset() const { return IntSize(scrollXOffset() + m_scrollLeftOverflow, scrollYOffset()); } + + int scrollXOffset() const { return m_scrollX + m_scrollOriginX; } + int scrollYOffset() const { return m_scrollY; } + + void scrollToOffset(int x, int y, bool updateScrollbars = true, bool repaint = true); + void scrollToXOffset(int x) { scrollToOffset(x, m_scrollY); } + void scrollToYOffset(int y) { scrollToOffset(m_scrollX + m_scrollOriginX, y); } + void scrollRectToVisible(const IntRect&, bool scrollToAnchor = false, const ScrollAlignment& alignX = gAlignCenterIfNeeded, const ScrollAlignment& alignY = gAlignCenterIfNeeded); + + IntRect getRectToExpose(const IntRect& visibleRect, const IntRect& exposeRect, const ScrollAlignment& alignX, const ScrollAlignment& alignY); + + void setHasHorizontalScrollbar(bool); + void setHasVerticalScrollbar(bool); + + PassRefPtr<Scrollbar> createScrollbar(ScrollbarOrientation); + void destroyScrollbar(ScrollbarOrientation); + + Scrollbar* horizontalScrollbar() const { return m_hBar.get(); } + Scrollbar* verticalScrollbar() const { return m_vBar.get(); } + + int verticalScrollbarWidth() const; + int horizontalScrollbarHeight() const; + + void positionOverflowControls(int tx, int ty); + bool isPointInResizeControl(const IntPoint& absolutePoint) const; + bool hitTestOverflowControls(HitTestResult&); + IntSize offsetFromResizeCorner(const IntPoint& absolutePoint) const; + + void paintOverflowControls(GraphicsContext*, int tx, int ty, const IntRect& damageRect); + void paintScrollCorner(GraphicsContext*, int tx, int ty, const IntRect& damageRect); + void paintResizer(GraphicsContext*, int tx, int ty, const IntRect& damageRect); + + void updateScrollInfoAfterLayout(); + + bool scroll(ScrollDirection, ScrollGranularity, float multiplier = 1.0f); + void autoscroll(); + + void resize(const PlatformMouseEvent&, const IntSize&); + bool inResizeMode() const { return m_inResizeMode; } + void setInResizeMode(bool b) { m_inResizeMode = b; } + + void updateLayerPosition(); + void updateLayerPositions(bool doFullRepaint = false, bool checkForRepaint = true); + + void updateTransform(); + + void relativePositionOffset(int& relX, int& relY) const { relX += m_relX; relY += m_relY; } + IntSize relativePositionOffset() const { return IntSize(m_relX, m_relY); } + + void clearClipRectsIncludingDescendants(); + void clearClipRects(); + + // Get the enclosing stacking context for this layer. A stacking context is a layer + // that has a non-auto z-index. + RenderLayer* stackingContext() const; + bool isStackingContext() const { return !hasAutoZIndex() || renderer()->isRenderView(); } + + void dirtyZOrderLists(); + void dirtyStackingContextZOrderLists(); + void updateZOrderLists(); + Vector<RenderLayer*>* posZOrderList() const { return m_posZOrderList; } + Vector<RenderLayer*>* negZOrderList() const { return m_negZOrderList; } + + void dirtyOverflowList(); + void updateOverflowList(); + Vector<RenderLayer*>* overflowList() const { return m_overflowList; } + + bool hasVisibleContent() const { return m_hasVisibleContent; } + void setHasVisibleContent(bool); + void dirtyVisibleContentStatus(); + + // Gets the nearest enclosing positioned ancestor layer (also includes + // the <html> layer and the root layer). + RenderLayer* enclosingPositionedAncestor() const; + + void convertToLayerCoords(const RenderLayer* ancestorLayer, int& x, int& y) const; + + bool hasAutoZIndex() const { return renderer()->style()->hasAutoZIndex(); } + int zIndex() const { return renderer()->style()->zIndex(); } + + // The two main functions that use the layer system. The paint method + // paints the layers that intersect the damage rect from back to + // front. The hitTest method looks for mouse events by walking + // layers that intersect the point from front to back. + void paint(GraphicsContext*, const IntRect& damageRect, PaintRestriction = PaintRestrictionNone, RenderObject* paintingRoot = 0); + bool hitTest(const HitTestRequest&, HitTestResult&); + + // This method figures out our layerBounds in coordinates relative to + // |rootLayer}. It also computes our background and foreground clip rects + // for painting/event handling. + void calculateRects(const RenderLayer* rootLayer, const IntRect& paintDirtyRect, IntRect& layerBounds, + IntRect& backgroundRect, IntRect& foregroundRect, IntRect& outlineRect, bool temporaryClipRects = false) const; + + // Compute and cache clip rects computed with the given layer as the root + void updateClipRects(const RenderLayer* rootLayer); + // Compute and return the clip rects. If useCached is true, will used previously computed clip rects on ancestors + // (rather than computing them all from scratch up the parent chain). + void calculateClipRects(const RenderLayer* rootLayer, ClipRects&, bool useCached = false) const; + ClipRects* clipRects() const { return m_clipRects; } + + IntRect childrenClipRect() const; // Returns the foreground clip rect of the layer in the document's coordinate space. + IntRect selfClipRect() const; // Returns the background clip rect of the layer in the document's coordinate space. + + bool intersectsDamageRect(const IntRect& layerBounds, const IntRect& damageRect, const RenderLayer* rootLayer) const; + + // Returns a bounding box for this layer only. + IntRect boundingBox(const RenderLayer* rootLayer) const; + + void updateHoverActiveState(const HitTestRequest&, HitTestResult&); + + IntRect repaintRect() const { return m_repaintRect; } + void setNeedsFullRepaint(bool f = true) { m_needsFullRepaint = f; } + + int staticX() const { return m_staticX; } + int staticY() const { return m_staticY; } + void setStaticX(int staticX) { m_staticX = staticX; } + void setStaticY(int staticY) { m_staticY = staticY; } + + bool hasTransform() const { return m_object->hasTransform(); } + TransformationMatrix* transform() const { return m_transform.get(); } + + void destroy(RenderArena*); + + // Overloaded new operator. Derived classes must override operator new + // in order to allocate out of the RenderArena. + void* operator new(size_t, RenderArena*) throw(); + + // Overridden to prevent the normal delete from being called. + void operator delete(void*, size_t); + +private: + // The normal operator new is disallowed on all render objects. + void* operator new(size_t) throw(); + +private: + void setNextSibling(RenderLayer* next) { m_next = next; } + void setPreviousSibling(RenderLayer* prev) { m_previous = prev; } + void setParent(RenderLayer* parent) { m_parent = parent; } + void setFirstChild(RenderLayer* first) { m_first = first; } + void setLastChild(RenderLayer* last) { m_last = last; } + + void collectLayers(Vector<RenderLayer*>*&, Vector<RenderLayer*>*&); + + void paintLayer(RenderLayer* rootLayer, GraphicsContext*, const IntRect& paintDirtyRect, + bool haveTransparency, PaintRestriction, RenderObject* paintingRoot, + bool appliedTransform = false, bool temporaryClipRects = false); + RenderLayer* hitTestLayer(RenderLayer* rootLayer, const HitTestRequest&, HitTestResult&, const IntRect& hitTestRect, const IntPoint& hitTestPoint, bool appliedTransform = false); + void computeScrollDimensions(bool* needHBar = 0, bool* needVBar = 0); + + bool shouldBeOverflowOnly() const; + + virtual void valueChanged(Scrollbar*); + virtual void invalidateScrollbarRect(Scrollbar*, const IntRect&); + virtual bool isActive() const; + virtual bool scrollbarCornerPresent() const; + + void updateOverflowStatus(bool horizontalOverflow, bool verticalOverflow); + + void childVisibilityChanged(bool newVisibility); + void dirtyVisibleDescendantStatus(); + void updateVisibilityStatus(); + + Node* enclosingElement() const; + + void createReflection(); + void updateReflectionStyle(); + bool paintingInsideReflection() const { return m_paintingInsideReflection; } + + RenderLayer* enclosingTransformedAncestor() const; + + // Convert a point in absolute coords into layer coords, taking transforms into account + IntPoint absoluteToContents(const IntPoint&) const; + + void updateScrollCornerStyle(); + void updateResizerStyle(); + +protected: + RenderObject* m_object; + + RenderLayer* m_parent; + RenderLayer* m_previous; + RenderLayer* m_next; + RenderLayer* m_first; + RenderLayer* m_last; + + IntRect m_repaintRect; // Cached repaint rects. Used by layout. + IntRect m_outlineBox; + + // Our current relative position offset. + int m_relX; + int m_relY; + + // Our (x,y) coordinates are in our parent layer's coordinate space. + int m_x; + int m_y; + + // The layer's width/height + int m_width; + int m_height; + + // Our scroll offsets if the view is scrolled. + int m_scrollX; + int m_scrollY; + int m_scrollOriginX; // only non-zero for rtl content + int m_scrollLeftOverflow; // only non-zero for rtl content + + // The width/height of our scrolled area. + int m_scrollWidth; + int m_scrollHeight; + + // For layers with overflow, we have a pair of scrollbars. + RefPtr<Scrollbar> m_hBar; + RefPtr<Scrollbar> m_vBar; + + // Keeps track of whether the layer is currently resizing, so events can cause resizing to start and stop. + bool m_inResizeMode; + + // For layers that establish stacking contexts, m_posZOrderList holds a sorted list of all the + // descendant layers within the stacking context that have z-indices of 0 or greater + // (auto will count as 0). m_negZOrderList holds descendants within our stacking context with negative + // z-indices. + Vector<RenderLayer*>* m_posZOrderList; + Vector<RenderLayer*>* m_negZOrderList; + + // This list contains child layers that cannot create stacking contexts. For now it is just + // overflow layers, but that may change in the future. + Vector<RenderLayer*>* m_overflowList; + + ClipRects* m_clipRects; // Cached clip rects used when painting and hit testing. +#ifndef NDEBUG + const RenderLayer* m_clipRectsRoot; // Root layer used to compute clip rects. +#endif + + bool m_scrollDimensionsDirty : 1; + bool m_zOrderListsDirty : 1; + bool m_overflowListDirty: 1; + bool m_isOverflowOnly : 1; + + bool m_usedTransparency : 1; // Tracks whether we need to close a transparent layer, i.e., whether + // we ended up painting this layer or any descendants (and therefore need to + // blend). + bool m_paintingInsideReflection : 1; // A state bit tracking if we are painting inside a replica. + bool m_inOverflowRelayout : 1; + bool m_needsFullRepaint : 1; + + bool m_overflowStatusDirty : 1; + bool m_horizontalOverflow : 1; + bool m_verticalOverflow : 1; + bool m_visibleContentStatusDirty : 1; + bool m_hasVisibleContent : 1; + bool m_visibleDescendantStatusDirty : 1; + bool m_hasVisibleDescendant : 1; + + RenderMarquee* m_marquee; // Used by layers with overflow:marquee + + // Cached normal flow values for absolute positioned elements with static left/top values. + int m_staticX; + int m_staticY; + + OwnPtr<TransformationMatrix> m_transform; + + // May ultimately be extended to many replicas (with their own paint order). + RenderReplica* m_reflection; + + // Renderers to hold our custom scroll corner and resizer. + RenderScrollbarPart* m_scrollCorner; + RenderScrollbarPart* m_resizer; +}; + +} // namespace WebCore + +#endif // RenderLayer_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderLegend.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderLegend.cpp new file mode 100644 index 0000000..1fac53f --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderLegend.cpp @@ -0,0 +1,36 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. + * + * 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" +#include "RenderLegend.h" + +namespace WebCore { + +RenderLegend::RenderLegend(Node* element) + : RenderBlock(element) +{ +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderLegend.h b/src/3rdparty/webkit/WebCore/rendering/RenderLegend.h new file mode 100644 index 0000000..649f132 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderLegend.h @@ -0,0 +1,42 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef RenderLegend_h +#define RenderLegend_h + +#include "RenderBlock.h" + +namespace WebCore { + + class RenderLegend : public RenderBlock { + public: + RenderLegend(Node*); + + virtual const char* renderName() const { return "RenderLegend"; } + }; + +} // namespace WebCore + +#endif // RenderLegend_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderListBox.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderListBox.cpp new file mode 100644 index 0000000..35b9451 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderListBox.cpp @@ -0,0 +1,649 @@ +/* + * This file is part of the select element renderer in WebCore. + * + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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 + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "RenderListBox.h" + +#include "AXObjectCache.h" +#include "CSSStyleSelector.h" +#include "Document.h" +#include "EventHandler.h" +#include "EventNames.h" +#include "FocusController.h" +#include "Frame.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "HTMLNames.h" +#include "HTMLOptGroupElement.h" +#include "HTMLOptionElement.h" +#include "HTMLSelectElement.h" +#include "HitTestResult.h" +#include "Page.h" +#include "RenderScrollbar.h" +#include "RenderTheme.h" +#include "RenderView.h" +#include "Scrollbar.h" +#include "SelectionController.h" +#include "NodeRenderStyle.h" +#include <math.h> + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +const int rowSpacing = 1; + +const int optionsSpacingHorizontal = 2; + +const int minSize = 4; +const int maxDefaultSize = 10; + +// FIXME: This hardcoded baselineAdjustment is what we used to do for the old +// widget, but I'm not sure this is right for the new control. +const int baselineAdjustment = 7; + +RenderListBox::RenderListBox(HTMLSelectElement* element) + : RenderBlock(element) + , m_optionsChanged(true) + , m_scrollToRevealSelectionAfterLayout(false) + , m_inAutoscroll(false) + , m_optionsWidth(0) + , m_indexOffset(0) +{ +} + +RenderListBox::~RenderListBox() +{ + setHasVerticalScrollbar(false); +} + +void RenderListBox::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +{ + RenderBlock::styleDidChange(diff, oldStyle); + setReplaced(isInline()); +} + +void RenderListBox::updateFromElement() +{ + if (m_optionsChanged) { + const Vector<HTMLElement*>& listItems = static_cast<HTMLSelectElement*>(node())->listItems(); + int size = numItems(); + + float width = 0; + for (int i = 0; i < size; ++i) { + HTMLElement* element = listItems[i]; + String text; + Font itemFont = style()->font(); + if (element->hasTagName(optionTag)) + text = static_cast<HTMLOptionElement*>(element)->optionText(); + else if (element->hasTagName(optgroupTag)) { + text = static_cast<HTMLOptGroupElement*>(element)->groupLabelText(); + FontDescription d = itemFont.fontDescription(); + d.setWeight(d.bolderWeight()); + itemFont = Font(d, itemFont.letterSpacing(), itemFont.wordSpacing()); + itemFont.update(document()->styleSelector()->fontSelector()); + } + + if (!text.isEmpty()) { + float textWidth = itemFont.floatWidth(TextRun(text.impl(), 0, 0, 0, false, false, false, false)); + width = max(width, textWidth); + } + } + m_optionsWidth = static_cast<int>(ceilf(width)); + m_optionsChanged = false; + + setHasVerticalScrollbar(true); + + setNeedsLayoutAndPrefWidthsRecalc(); + } +} + +void RenderListBox::selectionChanged() +{ + repaint(); + if (!m_inAutoscroll) { + if (m_optionsChanged || needsLayout()) + m_scrollToRevealSelectionAfterLayout = true; + else + scrollToRevealSelection(); + } + + if (AXObjectCache::accessibilityEnabled()) + document()->axObjectCache()->selectedChildrenChanged(this); +} + +void RenderListBox::layout() +{ + RenderBlock::layout(); + if (m_scrollToRevealSelectionAfterLayout) + scrollToRevealSelection(); +} + +void RenderListBox::scrollToRevealSelection() +{ + HTMLSelectElement* select = static_cast<HTMLSelectElement*>(node()); + + m_scrollToRevealSelectionAfterLayout = false; + + int firstIndex = select->activeSelectionStartListIndex(); + if (firstIndex >= 0 && !listIndexIsVisible(select->activeSelectionEndListIndex())) + scrollToRevealElementAtListIndex(firstIndex); +} + +void RenderListBox::calcPrefWidths() +{ + ASSERT(!m_optionsChanged); + + m_minPrefWidth = 0; + m_maxPrefWidth = 0; + + if (style()->width().isFixed() && style()->width().value() > 0) + m_minPrefWidth = m_maxPrefWidth = calcContentBoxWidth(style()->width().value()); + else { + m_maxPrefWidth = m_optionsWidth + 2 * optionsSpacingHorizontal; + if (m_vBar) + m_maxPrefWidth += m_vBar->width(); + } + + if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) { + m_maxPrefWidth = max(m_maxPrefWidth, calcContentBoxWidth(style()->minWidth().value())); + m_minPrefWidth = max(m_minPrefWidth, calcContentBoxWidth(style()->minWidth().value())); + } else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent())) + m_minPrefWidth = 0; + else + m_minPrefWidth = m_maxPrefWidth; + + if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) { + m_maxPrefWidth = min(m_maxPrefWidth, calcContentBoxWidth(style()->maxWidth().value())); + m_minPrefWidth = min(m_minPrefWidth, calcContentBoxWidth(style()->maxWidth().value())); + } + + int toAdd = paddingLeft() + paddingRight() + borderLeft() + borderRight(); + m_minPrefWidth += toAdd; + m_maxPrefWidth += toAdd; + + setPrefWidthsDirty(false); +} + +int RenderListBox::size() const +{ + int specifiedSize = static_cast<HTMLSelectElement*>(node())->size(); + if (specifiedSize > 1) + return max(minSize, specifiedSize); + return min(max(minSize, numItems()), maxDefaultSize); +} + +int RenderListBox::numVisibleItems() const +{ + // Only count fully visible rows. But don't return 0 even if only part of a row shows. + return max(1, (contentHeight() + rowSpacing) / itemHeight()); +} + +int RenderListBox::numItems() const +{ + return static_cast<HTMLSelectElement*>(node())->listItems().size(); +} + +int RenderListBox::listHeight() const +{ + return itemHeight() * numItems() - rowSpacing; +} + +void RenderListBox::calcHeight() +{ + int toAdd = paddingTop() + paddingBottom() + borderTop() + borderBottom(); + + int itemHeight = RenderListBox::itemHeight(); + m_height = itemHeight * size() - rowSpacing + toAdd; + + RenderBlock::calcHeight(); + + if (m_vBar) { + bool enabled = numVisibleItems() < numItems(); + m_vBar->setEnabled(enabled); + m_vBar->setSteps(1, min(1, numVisibleItems() - 1), itemHeight); + m_vBar->setProportion(numVisibleItems(), numItems()); + if (!enabled) + m_indexOffset = 0; + } +} + +int RenderListBox::baselinePosition(bool, bool) const +{ + return height() + marginTop() + marginBottom() - baselineAdjustment; +} + +IntRect RenderListBox::itemBoundingBoxRect(int tx, int ty, int index) +{ + return IntRect(tx + borderLeft() + paddingLeft(), + ty + borderTop() + paddingTop() + itemHeight() * (index - m_indexOffset), + contentWidth(), itemHeight()); +} + +void RenderListBox::paintObject(PaintInfo& paintInfo, int tx, int ty) +{ + if (style()->visibility() != VISIBLE) + return; + + int listItemsSize = numItems(); + + if (paintInfo.phase == PaintPhaseForeground) { + int index = m_indexOffset; + while (index < listItemsSize && index <= m_indexOffset + numVisibleItems()) { + paintItemForeground(paintInfo, tx, ty, index); + index++; + } + } + + // Paint the children. + RenderBlock::paintObject(paintInfo, tx, ty); + + if (paintInfo.phase == PaintPhaseBlockBackground) + paintScrollbar(paintInfo, tx, ty); + else if (paintInfo.phase == PaintPhaseChildBlockBackground || paintInfo.phase == PaintPhaseChildBlockBackgrounds) { + int index = m_indexOffset; + while (index < listItemsSize && index <= m_indexOffset + numVisibleItems()) { + paintItemBackground(paintInfo, tx, ty, index); + index++; + } + } +} + +void RenderListBox::paintScrollbar(PaintInfo& paintInfo, int tx, int ty) +{ + if (m_vBar) { + IntRect scrollRect(tx + width() - borderRight() - m_vBar->width(), + ty + borderTop(), + m_vBar->width(), + height() - (borderTop() + borderBottom())); + m_vBar->setFrameRect(scrollRect); + m_vBar->paint(paintInfo.context, paintInfo.rect); + } +} + +void RenderListBox::paintItemForeground(PaintInfo& paintInfo, int tx, int ty, int listIndex) +{ + HTMLSelectElement* select = static_cast<HTMLSelectElement*>(node()); + const Vector<HTMLElement*>& listItems = select->listItems(); + HTMLElement* element = listItems[listIndex]; + + String itemText; + if (element->hasTagName(optionTag)) + itemText = static_cast<HTMLOptionElement*>(element)->optionText(); + else if (element->hasTagName(optgroupTag)) + itemText = static_cast<HTMLOptGroupElement*>(element)->groupLabelText(); + + // Determine where the item text should be placed + IntRect r = itemBoundingBoxRect(tx, ty, listIndex); + r.move(optionsSpacingHorizontal, style()->font().ascent()); + + RenderStyle* itemStyle = element->renderStyle(); + if (!itemStyle) + itemStyle = style(); + + Color textColor = element->renderStyle() ? element->renderStyle()->color() : style()->color(); + if (element->hasTagName(optionTag) && static_cast<HTMLOptionElement*>(element)->selected()) { + if (document()->frame()->selection()->isFocusedAndActive() && document()->focusedNode() == node()) + textColor = theme()->activeListBoxSelectionForegroundColor(); + // Honor the foreground color for disabled items + else if (!element->disabled()) + textColor = theme()->inactiveListBoxSelectionForegroundColor(); + } + + paintInfo.context->setFillColor(textColor); + + Font itemFont = style()->font(); + if (element->hasTagName(optgroupTag)) { + FontDescription d = itemFont.fontDescription(); + d.setWeight(d.bolderWeight()); + itemFont = Font(d, itemFont.letterSpacing(), itemFont.wordSpacing()); + itemFont.update(document()->styleSelector()->fontSelector()); + } + paintInfo.context->setFont(itemFont); + + unsigned length = itemText.length(); + const UChar* string = itemText.characters(); + TextRun textRun(string, length, 0, 0, 0, itemStyle->direction() == RTL, itemStyle->unicodeBidi() == Override, false, false); + + // Draw the item text + if (itemStyle->visibility() != HIDDEN) + paintInfo.context->drawBidiText(textRun, r.location()); +} + +void RenderListBox::paintItemBackground(PaintInfo& paintInfo, int tx, int ty, int listIndex) +{ + HTMLSelectElement* select = static_cast<HTMLSelectElement*>(node()); + const Vector<HTMLElement*>& listItems = select->listItems(); + HTMLElement* element = listItems[listIndex]; + + Color backColor; + if (element->hasTagName(optionTag) && static_cast<HTMLOptionElement*>(element)->selected()) { + if (document()->frame()->selection()->isFocusedAndActive() && document()->focusedNode() == node()) + backColor = theme()->activeListBoxSelectionBackgroundColor(); + else + backColor = theme()->inactiveListBoxSelectionBackgroundColor(); + } else + backColor = element->renderStyle() ? element->renderStyle()->backgroundColor() : style()->backgroundColor(); + + // Draw the background for this list box item + if (!element->renderStyle() || element->renderStyle()->visibility() != HIDDEN) { + IntRect itemRect = itemBoundingBoxRect(tx, ty, listIndex); + itemRect.intersect(controlClipRect(tx, ty)); + paintInfo.context->fillRect(itemRect, backColor); + } +} + +bool RenderListBox::isPointInOverflowControl(HitTestResult& result, int _x, int _y, int _tx, int _ty) +{ + if (!m_vBar) + return false; + + IntRect vertRect(_tx + width() - borderRight() - m_vBar->width(), + _ty + borderTop() - borderTopExtra(), + m_vBar->width(), + height() + borderTopExtra() + borderBottomExtra() - borderTop() - borderBottom()); + + if (vertRect.contains(_x, _y)) { + result.setScrollbar(m_vBar.get()); + return true; + } + return false; +} + +int RenderListBox::listIndexAtOffset(int offsetX, int offsetY) +{ + if (!numItems()) + return -1; + + if (offsetY < borderTop() + paddingTop() || offsetY > height() - paddingBottom() - borderBottom()) + return -1; + + int scrollbarWidth = m_vBar ? m_vBar->width() : 0; + if (offsetX < borderLeft() + paddingLeft() || offsetX > width() - borderRight() - paddingRight() - scrollbarWidth) + return -1; + + int newOffset = (offsetY - borderTop() - paddingTop()) / itemHeight() + m_indexOffset; + return newOffset < numItems() ? newOffset : -1; +} + +void RenderListBox::panScroll(const IntPoint& panStartMousePosition) +{ + const int maxSpeed = 20; + const int iconRadius = 7; + const int speedReducer = 4; + + // FIXME: This doesn't work correctly with transforms. + FloatPoint absOffset = localToAbsolute(); + + IntPoint currentMousePosition = document()->frame()->eventHandler()->currentMousePosition(); + // We need to check if the current mouse position is out of the window. When the mouse is out of the window, the position is incoherent + static IntPoint previousMousePosition; + if (currentMousePosition.y() < 0) + currentMousePosition = previousMousePosition; + else + previousMousePosition = currentMousePosition; + + int yDelta = currentMousePosition.y() - panStartMousePosition.y(); + + // If the point is too far from the center we limit the speed + yDelta = max(min(yDelta, maxSpeed), -maxSpeed); + + if(abs(yDelta) < iconRadius) // at the center we let the space for the icon + return; + + if (yDelta > 0) + //offsetY = view()->viewHeight(); + absOffset.move(0, listHeight()); + else if (yDelta < 0) + yDelta--; + + // Let's attenuate the speed + yDelta /= speedReducer; + + IntPoint scrollPoint(0,0); + scrollPoint.setY(absOffset.y() + yDelta); + int newOffset = scrollToward(scrollPoint); + if (newOffset < 0) + return; + + m_inAutoscroll = true; + HTMLSelectElement* select = static_cast<HTMLSelectElement*>(node()); + select->updateListBoxSelection(!select->multiple()); + m_inAutoscroll = false; +} + +int RenderListBox::scrollToward(const IntPoint& destination) +{ + // FIXME: This doesn't work correctly with transforms. + FloatPoint absPos = localToAbsolute(); + int offsetX = destination.x() - absPos.x(); + int offsetY = destination.y() - absPos.y(); + + int rows = numVisibleItems(); + int offset = m_indexOffset; + + if (offsetY < borderTop() + paddingTop() && scrollToRevealElementAtListIndex(offset - 1)) + return offset - 1; + + if (offsetY > height() - paddingBottom() - borderBottom() && scrollToRevealElementAtListIndex(offset + rows)) + return offset + rows - 1; + + return listIndexAtOffset(offsetX, offsetY); +} + +void RenderListBox::autoscroll() +{ + IntPoint pos = document()->frame()->view()->windowToContents(document()->frame()->eventHandler()->currentMousePosition()); + + int endIndex = scrollToward(pos); + if (endIndex >= 0) { + HTMLSelectElement* select = static_cast<HTMLSelectElement*>(node()); + m_inAutoscroll = true; + + if (!select->multiple()) + select->setActiveSelectionAnchorIndex(endIndex); + + select->setActiveSelectionEndIndex(endIndex); + select->updateListBoxSelection(!select->multiple()); + m_inAutoscroll = false; + } +} + +void RenderListBox::stopAutoscroll() +{ + static_cast<HTMLSelectElement*>(node())->listBoxOnChange(); +} + +bool RenderListBox::scrollToRevealElementAtListIndex(int index) +{ + if (index < 0 || index >= numItems() || listIndexIsVisible(index)) + return false; + + int newOffset; + if (index < m_indexOffset) + newOffset = index; + else + newOffset = index - numVisibleItems() + 1; + + m_indexOffset = newOffset; + if (m_vBar) + m_vBar->setValue(m_indexOffset); + + return true; +} + +bool RenderListBox::listIndexIsVisible(int index) +{ + return index >= m_indexOffset && index < m_indexOffset + numVisibleItems(); +} + +bool RenderListBox::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier) +{ + return m_vBar && m_vBar->scroll(direction, granularity, multiplier); +} + +void RenderListBox::valueChanged(unsigned listIndex) +{ + HTMLSelectElement* select = static_cast<HTMLSelectElement*>(node()); + select->setSelectedIndex(select->listToOptionIndex(listIndex)); + select->onChange(); +} + +void RenderListBox::valueChanged(Scrollbar*) +{ + int newOffset = m_vBar->value(); + if (newOffset != m_indexOffset) { + m_indexOffset = newOffset; + repaint(); + // Fire the scroll DOM event. + EventTargetNodeCast(node())->dispatchEventForType(eventNames().scrollEvent, false, false); + } +} + +int RenderListBox::itemHeight() const +{ + return style()->font().height() + rowSpacing; +} + +int RenderListBox::verticalScrollbarWidth() const +{ + return m_vBar ? m_vBar->width() : 0; +} + +// FIXME: We ignore padding in the vertical direction as far as these values are concerned, since that's +// how the control currently paints. +int RenderListBox::scrollWidth() const +{ + // There is no horizontal scrolling allowed. + return clientWidth(); +} + +int RenderListBox::scrollHeight() const +{ + return max(clientHeight(), listHeight()); +} + +int RenderListBox::scrollLeft() const +{ + return 0; +} + +void RenderListBox::setScrollLeft(int) +{ +} + +int RenderListBox::scrollTop() const +{ + return m_indexOffset * itemHeight(); +} + +void RenderListBox::setScrollTop(int newTop) +{ + // Determine an index and scroll to it. + int index = newTop / itemHeight(); + if (index < 0 || index >= numItems() || index == m_indexOffset) + return; + m_indexOffset = index; + if (m_vBar) + m_vBar->setValue(index); +} + +IntRect RenderListBox::controlClipRect(int tx, int ty) const +{ + IntRect clipRect = contentBox(); + clipRect.move(tx, ty); + return clipRect; +} + +bool RenderListBox::isActive() const +{ + Page* page = document()->frame()->page(); + return page && page->focusController()->isActive(); +} + +void RenderListBox::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect) +{ + IntRect scrollRect = rect; + scrollRect.move(width() - borderRight() - scrollbar->width(), borderTop()); + repaintRectangle(scrollRect); +} + +bool RenderListBox::isScrollable() const +{ + if (numVisibleItems() < numItems()) + return true; + return RenderObject::isScrollable(); +} + +PassRefPtr<Scrollbar> RenderListBox::createScrollbar() +{ + RefPtr<Scrollbar> widget; + bool hasCustomScrollbarStyle = style()->hasPseudoStyle(RenderStyle::SCROLLBAR); + if (hasCustomScrollbarStyle) + widget = RenderScrollbar::createCustomScrollbar(this, VerticalScrollbar, this); + else + widget = Scrollbar::createNativeScrollbar(this, VerticalScrollbar, SmallScrollbar); + document()->view()->addChild(widget.get()); + return widget.release(); +} + +void RenderListBox::destroyScrollbar() +{ + if (!m_vBar) + return; + + m_vBar->removeFromParent(); + m_vBar->setClient(0); + m_vBar = 0; +} + +void RenderListBox::setHasVerticalScrollbar(bool hasScrollbar) +{ + if (hasScrollbar == (m_vBar != 0)) + return; + + if (hasScrollbar) + m_vBar = createScrollbar(); + else + destroyScrollbar(); + + if (m_vBar) + m_vBar->styleChanged(); + +#if ENABLE(DASHBOARD_SUPPORT) + // Force an update since we know the scrollbars have changed things. + if (document()->hasDashboardRegions()) + document()->setDashboardRegionsDirty(true); +#endif +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderListBox.h b/src/3rdparty/webkit/WebCore/rendering/RenderListBox.h new file mode 100644 index 0000000..ccc6847 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderListBox.h @@ -0,0 +1,132 @@ +/* + * This file is part of the select element renderer in WebCore. + * + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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 + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RenderListBox_h +#define RenderListBox_h + +#include "RenderBlock.h" +#include "ScrollbarClient.h" + +namespace WebCore { + +class HTMLSelectElement; + +class RenderListBox : public RenderBlock, private ScrollbarClient { +public: + RenderListBox(HTMLSelectElement*); + ~RenderListBox(); + + virtual const char* renderName() const { return "RenderListBox"; } + + virtual bool isListBox() const { return true; } + + virtual void updateFromElement(); + + virtual bool canHaveChildren() const { return false; } + + virtual bool hasControlClip() const { return true; } + virtual void paintObject(PaintInfo&, int tx, int ty); + virtual IntRect controlClipRect(int tx, int ty) const; + + virtual bool isPointInOverflowControl(HitTestResult&, int x, int y, int tx, int ty); + + virtual bool scroll(ScrollDirection, ScrollGranularity, float multiplier = 1.0f); + virtual bool isScrollable() const; + + virtual void calcPrefWidths(); + virtual int baselinePosition(bool firstLine, bool isRootLineBox) const; + virtual void calcHeight(); + + virtual void layout(); + + void selectionChanged(); + + void setOptionsChanged(bool changed) { m_optionsChanged = changed; } + + int listIndexAtOffset(int x, int y); + IntRect itemBoundingBoxRect(int tx, int ty, int index); + + bool scrollToRevealElementAtListIndex(int index); + bool listIndexIsVisible(int index); + + virtual bool canBeProgramaticallyScrolled(bool) const { return true; } + virtual void autoscroll(); + virtual void stopAutoscroll(); + + virtual bool shouldPanScroll() const { return true; } + virtual void panScroll(const IntPoint&); + + int scrollToward(const IntPoint&); // Returns the new index or -1 if no scroll occurred + + virtual int verticalScrollbarWidth() const; + virtual int scrollLeft() const; + virtual int scrollTop() const; + virtual int scrollWidth() const; + virtual int scrollHeight() const; + virtual void setScrollLeft(int); + virtual void setScrollTop(int); + +protected: + virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + +private: + // ScrollbarClient interface. + virtual void valueChanged(Scrollbar*); + virtual void invalidateScrollbarRect(Scrollbar*, const IntRect&); + virtual bool isActive() const; + virtual bool scrollbarCornerPresent() const { return false; } // We don't support resize on list boxes yet. If we did this would have to change. + + void setHasVerticalScrollbar(bool hasScrollbar); + PassRefPtr<Scrollbar> createScrollbar(); + void destroyScrollbar(); + + int itemHeight() const; + void valueChanged(unsigned listIndex); + int size() const; + int numVisibleItems() const; + int numItems() const; + int listHeight() const; + void paintScrollbar(PaintInfo&, int tx, int ty); + void paintItemForeground(PaintInfo&, int tx, int ty, int listIndex); + void paintItemBackground(PaintInfo&, int tx, int ty, int listIndex); + void scrollToRevealSelection(); + + bool m_optionsChanged; + bool m_scrollToRevealSelectionAfterLayout; + bool m_inAutoscroll; + int m_optionsWidth; + int m_indexOffset; + + RefPtr<Scrollbar> m_vBar; +}; + +} // namepace WebCore + +#endif // RenderListBox_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderListItem.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderListItem.cpp new file mode 100644 index 0000000..f88f131 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderListItem.cpp @@ -0,0 +1,335 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. + * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net) + * + * 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" +#include "RenderListItem.h" + +#include "CachedImage.h" +#include "HTMLNames.h" +#include "HTMLOListElement.h" +#include "RenderListMarker.h" +#include "RenderView.h" +#include <wtf/StdLibExtras.h> + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +RenderListItem::RenderListItem(Node* node) + : RenderBlock(node) + , m_marker(0) + , m_hasExplicitValue(false) + , m_isValueUpToDate(false) + , m_notInList(false) +{ + setInline(false); +} + +void RenderListItem::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +{ + RenderBlock::styleDidChange(diff, oldStyle); + + if (style()->listStyleType() != LNONE || + (style()->listStyleImage() && !style()->listStyleImage()->errorOccurred())) { + RefPtr<RenderStyle> newStyle = RenderStyle::create(); + // The marker always inherits from the list item, regardless of where it might end + // up (e.g., in some deeply nested line box). See CSS3 spec. + newStyle->inheritFrom(style()); + if (!m_marker) + m_marker = new (renderArena()) RenderListMarker(this); + m_marker->setStyle(newStyle.release()); + } else if (m_marker) { + m_marker->destroy(); + m_marker = 0; + } +} + +void RenderListItem::destroy() +{ + if (m_marker) { + m_marker->destroy(); + m_marker = 0; + } + RenderBlock::destroy(); +} + +static Node* enclosingList(Node* node) +{ + Node* parent = node->parentNode(); + for (Node* n = parent; n; n = n->parentNode()) + if (n->hasTagName(ulTag) || n->hasTagName(olTag)) + return n; + // If there's no actual <ul> or <ol> list element, then our parent acts as + // our list for purposes of determining what other list items should be + // numbered as part of the same list. + return parent; +} + +static RenderListItem* previousListItem(Node* list, const RenderListItem* item) +{ + for (Node* n = item->node()->traversePreviousNode(); n != list; n = n->traversePreviousNode()) { + RenderObject* o = n->renderer(); + if (o && o->isListItem()) { + Node* otherList = enclosingList(n); + // This item is part of our current list, so it's what we're looking for. + if (list == otherList) + return static_cast<RenderListItem*>(o); + // We found ourself inside another list; lets skip the rest of it. + // Use traverseNextNode() here because the other list itself may actually + // be a list item itself. We need to examine it, so we do this to counteract + // the traversePreviousNode() that will be done by the loop. + if (otherList) + n = otherList->traverseNextNode(); + } + } + return 0; +} + +inline int RenderListItem::calcValue() const +{ + if (m_hasExplicitValue) + return m_explicitValue; + Node* list = enclosingList(node()); + // FIXME: This recurses to a possible depth of the length of the list. + // That's not good -- we need to change this to an iterative algorithm. + if (RenderListItem* previousItem = previousListItem(list, this)) + return previousItem->value() + 1; + if (list && list->hasTagName(olTag)) + return static_cast<HTMLOListElement*>(list)->start(); + return 1; +} + +void RenderListItem::updateValueNow() const +{ + m_value = calcValue(); + m_isValueUpToDate = true; +} + +bool RenderListItem::isEmpty() const +{ + return lastChild() == m_marker; +} + +static RenderObject* getParentOfFirstLineBox(RenderBlock* curr, RenderObject* marker) +{ + RenderObject* firstChild = curr->firstChild(); + if (!firstChild) + return 0; + + for (RenderObject* currChild = firstChild; currChild; currChild = currChild->nextSibling()) { + if (currChild == marker) + continue; + + if (currChild->isInline() && (!currChild->isInlineFlow() || curr->generatesLineBoxesForInlineChild(currChild))) + return curr; + + if (currChild->isFloating() || currChild->isPositioned()) + continue; + + if (currChild->isTable() || !currChild->isRenderBlock()) + break; + + if (curr->isListItem() && currChild->style()->htmlHacks() && currChild->element() && + (currChild->element()->hasTagName(ulTag)|| currChild->element()->hasTagName(olTag))) + break; + + RenderObject* lineBox = getParentOfFirstLineBox(static_cast<RenderBlock*>(currChild), marker); + if (lineBox) + return lineBox; + } + + return 0; +} + +void RenderListItem::updateValue() +{ + if (!m_hasExplicitValue) { + m_isValueUpToDate = false; + if (m_marker) + m_marker->setNeedsLayoutAndPrefWidthsRecalc(); + } +} + +static RenderObject* firstNonMarkerChild(RenderObject* parent) +{ + RenderObject* result = parent->firstChild(); + while (result && result->isListMarker()) + result = result->nextSibling(); + return result; +} + +void RenderListItem::updateMarkerLocation() +{ + // Sanity check the location of our marker. + if (m_marker) { + RenderObject* markerPar = m_marker->parent(); + RenderObject* lineBoxParent = getParentOfFirstLineBox(this, m_marker); + if (!lineBoxParent) { + // If the marker is currently contained inside an anonymous box, + // then we are the only item in that anonymous box (since no line box + // parent was found). It's ok to just leave the marker where it is + // in this case. + if (markerPar && markerPar->isAnonymousBlock()) + lineBoxParent = markerPar; + else + lineBoxParent = this; + } + + if (markerPar != lineBoxParent || m_marker->prefWidthsDirty()) { + // Removing and adding the marker can trigger repainting in + // containers other than ourselves, so we need to disable LayoutState. + view()->disableLayoutState(); + updateFirstLetter(); + m_marker->remove(); + if (!lineBoxParent) + lineBoxParent = this; + lineBoxParent->addChild(m_marker, firstNonMarkerChild(lineBoxParent)); + if (m_marker->prefWidthsDirty()) + m_marker->calcPrefWidths(); + view()->enableLayoutState(); + } + } +} + +void RenderListItem::calcPrefWidths() +{ + ASSERT(prefWidthsDirty()); + + updateMarkerLocation(); + + RenderBlock::calcPrefWidths(); +} + +void RenderListItem::layout() +{ + ASSERT(needsLayout()); + + updateMarkerLocation(); + RenderBlock::layout(); +} + +void RenderListItem::positionListMarker() +{ + if (m_marker && !m_marker->isInside() && m_marker->inlineBoxWrapper()) { + int markerOldX = m_marker->xPos(); + int yOffset = 0; + int xOffset = 0; + for (RenderObject* o = m_marker->parent(); o != this; o = o->parent()) { + yOffset += o->yPos(); + xOffset += o->xPos(); + } + + bool adjustOverflow = false; + int markerXPos; + RootInlineBox* root = m_marker->inlineBoxWrapper()->root(); + + if (style()->direction() == LTR) { + int leftLineOffset = leftRelOffset(yOffset, leftOffset(yOffset)); + markerXPos = leftLineOffset - xOffset - paddingLeft() - borderLeft() + m_marker->marginLeft(); + m_marker->inlineBoxWrapper()->adjustPosition(markerXPos - markerOldX, 0); + if (markerXPos < root->leftOverflow()) { + root->setHorizontalOverflowPositions(markerXPos, root->rightOverflow()); + adjustOverflow = true; + } + } else { + int rightLineOffset = rightRelOffset(yOffset, rightOffset(yOffset)); + markerXPos = rightLineOffset - xOffset + paddingRight() + borderRight() + m_marker->marginLeft(); + m_marker->inlineBoxWrapper()->adjustPosition(markerXPos - markerOldX, 0); + if (markerXPos + m_marker->width() > root->rightOverflow()) { + root->setHorizontalOverflowPositions(root->leftOverflow(), markerXPos + m_marker->width()); + adjustOverflow = true; + } + } + + if (adjustOverflow) { + IntRect markerRect(markerXPos + xOffset, yOffset, m_marker->width(), m_marker->height()); + RenderObject* o = m_marker; + do { + o = o->parent(); + if (o->isRenderBlock()) + static_cast<RenderBlock*>(o)->addVisualOverflow(markerRect); + markerRect.move(-o->xPos(), -o->yPos()); + } while (o != this); + } + } +} + +void RenderListItem::paint(PaintInfo& paintInfo, int tx, int ty) +{ + if (!m_height) + return; + + RenderBlock::paint(paintInfo, tx, ty); +} + +const String& RenderListItem::markerText() const +{ + if (m_marker) + return m_marker->text(); + DEFINE_STATIC_LOCAL(String, staticNullString, ()); + return staticNullString; +} + +void RenderListItem::explicitValueChanged() +{ + if (m_marker) + m_marker->setNeedsLayoutAndPrefWidthsRecalc(); + Node* listNode = enclosingList(node()); + RenderObject* listRenderer = 0; + if (listNode) + listRenderer = listNode->renderer(); + for (RenderObject* r = this; r; r = r->nextInPreOrder(listRenderer)) + if (r->isListItem()) { + RenderListItem* item = static_cast<RenderListItem*>(r); + if (!item->m_hasExplicitValue) { + item->m_isValueUpToDate = false; + if (RenderListMarker* marker = item->m_marker) + marker->setNeedsLayoutAndPrefWidthsRecalc(); + } + } +} + +void RenderListItem::setExplicitValue(int value) +{ + if (m_hasExplicitValue && m_explicitValue == value) + return; + m_explicitValue = value; + m_value = value; + m_hasExplicitValue = true; + explicitValueChanged(); +} + +void RenderListItem::clearExplicitValue() +{ + if (!m_hasExplicitValue) + return; + m_hasExplicitValue = false; + m_isValueUpToDate = false; + explicitValueChanged(); +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderListItem.h b/src/3rdparty/webkit/WebCore/rendering/RenderListItem.h new file mode 100644 index 0000000..d4dd675 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderListItem.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef RenderListItem_h +#define RenderListItem_h + +#include "RenderBlock.h" + +namespace WebCore { + +class RenderListMarker; + +class RenderListItem : public RenderBlock { +public: + RenderListItem(Node*); + + virtual const char* renderName() const { return "RenderListItem"; } + + virtual bool isListItem() const { return true; } + + virtual void destroy(); + + int value() const { if (!m_isValueUpToDate) updateValueNow(); return m_value; } + void updateValue(); + + bool hasExplicitValue() const { return m_hasExplicitValue; } + int explicitValue() const { return m_explicitValue; } + void setExplicitValue(int value); + void clearExplicitValue(); + + virtual bool isEmpty() const; + virtual void paint(PaintInfo&, int tx, int ty); + + virtual void layout(); + virtual void calcPrefWidths(); + + virtual void positionListMarker(); + + void setNotInList(bool notInList) { m_notInList = notInList; } + bool notInList() const { return m_notInList; } + + const String& markerText() const; + +protected: + virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + +private: + void updateMarkerLocation(); + inline int calcValue() const; + void updateValueNow() const; + void explicitValueChanged(); + + RenderListMarker* m_marker; + int m_explicitValue; + mutable int m_value; + + bool m_hasExplicitValue : 1; + mutable bool m_isValueUpToDate : 1; + bool m_notInList : 1; +}; + +} // namespace WebCore + +#endif // RenderListItem_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderListMarker.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderListMarker.cpp new file mode 100644 index 0000000..4209906 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderListMarker.cpp @@ -0,0 +1,905 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net) + * + * 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" +#include "RenderListMarker.h" + +#include "CachedImage.h" +#include "CharacterNames.h" +#include "Document.h" +#include "GraphicsContext.h" +#include "ListMarkerBox.h" +#include "RenderLayer.h" +#include "RenderListItem.h" +#include "RenderView.h" + +using namespace std; +using namespace WTF; +using namespace Unicode; + +namespace WebCore { + +const int cMarkerPadding = 7; + +static String toRoman(int number, bool upper) +{ + // FIXME: CSS3 describes how to make this work for much larger numbers, + // using overbars and special characters. It also specifies the characters + // in the range U+2160 to U+217F instead of standard ASCII ones. + if (number < 1 || number > 3999) + return String::number(number); + + const int lettersSize = 12; // big enough for three each of I, X, C, and M + UChar letters[lettersSize]; + + int length = 0; + const UChar ldigits[] = { 'i', 'v', 'x', 'l', 'c', 'd', 'm' }; + const UChar udigits[] = { 'I', 'V', 'X', 'L', 'C', 'D', 'M' }; + const UChar* digits = upper ? udigits : ldigits; + int d = 0; + do { + int num = number % 10; + if (num % 5 < 4) + for (int i = num % 5; i > 0; i--) + letters[lettersSize - ++length] = digits[d]; + if (num >= 4 && num <= 8) + letters[lettersSize - ++length] = digits[d + 1]; + if (num == 9) + letters[lettersSize - ++length] = digits[d + 2]; + if (num % 5 == 4) + letters[lettersSize - ++length] = digits[d]; + number /= 10; + d += 2; + } while (number); + + ASSERT(length <= lettersSize); + return String(&letters[lettersSize - length], length); +} + +static String toAlphabetic(int number, const UChar* alphabet, int alphabetSize) +{ + ASSERT(alphabetSize >= 10); + + if (number < 1) + return String::number(number); + + const int lettersSize = 10; // big enough for a 32-bit int, with a 10-letter alphabet + UChar letters[lettersSize]; + + --number; + letters[lettersSize - 1] = alphabet[number % alphabetSize]; + int length = 1; + while ((number /= alphabetSize) > 0) + letters[lettersSize - ++length] = alphabet[number % alphabetSize - 1]; + + ASSERT(length <= lettersSize); + return String(&letters[lettersSize - length], length); +} + +static int toHebrewUnder1000(int number, UChar letters[5]) +{ + // FIXME: CSS3 mentions various refinements not implemented here. + // FIXME: Should take a look at Mozilla's HebrewToText function (in nsBulletFrame). + ASSERT(number >= 0 && number < 1000); + int length = 0; + int fourHundreds = number / 400; + for (int i = 0; i < fourHundreds; i++) + letters[length++] = 1511 + 3; + number %= 400; + if (number / 100) + letters[length++] = 1511 + (number / 100) - 1; + number %= 100; + if (number == 15 || number == 16) { + letters[length++] = 1487 + 9; + letters[length++] = 1487 + number - 9; + } else { + if (int tens = number / 10) { + static const UChar hebrewTens[9] = { 1497, 1499, 1500, 1502, 1504, 1505, 1506, 1508, 1510 }; + letters[length++] = hebrewTens[tens - 1]; + } + if (int ones = number % 10) + letters[length++] = 1487 + ones; + } + ASSERT(length <= 5); + return length; +} + +static String toHebrew(int number) +{ + // FIXME: CSS3 mentions ways to make this work for much larger numbers. + if (number < 0 || number > 999999) + return String::number(number); + + if (number == 0) { + static const UChar hebrewZero[3] = { 0x05D0, 0x05E4, 0x05E1 }; + return String(hebrewZero, 3); + } + + const int lettersSize = 11; // big enough for two 5-digit sequences plus a quote mark between + UChar letters[lettersSize]; + + int length; + if (number < 1000) + length = 0; + else { + length = toHebrewUnder1000(number / 1000, letters); + letters[length++] = '\''; + number = number % 1000; + } + length += toHebrewUnder1000(number, letters + length); + + ASSERT(length <= lettersSize); + return String(letters, length); +} + +static int toArmenianUnder10000(int number, bool upper, bool addCircumflex, UChar letters[9]) +{ + ASSERT(number >= 0 && number < 10000); + int length = 0; + + int lowerOffset = upper ? 0 : 0x0030; + + if (int thousands = number / 1000) + if (thousands == 7) { + letters[length++] = 0x0548 + lowerOffset; + letters[length++] = 0x0552 + lowerOffset; + if (addCircumflex) + letters[length++] = 0x0302; + } else { + letters[length++] = (0x054C - 1 + lowerOffset) + thousands; + if (addCircumflex) + letters[length++] = 0x0302; + } + + if (int hundreds = (number / 100) % 10) { + letters[length++] = (0x0543 - 1 + lowerOffset) + hundreds; + if (addCircumflex) + letters[length++] = 0x0302; + } + + if (int tens = (number / 10) % 10) { + letters[length++] = (0x053A - 1 + lowerOffset) + tens; + if (addCircumflex) + letters[length++] = 0x0302; + } + + if (int ones = number % 10) { + letters[length++] = (0x531 - 1 + lowerOffset) + ones; + if (addCircumflex) + letters[length++] = 0x0302; + } + + return length; +} + +static String toArmenian(int number, bool upper) +{ + if (number < 1 || number > 99999999) + return String::number(number); + + const int lettersSize = 18; // twice what toArmenianUnder10000 needs + UChar letters[lettersSize]; + + int length = toArmenianUnder10000(number / 10000, upper, true, letters); + length += toArmenianUnder10000(number % 10000, upper, false, letters + length); + + ASSERT(length <= lettersSize); + return String(letters, length); +} + +static String toGeorgian(int number) +{ + if (number < 1 || number > 19999) + return String::number(number); + + const int lettersSize = 5; + UChar letters[lettersSize]; + + int length = 0; + + if (number > 9999) + letters[length++] = 0x10F5; + + if (int thousands = (number / 1000) % 10) { + static const UChar georgianThousands[9] = { + 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10F4, 0x10EF, 0x10F0 + }; + letters[length++] = georgianThousands[thousands - 1]; + } + + if (int hundreds = (number / 100) % 10) { + static const UChar georgianHundreds[9] = { + 0x10E0, 0x10E1, 0x10E2, 0x10F3, 0x10E4, 0x10E5, 0x10E6, 0x10E7, 0x10E8 + }; + letters[length++] = georgianHundreds[hundreds - 1]; + } + + if (int tens = (number / 10) % 10) { + static const UChar georgianTens[9] = { + 0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10F2, 0x10DD, 0x10DE, 0x10DF + }; + letters[length++] = georgianTens[tens - 1]; + } + + if (int ones = number % 10) { + static const UChar georgianOnes[9] = { + 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10F1, 0x10D7 + }; + letters[length++] = georgianOnes[ones - 1]; + } + + ASSERT(length <= lettersSize); + return String(letters, length); +} + +// The table uses the order from the CSS3 specification: +// first 3 group markers, then 3 digit markers, then ten digits. +static String toCJKIdeographic(int number, const UChar table[16]) +{ + if (number < 0) + return String::number(number); + + enum AbstractCJKChar { + noChar, + secondGroupMarker, thirdGroupMarker, fourthGroupMarker, + secondDigitMarker, thirdDigitMarker, fourthDigitMarker, + digit0, digit1, digit2, digit3, digit4, + digit5, digit6, digit7, digit8, digit9 + }; + + if (number == 0) + return String(&table[digit0 - 1], 1); + + const int groupLength = 8; // 4 digits, 3 digit markers, and a group marker + const int bufferLength = 4 * groupLength; + AbstractCJKChar buffer[bufferLength] = { noChar }; + + for (int i = 0; i < 4; ++i) { + int groupValue = number % 10000; + number /= 10000; + + // Process least-significant group first, but put it in the buffer last. + AbstractCJKChar* group = &buffer[(3 - i) * groupLength]; + + if (groupValue && i) + group[7] = static_cast<AbstractCJKChar>(secondGroupMarker - 1 + i); + + // Put in the four digits and digit markers for any non-zero digits. + group[6] = static_cast<AbstractCJKChar>(digit0 + (groupValue % 10)); + if (number != 0 || groupValue > 9) { + int digitValue = ((groupValue / 10) % 10); + group[4] = static_cast<AbstractCJKChar>(digit0 + digitValue); + if (digitValue) + group[5] = secondDigitMarker; + } + if (number != 0 || groupValue > 99) { + int digitValue = ((groupValue / 100) % 10); + group[2] = static_cast<AbstractCJKChar>(digit0 + digitValue); + if (digitValue) + group[3] = thirdDigitMarker; + } + if (number != 0 || groupValue > 999) { + int digitValue = groupValue / 1000; + group[0] = static_cast<AbstractCJKChar>(digit0 + digitValue); + if (digitValue) + group[1] = fourthDigitMarker; + } + + // Remove the tens digit, but leave the marker, for any group that has + // a value of less than 20. + if (groupValue < 20) { + ASSERT(group[4] == noChar || group[4] == digit0 || group[4] == digit1); + group[4] = noChar; + } + + if (number == 0) + break; + } + + // Convert into characters, omitting consecutive runs of digit0 and + // any trailing digit0. + int length = 0; + UChar characters[bufferLength]; + AbstractCJKChar last = noChar; + for (int i = 0; i < bufferLength; ++i) { + AbstractCJKChar a = buffer[i]; + if (a != noChar) { + if (a != digit0 || last != digit0) + characters[length++] = table[a - 1]; + last = a; + } + } + if (last == digit0) + --length; + + return String(characters, length); +} + +String listMarkerText(EListStyleType type, int value) +{ + switch (type) { + case LNONE: + return ""; + + // We use the same characters for text security. + // See RenderText::setInternalString. + case CIRCLE: + return String(&whiteBullet, 1); + case DISC: + return String(&bullet, 1); + case SQUARE: + // The CSS 2.1 test suite uses U+25EE BLACK MEDIUM SMALL SQUARE + // instead, but I think this looks better. + return String(&blackSquare, 1); + + case LDECIMAL: + return String::number(value); + case DECIMAL_LEADING_ZERO: + if (value < -9 || value > 9) + return String::number(value); + if (value < 0) + return "-0" + String::number(-value); // -01 to -09 + return "0" + String::number(value); // 00 to 09 + + case LOWER_ALPHA: + case LOWER_LATIN: { + static const UChar lowerLatinAlphabet[26] = { + 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', + 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' + }; + return toAlphabetic(value, lowerLatinAlphabet, 26); + } + case UPPER_ALPHA: + case UPPER_LATIN: { + static const UChar upperLatinAlphabet[26] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', + 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' + }; + return toAlphabetic(value, upperLatinAlphabet, 26); + } + case LOWER_GREEK: { + static const UChar lowerGreekAlphabet[24] = { + 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8, + 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0, + 0x03C1, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9 + }; + return toAlphabetic(value, lowerGreekAlphabet, 24); + } + + case HIRAGANA: { + // FIXME: This table comes from the CSS3 draft, and is probably + // incorrect, given the comments in that draft. + static const UChar hiraganaAlphabet[48] = { + 0x3042, 0x3044, 0x3046, 0x3048, 0x304A, 0x304B, 0x304D, 0x304F, + 0x3051, 0x3053, 0x3055, 0x3057, 0x3059, 0x305B, 0x305D, 0x305F, + 0x3061, 0x3064, 0x3066, 0x3068, 0x306A, 0x306B, 0x306C, 0x306D, + 0x306E, 0x306F, 0x3072, 0x3075, 0x3078, 0x307B, 0x307E, 0x307F, + 0x3080, 0x3081, 0x3082, 0x3084, 0x3086, 0x3088, 0x3089, 0x308A, + 0x308B, 0x308C, 0x308D, 0x308F, 0x3090, 0x3091, 0x3092, 0x3093 + }; + return toAlphabetic(value, hiraganaAlphabet, 48); + } + case HIRAGANA_IROHA: { + // FIXME: This table comes from the CSS3 draft, and is probably + // incorrect, given the comments in that draft. + static const UChar hiraganaIrohaAlphabet[47] = { + 0x3044, 0x308D, 0x306F, 0x306B, 0x307B, 0x3078, 0x3068, 0x3061, + 0x308A, 0x306C, 0x308B, 0x3092, 0x308F, 0x304B, 0x3088, 0x305F, + 0x308C, 0x305D, 0x3064, 0x306D, 0x306A, 0x3089, 0x3080, 0x3046, + 0x3090, 0x306E, 0x304A, 0x304F, 0x3084, 0x307E, 0x3051, 0x3075, + 0x3053, 0x3048, 0x3066, 0x3042, 0x3055, 0x304D, 0x3086, 0x3081, + 0x307F, 0x3057, 0x3091, 0x3072, 0x3082, 0x305B, 0x3059 + }; + return toAlphabetic(value, hiraganaIrohaAlphabet, 47); + } + case KATAKANA: { + // FIXME: This table comes from the CSS3 draft, and is probably + // incorrect, given the comments in that draft. + static const UChar katakanaAlphabet[48] = { + 0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, 0x30AB, 0x30AD, 0x30AF, + 0x30B1, 0x30B3, 0x30B5, 0x30B7, 0x30B9, 0x30BB, 0x30BD, 0x30BF, + 0x30C1, 0x30C4, 0x30C6, 0x30C8, 0x30CA, 0x30CB, 0x30CC, 0x30CD, + 0x30CE, 0x30CF, 0x30D2, 0x30D5, 0x30D8, 0x30DB, 0x30DE, 0x30DF, + 0x30E0, 0x30E1, 0x30E2, 0x30E4, 0x30E6, 0x30E8, 0x30E9, 0x30EA, + 0x30EB, 0x30EC, 0x30ED, 0x30EF, 0x30F0, 0x30F1, 0x30F2, 0x30F3 + }; + return toAlphabetic(value, katakanaAlphabet, 48); + } + case KATAKANA_IROHA: { + // FIXME: This table comes from the CSS3 draft, and is probably + // incorrect, given the comments in that draft. + static const UChar katakanaIrohaAlphabet[47] = { + 0x30A4, 0x30ED, 0x30CF, 0x30CB, 0x30DB, 0x30D8, 0x30C8, 0x30C1, + 0x30EA, 0x30CC, 0x30EB, 0x30F2, 0x30EF, 0x30AB, 0x30E8, 0x30BF, + 0x30EC, 0x30BD, 0x30C4, 0x30CD, 0x30CA, 0x30E9, 0x30E0, 0x30A6, + 0x30F0, 0x30CE, 0x30AA, 0x30AF, 0x30E4, 0x30DE, 0x30B1, 0x30D5, + 0x30B3, 0x30A8, 0x30C6, 0x30A2, 0x30B5, 0x30AD, 0x30E6, 0x30E1, + 0x30DF, 0x30B7, 0x30F1, 0x30D2, 0x30E2, 0x30BB, 0x30B9 + }; + return toAlphabetic(value, katakanaIrohaAlphabet, 47); + } + + case CJK_IDEOGRAPHIC: { + static const UChar traditionalChineseInformalTable[16] = { + 0x842C, 0x5104, 0x5146, + 0x5341, 0x767E, 0x5343, + 0x96F6, 0x4E00, 0x4E8C, 0x4E09, 0x56DB, + 0x4E94, 0x516D, 0x4E03, 0x516B, 0x4E5D + }; + return toCJKIdeographic(value, traditionalChineseInformalTable); + } + + case LOWER_ROMAN: + return toRoman(value, false); + case UPPER_ROMAN: + return toRoman(value, true); + + case ARMENIAN: + // CSS3 says "armenian" means "lower-armenian". + // But the CSS2.1 test suite contains uppercase test results for "armenian", + // so we'll match the test suite. + return toArmenian(value, true); + case GEORGIAN: + return toGeorgian(value); + case HEBREW: + return toHebrew(value); + } + + ASSERT_NOT_REACHED(); + return ""; +} + +RenderListMarker::RenderListMarker(RenderListItem* item) + : RenderBox(item->document()) + , m_listItem(item) + , m_selectionState(SelectionNone) +{ + // init RenderObject attributes + setInline(true); // our object is Inline + setReplaced(true); // pretend to be replaced +} + +RenderListMarker::~RenderListMarker() +{ + if (m_image) + m_image->removeClient(this); +} + +void RenderListMarker::styleWillChange(RenderStyle::Diff diff, const RenderStyle* newStyle) +{ + if (style() && (newStyle->listStylePosition() != style()->listStylePosition() || newStyle->listStyleType() != style()->listStyleType())) + setNeedsLayoutAndPrefWidthsRecalc(); + + RenderBox::styleWillChange(diff, newStyle); +} + +void RenderListMarker::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +{ + RenderBox::styleDidChange(diff, oldStyle); + + if (m_image != style()->listStyleImage()) { + if (m_image) + m_image->removeClient(this); + m_image = style()->listStyleImage(); + if (m_image) + m_image->addClient(this); + } +} + +InlineBox* RenderListMarker::createInlineBox(bool, bool isRootLineBox, bool) +{ + ASSERT(!isRootLineBox); + ListMarkerBox* box = new (renderArena()) ListMarkerBox(this); + m_inlineBoxWrapper = box; + return box; +} + +bool RenderListMarker::isImage() const +{ + return m_image && !m_image->errorOccurred(); +} + +void RenderListMarker::paint(PaintInfo& paintInfo, int tx, int ty) +{ + if (paintInfo.phase != PaintPhaseForeground) + return; + + if (style()->visibility() != VISIBLE) + return; + + IntRect marker = getRelativeMarkerRect(); + marker.move(tx, ty); + + IntRect box(tx + m_x, ty + m_y, m_width, m_height); + + if (box.y() > paintInfo.rect.bottom() || box.y() + box.height() < paintInfo.rect.y()) + return; + + if (hasBoxDecorations()) + paintBoxDecorations(paintInfo, box.x(), box.y()); + + GraphicsContext* context = paintInfo.context; + context->setFont(style()->font()); + + if (isImage()) { +#if PLATFORM(MAC) + if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled()) + paintCustomHighlight(tx, ty, style()->highlight(), true); +#endif + context->drawImage(m_image->image(this, marker.size()), marker.location()); + if (selectionState() != SelectionNone) + context->fillRect(selectionRect(), selectionBackgroundColor()); + return; + } + +#if PLATFORM(MAC) + // FIXME: paint gap between marker and list item proper + if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled()) + paintCustomHighlight(tx, ty, style()->highlight(), true); +#endif + + if (selectionState() != SelectionNone) + context->fillRect(selectionRect(), selectionBackgroundColor()); + + const Color color(style()->color()); + context->setStrokeColor(color); + context->setStrokeStyle(SolidStroke); + context->setStrokeThickness(1.0f); + context->setFillColor(color); + + switch (style()->listStyleType()) { + case DISC: + context->drawEllipse(marker); + return; + case CIRCLE: + context->setFillColor(Color::transparent); + context->drawEllipse(marker); + return; + case SQUARE: + context->drawRect(marker); + return; + case LNONE: + return; + case ARMENIAN: + case CJK_IDEOGRAPHIC: + case DECIMAL_LEADING_ZERO: + case GEORGIAN: + case HEBREW: + case HIRAGANA: + case HIRAGANA_IROHA: + case KATAKANA: + case KATAKANA_IROHA: + case LDECIMAL: + case LOWER_ALPHA: + case LOWER_GREEK: + case LOWER_LATIN: + case LOWER_ROMAN: + case UPPER_ALPHA: + case UPPER_LATIN: + case UPPER_ROMAN: + break; + } + if (m_text.isEmpty()) + return; + + TextRun textRun(m_text); + + // Text is not arbitrary. We can judge whether it's RTL from the first character, + // and we only need to handle the direction RightToLeft for now. + bool textNeedsReversing = direction(m_text[0]) == RightToLeft; + Vector<UChar> reversedText; + if (textNeedsReversing) { + int length = m_text.length(); + reversedText.grow(length); + for (int i = 0; i < length; ++i) + reversedText[length - i - 1] = m_text[i]; + textRun = TextRun(reversedText.data(), length); + } + + const Font& font = style()->font(); + if (style()->direction() == LTR) { + int width = font.width(textRun); + context->drawText(textRun, marker.location()); + const UChar periodSpace[2] = { '.', ' ' }; + context->drawText(TextRun(periodSpace, 2), marker.location() + IntSize(width, 0)); + } else { + const UChar spacePeriod[2] = { ' ', '.' }; + TextRun spacePeriodRun(spacePeriod, 2); + int width = font.width(spacePeriodRun); + context->drawText(spacePeriodRun, marker.location()); + context->drawText(textRun, marker.location() + IntSize(width, 0)); + } +} + +void RenderListMarker::layout() +{ + ASSERT(needsLayout()); + ASSERT(!prefWidthsDirty()); + + if (isImage()) { + m_width = m_image->imageSize(this, style()->effectiveZoom()).width(); + m_height = m_image->imageSize(this, style()->effectiveZoom()).height(); + } else { + m_width = minPrefWidth(); + m_height = style()->font().height(); + } + + m_marginLeft = m_marginRight = 0; + + Length leftMargin = style()->marginLeft(); + Length rightMargin = style()->marginRight(); + if (leftMargin.isFixed()) + m_marginLeft = leftMargin.value(); + if (rightMargin.isFixed()) + m_marginRight = rightMargin.value(); + + setNeedsLayout(false); +} + +void RenderListMarker::imageChanged(WrappedImagePtr o, const IntRect*) +{ + // A list marker can't have a background or border image, so no need to call the base class method. + if (o != m_image->data()) + return; + + if (m_width != m_image->imageSize(this, style()->effectiveZoom()).width() || m_height != m_image->imageSize(this, style()->effectiveZoom()).height() || m_image->errorOccurred()) + setNeedsLayoutAndPrefWidthsRecalc(); + else + repaint(); +} + +void RenderListMarker::calcPrefWidths() +{ + ASSERT(prefWidthsDirty()); + + m_text = ""; + + const Font& font = style()->font(); + + if (isImage()) { + // FIXME: This is a somewhat arbitrary width. Generated images for markers really won't become particularly useful + // until we support the CSS3 marker pseudoclass to allow control over the width and height of the marker box. + int bulletWidth = font.ascent() / 2; + m_image->setImageContainerSize(IntSize(bulletWidth, bulletWidth)); + m_minPrefWidth = m_maxPrefWidth = m_image->imageSize(this, style()->effectiveZoom()).width(); + setPrefWidthsDirty(false); + updateMargins(); + return; + } + + int width = 0; + EListStyleType type = style()->listStyleType(); + switch (type) { + case LNONE: + break; + case CIRCLE: + case DISC: + case SQUARE: + m_text = listMarkerText(type, 0); // value is ignored for these types + width = (font.ascent() * 2 / 3 + 1) / 2 + 2; + break; + case ARMENIAN: + case CJK_IDEOGRAPHIC: + case DECIMAL_LEADING_ZERO: + case GEORGIAN: + case HEBREW: + case HIRAGANA: + case HIRAGANA_IROHA: + case KATAKANA: + case KATAKANA_IROHA: + case LDECIMAL: + case LOWER_ALPHA: + case LOWER_GREEK: + case LOWER_LATIN: + case LOWER_ROMAN: + case UPPER_ALPHA: + case UPPER_LATIN: + case UPPER_ROMAN: + m_text = listMarkerText(type, m_listItem->value()); + if (m_text.isEmpty()) + width = 0; + else { + int itemWidth = font.width(m_text); + const UChar periodSpace[2] = { '.', ' ' }; + int periodSpaceWidth = font.width(TextRun(periodSpace, 2)); + width = itemWidth + periodSpaceWidth; + } + break; + } + + m_minPrefWidth = width; + m_maxPrefWidth = width; + + setPrefWidthsDirty(false); + + updateMargins(); +} + +void RenderListMarker::updateMargins() +{ + const Font& font = style()->font(); + + int marginLeft = 0; + int marginRight = 0; + + if (isInside()) { + if (isImage()) { + if (style()->direction() == LTR) + marginRight = cMarkerPadding; + else + marginLeft = cMarkerPadding; + } else switch (style()->listStyleType()) { + case DISC: + case CIRCLE: + case SQUARE: + if (style()->direction() == LTR) { + marginLeft = -1; + marginRight = font.ascent() - minPrefWidth() + 1; + } else { + marginLeft = font.ascent() - minPrefWidth() + 1; + marginRight = -1; + } + break; + default: + break; + } + } else { + if (style()->direction() == LTR) { + if (isImage()) + marginLeft = -minPrefWidth() - cMarkerPadding; + else { + int offset = font.ascent() * 2 / 3; + switch (style()->listStyleType()) { + case DISC: + case CIRCLE: + case SQUARE: + marginLeft = -offset - cMarkerPadding - 1; + break; + case LNONE: + break; + default: + marginLeft = m_text.isEmpty() ? 0 : -minPrefWidth() - offset / 2; + } + } + } else { + if (isImage()) + marginLeft = cMarkerPadding; + else { + int offset = font.ascent() * 2 / 3; + switch (style()->listStyleType()) { + case DISC: + case CIRCLE: + case SQUARE: + marginLeft = offset + cMarkerPadding + 1 - minPrefWidth(); + break; + case LNONE: + break; + default: + marginLeft = m_text.isEmpty() ? 0 : offset / 2; + } + } + } + marginRight = -marginLeft - minPrefWidth(); + } + + style()->setMarginLeft(Length(marginLeft, Fixed)); + style()->setMarginRight(Length(marginRight, Fixed)); +} + +int RenderListMarker::lineHeight(bool, bool) const +{ + if (!isImage()) + return m_listItem->lineHeight(false, true); + return height(); +} + +int RenderListMarker::baselinePosition(bool, bool) const +{ + if (!isImage()) { + const Font& font = style()->font(); + return font.ascent() + (lineHeight(false) - font.height())/2; + } + return height(); +} + +bool RenderListMarker::isInside() const +{ + return m_listItem->notInList() || style()->listStylePosition() == INSIDE; +} + +IntRect RenderListMarker::getRelativeMarkerRect() +{ + if (isImage()) + return IntRect(m_x, m_y, m_image->imageSize(this, style()->effectiveZoom()).width(), m_image->imageSize(this, style()->effectiveZoom()).height()); + + switch (style()->listStyleType()) { + case DISC: + case CIRCLE: + case SQUARE: { + // FIXME: Are these particular rounding rules necessary? + const Font& font = style()->font(); + int ascent = font.ascent(); + int bulletWidth = (ascent * 2 / 3 + 1) / 2; + return IntRect(m_x + 1, m_y + 3 * (ascent - ascent * 2 / 3) / 2, bulletWidth, bulletWidth); + } + case LNONE: + return IntRect(); + case ARMENIAN: + case CJK_IDEOGRAPHIC: + case DECIMAL_LEADING_ZERO: + case GEORGIAN: + case HEBREW: + case HIRAGANA: + case HIRAGANA_IROHA: + case KATAKANA: + case KATAKANA_IROHA: + case LDECIMAL: + case LOWER_ALPHA: + case LOWER_GREEK: + case LOWER_LATIN: + case LOWER_ROMAN: + case UPPER_ALPHA: + case UPPER_LATIN: + case UPPER_ROMAN: + if (m_text.isEmpty()) + return IntRect(); + const Font& font = style()->font(); + int itemWidth = font.width(m_text); + const UChar periodSpace[2] = { '.', ' ' }; + int periodSpaceWidth = font.width(TextRun(periodSpace, 2)); + return IntRect(m_x, m_y + font.ascent(), itemWidth + periodSpaceWidth, font.height()); + } + + return IntRect(); +} + +void RenderListMarker::setSelectionState(SelectionState state) +{ + m_selectionState = state; + if (InlineBox* box = inlineBoxWrapper()) + if (RootInlineBox* root = box->root()) + root->setHasSelectedChildren(state != SelectionNone); + containingBlock()->setSelectionState(state); +} + +IntRect RenderListMarker::selectionRect(bool clipToVisibleContent) +{ + ASSERT(!needsLayout()); + + if (selectionState() == SelectionNone || !inlineBoxWrapper()) + return IntRect(); + + RootInlineBox* root = inlineBoxWrapper()->root(); + IntRect rect(0, root->selectionTop() - yPos(), width(), root->selectionHeight()); + + if (clipToVisibleContent) + computeAbsoluteRepaintRect(rect); + else { + FloatPoint absPos = localToAbsolute(); + rect.move(absPos.x(), absPos.y()); + } + + return rect; +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderListMarker.h b/src/3rdparty/webkit/WebCore/rendering/RenderListMarker.h new file mode 100644 index 0000000..738427c --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderListMarker.h @@ -0,0 +1,85 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef RenderListMarker_h +#define RenderListMarker_h + +#include "RenderBox.h" + +namespace WebCore { + +class RenderListItem; + +String listMarkerText(EListStyleType, int value); + +// Used to render the list item's marker. +// The RenderListMarker always has to be a child of a RenderListItem. +class RenderListMarker : public RenderBox { +public: + RenderListMarker(RenderListItem*); + ~RenderListMarker(); + + virtual const char* renderName() const { return "RenderListMarker"; } + + virtual bool isListMarker() const { return true; } + + virtual void paint(PaintInfo&, int tx, int ty); + + virtual void layout(); + virtual void calcPrefWidths(); + + virtual void imageChanged(WrappedImagePtr, const IntRect* = 0); + + virtual InlineBox* createInlineBox(bool, bool, bool); + + virtual int lineHeight(bool firstLine, bool isRootLineBox = false) const; + virtual int baselinePosition(bool firstLine, bool isRootLineBox = false) const; + + bool isImage() const; + bool isText() const { return !isImage(); } + const String& text() const { return m_text; } + + bool isInside() const; + + virtual SelectionState selectionState() const { return m_selectionState; } + virtual void setSelectionState(SelectionState); + virtual IntRect selectionRect(bool clipToVisibleContent = true); + virtual bool canBeSelectionLeaf() const { return true; } + + void updateMargins(); + +protected: + virtual void styleWillChange(RenderStyle::Diff, const RenderStyle* newStyle); + virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + +private: + IntRect getRelativeMarkerRect(); + + String m_text; + RefPtr<StyleImage> m_image; + RenderListItem* m_listItem; + SelectionState m_selectionState; +}; + +} // namespace WebCore + +#endif // RenderListMarker_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderMarquee.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderMarquee.cpp new file mode 100644 index 0000000..96d26ea --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderMarquee.cpp @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * Portions are Copyright (C) 1998 Netscape Communications Corporation. + * + * Other contributors: + * Robert O'Callahan <roc+@cs.cmu.edu> + * David Baron <dbaron@fas.harvard.edu> + * Christian Biesinger <cbiesinger@web.de> + * Randall Jesup <rjesup@wgate.com> + * Roland Mainz <roland.mainz@informatik.med.uni-giessen.de> + * Josh Soref <timeless@mac.com> + * Boris Zbarsky <bzbarsky@mit.edu> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Alternatively, the contents of this file may be used under the terms + * of either the Mozilla Public License Version 1.1, found at + * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public + * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html + * (the "GPL"), in which case the provisions of the MPL or the GPL are + * applicable instead of those above. If you wish to allow use of your + * version of this file only under the terms of one of those two + * licenses (the MPL or the GPL) and not to allow others to use your + * version of this file under the LGPL, indicate your decision by + * deletingthe provisions above and replace them with the notice and + * other provisions required by the MPL or the GPL, as the case may be. + * If you do not delete the provisions above, a recipient may use your + * version of this file under any of the LGPL, the MPL or the GPL. + */ + +#include "config.h" + +#include "RenderMarquee.h" + +#include "FrameView.h" +#include "HTMLMarqueeElement.h" +#include "HTMLNames.h" +#include "RenderLayer.h" + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +RenderMarquee::RenderMarquee(RenderLayer* l) + : m_layer(l), m_currentLoop(0) + , m_totalLoops(0) + , m_timer(this, &RenderMarquee::timerFired) + , m_start(0), m_end(0), m_speed(0), m_reset(false) + , m_suspended(false), m_stopped(false), m_direction(MAUTO) +{ +} + +int RenderMarquee::marqueeSpeed() const +{ + int result = m_layer->renderer()->style()->marqueeSpeed(); + Node* elt = m_layer->renderer()->element(); + if (elt && elt->hasTagName(marqueeTag)) { + HTMLMarqueeElement* marqueeElt = static_cast<HTMLMarqueeElement*>(elt); + result = max(result, marqueeElt->minimumDelay()); + } + return result; +} + +EMarqueeDirection RenderMarquee::direction() const +{ + // FIXME: Support the CSS3 "auto" value for determining the direction of the marquee. + // For now just map MAUTO to MBACKWARD + EMarqueeDirection result = m_layer->renderer()->style()->marqueeDirection(); + TextDirection dir = m_layer->renderer()->style()->direction(); + if (result == MAUTO) + result = MBACKWARD; + if (result == MFORWARD) + result = (dir == LTR) ? MRIGHT : MLEFT; + if (result == MBACKWARD) + result = (dir == LTR) ? MLEFT : MRIGHT; + + // Now we have the real direction. Next we check to see if the increment is negative. + // If so, then we reverse the direction. + Length increment = m_layer->renderer()->style()->marqueeIncrement(); + if (increment.isNegative()) + result = static_cast<EMarqueeDirection>(-result); + + return result; +} + +bool RenderMarquee::isHorizontal() const +{ + return direction() == MLEFT || direction() == MRIGHT; +} + +int RenderMarquee::computePosition(EMarqueeDirection dir, bool stopAtContentEdge) +{ + RenderObject* o = m_layer->renderer(); + RenderStyle* s = o->style(); + if (isHorizontal()) { + bool ltr = s->direction() == LTR; + int clientWidth = o->clientWidth(); + int contentWidth = ltr ? o->rightmostPosition(true, false) : o->leftmostPosition(true, false); + if (ltr) + contentWidth += (o->paddingRight() - o->borderLeft()); + else { + contentWidth = o->width() - contentWidth; + contentWidth += (o->paddingLeft() - o->borderRight()); + } + if (dir == MRIGHT) { + if (stopAtContentEdge) + return max(0, ltr ? (contentWidth - clientWidth) : (clientWidth - contentWidth)); + else + return ltr ? contentWidth : clientWidth; + } + else { + if (stopAtContentEdge) + return min(0, ltr ? (contentWidth - clientWidth) : (clientWidth - contentWidth)); + else + return ltr ? -clientWidth : -contentWidth; + } + } + else { + int contentHeight = m_layer->renderer()->lowestPosition(true, false) - + m_layer->renderer()->borderTop() + m_layer->renderer()->paddingBottom(); + int clientHeight = m_layer->renderer()->clientHeight(); + if (dir == MUP) { + if (stopAtContentEdge) + return min(contentHeight - clientHeight, 0); + else + return -clientHeight; + } + else { + if (stopAtContentEdge) + return max(contentHeight - clientHeight, 0); + else + return contentHeight; + } + } +} + +void RenderMarquee::start() +{ + if (m_timer.isActive() || m_layer->renderer()->style()->marqueeIncrement().isZero()) + return; + + // We may end up propagating a scroll event. It is important that we suspend events until + // the end of the function since they could delete the layer, including the marquee. + FrameView* frameView = m_layer->renderer()->document()->view(); + if (frameView) + frameView->pauseScheduledEvents(); + + if (!m_suspended && !m_stopped) { + if (isHorizontal()) + m_layer->scrollToOffset(m_start, 0, false, false); + else + m_layer->scrollToOffset(0, m_start, false, false); + } + else { + m_suspended = false; + m_stopped = false; + } + + m_timer.startRepeating(speed() * 0.001); + + if (frameView) + frameView->resumeScheduledEvents(); +} + +void RenderMarquee::suspend() +{ + m_timer.stop(); + m_suspended = true; +} + +void RenderMarquee::stop() +{ + m_timer.stop(); + m_stopped = true; +} + +void RenderMarquee::updateMarqueePosition() +{ + bool activate = (m_totalLoops <= 0 || m_currentLoop < m_totalLoops); + if (activate) { + EMarqueeBehavior behavior = m_layer->renderer()->style()->marqueeBehavior(); + m_start = computePosition(direction(), behavior == MALTERNATE); + m_end = computePosition(reverseDirection(), behavior == MALTERNATE || behavior == MSLIDE); + if (!m_stopped) + start(); + } +} + +void RenderMarquee::updateMarqueeStyle() +{ + RenderStyle* s = m_layer->renderer()->style(); + + if (m_direction != s->marqueeDirection() || (m_totalLoops != s->marqueeLoopCount() && m_currentLoop >= m_totalLoops)) + m_currentLoop = 0; // When direction changes or our loopCount is a smaller number than our current loop, reset our loop. + + m_totalLoops = s->marqueeLoopCount(); + m_direction = s->marqueeDirection(); + + if (m_layer->renderer()->isHTMLMarquee()) { + // Hack for WinIE. In WinIE, a value of 0 or lower for the loop count for SLIDE means to only do + // one loop. + if (m_totalLoops <= 0 && s->marqueeBehavior() == MSLIDE) + m_totalLoops = 1; + + // Hack alert: Set the white-space value to nowrap for horizontal marquees with inline children, thus ensuring + // all the text ends up on one line by default. Limit this hack to the <marquee> element to emulate + // WinIE's behavior. Someone using CSS3 can use white-space: nowrap on their own to get this effect. + // Second hack alert: Set the text-align back to auto. WinIE completely ignores text-align on the + // marquee element. + // FIXME: Bring these up with the CSS WG. + if (isHorizontal() && m_layer->renderer()->childrenInline()) { + s->setWhiteSpace(NOWRAP); + s->setTextAlign(TAAUTO); + } + } + + // Marquee height hack!! Make sure that, if it is a horizontal marquee, the height attribute is overridden + // if it is smaller than the font size. If it is a vertical marquee and height is not specified, we default + // to a marquee of 200px. + if (isHorizontal()) { + if (s->height().isFixed() && s->height().value() < s->fontSize()) + s->setHeight(Length(s->fontSize(),Fixed)); + } else if (s->height().isAuto()) //vertical marquee with no specified height + s->setHeight(Length(200, Fixed)); + + if (speed() != marqueeSpeed()) { + m_speed = marqueeSpeed(); + if (m_timer.isActive()) + m_timer.startRepeating(speed() * 0.001); + } + + // Check the loop count to see if we should now stop. + bool activate = (m_totalLoops <= 0 || m_currentLoop < m_totalLoops); + if (activate && !m_timer.isActive()) + m_layer->renderer()->setNeedsLayout(true); + else if (!activate && m_timer.isActive()) + m_timer.stop(); +} + +void RenderMarquee::timerFired(Timer<RenderMarquee>*) +{ + if (m_layer->renderer()->needsLayout()) + return; + + if (m_reset) { + m_reset = false; + if (isHorizontal()) + m_layer->scrollToXOffset(m_start); + else + m_layer->scrollToYOffset(m_start); + return; + } + + RenderStyle* s = m_layer->renderer()->style(); + + int endPoint = m_end; + int range = m_end - m_start; + int newPos; + if (range == 0) + newPos = m_end; + else { + bool addIncrement = direction() == MUP || direction() == MLEFT; + bool isReversed = s->marqueeBehavior() == MALTERNATE && m_currentLoop % 2; + if (isReversed) { + // We're going in the reverse direction. + endPoint = m_start; + range = -range; + addIncrement = !addIncrement; + } + bool positive = range > 0; + int clientSize = (isHorizontal() ? m_layer->renderer()->clientWidth() : m_layer->renderer()->clientHeight()); + int increment = max(1, abs(m_layer->renderer()->style()->marqueeIncrement().calcValue(clientSize))); + int currentPos = (isHorizontal() ? m_layer->scrollXOffset() : m_layer->scrollYOffset()); + newPos = currentPos + (addIncrement ? increment : -increment); + if (positive) + newPos = min(newPos, endPoint); + else + newPos = max(newPos, endPoint); + } + + if (newPos == endPoint) { + m_currentLoop++; + if (m_totalLoops > 0 && m_currentLoop >= m_totalLoops) + m_timer.stop(); + else if (s->marqueeBehavior() != MALTERNATE) + m_reset = true; + } + + if (isHorizontal()) + m_layer->scrollToXOffset(newPos); + else + m_layer->scrollToYOffset(newPos); +} + +} // namespace WebCore + diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderMarquee.h b/src/3rdparty/webkit/WebCore/rendering/RenderMarquee.h new file mode 100644 index 0000000..d9d20cd --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderMarquee.h @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2003 Apple Computer, Inc. + * + * Portions are Copyright (C) 1998 Netscape Communications Corporation. + * + * Other contributors: + * Robert O'Callahan <roc+@cs.cmu.edu> + * David Baron <dbaron@fas.harvard.edu> + * Christian Biesinger <cbiesinger@web.de> + * Randall Jesup <rjesup@wgate.com> + * Roland Mainz <roland.mainz@informatik.med.uni-giessen.de> + * Josh Soref <timeless@mac.com> + * Boris Zbarsky <bzbarsky@mit.edu> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * Alternatively, the contents of this file may be used under the terms + * of either the Mozilla Public License Version 1.1, found at + * http://www.mozilla.org/MPL/ (the "MPL") or the GNU General Public + * License Version 2.0, found at http://www.fsf.org/copyleft/gpl.html + * (the "GPL"), in which case the provisions of the MPL or the GPL are + * applicable instead of those above. If you wish to allow use of your + * version of this file only under the terms of one of those two + * licenses (the MPL or the GPL) and not to allow others to use your + * version of this file under the LGPL, indicate your decision by + * deletingthe provisions above and replace them with the notice and + * other provisions required by the MPL or the GPL, as the case may be. + * If you do not delete the provisions above, a recipient may use your + * version of this file under any of the LGPL, the MPL or the GPL. + */ + +#ifndef RenderMarquee_h +#define RenderMarquee_h + +#include "RenderStyle.h" +#include "Timer.h" + +namespace WebCore { + +class RenderLayer; + +// This class handles the auto-scrolling of layers with overflow: marquee. +class RenderMarquee { +public: + RenderMarquee(RenderLayer*); + + int speed() const { return m_speed; } + int marqueeSpeed() const; + + EMarqueeDirection reverseDirection() const { return static_cast<EMarqueeDirection>(-direction()); } + EMarqueeDirection direction() const; + + bool isHorizontal() const; + + int computePosition(EMarqueeDirection, bool stopAtClientEdge); + + void setEnd(int end) { m_end = end; } + + void start(); + void suspend(); + void stop(); + + void updateMarqueeStyle(); + void updateMarqueePosition(); + +private: + void timerFired(Timer<RenderMarquee>*); + + RenderLayer* m_layer; + int m_currentLoop; + int m_totalLoops; + Timer<RenderMarquee> m_timer; + int m_start; + int m_end; + int m_speed; + Length m_height; + bool m_reset: 1; + bool m_suspended : 1; + bool m_stopped : 1; + EMarqueeDirection m_direction : 4; +}; + +} // namespace WebCore + +#endif // RenderMarquee_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderMedia.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderMedia.cpp new file mode 100644 index 0000000..a224136 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderMedia.cpp @@ -0,0 +1,424 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(VIDEO) +#include "RenderMedia.h" + +#include "CSSStyleSelector.h" +#include "Event.h" +#include "EventNames.h" +#include "FloatConversion.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "HTMLMediaElement.h" +#include "HTMLNames.h" +#include "MediaControlElements.h" +#include "MouseEvent.h" +#include "MediaPlayer.h" +#include "RenderSlider.h" +#include "SystemTime.h" +#include <wtf/MathExtras.h> + +using namespace std; + +namespace WebCore { + +static const double cTimeUpdateRepeatDelay = 0.2; +static const double cOpacityAnimationRepeatDelay = 0.05; +// FIXME get this from style +static const double cOpacityAnimationDuration = 0.1; + +RenderMedia::RenderMedia(HTMLMediaElement* video) + : RenderReplaced(video) + , m_timeUpdateTimer(this, &RenderMedia::timeUpdateTimerFired) + , m_opacityAnimationTimer(this, &RenderMedia::opacityAnimationTimerFired) + , m_mouseOver(false) + , m_opacityAnimationStartTime(0) + , m_opacityAnimationFrom(0) + , m_opacityAnimationTo(1.0f) + , m_previousVisible(VISIBLE) +{ +} + +RenderMedia::RenderMedia(HTMLMediaElement* video, const IntSize& intrinsicSize) + : RenderReplaced(video, intrinsicSize) + , m_timeUpdateTimer(this, &RenderMedia::timeUpdateTimerFired) + , m_opacityAnimationTimer(this, &RenderMedia::opacityAnimationTimerFired) + , m_mouseOver(false) + , m_opacityAnimationStartTime(0) + , m_opacityAnimationFrom(0) + , m_opacityAnimationTo(1.0f) +{ +} + +RenderMedia::~RenderMedia() +{ +} + +void RenderMedia::destroy() +{ + if (m_controlsShadowRoot && m_controlsShadowRoot->renderer()) { + removeChild(m_controlsShadowRoot->renderer()); + m_controlsShadowRoot->detach(); + } + RenderReplaced::destroy(); +} + +HTMLMediaElement* RenderMedia::mediaElement() const +{ + return static_cast<HTMLMediaElement*>(node()); +} + +MediaPlayer* RenderMedia::player() const +{ + return mediaElement()->player(); +} + +void RenderMedia::layout() +{ + IntSize oldSize = contentBox().size(); + + RenderReplaced::layout(); + + RenderObject* controlsRenderer = m_controlsShadowRoot ? m_controlsShadowRoot->renderer() : 0; + if (!controlsRenderer) + return; + IntSize newSize = contentBox().size(); + if (newSize != oldSize || controlsRenderer->needsLayout()) { + controlsRenderer->setPos(borderLeft() + paddingLeft(), borderTop() + paddingTop()); + controlsRenderer->style()->setHeight(Length(newSize.height(), Fixed)); + controlsRenderer->style()->setWidth(Length(newSize.width(), Fixed)); + controlsRenderer->setNeedsLayout(true, false); + controlsRenderer->layout(); + setChildNeedsLayout(false); + } +} + +RenderObject* RenderMedia::firstChild() const +{ + return m_controlsShadowRoot ? m_controlsShadowRoot->renderer() : 0; +} + +RenderObject* RenderMedia::lastChild() const +{ + return m_controlsShadowRoot ? m_controlsShadowRoot->renderer() : 0; +} + +void RenderMedia::removeChild(RenderObject* child) +{ + ASSERT(m_controlsShadowRoot); + ASSERT(child == m_controlsShadowRoot->renderer()); + child->removeLayers(enclosingLayer()); + static_cast<RenderMediaControlShadowRoot*>(child)->setParent(0); +} + +void RenderMedia::createControlsShadowRoot() +{ + ASSERT(!m_controlsShadowRoot); + m_controlsShadowRoot = new MediaControlShadowRootElement(document(), mediaElement()); +} + +void RenderMedia::createPanel() +{ + ASSERT(!m_panel); + RenderStyle* style = getCachedPseudoStyle(RenderStyle::MEDIA_CONTROLS_PANEL); + m_panel = new HTMLDivElement(HTMLNames::divTag, document()); + RenderObject* renderer = m_panel->createRenderer(renderArena(), style); + if (renderer) { + m_panel->setRenderer(renderer); + renderer->setStyle(style); + m_panel->setAttached(); + m_panel->setInDocument(true); + m_controlsShadowRoot->addChild(m_panel); + m_controlsShadowRoot->renderer()->addChild(renderer); + } +} + +void RenderMedia::createMuteButton() +{ + ASSERT(!m_muteButton); + m_muteButton = new MediaControlMuteButtonElement(document(), mediaElement()); + m_muteButton->attachToParent(m_panel.get()); +} + +void RenderMedia::createPlayButton() +{ + ASSERT(!m_playButton); + m_playButton = new MediaControlPlayButtonElement(document(), mediaElement()); + m_playButton->attachToParent(m_panel.get()); +} + +void RenderMedia::createSeekBackButton() +{ + ASSERT(!m_seekBackButton); + m_seekBackButton = new MediaControlSeekButtonElement(document(), mediaElement(), false); + m_seekBackButton->attachToParent(m_panel.get()); +} + +void RenderMedia::createSeekForwardButton() +{ + ASSERT(!m_seekForwardButton); + m_seekForwardButton = new MediaControlSeekButtonElement(document(), mediaElement(), true); + m_seekForwardButton->attachToParent(m_panel.get()); +} + +void RenderMedia::createTimeline() +{ + ASSERT(!m_timeline); + m_timeline = new MediaControlTimelineElement(document(), mediaElement()); + m_timeline->attachToParent(m_panel.get()); +} + +void RenderMedia::createTimeDisplay() +{ + ASSERT(!m_timeDisplay); + RenderStyle* style = getCachedPseudoStyle(RenderStyle::MEDIA_CONTROLS_TIME_DISPLAY); + m_timeDisplay = new HTMLDivElement(HTMLNames::divTag, document()); + RenderObject* renderer = m_timeDisplay->createRenderer(renderArena(), style); + if (renderer) { + m_timeDisplay->setRenderer(renderer); + renderer->setStyle(style); + m_timeDisplay->setAttached(); + m_timeDisplay->setInDocument(true); + m_panel->addChild(m_timeDisplay); + m_panel->renderer()->addChild(renderer); + } +} + +void RenderMedia::createFullscreenButton() +{ + ASSERT(!m_fullscreenButton); + m_fullscreenButton = new MediaControlFullscreenButtonElement(document(), mediaElement()); + m_fullscreenButton->attachToParent(m_panel.get()); +} + +void RenderMedia::updateFromElement() +{ + updateControls(); +} + +void RenderMedia::updateControls() +{ + HTMLMediaElement* media = mediaElement(); + if (!media->controls() || !media->inActiveDocument()) { + if (m_controlsShadowRoot) { + m_controlsShadowRoot->detach(); + m_panel = 0; + m_muteButton = 0; + m_playButton = 0; + m_timeline = 0; + m_seekBackButton = 0; + m_seekForwardButton = 0; + m_timeDisplay = 0; + m_fullscreenButton = 0; + m_controlsShadowRoot = 0; + } + m_opacityAnimationTo = 1.0f; + m_opacityAnimationTimer.stop(); + m_timeUpdateTimer.stop(); + return; + } + + if (!m_controlsShadowRoot) { + createControlsShadowRoot(); + createPanel(); + createMuteButton(); + createPlayButton(); + createTimeline(); + createSeekBackButton(); + createSeekForwardButton(); + createTimeDisplay(); + createFullscreenButton(); + } + + if (media->paused() || media->ended() || media->networkState() < HTMLMediaElement::LOADED_METADATA) + m_timeUpdateTimer.stop(); + else + m_timeUpdateTimer.startRepeating(cTimeUpdateRepeatDelay); + + if (m_muteButton) + m_muteButton->update(); + if (m_playButton) + m_playButton->update(); + if (m_timeline) + m_timeline->update(); + if (m_seekBackButton) + m_seekBackButton->update(); + if (m_seekForwardButton) + m_seekForwardButton->update(); + if (m_fullscreenButton) + m_fullscreenButton->update(); + updateTimeDisplay(); + updateControlVisibility(); +} + +void RenderMedia::timeUpdateTimerFired(Timer<RenderMedia>*) +{ + if (m_timeline) + m_timeline->update(false); + updateTimeDisplay(); +} + +String RenderMedia::formatTime(float time) +{ + if (!isfinite(time)) + time = 0; + int seconds = (int)time; + int hours = seconds / (60 * 60); + int minutes = (seconds / 60) % 60; + seconds %= 60; + return String::format("%02d:%02d:%02d", hours, minutes, seconds); +} + +void RenderMedia::updateTimeDisplay() +{ + if (!m_timeDisplay) + return; + String timeString = formatTime(mediaElement()->currentTime()); + ExceptionCode ec; + m_timeDisplay->setInnerText(timeString, ec); +} + +void RenderMedia::updateControlVisibility() +{ + if (!m_panel || !m_panel->renderer()) + return; + // Don't fade for audio controls. + HTMLMediaElement* media = mediaElement(); + if (player() && !player()->hasVideo() || !media->isVideo()) + return; + // do fading manually, css animations don't work well with shadow trees + bool visible = style()->visibility() == VISIBLE && (m_mouseOver || media->paused() || media->ended() || media->networkState() < HTMLMediaElement::LOADED_METADATA); + if (visible == (m_opacityAnimationTo > 0)) + return; + + if (style()->visibility() != m_previousVisible) { + // don't fade gradually if it the element has just changed visibility + m_previousVisible = style()->visibility(); + m_opacityAnimationTo = m_previousVisible == VISIBLE ? 1.0f : 0; + changeOpacity(m_panel.get(), 0); + return; + } + + if (visible) { + m_opacityAnimationFrom = m_panel->renderer()->style()->opacity(); + m_opacityAnimationTo = 1.0f; + } else { + m_opacityAnimationFrom = m_panel->renderer()->style()->opacity(); + m_opacityAnimationTo = 0; + } + m_opacityAnimationStartTime = currentTime(); + m_opacityAnimationTimer.startRepeating(cOpacityAnimationRepeatDelay); +} + +void RenderMedia::changeOpacity(HTMLElement* e, float opacity) +{ + if (!e || !e->renderer() || !e->renderer()->style()) + return; + RefPtr<RenderStyle> s = RenderStyle::clone(e->renderer()->style()); + s->setOpacity(opacity); + // z-index can't be auto if opacity is used + s->setZIndex(0); + e->renderer()->setStyle(s.release()); +} + +void RenderMedia::opacityAnimationTimerFired(Timer<RenderMedia>*) +{ + double time = currentTime() - m_opacityAnimationStartTime; + if (time >= cOpacityAnimationDuration) { + time = cOpacityAnimationDuration; + m_opacityAnimationTimer.stop(); + } + float opacity = narrowPrecisionToFloat(m_opacityAnimationFrom + (m_opacityAnimationTo - m_opacityAnimationFrom) * time / cOpacityAnimationDuration); + changeOpacity(m_panel.get(), opacity); +} + +void RenderMedia::forwardEvent(Event* event) +{ + if (event->isMouseEvent() && m_controlsShadowRoot) { + MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); + IntPoint point(mouseEvent->pageX(), mouseEvent->pageY()); + if (m_muteButton && m_muteButton->hitTest(point)) + m_muteButton->defaultEventHandler(event); + + if (m_playButton && m_playButton->hitTest(point)) + m_playButton->defaultEventHandler(event); + + if (m_seekBackButton && m_seekBackButton->hitTest(point)) + m_seekBackButton->defaultEventHandler(event); + + if (m_seekForwardButton && m_seekForwardButton->hitTest(point)) + m_seekForwardButton->defaultEventHandler(event); + + if (m_timeline && m_timeline->hitTest(point)) + m_timeline->defaultEventHandler(event); + + if (m_fullscreenButton && m_fullscreenButton->hitTest(point)) + m_fullscreenButton->defaultEventHandler(event); + + if (event->type() == eventNames().mouseoverEvent) { + m_mouseOver = true; + updateControlVisibility(); + } + if (event->type() == eventNames().mouseoutEvent) { + // FIXME: moving over scrollbar thumb generates mouseout for the ancestor media element for some reason + m_mouseOver = absoluteBoundingBoxRect().contains(point); + updateControlVisibility(); + } + } +} + +int RenderMedia::lowestPosition(bool includeOverflowInterior, bool includeSelf) const +{ + int bottom = RenderReplaced::lowestPosition(includeOverflowInterior, includeSelf); + if (!m_controlsShadowRoot || !m_controlsShadowRoot->renderer()) + return bottom; + + return max(bottom, m_controlsShadowRoot->renderer()->yPos() + m_controlsShadowRoot->renderer()->lowestPosition(includeOverflowInterior, includeSelf)); +} + +int RenderMedia::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const +{ + int right = RenderReplaced::rightmostPosition(includeOverflowInterior, includeSelf); + if (!m_controlsShadowRoot || !m_controlsShadowRoot->renderer()) + return right; + + return max(right, m_controlsShadowRoot->renderer()->xPos() + m_controlsShadowRoot->renderer()->rightmostPosition(includeOverflowInterior, includeSelf)); +} + +int RenderMedia::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const +{ + int left = RenderReplaced::leftmostPosition(includeOverflowInterior, includeSelf); + if (!m_controlsShadowRoot || !m_controlsShadowRoot->renderer()) + return left; + + return min(left, m_controlsShadowRoot->renderer()->xPos() + m_controlsShadowRoot->renderer()->leftmostPosition(includeOverflowInterior, includeSelf)); +} + +} // namespace WebCore + +#endif diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderMedia.h b/src/3rdparty/webkit/WebCore/rendering/RenderMedia.h new file mode 100644 index 0000000..8f48caf --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderMedia.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RenderMedia_h +#define RenderMedia_h + +#if ENABLE(VIDEO) + +#include "RenderReplaced.h" +#include "Timer.h" + +namespace WebCore { + +class HTMLInputElement; +class HTMLMediaElement; +class MediaControlMuteButtonElement; +class MediaControlPlayButtonElement; +class MediaControlSeekButtonElement; +class MediaControlTimelineElement; +class MediaControlFullscreenButtonElement; +class MediaPlayer; + +class RenderMedia : public RenderReplaced { +public: + RenderMedia(HTMLMediaElement*); + RenderMedia(HTMLMediaElement*, const IntSize& intrinsicSize); + virtual ~RenderMedia(); + + virtual RenderObject* firstChild() const; + virtual RenderObject* lastChild() const; + virtual void removeChild(RenderObject*); + virtual void destroy(); + + virtual void layout(); + + virtual const char* renderName() const { return "RenderMedia"; } + virtual bool isMedia() const { return true; } + + HTMLMediaElement* mediaElement() const; + MediaPlayer* player() const; + + static String formatTime(float time); + + void updateFromElement(); + void updatePlayer(); + void updateControls(); + + void forwardEvent(Event*); + + virtual int lowestPosition(bool includeOverflowInterior = true, bool includeSelf = true) const; + virtual int rightmostPosition(bool includeOverflowInterior = true, bool includeSelf = true) const; + virtual int leftmostPosition(bool includeOverflowInterior = true, bool includeSelf = true) const; + +private: + void createControlsShadowRoot(); + void createPanel(); + void createMuteButton(); + void createPlayButton(); + void createSeekBackButton(); + void createSeekForwardButton(); + void createTimeline(); + void createTimeDisplay(); + void createFullscreenButton(); + + void timeUpdateTimerFired(Timer<RenderMedia>*); + void updateTimeDisplay(); + + void updateControlVisibility(); + void changeOpacity(HTMLElement*, float opacity); + void opacityAnimationTimerFired(Timer<RenderMedia>*); + + RefPtr<HTMLElement> m_controlsShadowRoot; + RefPtr<HTMLElement> m_panel; + RefPtr<MediaControlMuteButtonElement> m_muteButton; + RefPtr<MediaControlPlayButtonElement> m_playButton; + RefPtr<MediaControlSeekButtonElement> m_seekBackButton; + RefPtr<MediaControlSeekButtonElement> m_seekForwardButton; + RefPtr<MediaControlTimelineElement> m_timeline; + RefPtr<MediaControlFullscreenButtonElement> m_fullscreenButton; + RefPtr<HTMLElement> m_timeDisplay; + EventTargetNode* m_lastUnderNode; + EventTargetNode* m_nodeUnderMouse; + + Timer<RenderMedia> m_timeUpdateTimer; + Timer<RenderMedia> m_opacityAnimationTimer; + bool m_mouseOver; + double m_opacityAnimationStartTime; + float m_opacityAnimationFrom; + float m_opacityAnimationTo; + EVisibility m_previousVisible; +}; + +} // namespace WebCore + +#endif +#endif // RenderMedia_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderMenuList.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderMenuList.cpp new file mode 100644 index 0000000..baec309 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderMenuList.cpp @@ -0,0 +1,441 @@ +/** + * This file is part of the select element renderer in WebCore. + * + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * + * 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" +#include "RenderMenuList.h" + +#include "CSSStyleSelector.h" +#include "Document.h" +#include "FontSelector.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "HTMLNames.h" +#include "HTMLOptionElement.h" +#include "HTMLOptGroupElement.h" +#include "HTMLSelectElement.h" +#include "PopupMenu.h" +#include "RenderBR.h" +#include "RenderScrollbar.h" +#include "RenderText.h" +#include "RenderTheme.h" +#include "NodeRenderStyle.h" +#include <math.h> + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +RenderMenuList::RenderMenuList(HTMLSelectElement* element) + : RenderFlexibleBox(element) + , m_buttonText(0) + , m_innerBlock(0) + , m_optionsChanged(true) + , m_optionsWidth(0) + , m_popup(0) + , m_popupIsVisible(false) +{ +} + +RenderMenuList::~RenderMenuList() +{ + if (m_popup) + m_popup->disconnectClient(); + m_popup = 0; +} + +// this static cast is safe because RenderMenuLists are only created for HTMLSelectElements +HTMLSelectElement* RenderMenuList::selectElement() +{ + return static_cast<HTMLSelectElement*>(node()); +} + +void RenderMenuList::createInnerBlock() +{ + if (m_innerBlock) { + ASSERT(firstChild() == m_innerBlock); + ASSERT(!m_innerBlock->nextSibling()); + return; + } + + // Create an anonymous block. + ASSERT(!firstChild()); + m_innerBlock = createAnonymousBlock(); + adjustInnerStyle(); + RenderFlexibleBox::addChild(m_innerBlock); +} + +void RenderMenuList::adjustInnerStyle() +{ + m_innerBlock->style()->setBoxFlex(1.0f); + + m_innerBlock->style()->setPaddingLeft(Length(theme()->popupInternalPaddingLeft(style()), Fixed)); + m_innerBlock->style()->setPaddingRight(Length(theme()->popupInternalPaddingRight(style()), Fixed)); + m_innerBlock->style()->setPaddingTop(Length(theme()->popupInternalPaddingTop(style()), Fixed)); + m_innerBlock->style()->setPaddingBottom(Length(theme()->popupInternalPaddingBottom(style()), Fixed)); + + if (PopupMenu::itemWritingDirectionIsNatural()) { + // Items in the popup will not respect the CSS text-align and direction properties, + // so we must adjust our own style to match. + m_innerBlock->style()->setTextAlign(LEFT); + TextDirection direction = (m_buttonText && m_buttonText->text()->defaultWritingDirection() == WTF::Unicode::RightToLeft) ? RTL : LTR; + m_innerBlock->style()->setDirection(direction); + } +} + +void RenderMenuList::addChild(RenderObject* newChild, RenderObject* beforeChild) +{ + createInnerBlock(); + m_innerBlock->addChild(newChild, beforeChild); +} + +void RenderMenuList::removeChild(RenderObject* oldChild) +{ + if (oldChild == m_innerBlock || !m_innerBlock) { + RenderFlexibleBox::removeChild(oldChild); + m_innerBlock = 0; + } else + m_innerBlock->removeChild(oldChild); +} + +void RenderMenuList::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +{ + RenderBlock::styleDidChange(diff, oldStyle); + + if (m_buttonText) + m_buttonText->setStyle(style()); + if (m_innerBlock) // RenderBlock handled updating the anonymous block's style. + adjustInnerStyle(); + + setReplaced(isInline()); + + bool fontChanged = !oldStyle || oldStyle->font() != style()->font(); + if (fontChanged) + updateOptionsWidth(); +} + +void RenderMenuList::updateOptionsWidth() +{ + float maxOptionWidth = 0; + const Vector<HTMLElement*>& listItems = static_cast<HTMLSelectElement*>(node())->listItems(); + int size = listItems.size(); + for (int i = 0; i < size; ++i) { + HTMLElement* element = listItems[i]; + if (element->hasTagName(optionTag)) { + String text = static_cast<HTMLOptionElement*>(element)->optionText(); + if (!text.isEmpty()) + maxOptionWidth = max(maxOptionWidth, style()->font().floatWidth(text)); + } + } + + int width = static_cast<int>(ceilf(maxOptionWidth)); + if (m_optionsWidth == width) + return; + + m_optionsWidth = width; + setNeedsLayoutAndPrefWidthsRecalc(); +} + +void RenderMenuList::updateFromElement() +{ + if (m_optionsChanged) { + updateOptionsWidth(); + m_optionsChanged = false; + } + + if (m_popupIsVisible) + m_popup->updateFromElement(); + else + setTextFromOption(static_cast<HTMLSelectElement*>(node())->selectedIndex()); +} + +void RenderMenuList::setTextFromOption(int optionIndex) +{ + HTMLSelectElement* select = static_cast<HTMLSelectElement*>(node()); + const Vector<HTMLElement*>& listItems = select->listItems(); + int size = listItems.size(); + + int i = select->optionToListIndex(optionIndex); + String text = ""; + if (i >= 0 && i < size) { + HTMLElement* element = listItems[i]; + if (element->hasTagName(optionTag)) + text = static_cast<HTMLOptionElement*>(listItems[i])->optionText(); + } + setText(text.stripWhiteSpace()); +} + +void RenderMenuList::setText(const String& s) +{ + if (s.isEmpty()) { + if (!m_buttonText || !m_buttonText->isBR()) { + if (m_buttonText) + m_buttonText->destroy(); + m_buttonText = new (renderArena()) RenderBR(document()); + m_buttonText->setStyle(style()); + addChild(m_buttonText); + } + } else { + if (m_buttonText && !m_buttonText->isBR()) + m_buttonText->setText(s.impl()); + else { + if (m_buttonText) + m_buttonText->destroy(); + m_buttonText = new (renderArena()) RenderText(document(), s.impl()); + m_buttonText->setStyle(style()); + addChild(m_buttonText); + } + adjustInnerStyle(); + } +} + +String RenderMenuList::text() const +{ + return m_buttonText ? m_buttonText->text() : 0; +} + +IntRect RenderMenuList::controlClipRect(int tx, int ty) const +{ + // Clip to the intersection of the content box and the content box for the inner box + // This will leave room for the arrows which sit in the inner box padding, + // and if the inner box ever spills out of the outer box, that will get clipped too. + IntRect outerBox(tx + borderLeft() + paddingLeft(), + ty + borderTop() + paddingTop(), + contentWidth(), + contentHeight()); + + IntRect innerBox(tx + m_innerBlock->xPos() + m_innerBlock->paddingLeft(), + ty + m_innerBlock->yPos() + m_innerBlock->paddingTop(), + m_innerBlock->contentWidth(), + m_innerBlock->contentHeight()); + + return intersection(outerBox, innerBox); +} + +void RenderMenuList::calcPrefWidths() +{ + m_minPrefWidth = 0; + m_maxPrefWidth = 0; + + if (style()->width().isFixed() && style()->width().value() > 0) + m_minPrefWidth = m_maxPrefWidth = calcContentBoxWidth(style()->width().value()); + else + m_maxPrefWidth = max(m_optionsWidth, theme()->minimumMenuListSize(style())) + m_innerBlock->paddingLeft() + m_innerBlock->paddingRight(); + + if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) { + m_maxPrefWidth = max(m_maxPrefWidth, calcContentBoxWidth(style()->minWidth().value())); + m_minPrefWidth = max(m_minPrefWidth, calcContentBoxWidth(style()->minWidth().value())); + } else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent())) + m_minPrefWidth = 0; + else + m_minPrefWidth = m_maxPrefWidth; + + if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) { + m_maxPrefWidth = min(m_maxPrefWidth, calcContentBoxWidth(style()->maxWidth().value())); + m_minPrefWidth = min(m_minPrefWidth, calcContentBoxWidth(style()->maxWidth().value())); + } + + int toAdd = paddingLeft() + paddingRight() + borderLeft() + borderRight(); + m_minPrefWidth += toAdd; + m_maxPrefWidth += toAdd; + + setPrefWidthsDirty(false); +} + +void RenderMenuList::showPopup() +{ + if (m_popupIsVisible) + return; + + // Create m_innerBlock here so it ends up as the first child. + // This is important because otherwise we might try to create m_innerBlock + // inside the showPopup call and it would fail. + createInnerBlock(); + if (!m_popup) + m_popup = PopupMenu::create(this); + HTMLSelectElement* select = static_cast<HTMLSelectElement*>(node()); + m_popupIsVisible = true; + + // Compute the top left taking transforms into account, but use + // the actual width of the element to size the popup. + FloatPoint absTopLeft = localToAbsolute(FloatPoint(), false, true); + IntRect absBounds = absoluteBoundingBoxRect(); + absBounds.setLocation(roundedIntPoint(absTopLeft)); + m_popup->show(absBounds, document()->view(), + select->optionToListIndex(select->selectedIndex())); +} + +void RenderMenuList::hidePopup() +{ + if (m_popup) + m_popup->hide(); + m_popupIsVisible = false; +} + +void RenderMenuList::valueChanged(unsigned listIndex, bool fireOnChange) +{ + HTMLSelectElement* select = static_cast<HTMLSelectElement*>(node()); + select->setSelectedIndex(select->listToOptionIndex(listIndex), true, fireOnChange); +} + +String RenderMenuList::itemText(unsigned listIndex) const +{ + HTMLSelectElement* select = static_cast<HTMLSelectElement*>(node()); + HTMLElement* element = select->listItems()[listIndex]; + if (element->hasTagName(optgroupTag)) + return static_cast<HTMLOptGroupElement*>(element)->groupLabelText(); + else if (element->hasTagName(optionTag)) + return static_cast<HTMLOptionElement*>(element)->optionText(); + return String(); +} + +bool RenderMenuList::itemIsEnabled(unsigned listIndex) const +{ + HTMLSelectElement* select = static_cast<HTMLSelectElement*>(node()); + HTMLElement* element = select->listItems()[listIndex]; + if (!element->hasTagName(optionTag)) + return false; + bool groupEnabled = true; + if (element->parentNode() && element->parentNode()->hasTagName(optgroupTag)) + groupEnabled = element->parentNode()->isEnabled(); + return element->isEnabled() && groupEnabled; +} + +PopupMenuStyle RenderMenuList::itemStyle(unsigned listIndex) const +{ + HTMLSelectElement* select = static_cast<HTMLSelectElement*>(node()); + HTMLElement* element = select->listItems()[listIndex]; + + RenderStyle* style = element->renderStyle() ? element->renderStyle() : element->computedStyle(); + return style ? PopupMenuStyle(style->color(), itemBackgroundColor(listIndex), style->font(), style->visibility() == VISIBLE) : menuStyle(); +} + +Color RenderMenuList::itemBackgroundColor(unsigned listIndex) const +{ + HTMLSelectElement* select = static_cast<HTMLSelectElement*>(node()); + HTMLElement* element = select->listItems()[listIndex]; + + Color backgroundColor; + if (element->renderStyle()) + backgroundColor = element->renderStyle()->backgroundColor(); + // If the item has an opaque background color, return that. + if (!backgroundColor.hasAlpha()) + return backgroundColor; + + // Otherwise, the item's background is overlayed on top of the menu background. + backgroundColor = style()->backgroundColor().blend(backgroundColor); + if (!backgroundColor.hasAlpha()) + return backgroundColor; + + // If the menu background is not opaque, then add an opaque white background behind. + return Color(Color::white).blend(backgroundColor); +} + +PopupMenuStyle RenderMenuList::menuStyle() const +{ + + RenderStyle* s = m_innerBlock ? m_innerBlock->style() : style(); + return PopupMenuStyle(s->color(), s->backgroundColor(), s->font(), s->visibility() == VISIBLE); +} + +HostWindow* RenderMenuList::hostWindow() const +{ + return document()->view()->hostWindow(); +} + +PassRefPtr<Scrollbar> RenderMenuList::createScrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, ScrollbarControlSize controlSize) +{ + RefPtr<Scrollbar> widget; + bool hasCustomScrollbarStyle = style()->hasPseudoStyle(RenderStyle::SCROLLBAR); + if (hasCustomScrollbarStyle) + widget = RenderScrollbar::createCustomScrollbar(client, orientation, this); + else + widget = Scrollbar::createNativeScrollbar(client, orientation, controlSize); + return widget.release(); +} + +int RenderMenuList::clientInsetLeft() const +{ + return 0; +} + +int RenderMenuList::clientInsetRight() const +{ + return 0; +} + +int RenderMenuList::clientPaddingLeft() const +{ + return paddingLeft(); +} + +int RenderMenuList::clientPaddingRight() const +{ + return paddingRight(); +} + +int RenderMenuList::listSize() const +{ + HTMLSelectElement* select = static_cast<HTMLSelectElement*>(node()); + return select->listItems().size(); +} + +int RenderMenuList::selectedIndex() const +{ + HTMLSelectElement* select = static_cast<HTMLSelectElement*>(node()); + return select->optionToListIndex(select->selectedIndex()); +} + +bool RenderMenuList::itemIsSeparator(unsigned listIndex) const +{ + HTMLSelectElement* select = static_cast<HTMLSelectElement*>(node()); + HTMLElement* element = select->listItems()[listIndex]; + return element->hasTagName(hrTag); +} + +bool RenderMenuList::itemIsLabel(unsigned listIndex) const +{ + HTMLSelectElement* select = static_cast<HTMLSelectElement*>(node()); + HTMLElement* element = select->listItems()[listIndex]; + return element->hasTagName(optgroupTag); +} + +bool RenderMenuList::itemIsSelected(unsigned listIndex) const +{ + HTMLSelectElement* select = static_cast<HTMLSelectElement*>(node()); + HTMLElement* element = select->listItems()[listIndex]; + return element->hasTagName(optionTag)&& static_cast<HTMLOptionElement*>(element)->selected(); +} + +void RenderMenuList::setTextFromItem(unsigned listIndex) +{ + HTMLSelectElement* select = static_cast<HTMLSelectElement*>(node()); + setTextFromOption(select->listToOptionIndex(listIndex)); +} + +FontSelector* RenderMenuList::fontSelector() const +{ + return document()->styleSelector()->fontSelector(); +} + +} diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderMenuList.h b/src/3rdparty/webkit/WebCore/rendering/RenderMenuList.h new file mode 100644 index 0000000..f31ef32 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderMenuList.h @@ -0,0 +1,119 @@ +/* + * This file is part of the select element renderer in WebCore. + * + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef RenderMenuList_h +#define RenderMenuList_h + +#include "RenderFlexibleBox.h" +#include "PopupMenuClient.h" + +#if PLATFORM(MAC) +#define POPUP_MENU_PULLS_DOWN 0 +#else +#define POPUP_MENU_PULLS_DOWN 1 +#endif + +namespace WebCore { + +class HTMLSelectElement; +class PopupMenu; + +class RenderMenuList : public RenderFlexibleBox, private PopupMenuClient { +public: + RenderMenuList(HTMLSelectElement*); + ~RenderMenuList(); + + HTMLSelectElement* selectElement(); + + virtual bool isMenuList() const { return true; } + + virtual void addChild(RenderObject* newChild, RenderObject* beforeChild = 0); + virtual void removeChild(RenderObject*); + virtual bool createsAnonymousWrapper() const { return true; } + virtual bool canHaveChildren() const { return false; } + + virtual void updateFromElement(); + + virtual bool hasControlClip() const { return true; } + virtual IntRect controlClipRect(int tx, int ty) const; + + virtual const char* renderName() const { return "RenderMenuList"; } + + virtual void calcPrefWidths(); + + bool popupIsVisible() const { return m_popupIsVisible; } + void showPopup(); + void hidePopup(); + + void setOptionsChanged(bool changed) { m_optionsChanged = changed; } + + String text() const; + +protected: + virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + +private: + // PopupMenuClient methods + virtual String itemText(unsigned listIndex) const; + virtual bool itemIsEnabled(unsigned listIndex) const; + virtual PopupMenuStyle itemStyle(unsigned listIndex) const; + virtual PopupMenuStyle menuStyle() const; + virtual int clientInsetLeft() const; + virtual int clientInsetRight() const; + virtual int clientPaddingLeft() const; + virtual int clientPaddingRight() const; + virtual int listSize() const; + virtual int selectedIndex() const; + virtual bool itemIsSeparator(unsigned listIndex) const; + virtual bool itemIsLabel(unsigned listIndex) const; + virtual bool itemIsSelected(unsigned listIndex) const; + virtual void setTextFromItem(unsigned listIndex); + virtual bool valueShouldChangeOnHotTrack() const { return true; } + virtual bool shouldPopOver() const { return !POPUP_MENU_PULLS_DOWN; } + virtual void valueChanged(unsigned listIndex, bool fireOnChange = true); + virtual FontSelector* fontSelector() const; + virtual HostWindow* hostWindow() const; + virtual PassRefPtr<Scrollbar> createScrollbar(ScrollbarClient*, ScrollbarOrientation, ScrollbarControlSize); + + virtual bool hasLineIfEmpty() const { return true; } + + Color itemBackgroundColor(unsigned listIndex) const; + + void createInnerBlock(); + void adjustInnerStyle(); + void setText(const String&); + void setTextFromOption(int optionIndex); + void updateOptionsWidth(); + + RenderText* m_buttonText; + RenderBlock* m_innerBlock; + + bool m_optionsChanged; + int m_optionsWidth; + + RefPtr<PopupMenu> m_popup; + bool m_popupIsVisible; +}; + +} + +#endif diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderObject.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderObject.cpp new file mode 100644 index 0000000..3ef7af7 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderObject.cpp @@ -0,0 +1,3303 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * (C) 2004 Allan Sandfeld Jensen (kde@carewolf.com) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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" +#include "RenderObject.h" + +#include "AXObjectCache.h" +#include "TransformationMatrix.h" +#include "AnimationController.h" +#include "CSSStyleSelector.h" +#include "CachedImage.h" +#include "Chrome.h" +#include "Document.h" +#include "Element.h" +#include "EventHandler.h" +#include "FloatRect.h" +#include "Frame.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "HTMLNames.h" +#include "HTMLOListElement.h" +#include "HitTestRequest.h" +#include "HitTestResult.h" +#include "KURL.h" +#include "Page.h" +#include "PlatformScreen.h" +#include "Position.h" +#include "RenderArena.h" +#include "RenderCounter.h" +#include "RenderFlexibleBox.h" +#include "RenderImageGeneratedContent.h" +#include "RenderInline.h" +#include "RenderListItem.h" +#include "RenderTableCell.h" +#include "RenderTableCol.h" +#include "RenderTableRow.h" +#include "RenderText.h" +#include "RenderTheme.h" +#include "RenderView.h" +#include "SelectionController.h" +#include "TextResourceDecoder.h" +#include <algorithm> +#include <stdio.h> +#include <wtf/RefCountedLeakCounter.h> + +#if ENABLE(WML) +#include "WMLNames.h" +#endif + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +#ifndef NDEBUG +static void* baseOfRenderObjectBeingDeleted; +#endif + +bool RenderObject::s_affectsParentBlock = false; + +void* RenderObject::operator new(size_t sz, RenderArena* renderArena) throw() +{ + return renderArena->allocate(sz); +} + +void RenderObject::operator delete(void* ptr, size_t sz) +{ + ASSERT(baseOfRenderObjectBeingDeleted == ptr); + + // Stash size where destroy can find it. + *(size_t *)ptr = sz; +} + +RenderObject* RenderObject::createObject(Node* node, RenderStyle* style) +{ + Document* doc = node->document(); + RenderArena* arena = doc->renderArena(); + + // Minimal support for content properties replacing an entire element. + // Works only if we have exactly one piece of content and it's a URL. + // Otherwise acts as if we didn't support this feature. + const ContentData* contentData = style->contentData(); + if (contentData && !contentData->m_next && contentData->m_type == CONTENT_OBJECT && doc != node) { + RenderImageGeneratedContent* image = new (arena) RenderImageGeneratedContent(node); + image->setStyle(style); + if (StyleImage* styleImage = contentData->m_content.m_image) + image->setStyleImage(styleImage); + return image; + } + + RenderObject* o = 0; + + switch (style->display()) { + case NONE: + break; + case INLINE: + o = new (arena) RenderInline(node); + break; + case BLOCK: + o = new (arena) RenderBlock(node); + break; + case INLINE_BLOCK: + o = new (arena) RenderBlock(node); + break; + case LIST_ITEM: + o = new (arena) RenderListItem(node); + break; + case RUN_IN: + case COMPACT: + o = new (arena) RenderBlock(node); + break; + case TABLE: + case INLINE_TABLE: + o = new (arena) RenderTable(node); + break; + case TABLE_ROW_GROUP: + case TABLE_HEADER_GROUP: + case TABLE_FOOTER_GROUP: + o = new (arena) RenderTableSection(node); + break; + case TABLE_ROW: + o = new (arena) RenderTableRow(node); + break; + case TABLE_COLUMN_GROUP: + case TABLE_COLUMN: + o = new (arena) RenderTableCol(node); + break; + case TABLE_CELL: + o = new (arena) RenderTableCell(node); + break; + case TABLE_CAPTION: + o = new (arena) RenderBlock(node); + break; + case BOX: + case INLINE_BOX: + o = new (arena) RenderFlexibleBox(node); + break; + } + + return o; +} + +#ifndef NDEBUG +static WTF::RefCountedLeakCounter renderObjectCounter("RenderObject"); +#endif + +RenderObject::RenderObject(Node* node) + : CachedResourceClient() + , m_style(0) + , m_node(node) + , m_parent(0) + , m_previous(0) + , m_next(0) +#ifndef NDEBUG + , m_hasAXObject(false) +#endif + , m_verticalPosition(PositionUndefined) + , m_needsLayout(false) + , m_needsPositionedMovementLayout(false) + , m_normalChildNeedsLayout(false) + , m_posChildNeedsLayout(false) + , m_prefWidthsDirty(false) + , m_floating(false) + , m_positioned(false) + , m_relPositioned(false) + , m_paintBackground(false) + , m_isAnonymous(node == node->document()) + , m_isText(false) + , m_inline(true) + , m_replaced(false) + , m_isDragging(false) + , m_hasLayer(false) + , m_hasOverflowClip(false) + , m_hasTransform(false) + , m_hasReflection(false) + , m_hasOverrideSize(false) + , m_hasCounterNodeMap(false) + , m_everHadLayout(false) +{ +#ifndef NDEBUG + renderObjectCounter.increment(); +#endif +} + +RenderObject::~RenderObject() +{ + ASSERT(!node() || documentBeingDestroyed() || !document()->frame()->view() || document()->frame()->view()->layoutRoot() != this); +#ifndef NDEBUG + ASSERT(!m_hasAXObject); + renderObjectCounter.decrement(); +#endif +} + +bool RenderObject::isDescendantOf(const RenderObject* obj) const +{ + for (const RenderObject* r = this; r; r = r->m_parent) { + if (r == obj) + return true; + } + return false; +} + +bool RenderObject::isBody() const +{ + return node()->hasTagName(bodyTag); +} + +bool RenderObject::isHR() const +{ + return element() && element()->hasTagName(hrTag); +} + +bool RenderObject::isHTMLMarquee() const +{ + return element() && element()->renderer() == this && element()->hasTagName(marqueeTag); +} + +bool RenderObject::canHaveChildren() const +{ + return false; +} + +RenderFlow* RenderObject::continuation() const +{ + return 0; +} + +bool RenderObject::isInlineContinuation() const +{ + return false; +} + +void RenderObject::addChild(RenderObject*, RenderObject*) +{ + ASSERT_NOT_REACHED(); +} + +RenderObject* RenderObject::removeChildNode(RenderObject*, bool) +{ + ASSERT_NOT_REACHED(); + return 0; +} + +void RenderObject::removeChild(RenderObject*) +{ + ASSERT_NOT_REACHED(); +} + +void RenderObject::moveChildNode(RenderObject*) +{ + ASSERT_NOT_REACHED(); +} + +void RenderObject::appendChildNode(RenderObject*, bool) +{ + ASSERT_NOT_REACHED(); +} + +void RenderObject::insertChildNode(RenderObject*, RenderObject*, bool) +{ + ASSERT_NOT_REACHED(); +} + +RenderObject* RenderObject::nextInPreOrder() const +{ + if (RenderObject* o = firstChild()) + return o; + + return nextInPreOrderAfterChildren(); +} + +RenderObject* RenderObject::nextInPreOrderAfterChildren() const +{ + RenderObject* o; + if (!(o = nextSibling())) { + o = parent(); + while (o && !o->nextSibling()) + o = o->parent(); + if (o) + o = o->nextSibling(); + } + + return o; +} + +RenderObject* RenderObject::nextInPreOrder(RenderObject* stayWithin) const +{ + if (RenderObject* o = firstChild()) + return o; + + return nextInPreOrderAfterChildren(stayWithin); +} + +RenderObject* RenderObject::nextInPreOrderAfterChildren(RenderObject* stayWithin) const +{ + if (this == stayWithin) + return 0; + + RenderObject* o; + if (!(o = nextSibling())) { + o = parent(); + while (o && !o->nextSibling()) { + if (o == stayWithin) + return 0; + o = o->parent(); + } + if (o) + o = o->nextSibling(); + } + + return o; +} + +RenderObject* RenderObject::previousInPreOrder() const +{ + if (RenderObject* o = previousSibling()) { + while (o->lastChild()) + o = o->lastChild(); + return o; + } + + return parent(); +} + +RenderObject* RenderObject::childAt(unsigned index) const +{ + RenderObject* child = firstChild(); + for (unsigned i = 0; child && i < index; i++) + child = child->nextSibling(); + return child; +} + +bool RenderObject::isEditable() const +{ + RenderText* textRenderer = 0; + if (isText()) + textRenderer = static_cast<RenderText*>(const_cast<RenderObject*>(this)); + + return style()->visibility() == VISIBLE && + element() && element()->isContentEditable() && + ((isBlockFlow() && !firstChild()) || + isReplaced() || + isBR() || + (textRenderer && textRenderer->firstTextBox())); +} + +RenderObject* RenderObject::firstLeafChild() const +{ + RenderObject* r = firstChild(); + while (r) { + RenderObject* n = 0; + n = r->firstChild(); + if (!n) + break; + r = n; + } + return r; +} + +RenderObject* RenderObject::lastLeafChild() const +{ + RenderObject* r = lastChild(); + while (r) { + RenderObject* n = 0; + n = r->lastChild(); + if (!n) + break; + r = n; + } + return r; +} + +static void addLayers(RenderObject* obj, RenderLayer* parentLayer, RenderObject*& newObject, + RenderLayer*& beforeChild) +{ + if (obj->hasLayer()) { + if (!beforeChild && newObject) { + // We need to figure out the layer that follows newObject. We only do + // this the first time we find a child layer, and then we update the + // pointer values for newObject and beforeChild used by everyone else. + beforeChild = newObject->parent()->findNextLayer(parentLayer, newObject); + newObject = 0; + } + parentLayer->addChild(obj->layer(), beforeChild); + return; + } + + for (RenderObject* curr = obj->firstChild(); curr; curr = curr->nextSibling()) + addLayers(curr, parentLayer, newObject, beforeChild); +} + +void RenderObject::addLayers(RenderLayer* parentLayer, RenderObject* newObject) +{ + if (!parentLayer) + return; + + RenderObject* object = newObject; + RenderLayer* beforeChild = 0; + WebCore::addLayers(this, parentLayer, object, beforeChild); +} + +void RenderObject::removeLayers(RenderLayer* parentLayer) +{ + if (!parentLayer) + return; + + if (hasLayer()) { + parentLayer->removeChild(layer()); + return; + } + + for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) + curr->removeLayers(parentLayer); +} + +void RenderObject::moveLayers(RenderLayer* oldParent, RenderLayer* newParent) +{ + if (!newParent) + return; + + if (hasLayer()) { + if (oldParent) + oldParent->removeChild(layer()); + newParent->addChild(layer()); + return; + } + + for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) + curr->moveLayers(oldParent, newParent); +} + +RenderLayer* RenderObject::findNextLayer(RenderLayer* parentLayer, RenderObject* startPoint, + bool checkParent) +{ + // Error check the parent layer passed in. If it's null, we can't find anything. + if (!parentLayer) + return 0; + + // Step 1: If our layer is a child of the desired parent, then return our layer. + RenderLayer* ourLayer = layer(); + if (ourLayer && ourLayer->parent() == parentLayer) + return ourLayer; + + // Step 2: If we don't have a layer, or our layer is the desired parent, then descend + // into our siblings trying to find the next layer whose parent is the desired parent. + if (!ourLayer || ourLayer == parentLayer) { + for (RenderObject* curr = startPoint ? startPoint->nextSibling() : firstChild(); + curr; curr = curr->nextSibling()) { + RenderLayer* nextLayer = curr->findNextLayer(parentLayer, 0, false); + if (nextLayer) + return nextLayer; + } + } + + // Step 3: If our layer is the desired parent layer, then we're finished. We didn't + // find anything. + if (parentLayer == ourLayer) + return 0; + + // Step 4: If |checkParent| is set, climb up to our parent and check its siblings that + // follow us to see if we can locate a layer. + if (checkParent && parent()) + return parent()->findNextLayer(parentLayer, this, true); + + return 0; +} + +RenderLayer* RenderObject::enclosingLayer() const +{ + const RenderObject* curr = this; + while (curr) { + RenderLayer* layer = curr->layer(); + if (layer) + return layer; + curr = curr->parent(); + } + return 0; +} + +bool RenderObject::requiresLayer() +{ + return isRoot() || isPositioned() || isRelPositioned() || isTransparent() || hasOverflowClip() || hasTransform() || hasMask() || hasReflection(); +} + +RenderBlock* RenderObject::firstLineBlock() const +{ + return 0; +} + +int RenderObject::offsetLeft() const +{ + RenderObject* offsetPar = offsetParent(); + if (!offsetPar) + return 0; + int x = xPos() - offsetPar->borderLeft(); + if (!isPositioned()) { + if (isRelPositioned()) + x += static_cast<const RenderBox*>(this)->relativePositionOffsetX(); + RenderObject* curr = parent(); + while (curr && curr != offsetPar) { + x += curr->xPos(); + curr = curr->parent(); + } + if (offsetPar->isBody() && !offsetPar->isRelPositioned() && !offsetPar->isPositioned()) + x += offsetPar->xPos(); + } + return x; +} + +int RenderObject::offsetTop() const +{ + RenderObject* offsetPar = offsetParent(); + if (!offsetPar) + return 0; + int y = yPos() - borderTopExtra() - offsetPar->borderTop(); + if (!isPositioned()) { + if (isRelPositioned()) + y += static_cast<const RenderBox*>(this)->relativePositionOffsetY(); + RenderObject* curr = parent(); + while (curr && curr != offsetPar) { + if (!curr->isTableRow()) + y += curr->yPos(); + curr = curr->parent(); + } + if (offsetPar->isBody() && !offsetPar->isRelPositioned() && !offsetPar->isPositioned()) + y += offsetPar->yPos(); + } + return y; +} + +RenderObject* RenderObject::offsetParent() const +{ + // FIXME: It feels like this function could almost be written using containing blocks. + if (isBody()) + return 0; + + bool skipTables = isPositioned() || isRelPositioned(); + float currZoom = style()->effectiveZoom(); + RenderObject* curr = parent(); + while (curr && (!curr->element() || + (!curr->isPositioned() && !curr->isRelPositioned() && !curr->isBody()))) { + Node* element = curr->element(); + if (!skipTables && element) { + bool isTableElement = element->hasTagName(tableTag) || + element->hasTagName(tdTag) || + element->hasTagName(thTag); + +#if ENABLE(WML) + if (!isTableElement && element->isWMLElement()) + isTableElement = element->hasTagName(WMLNames::tableTag) || + element->hasTagName(WMLNames::tdTag); +#endif + + if (isTableElement) + break; + } + + float newZoom = curr->style()->effectiveZoom(); + if (currZoom != newZoom) + break; + currZoom = newZoom; + curr = curr->parent(); + } + return curr; +} + +int RenderObject::verticalScrollbarWidth() const +{ + return includeVerticalScrollbarSize() ? layer()->verticalScrollbarWidth() : 0; +} + +int RenderObject::horizontalScrollbarHeight() const +{ + return includeHorizontalScrollbarSize() ? layer()->horizontalScrollbarHeight() : 0; +} + +// More IE extensions. clientWidth and clientHeight represent the interior of an object +// excluding border and scrollbar. +int RenderObject::clientWidth() const +{ + return width() - borderLeft() - borderRight() - verticalScrollbarWidth(); +} + +int RenderObject::clientHeight() const +{ + return height() - borderTop() - borderBottom() - horizontalScrollbarHeight(); +} + +// scrollWidth/scrollHeight will be the same as clientWidth/clientHeight unless the +// object has overflow:hidden/scroll/auto specified and also has overflow. +int RenderObject::scrollWidth() const +{ + return hasOverflowClip() ? layer()->scrollWidth() : overflowWidth(); +} + +int RenderObject::scrollHeight() const +{ + return hasOverflowClip() ? layer()->scrollHeight() : overflowHeight(); +} + +int RenderObject::scrollLeft() const +{ + return hasOverflowClip() ? layer()->scrollXOffset() : 0; +} + +int RenderObject::scrollTop() const +{ + return hasOverflowClip() ? layer()->scrollYOffset() : 0; +} + +void RenderObject::setScrollLeft(int newLeft) +{ + if (hasOverflowClip()) + layer()->scrollToXOffset(newLeft); +} + +void RenderObject::setScrollTop(int newTop) +{ + if (hasOverflowClip()) + layer()->scrollToYOffset(newTop); +} + +bool RenderObject::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier) +{ + RenderLayer* l = layer(); + if (l && l->scroll(direction, granularity, multiplier)) + return true; + RenderBlock* b = containingBlock(); + if (b && !b->isRenderView()) + return b->scroll(direction, granularity, multiplier); + return false; +} + +bool RenderObject::canBeProgramaticallyScrolled(bool) const +{ + if (!layer()) + return false; + + return (hasOverflowClip() && (scrollsOverflow() || (node() && node()->isContentEditable()))) || (node() && node()->isDocumentNode()); +} + +void RenderObject::autoscroll() +{ + layer()->autoscroll(); +} + +void RenderObject::panScroll(const IntPoint& source) +{ + layer()->panScrollFromPoint(source); +} + +bool RenderObject::hasStaticX() const +{ + return (style()->left().isAuto() && style()->right().isAuto()) || style()->left().isStatic() || style()->right().isStatic(); +} + +bool RenderObject::hasStaticY() const +{ + return (style()->top().isAuto() && style()->bottom().isAuto()) || style()->top().isStatic(); +} + +void RenderObject::markAllDescendantsWithFloatsForLayout(RenderObject*) +{ +} + +void RenderObject::setPrefWidthsDirty(bool b, bool markParents) +{ + bool alreadyDirty = m_prefWidthsDirty; + m_prefWidthsDirty = b; + if (b && !alreadyDirty && markParents && (isText() || (style()->position() != FixedPosition && style()->position() != AbsolutePosition))) + invalidateContainerPrefWidths(); +} + +void RenderObject::invalidateContainerPrefWidths() +{ + // In order to avoid pathological behavior when inlines are deeply nested, we do include them + // in the chain that we mark dirty (even though they're kind of irrelevant). + RenderObject* o = isTableCell() ? containingBlock() : container(); + while (o && !o->m_prefWidthsDirty) { + o->m_prefWidthsDirty = true; + if (o->style()->position() == FixedPosition || o->style()->position() == AbsolutePosition) + // A positioned object has no effect on the min/max width of its containing block ever. + // We can optimize this case and not go up any further. + break; + o = o->isTableCell() ? o->containingBlock() : o->container(); + } +} + +void RenderObject::setNeedsLayout(bool b, bool markParents) +{ + bool alreadyNeededLayout = m_needsLayout; + m_needsLayout = b; + if (b) { + if (!alreadyNeededLayout) { + if (markParents) + markContainingBlocksForLayout(); + if (hasLayer()) + layer()->setNeedsFullRepaint(); + } + } else { + m_everHadLayout = true; + m_posChildNeedsLayout = false; + m_normalChildNeedsLayout = false; + m_needsPositionedMovementLayout = false; + } +} + +void RenderObject::setChildNeedsLayout(bool b, bool markParents) +{ + bool alreadyNeededLayout = m_normalChildNeedsLayout; + m_normalChildNeedsLayout = b; + if (b) { + if (!alreadyNeededLayout && markParents) + markContainingBlocksForLayout(); + } else { + m_posChildNeedsLayout = false; + m_normalChildNeedsLayout = false; + m_needsPositionedMovementLayout = false; + } +} + +void RenderObject::setNeedsPositionedMovementLayout() +{ + bool alreadyNeededLayout = needsLayout(); + m_needsPositionedMovementLayout = true; + if (!alreadyNeededLayout) { + markContainingBlocksForLayout(); + if (hasLayer()) + layer()->setNeedsFullRepaint(); + } +} + +static inline bool objectIsRelayoutBoundary(const RenderObject *obj) +{ + // FIXME: In future it may be possible to broaden this condition in order to improve performance. + // Table cells are excluded because even when their CSS height is fixed, their height() + // may depend on their contents. + return obj->isTextField() || obj->isTextArea() + || obj->hasOverflowClip() && !obj->style()->width().isIntrinsicOrAuto() && !obj->style()->height().isIntrinsicOrAuto() && !obj->style()->height().isPercent() && !obj->isTableCell() +#if ENABLE(SVG) + || obj->isSVGRoot() +#endif + ; +} + +void RenderObject::markContainingBlocksForLayout(bool scheduleRelayout, RenderObject* newRoot) +{ + ASSERT(!scheduleRelayout || !newRoot); + + RenderObject* o = container(); + RenderObject* last = this; + + while (o) { + if (!last->isText() && (last->style()->position() == FixedPosition || last->style()->position() == AbsolutePosition)) { + if (last->hasStaticY()) { + RenderObject* parent = last->parent(); + if (!parent->normalChildNeedsLayout()) { + parent->setChildNeedsLayout(true, false); + if (parent != newRoot) + parent->markContainingBlocksForLayout(scheduleRelayout, newRoot); + } + } + if (o->m_posChildNeedsLayout) + return; + o->m_posChildNeedsLayout = true; + } else { + if (o->m_normalChildNeedsLayout) + return; + o->m_normalChildNeedsLayout = true; + } + + if (o == newRoot) + return; + + last = o; + if (scheduleRelayout && objectIsRelayoutBoundary(last)) + break; + o = o->container(); + } + + if (scheduleRelayout) + last->scheduleRelayout(); +} + +RenderBlock* RenderObject::containingBlock() const +{ + if (isTableCell()) { + const RenderTableCell* cell = static_cast<const RenderTableCell*>(this); + if (parent() && cell->section()) + return cell->table(); + return 0; + } + + if (isRenderView()) + return const_cast<RenderBlock*>(static_cast<const RenderBlock*>(this)); + + RenderObject* o = parent(); + if (!isText() && m_style->position() == FixedPosition) { + while (o && !o->isRenderView() && !(o->hasTransform() && o->isRenderBlock())) + o = o->parent(); + } else if (!isText() && m_style->position() == AbsolutePosition) { + while (o && (o->style()->position() == StaticPosition || (o->isInline() && !o->isReplaced())) && !o->isRenderView() && !(o->hasTransform() && o->isRenderBlock())) { + // For relpositioned inlines, we return the nearest enclosing block. We don't try + // to return the inline itself. This allows us to avoid having a positioned objects + // list in all RenderInlines and lets us return a strongly-typed RenderBlock* result + // from this method. The container() method can actually be used to obtain the + // inline directly. + if (o->style()->position() == RelativePosition && o->isInline() && !o->isReplaced()) + return o->containingBlock(); + o = o->parent(); + } + } else { + while (o && ((o->isInline() && !o->isReplaced()) || o->isTableRow() || o->isTableSection() + || o->isTableCol() || o->isFrameSet() || o->isMedia() +#if ENABLE(SVG) + || o->isSVGContainer() || o->isSVGRoot() +#endif + )) + o = o->parent(); + } + + if (!o || !o->isRenderBlock()) + return 0; // Probably doesn't happen any more, but leave just in case. -dwh + + return static_cast<RenderBlock*>(o); +} + +int RenderObject::containingBlockWidth() const +{ + // FIXME ? + return containingBlock()->availableWidth(); +} + +int RenderObject::containingBlockHeight() const +{ + // FIXME ? + return containingBlock()->contentHeight(); +} + +static bool mustRepaintFillLayers(const RenderObject* renderer, const FillLayer* layer) +{ + // Nobody will use multiple layers without wanting fancy positioning. + if (layer->next()) + return true; + + // Make sure we have a valid image. + StyleImage* img = layer->image(); + bool shouldPaintBackgroundImage = img && img->canRender(renderer->style()->effectiveZoom()); + + // These are always percents or auto. + if (shouldPaintBackgroundImage && + (!layer->xPosition().isZero() || !layer->yPosition().isZero() || + layer->size().width().isPercent() || layer->size().height().isPercent())) + // The image will shift unpredictably if the size changes. + return true; + + return false; +} + +bool RenderObject::mustRepaintBackgroundOrBorder() const +{ + if (hasMask() && mustRepaintFillLayers(this, style()->maskLayers())) + return true; + + // If we don't have a background/border/mask, then nothing to do. + if (!hasBoxDecorations()) + return false; + + if (mustRepaintFillLayers(this, style()->backgroundLayers())) + return true; + + // Our fill layers are ok. Let's check border. + if (style()->hasBorder()) { + // Border images are not ok. + StyleImage* borderImage = style()->borderImage().image(); + bool shouldPaintBorderImage = borderImage && borderImage->canRender(style()->effectiveZoom()); + + // If the image hasn't loaded, we're still using the normal border style. + if (shouldPaintBorderImage && borderImage->isLoaded()) + return true; + } + + return false; +} + +void RenderObject::drawBorderArc(GraphicsContext* graphicsContext, int x, int y, float thickness, IntSize radius, + int angleStart, int angleSpan, BorderSide s, Color c, const Color& textColor, + EBorderStyle style, bool firstCorner) +{ + if ((style == DOUBLE && thickness / 2 < 3) || ((style == RIDGE || style == GROOVE) && thickness / 2 < 2)) + style = SOLID; + + if (!c.isValid()) { + if (style == INSET || style == OUTSET || style == RIDGE || style == GROOVE) + c.setRGB(238, 238, 238); + else + c = textColor; + } + + switch (style) { + case BNONE: + case BHIDDEN: + return; + case DOTTED: + case DASHED: + graphicsContext->setStrokeColor(c); + graphicsContext->setStrokeStyle(style == DOTTED ? DottedStroke : DashedStroke); + graphicsContext->setStrokeThickness(thickness); + graphicsContext->strokeArc(IntRect(x, y, radius.width() * 2, radius.height() * 2), angleStart, angleSpan); + break; + case DOUBLE: { + float third = thickness / 3.0f; + float innerThird = (thickness + 1.0f) / 6.0f; + int shiftForInner = static_cast<int>(innerThird * 2.5f); + + int outerY = y; + int outerHeight = radius.height() * 2; + int innerX = x + shiftForInner; + int innerY = y + shiftForInner; + int innerWidth = (radius.width() - shiftForInner) * 2; + int innerHeight = (radius.height() - shiftForInner) * 2; + if (innerThird > 1 && (s == BSTop || (firstCorner && (s == BSLeft || s == BSRight)))) { + outerHeight += 2; + innerHeight += 2; + } + + graphicsContext->setStrokeStyle(SolidStroke); + graphicsContext->setStrokeColor(c); + graphicsContext->setStrokeThickness(third); + graphicsContext->strokeArc(IntRect(x, outerY, radius.width() * 2, outerHeight), angleStart, angleSpan); + graphicsContext->setStrokeThickness(innerThird > 2 ? innerThird - 1 : innerThird); + graphicsContext->strokeArc(IntRect(innerX, innerY, innerWidth, innerHeight), angleStart, angleSpan); + break; + } + case GROOVE: + case RIDGE: { + Color c2; + if ((style == RIDGE && (s == BSTop || s == BSLeft)) || + (style == GROOVE && (s == BSBottom || s == BSRight))) + c2 = c.dark(); + else { + c2 = c; + c = c.dark(); + } + + graphicsContext->setStrokeStyle(SolidStroke); + graphicsContext->setStrokeColor(c); + graphicsContext->setStrokeThickness(thickness); + graphicsContext->strokeArc(IntRect(x, y, radius.width() * 2, radius.height() * 2), angleStart, angleSpan); + + float halfThickness = (thickness + 1.0f) / 4.0f; + int shiftForInner = static_cast<int>(halfThickness * 1.5f); + graphicsContext->setStrokeColor(c2); + graphicsContext->setStrokeThickness(halfThickness > 2 ? halfThickness - 1 : halfThickness); + graphicsContext->strokeArc(IntRect(x + shiftForInner, y + shiftForInner, (radius.width() - shiftForInner) * 2, + (radius.height() - shiftForInner) * 2), angleStart, angleSpan); + break; + } + case INSET: + if (s == BSTop || s == BSLeft) + c = c.dark(); + case OUTSET: + if (style == OUTSET && (s == BSBottom || s == BSRight)) + c = c.dark(); + case SOLID: + graphicsContext->setStrokeStyle(SolidStroke); + graphicsContext->setStrokeColor(c); + graphicsContext->setStrokeThickness(thickness); + graphicsContext->strokeArc(IntRect(x, y, radius.width() * 2, radius.height() * 2), angleStart, angleSpan); + break; + } +} + +void RenderObject::drawBorder(GraphicsContext* graphicsContext, int x1, int y1, int x2, int y2, + BorderSide s, Color c, const Color& textcolor, EBorderStyle style, + int adjbw1, int adjbw2) +{ + int width = (s == BSTop || s == BSBottom ? y2 - y1 : x2 - x1); + + if (style == DOUBLE && width < 3) + style = SOLID; + + if (!c.isValid()) { + if (style == INSET || style == OUTSET || style == RIDGE || style == GROOVE) + c.setRGB(238, 238, 238); + else + c = textcolor; + } + + switch (style) { + case BNONE: + case BHIDDEN: + return; + case DOTTED: + case DASHED: + graphicsContext->setStrokeColor(c); + graphicsContext->setStrokeThickness(width); + graphicsContext->setStrokeStyle(style == DASHED ? DashedStroke : DottedStroke); + + if (width > 0) + switch (s) { + case BSBottom: + case BSTop: + graphicsContext->drawLine(IntPoint(x1, (y1 + y2) / 2), IntPoint(x2, (y1 + y2) / 2)); + break; + case BSRight: + case BSLeft: + graphicsContext->drawLine(IntPoint((x1 + x2) / 2, y1), IntPoint((x1 + x2) / 2, y2)); + break; + } + break; + case DOUBLE: { + int third = (width + 1) / 3; + + if (adjbw1 == 0 && adjbw2 == 0) { + graphicsContext->setStrokeStyle(NoStroke); + graphicsContext->setFillColor(c); + switch (s) { + case BSTop: + case BSBottom: + graphicsContext->drawRect(IntRect(x1, y1, x2 - x1, third)); + graphicsContext->drawRect(IntRect(x1, y2 - third, x2 - x1, third)); + break; + case BSLeft: + graphicsContext->drawRect(IntRect(x1, y1 + 1, third, y2 - y1 - 1)); + graphicsContext->drawRect(IntRect(x2 - third, y1 + 1, third, y2 - y1 - 1)); + break; + case BSRight: + graphicsContext->drawRect(IntRect(x1, y1 + 1, third, y2 - y1 - 1)); + graphicsContext->drawRect(IntRect(x2 - third, y1 + 1, third, y2 - y1 - 1)); + break; + } + } else { + int adjbw1bigthird = ((adjbw1 > 0) ? adjbw1 + 1 : adjbw1 - 1) / 3; + int adjbw2bigthird = ((adjbw2 > 0) ? adjbw2 + 1 : adjbw2 - 1) / 3; + + switch (s) { + case BSTop: + drawBorder(graphicsContext, x1 + max((-adjbw1 * 2 + 1) / 3, 0), + y1, x2 - max((-adjbw2 * 2 + 1) / 3, 0), y1 + third, + s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); + drawBorder(graphicsContext, x1 + max((adjbw1 * 2 + 1) / 3, 0), + y2 - third, x2 - max((adjbw2 * 2 + 1) / 3, 0), y2, + s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); + break; + case BSLeft: + drawBorder(graphicsContext, x1, y1 + max((-adjbw1 * 2 + 1) / 3, 0), + x1 + third, y2 - max((-adjbw2 * 2 + 1) / 3, 0), + s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); + drawBorder(graphicsContext, x2 - third, y1 + max((adjbw1 * 2 + 1) / 3, 0), + x2, y2 - max((adjbw2 * 2 + 1) / 3, 0), + s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); + break; + case BSBottom: + drawBorder(graphicsContext, x1 + max((adjbw1 * 2 + 1) / 3, 0), + y1, x2 - max((adjbw2 * 2 + 1) / 3, 0), y1 + third, + s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); + drawBorder(graphicsContext, x1 + max((-adjbw1 * 2 + 1) / 3, 0), + y2 - third, x2 - max((-adjbw2 * 2 + 1) / 3, 0), y2, + s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); + break; + case BSRight: + drawBorder(graphicsContext, x1, y1 + max((adjbw1 * 2 + 1) / 3, 0), + x1 + third, y2 - max(( adjbw2 * 2 + 1) / 3, 0), + s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); + drawBorder(graphicsContext, x2 - third, y1 + max((-adjbw1 * 2 + 1) / 3, 0), + x2, y2 - max((-adjbw2 * 2 + 1) / 3, 0), + s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird); + break; + default: + break; + } + } + break; + } + case RIDGE: + case GROOVE: + { + EBorderStyle s1; + EBorderStyle s2; + if (style == GROOVE) { + s1 = INSET; + s2 = OUTSET; + } else { + s1 = OUTSET; + s2 = INSET; + } + + int adjbw1bighalf = ((adjbw1 > 0) ? adjbw1 + 1 : adjbw1 - 1) / 2; + int adjbw2bighalf = ((adjbw2 > 0) ? adjbw2 + 1 : adjbw2 - 1) / 2; + + switch (s) { + case BSTop: + drawBorder(graphicsContext, x1 + max(-adjbw1, 0) / 2, y1, x2 - max(-adjbw2, 0) / 2, (y1 + y2 + 1) / 2, + s, c, textcolor, s1, adjbw1bighalf, adjbw2bighalf); + drawBorder(graphicsContext, x1 + max(adjbw1 + 1, 0) / 2, (y1 + y2 + 1) / 2, x2 - max(adjbw2 + 1, 0) / 2, y2, + s, c, textcolor, s2, adjbw1 / 2, adjbw2 / 2); + break; + case BSLeft: + drawBorder(graphicsContext, x1, y1 + max(-adjbw1, 0) / 2, (x1 + x2 + 1) / 2, y2 - max(-adjbw2, 0) / 2, + s, c, textcolor, s1, adjbw1bighalf, adjbw2bighalf); + drawBorder(graphicsContext, (x1 + x2 + 1) / 2, y1 + max(adjbw1 + 1, 0) / 2, x2, y2 - max(adjbw2 + 1, 0) / 2, + s, c, textcolor, s2, adjbw1 / 2, adjbw2 / 2); + break; + case BSBottom: + drawBorder(graphicsContext, x1 + max(adjbw1, 0) / 2, y1, x2 - max(adjbw2, 0) / 2, (y1 + y2 + 1) / 2, + s, c, textcolor, s2, adjbw1bighalf, adjbw2bighalf); + drawBorder(graphicsContext, x1 + max(-adjbw1 + 1, 0) / 2, (y1 + y2 + 1) / 2, x2 - max(-adjbw2 + 1, 0) / 2, y2, + s, c, textcolor, s1, adjbw1/2, adjbw2/2); + break; + case BSRight: + drawBorder(graphicsContext, x1, y1 + max(adjbw1, 0) / 2, (x1 + x2 + 1) / 2, y2 - max(adjbw2, 0) / 2, + s, c, textcolor, s2, adjbw1bighalf, adjbw2bighalf); + drawBorder(graphicsContext, (x1 + x2 + 1) / 2, y1 + max(-adjbw1 + 1, 0) / 2, x2, y2 - max(-adjbw2 + 1, 0) / 2, + s, c, textcolor, s1, adjbw1/2, adjbw2/2); + break; + } + break; + } + case INSET: + if (s == BSTop || s == BSLeft) + c = c.dark(); + // fall through + case OUTSET: + if (style == OUTSET && (s == BSBottom || s == BSRight)) + c = c.dark(); + // fall through + case SOLID: { + graphicsContext->setStrokeStyle(NoStroke); + graphicsContext->setFillColor(c); + ASSERT(x2 >= x1); + ASSERT(y2 >= y1); + if (!adjbw1 && !adjbw2) { + graphicsContext->drawRect(IntRect(x1, y1, x2 - x1, y2 - y1)); + return; + } + FloatPoint quad[4]; + switch (s) { + case BSTop: + quad[0] = FloatPoint(x1 + max(-adjbw1, 0), y1); + quad[1] = FloatPoint(x1 + max(adjbw1, 0), y2); + quad[2] = FloatPoint(x2 - max(adjbw2, 0), y2); + quad[3] = FloatPoint(x2 - max(-adjbw2, 0), y1); + break; + case BSBottom: + quad[0] = FloatPoint(x1 + max(adjbw1, 0), y1); + quad[1] = FloatPoint(x1 + max(-adjbw1, 0), y2); + quad[2] = FloatPoint(x2 - max(-adjbw2, 0), y2); + quad[3] = FloatPoint(x2 - max(adjbw2, 0), y1); + break; + case BSLeft: + quad[0] = FloatPoint(x1, y1 + max(-adjbw1, 0)); + quad[1] = FloatPoint(x1, y2 - max(-adjbw2, 0)); + quad[2] = FloatPoint(x2, y2 - max(adjbw2, 0)); + quad[3] = FloatPoint(x2, y1 + max(adjbw1, 0)); + break; + case BSRight: + quad[0] = FloatPoint(x1, y1 + max(adjbw1, 0)); + quad[1] = FloatPoint(x1, y2 - max(adjbw2, 0)); + quad[2] = FloatPoint(x2, y2 - max(-adjbw2, 0)); + quad[3] = FloatPoint(x2, y1 + max(-adjbw1, 0)); + break; + } + graphicsContext->drawConvexPolygon(4, quad); + break; + } + } +} + +bool RenderObject::paintNinePieceImage(GraphicsContext* graphicsContext, int tx, int ty, int w, int h, const RenderStyle* style, + const NinePieceImage& ninePieceImage, CompositeOperator op) +{ + StyleImage* styleImage = ninePieceImage.image(); + if (!styleImage || !styleImage->canRender(style->effectiveZoom())) + return false; + + if (!styleImage->isLoaded()) + return true; // Never paint a nine-piece image incrementally, but don't paint the fallback borders either. + + // If we have a border radius, the image gets clipped to the rounded rect. + bool clipped = false; + if (style->hasBorderRadius()) { + IntRect clipRect(tx, ty, w, h); + graphicsContext->save(); + graphicsContext->addRoundedRectClip(clipRect, style->borderTopLeftRadius(), style->borderTopRightRadius(), + style->borderBottomLeftRadius(), style->borderBottomRightRadius()); + clipped = true; + } + + // FIXME: border-image is broken with full page zooming when tiling has to happen, since the tiling function + // doesn't have any understanding of the zoom that is in effect on the tile. + styleImage->setImageContainerSize(IntSize(w, h)); + IntSize imageSize = styleImage->imageSize(this, 1.0f); + int imageWidth = imageSize.width(); + int imageHeight = imageSize.height(); + + int topSlice = min(imageHeight, ninePieceImage.m_slices.top().calcValue(imageHeight)); + int bottomSlice = min(imageHeight, ninePieceImage.m_slices.bottom().calcValue(imageHeight)); + int leftSlice = min(imageWidth, ninePieceImage.m_slices.left().calcValue(imageWidth)); + int rightSlice = min(imageWidth, ninePieceImage.m_slices.right().calcValue(imageWidth)); + + ENinePieceImageRule hRule = ninePieceImage.horizontalRule(); + ENinePieceImageRule vRule = ninePieceImage.verticalRule(); + + bool fitToBorder = style->borderImage() == ninePieceImage; + + int leftWidth = fitToBorder ? style->borderLeftWidth() : leftSlice; + int topWidth = fitToBorder ? style->borderTopWidth() : topSlice; + int rightWidth = fitToBorder ? style->borderRightWidth() : rightSlice; + int bottomWidth = fitToBorder ? style->borderBottomWidth() : bottomSlice; + + bool drawLeft = leftSlice > 0 && leftWidth > 0; + bool drawTop = topSlice > 0 && topWidth > 0; + bool drawRight = rightSlice > 0 && rightWidth > 0; + bool drawBottom = bottomSlice > 0 && bottomWidth > 0; + bool drawMiddle = (imageWidth - leftSlice - rightSlice) > 0 && (w - leftWidth - rightWidth) > 0 && + (imageHeight - topSlice - bottomSlice) > 0 && (h - topWidth - bottomWidth) > 0; + + Image* image = styleImage->image(this, imageSize); + + if (drawLeft) { + // Paint the top and bottom left corners. + + // The top left corner rect is (tx, ty, leftWidth, topWidth) + // The rect to use from within the image is obtained from our slice, and is (0, 0, leftSlice, topSlice) + if (drawTop) + graphicsContext->drawImage(image, IntRect(tx, ty, leftWidth, topWidth), + IntRect(0, 0, leftSlice, topSlice), op); + + // The bottom left corner rect is (tx, ty + h - bottomWidth, leftWidth, bottomWidth) + // The rect to use from within the image is (0, imageHeight - bottomSlice, leftSlice, botomSlice) + if (drawBottom) + graphicsContext->drawImage(image, IntRect(tx, ty + h - bottomWidth, leftWidth, bottomWidth), + IntRect(0, imageHeight - bottomSlice, leftSlice, bottomSlice), op); + + // Paint the left edge. + // Have to scale and tile into the border rect. + graphicsContext->drawTiledImage(image, IntRect(tx, ty + topWidth, leftWidth, + h - topWidth - bottomWidth), + IntRect(0, topSlice, leftSlice, imageHeight - topSlice - bottomSlice), + Image::StretchTile, (Image::TileRule)vRule, op); + } + + if (drawRight) { + // Paint the top and bottom right corners + // The top right corner rect is (tx + w - rightWidth, ty, rightWidth, topWidth) + // The rect to use from within the image is obtained from our slice, and is (imageWidth - rightSlice, 0, rightSlice, topSlice) + if (drawTop) + graphicsContext->drawImage(image, IntRect(tx + w - rightWidth, ty, rightWidth, topWidth), + IntRect(imageWidth - rightSlice, 0, rightSlice, topSlice), op); + + // The bottom right corner rect is (tx + w - rightWidth, ty + h - bottomWidth, rightWidth, bottomWidth) + // The rect to use from within the image is (imageWidth - rightSlice, imageHeight - bottomSlice, rightSlice, bottomSlice) + if (drawBottom) + graphicsContext->drawImage(image, IntRect(tx + w - rightWidth, ty + h - bottomWidth, rightWidth, bottomWidth), + IntRect(imageWidth - rightSlice, imageHeight - bottomSlice, rightSlice, bottomSlice), op); + + // Paint the right edge. + graphicsContext->drawTiledImage(image, IntRect(tx + w - rightWidth, ty + topWidth, rightWidth, + h - topWidth - bottomWidth), + IntRect(imageWidth - rightSlice, topSlice, rightSlice, imageHeight - topSlice - bottomSlice), + Image::StretchTile, (Image::TileRule)vRule, op); + } + + // Paint the top edge. + if (drawTop) + graphicsContext->drawTiledImage(image, IntRect(tx + leftWidth, ty, w - leftWidth - rightWidth, topWidth), + IntRect(leftSlice, 0, imageWidth - rightSlice - leftSlice, topSlice), + (Image::TileRule)hRule, Image::StretchTile, op); + + // Paint the bottom edge. + if (drawBottom) + graphicsContext->drawTiledImage(image, IntRect(tx + leftWidth, ty + h - bottomWidth, + w - leftWidth - rightWidth, bottomWidth), + IntRect(leftSlice, imageHeight - bottomSlice, imageWidth - rightSlice - leftSlice, bottomSlice), + (Image::TileRule)hRule, Image::StretchTile, op); + + // Paint the middle. + if (drawMiddle) + graphicsContext->drawTiledImage(image, IntRect(tx + leftWidth, ty + topWidth, w - leftWidth - rightWidth, + h - topWidth - bottomWidth), + IntRect(leftSlice, topSlice, imageWidth - rightSlice - leftSlice, imageHeight - topSlice - bottomSlice), + (Image::TileRule)hRule, (Image::TileRule)vRule, op); + + // Clear the clip for the border radius. + if (clipped) + graphicsContext->restore(); + + return true; +} + +void RenderObject::paintBorder(GraphicsContext* graphicsContext, int tx, int ty, int w, int h, + const RenderStyle* style, bool begin, bool end) +{ + if (paintNinePieceImage(graphicsContext, tx, ty, w, h, style, style->borderImage())) + return; + + const Color& tc = style->borderTopColor(); + const Color& bc = style->borderBottomColor(); + const Color& lc = style->borderLeftColor(); + const Color& rc = style->borderRightColor(); + + bool tt = style->borderTopIsTransparent(); + bool bt = style->borderBottomIsTransparent(); + bool rt = style->borderRightIsTransparent(); + bool lt = style->borderLeftIsTransparent(); + + EBorderStyle ts = style->borderTopStyle(); + EBorderStyle bs = style->borderBottomStyle(); + EBorderStyle ls = style->borderLeftStyle(); + EBorderStyle rs = style->borderRightStyle(); + + bool renderTop = ts > BHIDDEN && !tt; + bool renderLeft = ls > BHIDDEN && begin && !lt; + bool renderRight = rs > BHIDDEN && end && !rt; + bool renderBottom = bs > BHIDDEN && !bt; + + // Need sufficient width and height to contain border radius curves. Sanity check our border radii + // and our width/height values to make sure the curves can all fit. If not, then we won't paint + // any border radii. + bool renderRadii = false; + IntSize topLeft = style->borderTopLeftRadius(); + IntSize topRight = style->borderTopRightRadius(); + IntSize bottomLeft = style->borderBottomLeftRadius(); + IntSize bottomRight = style->borderBottomRightRadius(); + + if (style->hasBorderRadius() && + static_cast<unsigned>(w) >= static_cast<unsigned>(topLeft.width()) + static_cast<unsigned>(topRight.width()) && + static_cast<unsigned>(w) >= static_cast<unsigned>(bottomLeft.width()) + static_cast<unsigned>(bottomRight.width()) && + static_cast<unsigned>(h) >= static_cast<unsigned>(topLeft.height()) + static_cast<unsigned>(bottomLeft.height()) && + static_cast<unsigned>(h) >= static_cast<unsigned>(topRight.height()) + static_cast<unsigned>(bottomRight.height())) + renderRadii = true; + + // Clip to the rounded rectangle. + if (renderRadii) { + graphicsContext->save(); + graphicsContext->addRoundedRectClip(IntRect(tx, ty, w, h), topLeft, topRight, bottomLeft, bottomRight); + } + + int firstAngleStart, secondAngleStart, firstAngleSpan, secondAngleSpan; + float thickness; + bool upperLeftBorderStylesMatch = renderLeft && (ts == ls) && (tc == lc); + bool upperRightBorderStylesMatch = renderRight && (ts == rs) && (tc == rc) && (ts != OUTSET) && (ts != RIDGE) && (ts != INSET) && (ts != GROOVE); + bool lowerLeftBorderStylesMatch = renderLeft && (bs == ls) && (bc == lc) && (bs != OUTSET) && (bs != RIDGE) && (bs != INSET) && (bs != GROOVE); + bool lowerRightBorderStylesMatch = renderRight && (bs == rs) && (bc == rc); + + if (renderTop) { + bool ignore_left = (renderRadii && topLeft.width() > 0) || + (tc == lc && tt == lt && ts >= OUTSET && + (ls == DOTTED || ls == DASHED || ls == SOLID || ls == OUTSET)); + + bool ignore_right = (renderRadii && topRight.width() > 0) || + (tc == rc && tt == rt && ts >= OUTSET && + (rs == DOTTED || rs == DASHED || rs == SOLID || rs == INSET)); + + int x = tx; + int x2 = tx + w; + if (renderRadii) { + x += topLeft.width(); + x2 -= topRight.width(); + } + + drawBorder(graphicsContext, x, ty, x2, ty + style->borderTopWidth(), BSTop, tc, style->color(), ts, + ignore_left ? 0 : style->borderLeftWidth(), ignore_right ? 0 : style->borderRightWidth()); + + if (renderRadii) { + int leftY = ty; + + // We make the arc double thick and let the clip rect take care of clipping the extra off. + // We're doing this because it doesn't seem possible to match the curve of the clip exactly + // with the arc-drawing function. + thickness = style->borderTopWidth() * 2; + + if (topLeft.width()) { + int leftX = tx; + // The inner clip clips inside the arc. This is especially important for 1px borders. + bool applyLeftInnerClip = (style->borderLeftWidth() < topLeft.width()) + && (style->borderTopWidth() < topLeft.height()) + && (ts != DOUBLE || style->borderTopWidth() > 6); + if (applyLeftInnerClip) { + graphicsContext->save(); + graphicsContext->addInnerRoundedRectClip(IntRect(leftX, leftY, topLeft.width() * 2, topLeft.height() * 2), + style->borderTopWidth()); + } + + firstAngleStart = 90; + firstAngleSpan = upperLeftBorderStylesMatch ? 90 : 45; + + // Draw upper left arc + drawBorderArc(graphicsContext, leftX, leftY, thickness, topLeft, firstAngleStart, firstAngleSpan, + BSTop, tc, style->color(), ts, true); + if (applyLeftInnerClip) + graphicsContext->restore(); + } + + if (topRight.width()) { + int rightX = tx + w - topRight.width() * 2; + bool applyRightInnerClip = (style->borderRightWidth() < topRight.width()) + && (style->borderTopWidth() < topRight.height()) + && (ts != DOUBLE || style->borderTopWidth() > 6); + if (applyRightInnerClip) { + graphicsContext->save(); + graphicsContext->addInnerRoundedRectClip(IntRect(rightX, leftY, topRight.width() * 2, topRight.height() * 2), + style->borderTopWidth()); + } + + if (upperRightBorderStylesMatch) { + secondAngleStart = 0; + secondAngleSpan = 90; + } else { + secondAngleStart = 45; + secondAngleSpan = 45; + } + + // Draw upper right arc + drawBorderArc(graphicsContext, rightX, leftY, thickness, topRight, secondAngleStart, secondAngleSpan, + BSTop, tc, style->color(), ts, false); + if (applyRightInnerClip) + graphicsContext->restore(); + } + } + } + + if (renderBottom) { + bool ignore_left = (renderRadii && bottomLeft.width() > 0) || + (bc == lc && bt == lt && bs >= OUTSET && + (ls == DOTTED || ls == DASHED || ls == SOLID || ls == OUTSET)); + + bool ignore_right = (renderRadii && bottomRight.width() > 0) || + (bc == rc && bt == rt && bs >= OUTSET && + (rs == DOTTED || rs == DASHED || rs == SOLID || rs == INSET)); + + int x = tx; + int x2 = tx + w; + if (renderRadii) { + x += bottomLeft.width(); + x2 -= bottomRight.width(); + } + + drawBorder(graphicsContext, x, ty + h - style->borderBottomWidth(), x2, ty + h, BSBottom, bc, style->color(), bs, + ignore_left ? 0 : style->borderLeftWidth(), ignore_right ? 0 : style->borderRightWidth()); + + if (renderRadii) { + thickness = style->borderBottomWidth() * 2; + + if (bottomLeft.width()) { + int leftX = tx; + int leftY = ty + h - bottomLeft.height() * 2; + bool applyLeftInnerClip = (style->borderLeftWidth() < bottomLeft.width()) + && (style->borderBottomWidth() < bottomLeft.height()) + && (bs != DOUBLE || style->borderBottomWidth() > 6); + if (applyLeftInnerClip) { + graphicsContext->save(); + graphicsContext->addInnerRoundedRectClip(IntRect(leftX, leftY, bottomLeft.width() * 2, bottomLeft.height() * 2), + style->borderBottomWidth()); + } + + if (lowerLeftBorderStylesMatch) { + firstAngleStart = 180; + firstAngleSpan = 90; + } else { + firstAngleStart = 225; + firstAngleSpan = 45; + } + + // Draw lower left arc + drawBorderArc(graphicsContext, leftX, leftY, thickness, bottomLeft, firstAngleStart, firstAngleSpan, + BSBottom, bc, style->color(), bs, true); + if (applyLeftInnerClip) + graphicsContext->restore(); + } + + if (bottomRight.width()) { + int rightY = ty + h - bottomRight.height() * 2; + int rightX = tx + w - bottomRight.width() * 2; + bool applyRightInnerClip = (style->borderRightWidth() < bottomRight.width()) + && (style->borderBottomWidth() < bottomRight.height()) + && (bs != DOUBLE || style->borderBottomWidth() > 6); + if (applyRightInnerClip) { + graphicsContext->save(); + graphicsContext->addInnerRoundedRectClip(IntRect(rightX, rightY, bottomRight.width() * 2, bottomRight.height() * 2), + style->borderBottomWidth()); + } + + secondAngleStart = 270; + secondAngleSpan = lowerRightBorderStylesMatch ? 90 : 45; + + // Draw lower right arc + drawBorderArc(graphicsContext, rightX, rightY, thickness, bottomRight, secondAngleStart, secondAngleSpan, + BSBottom, bc, style->color(), bs, false); + if (applyRightInnerClip) + graphicsContext->restore(); + } + } + } + + if (renderLeft) { + bool ignore_top = (renderRadii && topLeft.height() > 0) || + (tc == lc && tt == lt && ls >= OUTSET && + (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET)); + + bool ignore_bottom = (renderRadii && bottomLeft.height() > 0) || + (bc == lc && bt == lt && ls >= OUTSET && + (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET)); + + int y = ty; + int y2 = ty + h; + if (renderRadii) { + y += topLeft.height(); + y2 -= bottomLeft.height(); + } + + drawBorder(graphicsContext, tx, y, tx + style->borderLeftWidth(), y2, BSLeft, lc, style->color(), ls, + ignore_top ? 0 : style->borderTopWidth(), ignore_bottom ? 0 : style->borderBottomWidth()); + + if (renderRadii && (!upperLeftBorderStylesMatch || !lowerLeftBorderStylesMatch)) { + int topX = tx; + thickness = style->borderLeftWidth() * 2; + + if (!upperLeftBorderStylesMatch && topLeft.width()) { + int topY = ty; + bool applyTopInnerClip = (style->borderLeftWidth() < topLeft.width()) + && (style->borderTopWidth() < topLeft.height()) + && (ls != DOUBLE || style->borderLeftWidth() > 6); + if (applyTopInnerClip) { + graphicsContext->save(); + graphicsContext->addInnerRoundedRectClip(IntRect(topX, topY, topLeft.width() * 2, topLeft.height() * 2), + style->borderLeftWidth()); + } + + firstAngleStart = 135; + firstAngleSpan = 45; + + // Draw top left arc + drawBorderArc(graphicsContext, topX, topY, thickness, topLeft, firstAngleStart, firstAngleSpan, + BSLeft, lc, style->color(), ls, true); + if (applyTopInnerClip) + graphicsContext->restore(); + } + + if (!lowerLeftBorderStylesMatch && bottomLeft.width()) { + int bottomY = ty + h - bottomLeft.height() * 2; + bool applyBottomInnerClip = (style->borderLeftWidth() < bottomLeft.width()) + && (style->borderBottomWidth() < bottomLeft.height()) + && (ls != DOUBLE || style->borderLeftWidth() > 6); + if (applyBottomInnerClip) { + graphicsContext->save(); + graphicsContext->addInnerRoundedRectClip(IntRect(topX, bottomY, bottomLeft.width() * 2, bottomLeft.height() * 2), + style->borderLeftWidth()); + } + + secondAngleStart = 180; + secondAngleSpan = 45; + + // Draw bottom left arc + drawBorderArc(graphicsContext, topX, bottomY, thickness, bottomLeft, secondAngleStart, secondAngleSpan, + BSLeft, lc, style->color(), ls, false); + if (applyBottomInnerClip) + graphicsContext->restore(); + } + } + } + + if (renderRight) { + bool ignore_top = (renderRadii && topRight.height() > 0) || + ((tc == rc) && (tt == rt) && + (rs >= DOTTED || rs == INSET) && + (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET)); + + bool ignore_bottom = (renderRadii && bottomRight.height() > 0) || + ((bc == rc) && (bt == rt) && + (rs >= DOTTED || rs == INSET) && + (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET)); + + int y = ty; + int y2 = ty + h; + if (renderRadii) { + y += topRight.height(); + y2 -= bottomRight.height(); + } + + drawBorder(graphicsContext, tx + w - style->borderRightWidth(), y, tx + w, y2, BSRight, rc, style->color(), rs, + ignore_top ? 0 : style->borderTopWidth(), ignore_bottom ? 0 : style->borderBottomWidth()); + + if (renderRadii && (!upperRightBorderStylesMatch || !lowerRightBorderStylesMatch)) { + thickness = style->borderRightWidth() * 2; + + if (!upperRightBorderStylesMatch && topRight.width()) { + int topX = tx + w - topRight.width() * 2; + int topY = ty; + bool applyTopInnerClip = (style->borderRightWidth() < topRight.width()) + && (style->borderTopWidth() < topRight.height()) + && (rs != DOUBLE || style->borderRightWidth() > 6); + if (applyTopInnerClip) { + graphicsContext->save(); + graphicsContext->addInnerRoundedRectClip(IntRect(topX, topY, topRight.width() * 2, topRight.height() * 2), + style->borderRightWidth()); + } + + firstAngleStart = 0; + firstAngleSpan = 45; + + // Draw top right arc + drawBorderArc(graphicsContext, topX, topY, thickness, topRight, firstAngleStart, firstAngleSpan, + BSRight, rc, style->color(), rs, true); + if (applyTopInnerClip) + graphicsContext->restore(); + } + + if (!lowerRightBorderStylesMatch && bottomRight.width()) { + int bottomX = tx + w - bottomRight.width() * 2; + int bottomY = ty + h - bottomRight.height() * 2; + bool applyBottomInnerClip = (style->borderRightWidth() < bottomRight.width()) + && (style->borderBottomWidth() < bottomRight.height()) + && (rs != DOUBLE || style->borderRightWidth() > 6); + if (applyBottomInnerClip) { + graphicsContext->save(); + graphicsContext->addInnerRoundedRectClip(IntRect(bottomX, bottomY, bottomRight.width() * 2, bottomRight.height() * 2), + style->borderRightWidth()); + } + + secondAngleStart = 315; + secondAngleSpan = 45; + + // Draw bottom right arc + drawBorderArc(graphicsContext, bottomX, bottomY, thickness, bottomRight, secondAngleStart, secondAngleSpan, + BSRight, rc, style->color(), rs, false); + if (applyBottomInnerClip) + graphicsContext->restore(); + } + } + } + + if (renderRadii) + graphicsContext->restore(); +} + +void RenderObject::paintBoxShadow(GraphicsContext* context, int tx, int ty, int w, int h, const RenderStyle* s, bool begin, bool end) +{ + // FIXME: Deal with border-image. Would be great to use border-image as a mask. + + IntRect rect(tx, ty, w, h); + bool hasBorderRadius = s->hasBorderRadius(); + bool hasOpaqueBackground = s->backgroundColor().isValid() && s->backgroundColor().alpha() == 255; + for (ShadowData* shadow = s->boxShadow(); shadow; shadow = shadow->next) { + context->save(); + + IntSize shadowOffset(shadow->x, shadow->y); + int shadowBlur = shadow->blur; + IntRect fillRect(rect); + + if (hasBorderRadius) { + IntRect shadowRect(rect); + shadowRect.inflate(shadowBlur); + shadowRect.move(shadowOffset); + context->clip(shadowRect); + + // Move the fill just outside the clip, adding 1 pixel separation so that the fill does not + // bleed in (due to antialiasing) if the context is transformed. + IntSize extraOffset(w + max(0, shadowOffset.width()) + shadowBlur + 1, 0); + shadowOffset -= extraOffset; + fillRect.move(extraOffset); + } + + context->setShadow(shadowOffset, shadowBlur, shadow->color); + if (hasBorderRadius) { + IntSize topLeft = begin ? s->borderTopLeftRadius() : IntSize(); + IntSize topRight = end ? s->borderTopRightRadius() : IntSize(); + IntSize bottomLeft = begin ? s->borderBottomLeftRadius() : IntSize(); + IntSize bottomRight = end ? s->borderBottomRightRadius() : IntSize(); + if (!hasOpaqueBackground) + context->clipOutRoundedRect(rect, topLeft, topRight, bottomLeft, bottomRight); + context->fillRoundedRect(fillRect, topLeft, topRight, bottomLeft, bottomRight, Color::black); + } else { + if (!hasOpaqueBackground) + context->clipOut(rect); + context->fillRect(fillRect, Color::black); + } + context->restore(); + } +} + +void RenderObject::addLineBoxRects(Vector<IntRect>&, unsigned, unsigned, bool) +{ +} + +void RenderObject::absoluteRects(Vector<IntRect>& rects, int tx, int ty, bool topLevel) +{ + // For blocks inside inlines, we go ahead and include margins so that we run right up to the + // inline boxes above and below us (thus getting merged with them to form a single irregular + // shape). + if (topLevel && continuation()) { + rects.append(IntRect(tx, ty - collapsedMarginTop(), + width(), height() + collapsedMarginTop() + collapsedMarginBottom())); + continuation()->absoluteRects(rects, + tx - xPos() + continuation()->containingBlock()->xPos(), + ty - yPos() + continuation()->containingBlock()->yPos(), topLevel); + } else + rects.append(IntRect(tx, ty, width(), height() + borderTopExtra() + borderBottomExtra())); +} + +IntRect RenderObject::absoluteBoundingBoxRect(bool useTransforms) +{ + if (useTransforms) { + Vector<FloatQuad> quads; + absoluteQuads(quads); + + size_t n = quads.size(); + if (!n) + return IntRect(); + + IntRect result = quads[0].enclosingBoundingBox(); + for (size_t i = 1; i < n; ++i) + result.unite(quads[i].enclosingBoundingBox()); + return result; + } + + FloatPoint absPos = localToAbsolute(); + Vector<IntRect> rects; + absoluteRects(rects, absPos.x(), absPos.y()); + + size_t n = rects.size(); + if (!n) + return IntRect(); + + IntRect result = rects[0]; + for (size_t i = 1; i < n; ++i) + result.unite(rects[i]); + return result; +} + +void RenderObject::collectAbsoluteLineBoxQuads(Vector<FloatQuad>&, unsigned, unsigned, bool) +{ +} + +void RenderObject::absoluteQuads(Vector<FloatQuad>& quads, bool topLevel) +{ + // For blocks inside inlines, we go ahead and include margins so that we run right up to the + // inline boxes above and below us (thus getting merged with them to form a single irregular + // shape). + if (topLevel && continuation()) { + FloatRect localRect(0, -collapsedMarginTop(), + width(), height() + collapsedMarginTop() + collapsedMarginBottom()); + quads.append(localToAbsoluteQuad(localRect)); + continuation()->absoluteQuads(quads, topLevel); + } else + quads.append(localToAbsoluteQuad(FloatRect(0, 0, width(), height() + borderTopExtra() + borderBottomExtra()))); +} + +void RenderObject::addAbsoluteRectForLayer(IntRect& result) +{ + if (hasLayer()) + result.unite(absoluteBoundingBoxRect()); + for (RenderObject* current = firstChild(); current; current = current->nextSibling()) + current->addAbsoluteRectForLayer(result); +} + +IntRect RenderObject::paintingRootRect(IntRect& topLevelRect) +{ + IntRect result = absoluteBoundingBoxRect(); + topLevelRect = result; + for (RenderObject* current = firstChild(); current; current = current->nextSibling()) + current->addAbsoluteRectForLayer(result); + return result; +} + +void RenderObject::addPDFURLRect(GraphicsContext* context, const IntRect& rect) +{ + if (rect.isEmpty()) + return; + Node* node = element(); + if (!node || !node->isLink() || !node->isElementNode()) + return; + const AtomicString& href = static_cast<Element*>(node)->getAttribute(hrefAttr); + if (href.isNull()) + return; + context->setURLForRect(node->document()->completeURL(href), rect); +} + + +void RenderObject::addFocusRingRects(GraphicsContext* graphicsContext, int tx, int ty) +{ + // For blocks inside inlines, we go ahead and include margins so that we run right up to the + // inline boxes above and below us (thus getting merged with them to form a single irregular + // shape). + if (continuation()) { + graphicsContext->addFocusRingRect(IntRect(tx, ty - collapsedMarginTop(), width(), height() + collapsedMarginTop() + collapsedMarginBottom())); + continuation()->addFocusRingRects(graphicsContext, + tx - xPos() + continuation()->containingBlock()->xPos(), + ty - yPos() + continuation()->containingBlock()->yPos()); + } else + graphicsContext->addFocusRingRect(IntRect(tx, ty, width(), height())); +} + +void RenderObject::paintOutline(GraphicsContext* graphicsContext, int tx, int ty, int w, int h, const RenderStyle* style) +{ + if (!hasOutline()) + return; + + int ow = style->outlineWidth(); + + EBorderStyle os = style->outlineStyle(); + + Color oc = style->outlineColor(); + if (!oc.isValid()) + oc = style->color(); + + int offset = style->outlineOffset(); + + if (style->outlineStyleIsAuto() || hasOutlineAnnotation()) { + if (!theme()->supportsFocusRing(style)) { + // Only paint the focus ring by hand if the theme isn't able to draw the focus ring. + graphicsContext->initFocusRing(ow, offset); + if (style->outlineStyleIsAuto()) + addFocusRingRects(graphicsContext, tx, ty); + else + addPDFURLRect(graphicsContext, graphicsContext->focusRingBoundingRect()); + graphicsContext->drawFocusRing(oc); + graphicsContext->clearFocusRing(); + } + } + + if (style->outlineStyleIsAuto() || style->outlineStyle() == BNONE) + return; + + tx -= offset; + ty -= offset; + w += 2 * offset; + h += 2 * offset; + + if (h < 0 || w < 0) + return; + + drawBorder(graphicsContext, tx - ow, ty - ow, tx, ty + h + ow, + BSLeft, Color(oc), style->color(), os, ow, ow); + + drawBorder(graphicsContext, tx - ow, ty - ow, tx + w + ow, ty, + BSTop, Color(oc), style->color(), os, ow, ow); + + drawBorder(graphicsContext, tx + w, ty - ow, tx + w + ow, ty + h + ow, + BSRight, Color(oc), style->color(), os, ow, ow); + + drawBorder(graphicsContext, tx - ow, ty + h, tx + w + ow, ty + h + ow, + BSBottom, Color(oc), style->color(), os, ow, ow); +} + +void RenderObject::paint(PaintInfo& /*paintInfo*/, int /*tx*/, int /*ty*/) +{ +} + +void RenderObject::repaint(bool immediate) +{ + // Can't use view(), since we might be unrooted. + RenderObject* o = this; + while (o->parent()) + o = o->parent(); + if (!o->isRenderView()) + return; + RenderView* view = static_cast<RenderView*>(o); + if (view->printing()) + return; // Don't repaint if we're printing. + view->repaintViewRectangle(absoluteClippedOverflowRect(), immediate); +} + +void RenderObject::repaintRectangle(const IntRect& r, bool immediate) +{ + // Can't use view(), since we might be unrooted. + RenderObject* o = this; + while (o->parent()) + o = o->parent(); + if (!o->isRenderView()) + return; + RenderView* view = static_cast<RenderView*>(o); + if (view->printing()) + return; // Don't repaint if we're printing. + IntRect absRect(r); + absRect.move(view->layoutDelta()); + computeAbsoluteRepaintRect(absRect); + view->repaintViewRectangle(absRect, immediate); +} + +bool RenderObject::repaintAfterLayoutIfNeeded(const IntRect& oldBounds, const IntRect& oldOutlineBox) +{ + RenderView* v = view(); + if (v->printing()) + return false; // Don't repaint if we're printing. + + IntRect newBounds = absoluteClippedOverflowRect(); + IntRect newOutlineBox; + + bool fullRepaint = selfNeedsLayout(); + // Presumably a background or a border exists if border-fit:lines was specified. + if (!fullRepaint && style()->borderFit() == BorderFitLines) + fullRepaint = true; + if (!fullRepaint) { + newOutlineBox = absoluteOutlineBounds(); + if (newOutlineBox.location() != oldOutlineBox.location() || (mustRepaintBackgroundOrBorder() && (newBounds != oldBounds || newOutlineBox != oldOutlineBox))) + fullRepaint = true; + } + if (fullRepaint) { + v->repaintViewRectangle(oldBounds); + if (newBounds != oldBounds) + v->repaintViewRectangle(newBounds); + return true; + } + + if (newBounds == oldBounds && newOutlineBox == oldOutlineBox) + return false; + + int deltaLeft = newBounds.x() - oldBounds.x(); + if (deltaLeft > 0) + v->repaintViewRectangle(IntRect(oldBounds.x(), oldBounds.y(), deltaLeft, oldBounds.height())); + else if (deltaLeft < 0) + v->repaintViewRectangle(IntRect(newBounds.x(), newBounds.y(), -deltaLeft, newBounds.height())); + + int deltaRight = newBounds.right() - oldBounds.right(); + if (deltaRight > 0) + v->repaintViewRectangle(IntRect(oldBounds.right(), newBounds.y(), deltaRight, newBounds.height())); + else if (deltaRight < 0) + v->repaintViewRectangle(IntRect(newBounds.right(), oldBounds.y(), -deltaRight, oldBounds.height())); + + int deltaTop = newBounds.y() - oldBounds.y(); + if (deltaTop > 0) + v->repaintViewRectangle(IntRect(oldBounds.x(), oldBounds.y(), oldBounds.width(), deltaTop)); + else if (deltaTop < 0) + v->repaintViewRectangle(IntRect(newBounds.x(), newBounds.y(), newBounds.width(), -deltaTop)); + + int deltaBottom = newBounds.bottom() - oldBounds.bottom(); + if (deltaBottom > 0) + v->repaintViewRectangle(IntRect(newBounds.x(), oldBounds.bottom(), newBounds.width(), deltaBottom)); + else if (deltaBottom < 0) + v->repaintViewRectangle(IntRect(oldBounds.x(), newBounds.bottom(), oldBounds.width(), -deltaBottom)); + + if (newOutlineBox == oldOutlineBox) + return false; + + // We didn't move, but we did change size. Invalidate the delta, which will consist of possibly + // two rectangles (but typically only one). + RenderStyle* outlineStyle = !isInline() && continuation() ? continuation()->style() : style(); + int ow = outlineStyle->outlineSize(); + ShadowData* boxShadow = style()->boxShadow(); + int width = abs(newOutlineBox.width() - oldOutlineBox.width()); + if (width) { + int shadowRight = 0; + for (ShadowData* shadow = boxShadow; shadow; shadow = shadow->next) + shadowRight = max(shadow->x + shadow->blur, shadowRight); + + int borderWidth = max(-outlineStyle->outlineOffset(), max(borderRight(), max(style()->borderTopRightRadius().width(), style()->borderBottomRightRadius().width()))) + max(ow, shadowRight); + IntRect rightRect(newOutlineBox.x() + min(newOutlineBox.width(), oldOutlineBox.width()) - borderWidth, + newOutlineBox.y(), + width + borderWidth, + max(newOutlineBox.height(), oldOutlineBox.height())); + int right = min(newBounds.right(), oldBounds.right()); + if (rightRect.x() < right) { + rightRect.setWidth(min(rightRect.width(), right - rightRect.x())); + v->repaintViewRectangle(rightRect); + } + } + int height = abs(newOutlineBox.height() - oldOutlineBox.height()); + if (height) { + int shadowBottom = 0; + for (ShadowData* shadow = boxShadow; shadow; shadow = shadow->next) + shadowBottom = max(shadow->y + shadow->blur, shadowBottom); + + int borderHeight = max(-outlineStyle->outlineOffset(), max(borderBottom(), max(style()->borderBottomLeftRadius().height(), style()->borderBottomRightRadius().height()))) + max(ow, shadowBottom); + IntRect bottomRect(newOutlineBox.x(), + min(newOutlineBox.bottom(), oldOutlineBox.bottom()) - borderHeight, + max(newOutlineBox.width(), oldOutlineBox.width()), + height + borderHeight); + int bottom = min(newBounds.bottom(), oldBounds.bottom()); + if (bottomRect.y() < bottom) { + bottomRect.setHeight(min(bottomRect.height(), bottom - bottomRect.y())); + v->repaintViewRectangle(bottomRect); + } + } + return false; +} + +void RenderObject::repaintDuringLayoutIfMoved(const IntRect&) +{ +} + +void RenderObject::repaintOverhangingFloats(bool) +{ +} + +bool RenderObject::checkForRepaintDuringLayout() const +{ + // FIXME: <https://bugs.webkit.org/show_bug.cgi?id=20885> It is probably safe to also require + // m_everHadLayout. Currently, only RenderBlock::layoutBlock() adds this condition. See also + // <https://bugs.webkit.org/show_bug.cgi?id=15129>. + return !document()->view()->needsFullRepaint() && !hasLayer(); +} + +IntRect RenderObject::getAbsoluteRepaintRectWithOutline(int ow) +{ + IntRect r(absoluteClippedOverflowRect()); + r.inflate(ow); + + if (continuation() && !isInline()) + r.inflateY(collapsedMarginTop()); + + if (isInlineFlow()) { + for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) { + if (!curr->isText()) + r.unite(curr->getAbsoluteRepaintRectWithOutline(ow)); + } + } + + return r; +} + +IntRect RenderObject::absoluteClippedOverflowRect() +{ + if (parent()) + return parent()->absoluteClippedOverflowRect(); + return IntRect(); +} + +void RenderObject::computeAbsoluteRepaintRect(IntRect& rect, bool fixed) +{ + if (RenderObject* o = parent()) { + if (o->isBlockFlow()) { + RenderBlock* cb = static_cast<RenderBlock*>(o); + if (cb->hasColumns()) + cb->adjustRectForColumns(rect); + } + + if (o->hasOverflowClip()) { + // o->height() is inaccurate if we're in the middle of a layout of |o|, so use the + // layer's size instead. Even if the layer's size is wrong, the layer itself will repaint + // anyway if its size does change. + IntRect boxRect(0, 0, o->layer()->width(), o->layer()->height()); + int x = rect.x(); + int y = rect.y(); + o->layer()->subtractScrolledContentOffset(x, y); // For overflow:auto/scroll/hidden. + IntRect repaintRect(x, y, rect.width(), rect.height()); + rect = intersection(repaintRect, boxRect); + if (rect.isEmpty()) + return; + } + + o->computeAbsoluteRepaintRect(rect, fixed); + } +} + +void RenderObject::dirtyLinesFromChangedChild(RenderObject*) +{ +} + +#ifndef NDEBUG + +void RenderObject::showTreeForThis() const +{ + if (element()) + element()->showTreeForThis(); +} + +#endif // NDEBUG + +Color RenderObject::selectionBackgroundColor() const +{ + Color color; + if (style()->userSelect() != SELECT_NONE) { + RenderStyle* pseudoStyle = getCachedPseudoStyle(RenderStyle::SELECTION); + if (pseudoStyle && pseudoStyle->backgroundColor().isValid()) + color = pseudoStyle->backgroundColor().blendWithWhite(); + else + color = document()->frame()->selection()->isFocusedAndActive() ? + theme()->activeSelectionBackgroundColor() : + theme()->inactiveSelectionBackgroundColor(); + } + + return color; +} + +Color RenderObject::selectionForegroundColor() const +{ + Color color; + if (style()->userSelect() == SELECT_NONE) + return color; + + if (RenderStyle* pseudoStyle = getCachedPseudoStyle(RenderStyle::SELECTION)) { + color = pseudoStyle->textFillColor(); + if (!color.isValid()) + color = pseudoStyle->color(); + } else + color = document()->frame()->selection()->isFocusedAndActive() ? + theme()->platformActiveSelectionForegroundColor() : + theme()->platformInactiveSelectionForegroundColor(); + + return color; +} + +Node* RenderObject::draggableNode(bool dhtmlOK, bool uaOK, int x, int y, bool& dhtmlWillDrag) const +{ + if (!dhtmlOK && !uaOK) + return 0; + + for (const RenderObject* curr = this; curr; curr = curr->parent()) { + Node* elt = curr->element(); + if (elt && elt->nodeType() == Node::TEXT_NODE) { + // Since there's no way for the author to address the -webkit-user-drag style for a text node, + // we use our own judgement. + if (uaOK && view()->frameView()->frame()->eventHandler()->shouldDragAutoNode(curr->node(), IntPoint(x, y))) { + dhtmlWillDrag = false; + return curr->node(); + } + if (elt->canStartSelection()) + // In this case we have a click in the unselected portion of text. If this text is + // selectable, we want to start the selection process instead of looking for a parent + // to try to drag. + return 0; + } else { + EUserDrag dragMode = curr->style()->userDrag(); + if (dhtmlOK && dragMode == DRAG_ELEMENT) { + dhtmlWillDrag = true; + return curr->node(); + } + if (uaOK && dragMode == DRAG_AUTO + && view()->frameView()->frame()->eventHandler()->shouldDragAutoNode(curr->node(), IntPoint(x, y))) { + dhtmlWillDrag = false; + return curr->node(); + } + } + } + return 0; +} + +void RenderObject::selectionStartEnd(int& spos, int& epos) const +{ + view()->selectionStartEnd(spos, epos); +} + +RenderBlock* RenderObject::createAnonymousBlock() +{ + RefPtr<RenderStyle> newStyle = RenderStyle::create(); + newStyle->inheritFrom(m_style.get()); + newStyle->setDisplay(BLOCK); + + RenderBlock* newBox = new (renderArena()) RenderBlock(document() /* anonymous box */); + newBox->setStyle(newStyle.release()); + return newBox; +} + +void RenderObject::handleDynamicFloatPositionChange() +{ + // We have gone from not affecting the inline status of the parent flow to suddenly + // having an impact. See if there is a mismatch between the parent flow's + // childrenInline() state and our state. + setInline(style()->isDisplayInlineType()); + if (isInline() != parent()->childrenInline()) { + if (!isInline()) { + if (parent()->isRenderInline()) { + // We have to split the parent flow. + RenderInline* parentInline = static_cast<RenderInline*>(parent()); + RenderBlock* newBox = parentInline->createAnonymousBlock(); + + RenderFlow* oldContinuation = parent()->continuation(); + parentInline->setContinuation(newBox); + + RenderObject* beforeChild = nextSibling(); + parent()->removeChildNode(this); + parentInline->splitFlow(beforeChild, newBox, this, oldContinuation); + } else if (parent()->isRenderBlock()) { + RenderBlock* o = static_cast<RenderBlock*>(parent()); + o->makeChildrenNonInline(); + if (o->isAnonymousBlock() && o->parent()) + o->parent()->removeLeftoverAnonymousBlock(o); + // o may be dead here + } + } else { + // An anonymous block must be made to wrap this inline. + RenderBlock* box = createAnonymousBlock(); + parent()->insertChildNode(box, this); + box->appendChildNode(parent()->removeChildNode(this)); + } + } +} + +void RenderObject::setAnimatableStyle(PassRefPtr<RenderStyle> style) +{ + if (!isText() && style) + setStyle(animation()->updateAnimations(this, style.get())); + else + setStyle(style); +} + +void RenderObject::setStyle(PassRefPtr<RenderStyle> style) +{ + if (m_style == style) + return; + + RenderStyle::Diff diff = RenderStyle::Equal; + if (m_style) + diff = m_style->diff(style.get()); + + // If we have no layer(), just treat a RepaintLayer hint as a normal Repaint. + if (diff == RenderStyle::RepaintLayer && !hasLayer()) + diff = RenderStyle::Repaint; + + styleWillChange(diff, style.get()); + + RefPtr<RenderStyle> oldStyle = m_style.release(); + m_style = style; + + updateFillImages(oldStyle ? oldStyle->backgroundLayers() : 0, m_style ? m_style->backgroundLayers() : 0); + updateFillImages(oldStyle ? oldStyle->maskLayers() : 0, m_style ? m_style->maskLayers() : 0); + + updateImage(oldStyle ? oldStyle->borderImage().image() : 0, m_style ? m_style->borderImage().image() : 0); + updateImage(oldStyle ? oldStyle->maskBoxImage().image() : 0, m_style ? m_style->maskBoxImage().image() : 0); + + styleDidChange(diff, oldStyle.get()); +} + +void RenderObject::setStyleInternal(PassRefPtr<RenderStyle> style) +{ + m_style = style; +} + +void RenderObject::styleWillChange(RenderStyle::Diff diff, const RenderStyle* newStyle) +{ + if (m_style) { + // If our z-index changes value or our visibility changes, + // we need to dirty our stacking context's z-order list. + if (newStyle) { +#if ENABLE(DASHBOARD_SUPPORT) + if (m_style->visibility() != newStyle->visibility() || + m_style->zIndex() != newStyle->zIndex() || + m_style->hasAutoZIndex() != newStyle->hasAutoZIndex()) + document()->setDashboardRegionsDirty(true); +#endif + + if ((m_style->hasAutoZIndex() != newStyle->hasAutoZIndex() || + m_style->zIndex() != newStyle->zIndex() || + m_style->visibility() != newStyle->visibility()) && hasLayer()) { + layer()->dirtyStackingContextZOrderLists(); + if (m_style->hasAutoZIndex() != newStyle->hasAutoZIndex() || + m_style->visibility() != newStyle->visibility()) + layer()->dirtyZOrderLists(); + } + // keep layer hierarchy visibility bits up to date if visibility changes + if (m_style->visibility() != newStyle->visibility()) { + if (RenderLayer* l = enclosingLayer()) { + if (newStyle->visibility() == VISIBLE) + l->setHasVisibleContent(true); + else if (l->hasVisibleContent() && (this == l->renderer() || l->renderer()->style()->visibility() != VISIBLE)) { + l->dirtyVisibleContentStatus(); + if (diff > RenderStyle::RepaintLayer) + repaint(); + } + } + } + } + + // The background of the root element or the body element could propagate up to + // the canvas. Just dirty the entire canvas when our style changes substantially. + if (diff >= RenderStyle::Repaint && element() && + (element()->hasTagName(htmlTag) || element()->hasTagName(bodyTag))) + view()->repaint(); + else if (m_parent && !isText()) { + // Do a repaint with the old style first, e.g., for example if we go from + // having an outline to not having an outline. + if (diff == RenderStyle::RepaintLayer) { + layer()->repaintIncludingDescendants(); + if (!(m_style->clip() == newStyle->clip())) + layer()->clearClipRectsIncludingDescendants(); + } else if (diff == RenderStyle::Repaint || newStyle->outlineSize() < m_style->outlineSize()) + repaint(); + } + + if (diff == RenderStyle::Layout) { + // When a layout hint happens, we go ahead and do a repaint of the layer, since the layer could + // end up being destroyed. + if (hasLayer()) { + if (m_style->position() != newStyle->position() || + m_style->zIndex() != newStyle->zIndex() || + m_style->hasAutoZIndex() != newStyle->hasAutoZIndex() || + !(m_style->clip() == newStyle->clip()) || + m_style->hasClip() != newStyle->hasClip() || + m_style->opacity() != newStyle->opacity() || + m_style->transform() != newStyle->transform()) + layer()->repaintIncludingDescendants(); + } else if (newStyle->hasTransform() || newStyle->opacity() < 1) { + // If we don't have a layer yet, but we are going to get one because of transform or opacity, + // then we need to repaint the old position of the object. + repaint(); + } + } + + // When a layout hint happens and an object's position style changes, we have to do a layout + // to dirty the render tree using the old position value now. + if (diff == RenderStyle::Layout && m_parent && m_style->position() != newStyle->position()) { + markContainingBlocksForLayout(); + if (m_style->position() == StaticPosition) + repaint(); + if (isFloating() && !isPositioned() && (newStyle->position() == AbsolutePosition || newStyle->position() == FixedPosition)) + removeFromObjectLists(); + if (isRenderBlock()) { + if (newStyle->position() == StaticPosition) + // Clear our positioned objects list. Our absolutely positioned descendants will be + // inserted into our containing block's positioned objects list during layout. + removePositionedObjects(0); + else if (m_style->position() == StaticPosition) { + // Remove our absolutely positioned descendants from their current containing block. + // They will be inserted into our positioned objects list during layout. + RenderObject* cb = parent(); + while (cb && (cb->style()->position() == StaticPosition || (cb->isInline() && !cb->isReplaced())) && !cb->isRenderView()) { + if (cb->style()->position() == RelativePosition && cb->isInline() && !cb->isReplaced()) { + cb = cb->containingBlock(); + break; + } + cb = cb->parent(); + } + cb->removePositionedObjects(static_cast<RenderBlock*>(this)); + } + } + } + + if (isFloating() && (m_style->floating() != newStyle->floating())) + // For changes in float styles, we need to conceivably remove ourselves + // from the floating objects list. + removeFromObjectLists(); + else if (isPositioned() && (newStyle->position() != AbsolutePosition && newStyle->position() != FixedPosition)) + // For changes in positioning styles, we need to conceivably remove ourselves + // from the positioned objects list. + removeFromObjectLists(); + + s_affectsParentBlock = isFloatingOrPositioned() && + (!newStyle->isFloating() && newStyle->position() != AbsolutePosition && newStyle->position() != FixedPosition) + && parent() && (parent()->isBlockFlow() || parent()->isInlineFlow()); + + // reset style flags + if (diff == RenderStyle::Layout || diff == RenderStyle::LayoutPositionedMovementOnly) { + m_floating = false; + m_positioned = false; + m_relPositioned = false; + } + m_paintBackground = false; + m_hasOverflowClip = false; + m_hasTransform = false; + m_hasReflection = false; + } else + s_affectsParentBlock = false; + + if (view()->frameView()) { + // FIXME: A better solution would be to only invalidate the fixed regions when scrolling. It's overkill to + // prevent the entire view from blitting on a scroll. + bool newStyleSlowScroll = newStyle && (newStyle->position() == FixedPosition || newStyle->hasFixedBackgroundImage()); + bool oldStyleSlowScroll = m_style && (m_style->position() == FixedPosition || m_style->hasFixedBackgroundImage()); + if (oldStyleSlowScroll != newStyleSlowScroll) { + if (oldStyleSlowScroll) + view()->frameView()->removeSlowRepaintObject(); + if (newStyleSlowScroll) + view()->frameView()->addSlowRepaintObject(); + } + } +} + +void RenderObject::styleDidChange(RenderStyle::Diff diff, const RenderStyle*) +{ + setHasBoxDecorations(m_style->hasBorder() || m_style->hasBackground() || m_style->hasAppearance() || m_style->boxShadow()); + + if (s_affectsParentBlock) + handleDynamicFloatPositionChange(); + + // No need to ever schedule repaints from a style change of a text run, since + // we already did this for the parent of the text run. + // We do have to schedule layouts, though, since a style change can force us to + // need to relayout. + if (diff == RenderStyle::Layout && m_parent) + setNeedsLayoutAndPrefWidthsRecalc(); + else if (diff == RenderStyle::LayoutPositionedMovementOnly && m_parent && !isText()) + setNeedsPositionedMovementLayout(); + else if (m_parent && !isText() && (diff == RenderStyle::RepaintLayer || diff == RenderStyle::Repaint)) + // Do a repaint with the new style now, e.g., for example if we go from + // not having an outline to having an outline. + repaint(); +} + +void RenderObject::updateFillImages(const FillLayer* oldLayers, const FillLayer* newLayers) +{ + // FIXME: This will be slow when a large number of images is used. Fix by using a dict. + for (const FillLayer* currOld = oldLayers; currOld; currOld = currOld->next()) { + if (currOld->image() && (!newLayers || !newLayers->containsImage(currOld->image()))) + currOld->image()->removeClient(this); + } + for (const FillLayer* currNew = newLayers; currNew; currNew = currNew->next()) { + if (currNew->image() && (!oldLayers || !oldLayers->containsImage(currNew->image()))) + currNew->image()->addClient(this); + } +} + +void RenderObject::updateImage(StyleImage* oldImage, StyleImage* newImage) +{ + if (oldImage != newImage) { + if (oldImage) + oldImage->removeClient(this); + if (newImage) + newImage->addClient(this); + } +} + +IntRect RenderObject::viewRect() const +{ + return view()->viewRect(); +} + +FloatPoint RenderObject::localToAbsolute(FloatPoint localPoint, bool fixed, bool useTransforms) const +{ + RenderObject* o = parent(); + if (o) { + localPoint.move(0.0f, static_cast<float>(o->borderTopExtra())); + if (o->hasOverflowClip()) + localPoint -= o->layer()->scrolledContentOffset(); + return o->localToAbsolute(localPoint, fixed, useTransforms); + } + + return FloatPoint(); +} + +FloatPoint RenderObject::absoluteToLocal(FloatPoint containerPoint, bool fixed, bool useTransforms) const +{ + RenderObject* o = parent(); + if (o) { + FloatPoint localPoint = o->absoluteToLocal(containerPoint, fixed, useTransforms); + localPoint.move(0.0f, -static_cast<float>(o->borderTopExtra())); + if (o->hasOverflowClip()) + localPoint += o->layer()->scrolledContentOffset(); + return localPoint; + } + return FloatPoint(); +} + +FloatQuad RenderObject::localToAbsoluteQuad(const FloatQuad& localQuad, bool fixed) const +{ + RenderObject* o = parent(); + if (o) { + FloatQuad quad = localQuad; + quad.move(0.0f, static_cast<float>(o->borderTopExtra())); + if (o->hasOverflowClip()) + quad -= o->layer()->scrolledContentOffset(); + return o->localToAbsoluteQuad(quad, fixed); + } + + return FloatQuad(); +} + +IntSize RenderObject::offsetFromContainer(RenderObject* o) const +{ + ASSERT(o == container()); + + IntSize offset; + offset.expand(0, o->borderTopExtra()); + + if (o->hasOverflowClip()) + offset -= o->layer()->scrolledContentOffset(); + + return offset; +} + +IntRect RenderObject::localCaretRect(InlineBox*, int, int* extraWidthToEndOfLine) +{ + if (extraWidthToEndOfLine) + *extraWidthToEndOfLine = 0; + + return IntRect(); +} + +int RenderObject::paddingTop() const +{ + int w = 0; + Length padding = m_style->paddingTop(); + if (padding.isPercent()) + w = containingBlock()->availableWidth(); + return padding.calcMinValue(w); +} + +int RenderObject::paddingBottom() const +{ + int w = 0; + Length padding = style()->paddingBottom(); + if (padding.isPercent()) + w = containingBlock()->availableWidth(); + return padding.calcMinValue(w); +} + +int RenderObject::paddingLeft() const +{ + int w = 0; + Length padding = style()->paddingLeft(); + if (padding.isPercent()) + w = containingBlock()->availableWidth(); + return padding.calcMinValue(w); +} + +int RenderObject::paddingRight() const +{ + int w = 0; + Length padding = style()->paddingRight(); + if (padding.isPercent()) + w = containingBlock()->availableWidth(); + return padding.calcMinValue(w); +} + +RenderView* RenderObject::view() const +{ + return static_cast<RenderView*>(document()->renderer()); +} + +bool RenderObject::hasOutlineAnnotation() const +{ + return element() && element()->isLink() && document()->printing(); +} + +RenderObject* RenderObject::container() const +{ + // This method is extremely similar to containingBlock(), but with a few notable + // exceptions. + // (1) It can be used on orphaned subtrees, i.e., it can be called safely even when + // the object is not part of the primary document subtree yet. + // (2) For normal flow elements, it just returns the parent. + // (3) For absolute positioned elements, it will return a relative positioned inline. + // containingBlock() simply skips relpositioned inlines and lets an enclosing block handle + // the layout of the positioned object. This does mean that calcAbsoluteHorizontal and + // calcAbsoluteVertical have to use container(). + RenderObject* o = parent(); + + if (isText()) + return o; + + EPosition pos = m_style->position(); + if (pos == FixedPosition) { + // container() can be called on an object that is not in the + // tree yet. We don't call view() since it will assert if it + // can't get back to the canvas. Instead we just walk as high up + // as we can. If we're in the tree, we'll get the root. If we + // aren't we'll get the root of our little subtree (most likely + // we'll just return 0). + while (o && o->parent() && !(o->hasTransform() && o->isRenderBlock())) + o = o->parent(); + } else if (pos == AbsolutePosition) { + // Same goes here. We technically just want our containing block, but + // we may not have one if we're part of an uninstalled subtree. We'll + // climb as high as we can though. + while (o && o->style()->position() == StaticPosition && !o->isRenderView() && !(o->hasTransform() && o->isRenderBlock())) + o = o->parent(); + } + + return o; +} + +// This code has been written to anticipate the addition of CSS3-::outside and ::inside generated +// content (and perhaps XBL). That's why it uses the render tree and not the DOM tree. +RenderObject* RenderObject::hoverAncestor() const +{ + return (!isInline() && continuation()) ? continuation() : parent(); +} + +bool RenderObject::isSelectionBorder() const +{ + SelectionState st = selectionState(); + return st == SelectionStart || st == SelectionEnd || st == SelectionBoth; +} + +void RenderObject::removeFromObjectLists() +{ + if (documentBeingDestroyed()) + return; + + if (isFloating()) { + RenderBlock* outermostBlock = containingBlock(); + for (RenderBlock* p = outermostBlock; p && !p->isRenderView(); p = p->containingBlock()) { + if (p->containsFloat(this)) + outermostBlock = p; + } + + if (outermostBlock) + outermostBlock->markAllDescendantsWithFloatsForLayout(this); + } + + if (isPositioned()) { + RenderObject* p; + for (p = parent(); p; p = p->parent()) { + if (p->isRenderBlock()) + static_cast<RenderBlock*>(p)->removePositionedObject(this); + } + } +} + +bool RenderObject::documentBeingDestroyed() const +{ + return !document()->renderer(); +} + +void RenderObject::destroy() +{ + // If this renderer is being autoscrolled, stop the autoscroll timer + if (document()->frame() && document()->frame()->eventHandler()->autoscrollRenderer() == this) + document()->frame()->eventHandler()->stopAutoscrollTimer(true); + + if (m_hasCounterNodeMap) + RenderCounter::destroyCounterNodes(this); + + if (AXObjectCache::accessibilityEnabled()) + document()->axObjectCache()->remove(this); + + animation()->cancelAnimations(this); + + // By default no ref-counting. RenderWidget::destroy() doesn't call + // this function because it needs to do ref-counting. If anything + // in this function changes, be sure to fix RenderWidget::destroy() as well. + + remove(); + + RenderArena* arena = renderArena(); + + if (hasLayer()) + layer()->destroy(arena); + + arenaDelete(arena, this); +} + +void RenderObject::arenaDelete(RenderArena* arena, void* base) +{ + if (m_style) { + for (const FillLayer* bgLayer = m_style->backgroundLayers(); bgLayer; bgLayer = bgLayer->next()) { + if (StyleImage* backgroundImage = bgLayer->image()) + backgroundImage->removeClient(this); + } + + for (const FillLayer* maskLayer = m_style->maskLayers(); maskLayer; maskLayer = maskLayer->next()) { + if (StyleImage* maskImage = maskLayer->image()) + maskImage->removeClient(this); + } + + if (StyleImage* borderImage = m_style->borderImage().image()) + borderImage->removeClient(this); + + if (StyleImage* maskBoxImage = m_style->maskBoxImage().image()) + maskBoxImage->removeClient(this); + } + +#ifndef NDEBUG + void* savedBase = baseOfRenderObjectBeingDeleted; + baseOfRenderObjectBeingDeleted = base; +#endif + delete this; +#ifndef NDEBUG + baseOfRenderObjectBeingDeleted = savedBase; +#endif + + // Recover the size left there for us by operator delete and free the memory. + arena->free(*(size_t*)base, base); +} + +VisiblePosition RenderObject::positionForCoordinates(int, int) +{ + return VisiblePosition(element(), caretMinOffset(), DOWNSTREAM); +} + +void RenderObject::updateDragState(bool dragOn) +{ + bool valueChanged = (dragOn != m_isDragging); + m_isDragging = dragOn; + if (valueChanged && style()->affectedByDragRules()) + element()->setChanged(); + for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) + curr->updateDragState(dragOn); + if (continuation()) + continuation()->updateDragState(dragOn); +} + +bool RenderObject::hitTest(const HitTestRequest& request, HitTestResult& result, const IntPoint& point, int tx, int ty, HitTestFilter hitTestFilter) +{ + bool inside = false; + if (hitTestFilter != HitTestSelf) { + // First test the foreground layer (lines and inlines). + inside = nodeAtPoint(request, result, point.x(), point.y(), tx, ty, HitTestForeground); + + // Test floats next. + if (!inside) + inside = nodeAtPoint(request, result, point.x(), point.y(), tx, ty, HitTestFloat); + + // Finally test to see if the mouse is in the background (within a child block's background). + if (!inside) + inside = nodeAtPoint(request, result, point.x(), point.y(), tx, ty, HitTestChildBlockBackgrounds); + } + + // See if the mouse is inside us but not any of our descendants + if (hitTestFilter != HitTestDescendants && !inside) + inside = nodeAtPoint(request, result, point.x(), point.y(), tx, ty, HitTestBlockBackground); + + return inside; +} + +void RenderObject::updateHitTestResult(HitTestResult& result, const IntPoint& point) +{ + if (result.innerNode()) + return; + + Node* node = element(); + IntPoint localPoint(point); + if (isRenderView()) + node = document()->documentElement(); + else if (!isInline() && continuation()) + // We are in the margins of block elements that are part of a continuation. In + // this case we're actually still inside the enclosing inline element that was + // split. Go ahead and set our inner node accordingly. + node = continuation()->element(); + + if (node) { + if (node->renderer() && node->renderer()->continuation() && node->renderer() != this) { + // We're in the continuation of a split inline. Adjust our local point to be in the coordinate space + // of the principal renderer's containing block. This will end up being the innerNonSharedNode. + RenderObject* firstBlock = node->renderer()->containingBlock(); + + // Get our containing block. + RenderObject* block = this; + if (isInline()) + block = containingBlock(); + + localPoint.move(block->xPos() - firstBlock->xPos(), block->yPos() - firstBlock->yPos()); + } + + result.setInnerNode(node); + if (!result.innerNonSharedNode()) + result.setInnerNonSharedNode(node); + result.setLocalPoint(localPoint); + } +} + +bool RenderObject::nodeAtPoint(const HitTestRequest&, HitTestResult&, int /*x*/, int /*y*/, int /*tx*/, int /*ty*/, HitTestAction) +{ + return false; +} + +int RenderObject::verticalPositionHint(bool firstLine) const +{ + if (firstLine) // We're only really a first-line style if the document actually uses first-line rules. + firstLine = document()->usesFirstLineRules(); + int vpos = m_verticalPosition; + if (m_verticalPosition == PositionUndefined || firstLine) { + vpos = getVerticalPosition(firstLine); + if (!firstLine) + m_verticalPosition = vpos; + } + + return vpos; +} + +int RenderObject::getVerticalPosition(bool firstLine) const +{ + if (!isInline()) + return 0; + + // This method determines the vertical position for inline elements. + int vpos = 0; + EVerticalAlign va = style()->verticalAlign(); + if (va == TOP) + vpos = PositionTop; + else if (va == BOTTOM) + vpos = PositionBottom; + else { + bool checkParent = parent()->isInline() && !parent()->isInlineBlockOrInlineTable() && parent()->style()->verticalAlign() != TOP && parent()->style()->verticalAlign() != BOTTOM; + vpos = checkParent ? parent()->verticalPositionHint(firstLine) : 0; + // don't allow elements nested inside text-top to have a different valignment. + if (va == BASELINE) + return vpos; + + const Font& f = parent()->style(firstLine)->font(); + int fontsize = f.pixelSize(); + + if (va == SUB) + vpos += fontsize / 5 + 1; + else if (va == SUPER) + vpos -= fontsize / 3 + 1; + else if (va == TEXT_TOP) + vpos += baselinePosition(firstLine) - f.ascent(); + else if (va == MIDDLE) + vpos += -static_cast<int>(f.xHeight() / 2) - lineHeight(firstLine) / 2 + baselinePosition(firstLine); + else if (va == TEXT_BOTTOM) { + vpos += f.descent(); + if (!isReplaced()) + vpos -= style(firstLine)->font().descent(); + } else if (va == BASELINE_MIDDLE) + vpos += -lineHeight(firstLine) / 2 + baselinePosition(firstLine); + else if (va == LENGTH) + vpos -= style()->verticalAlignLength().calcValue(lineHeight(firstLine)); + } + + return vpos; +} + +int RenderObject::lineHeight(bool firstLine, bool /*isRootLineBox*/) const +{ + RenderStyle* s = style(firstLine); + + Length lh = s->lineHeight(); + + // its "unset", choose nice default + if (lh.isNegative()) + return s->font().lineSpacing(); + + if (lh.isPercent()) + return lh.calcMinValue(s->fontSize()); + + // its fixed + return lh.value(); +} + +int RenderObject::baselinePosition(bool firstLine, bool isRootLineBox) const +{ + const Font& f = style(firstLine)->font(); + return f.ascent() + (lineHeight(firstLine, isRootLineBox) - f.height()) / 2; +} + +void RenderObject::scheduleRelayout() +{ + if (isRenderView()) { + FrameView* view = static_cast<RenderView*>(this)->frameView(); + if (view) + view->scheduleRelayout(); + } else if (parent()) { + FrameView* v = view() ? view()->frameView() : 0; + if (v) + v->scheduleRelayoutOfSubtree(this); + } +} + +void RenderObject::removeLeftoverAnonymousBlock(RenderBlock*) +{ +} + +InlineBox* RenderObject::createInlineBox(bool, bool isRootLineBox, bool) +{ + ASSERT(!isRootLineBox); + return new (renderArena()) InlineBox(this); +} + +void RenderObject::dirtyLineBoxes(bool, bool) +{ +} + +InlineBox* RenderObject::inlineBoxWrapper() const +{ + return 0; +} + +void RenderObject::setInlineBoxWrapper(InlineBox*) +{ +} + +void RenderObject::deleteLineBoxWrapper() +{ +} + +RenderStyle* RenderObject::firstLineStyle() const +{ + if (!document()->usesFirstLineRules()) + return m_style.get(); + + RenderStyle* s = m_style.get(); + const RenderObject* obj = isText() ? parent() : this; + if (obj->isBlockFlow()) { + RenderBlock* firstLineBlock = obj->firstLineBlock(); + if (firstLineBlock) + s = firstLineBlock->getCachedPseudoStyle(RenderStyle::FIRST_LINE, style()); + } else if (!obj->isAnonymous() && obj->isInlineFlow()) { + RenderStyle* parentStyle = obj->parent()->firstLineStyle(); + if (parentStyle != obj->parent()->style()) { + // A first-line style is in effect. We need to cache a first-line style + // for ourselves. + style()->setHasPseudoStyle(RenderStyle::FIRST_LINE_INHERITED); + s = obj->getCachedPseudoStyle(RenderStyle::FIRST_LINE_INHERITED, parentStyle); + } + } + return s; +} + +RenderStyle* RenderObject::getCachedPseudoStyle(RenderStyle::PseudoId pseudo, RenderStyle* parentStyle) const +{ + if (pseudo < RenderStyle::FIRST_INTERNAL_PSEUDOID && !style()->hasPseudoStyle(pseudo)) + return 0; + + RenderStyle* cachedStyle = style()->getCachedPseudoStyle(pseudo); + if (cachedStyle) + return cachedStyle; + + RefPtr<RenderStyle> result = getUncachedPseudoStyle(pseudo, parentStyle); + if (result) + return style()->addCachedPseudoStyle(result.release()); + return 0; +} + +PassRefPtr<RenderStyle> RenderObject::getUncachedPseudoStyle(RenderStyle::PseudoId pseudo, RenderStyle* parentStyle) const +{ + if (pseudo < RenderStyle::FIRST_INTERNAL_PSEUDOID && !style()->hasPseudoStyle(pseudo)) + return 0; + + if (!parentStyle) + parentStyle = style(); + + Node* node = element(); + while (node && !node->isElementNode()) + node = node->parentNode(); + if (!node) + return 0; + + RefPtr<RenderStyle> result; + if (pseudo == RenderStyle::FIRST_LINE_INHERITED) { + result = document()->styleSelector()->styleForElement(static_cast<Element*>(node), parentStyle, false); + result->setStyleType(RenderStyle::FIRST_LINE_INHERITED); + } else + result = document()->styleSelector()->pseudoStyleForElement(pseudo, static_cast<Element*>(node), parentStyle); + return result.release(); +} + +static Color decorationColor(RenderStyle* style) +{ + Color result; + if (style->textStrokeWidth() > 0) { + // Prefer stroke color if possible but not if it's fully transparent. + result = style->textStrokeColor(); + if (!result.isValid()) + result = style->color(); + if (result.alpha()) + return result; + } + + result = style->textFillColor(); + if (!result.isValid()) + result = style->color(); + return result; +} + +void RenderObject::getTextDecorationColors(int decorations, Color& underline, Color& overline, + Color& linethrough, bool quirksMode) +{ + RenderObject* curr = this; + do { + int currDecs = curr->style()->textDecoration(); + if (currDecs) { + if (currDecs & UNDERLINE) { + decorations &= ~UNDERLINE; + underline = decorationColor(curr->style()); + } + if (currDecs & OVERLINE) { + decorations &= ~OVERLINE; + overline = decorationColor(curr->style()); + } + if (currDecs & LINE_THROUGH) { + decorations &= ~LINE_THROUGH; + linethrough = decorationColor(curr->style()); + } + } + curr = curr->parent(); + if (curr && curr->isRenderBlock() && curr->continuation()) + curr = curr->continuation(); + } while (curr && decorations && (!quirksMode || !curr->element() || + (!curr->element()->hasTagName(aTag) && !curr->element()->hasTagName(fontTag)))); + + // If we bailed out, use the element we bailed out at (typically a <font> or <a> element). + if (decorations && curr) { + if (decorations & UNDERLINE) + underline = decorationColor(curr->style()); + if (decorations & OVERLINE) + overline = decorationColor(curr->style()); + if (decorations & LINE_THROUGH) + linethrough = decorationColor(curr->style()); + } +} + +void RenderObject::updateWidgetPosition() +{ +} + +#if ENABLE(DASHBOARD_SUPPORT) +void RenderObject::addDashboardRegions(Vector<DashboardRegionValue>& regions) +{ + // Convert the style regions to absolute coordinates. + if (style()->visibility() != VISIBLE) + return; + + const Vector<StyleDashboardRegion>& styleRegions = style()->dashboardRegions(); + unsigned i, count = styleRegions.size(); + for (i = 0; i < count; i++) { + StyleDashboardRegion styleRegion = styleRegions[i]; + + int w = width(); + int h = height(); + + DashboardRegionValue region; + region.label = styleRegion.label; + region.bounds = IntRect(styleRegion.offset.left().value(), + styleRegion.offset.top().value(), + w - styleRegion.offset.left().value() - styleRegion.offset.right().value(), + h - styleRegion.offset.top().value() - styleRegion.offset.bottom().value()); + region.type = styleRegion.type; + + region.clip = region.bounds; + computeAbsoluteRepaintRect(region.clip); + if (region.clip.height() < 0) { + region.clip.setHeight(0); + region.clip.setWidth(0); + } + + FloatPoint absPos = localToAbsolute(); + region.bounds.setX(absPos.x() + styleRegion.offset.left().value()); + region.bounds.setY(absPos.y() + styleRegion.offset.top().value()); + + if (document()->frame()) { + float pageScaleFactor = document()->frame()->page()->chrome()->scaleFactor(); + if (pageScaleFactor != 1.0f) { + region.bounds.scale(pageScaleFactor); + region.clip.scale(pageScaleFactor); + } + } + + regions.append(region); + } +} + +void RenderObject::collectDashboardRegions(Vector<DashboardRegionValue>& regions) +{ + // RenderTexts don't have their own style, they just use their parent's style, + // so we don't want to include them. + if (isText()) + return; + + addDashboardRegions(regions); + for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) + curr->collectDashboardRegions(regions); +} +#endif + +bool RenderObject::avoidsFloats() const +{ + return isReplaced() || hasOverflowClip() || isHR(); +} + +bool RenderObject::shrinkToAvoidFloats() const +{ + // FIXME: Technically we should be able to shrink replaced elements on a line, but this is difficult to accomplish, since this + // involves doing a relayout during findNextLineBreak and somehow overriding the containingBlockWidth method to return the + // current remaining width on a line. + if (isInline() && !isHTMLMarquee() || !avoidsFloats()) + return false; + + // All auto-width objects that avoid floats should always use lineWidth. + return style()->width().isAuto(); +} + +UChar RenderObject::backslashAsCurrencySymbol() const +{ + if (Node *node = element()) { + if (TextResourceDecoder* decoder = node->document()->decoder()) + return decoder->encoding().backslashAsCurrencySymbol(); + } + return '\\'; +} + +bool RenderObject::willRenderImage(CachedImage*) +{ + // Without visibility we won't render (and therefore don't care about animation). + if (style()->visibility() != VISIBLE) + return false; + + // If we're not in a window (i.e., we're dormant from being put in the b/f cache or in a background tab) + // then we don't want to render either. + return !document()->inPageCache() && !document()->view()->isOffscreen(); +} + +int RenderObject::maximalOutlineSize(PaintPhase p) const +{ + if (p != PaintPhaseOutline && p != PaintPhaseSelfOutline && p != PaintPhaseChildOutlines) + return 0; + return static_cast<RenderView*>(document()->renderer())->maximalOutlineSize(); +} + +int RenderObject::caretMinOffset() const +{ + return 0; +} + +int RenderObject::caretMaxOffset() const +{ + if (isReplaced()) + return element() ? max(1U, element()->childNodeCount()) : 1; + if (isHR()) + return 1; + return 0; +} + +unsigned RenderObject::caretMaxRenderedOffset() const +{ + return 0; +} + +int RenderObject::previousOffset(int current) const +{ + return current - 1; +} + +int RenderObject::nextOffset(int current) const +{ + return current + 1; +} + +int RenderObject::maxTopMargin(bool positive) const +{ + return positive ? max(0, marginTop()) : -min(0, marginTop()); +} + +int RenderObject::maxBottomMargin(bool positive) const +{ + return positive ? max(0, marginBottom()) : -min(0, marginBottom()); +} + +IntRect RenderObject::contentBox() const +{ + return IntRect(borderLeft() + paddingLeft(), borderTop() + paddingTop(), + contentWidth(), contentHeight()); +} + +IntRect RenderObject::absoluteContentBox() const +{ + IntRect rect = contentBox(); + FloatPoint absPos = localToAbsoluteForContent(FloatPoint()); + rect.move(absPos.x(), absPos.y()); + return rect; +} + +FloatQuad RenderObject::absoluteContentQuad() const +{ + IntRect rect = contentBox(); + return localToAbsoluteQuad(FloatRect(rect)); +} + +void RenderObject::adjustRectForOutlineAndShadow(IntRect& rect) const +{ + int outlineSize = !isInline() && continuation() ? continuation()->style()->outlineSize() : style()->outlineSize(); + if (ShadowData* boxShadow = style()->boxShadow()) { + int shadowLeft = 0; + int shadowRight = 0; + int shadowTop = 0; + int shadowBottom = 0; + + do { + shadowLeft = min(boxShadow->x - boxShadow->blur - outlineSize, shadowLeft); + shadowRight = max(boxShadow->x + boxShadow->blur + outlineSize, shadowRight); + shadowTop = min(boxShadow->y - boxShadow->blur - outlineSize, shadowTop); + shadowBottom = max(boxShadow->y + boxShadow->blur + outlineSize, shadowBottom); + + boxShadow = boxShadow->next; + } while (boxShadow); + + rect.move(shadowLeft, shadowTop); + rect.setWidth(rect.width() - shadowLeft + shadowRight); + rect.setHeight(rect.height() - shadowTop + shadowBottom); + } else + rect.inflate(outlineSize); +} + +IntRect RenderObject::absoluteOutlineBounds() const +{ + IntRect box = borderBox(); + adjustRectForOutlineAndShadow(box); + + FloatQuad absOutlineQuad = localToAbsoluteQuad(FloatRect(box)); + box = absOutlineQuad.enclosingBoundingBox(); + box.move(view()->layoutDelta()); + + return box; +} + +bool RenderObject::isScrollable() const +{ + RenderLayer* l = enclosingLayer(); + return l && (l->verticalScrollbar() || l->horizontalScrollbar()); +} + +AnimationController* RenderObject::animation() const +{ + return document()->frame()->animation(); +} + +void RenderObject::imageChanged(CachedImage* image, const IntRect* rect) +{ + imageChanged(static_cast<WrappedImagePtr>(image), rect); +} + +IntRect RenderObject::reflectionBox() const +{ + IntRect result; + if (!m_style->boxReflect()) + return result; + IntRect box = borderBox(); + result = box; + switch (m_style->boxReflect()->direction()) { + case ReflectionBelow: + result.move(0, box.height() + reflectionOffset()); + break; + case ReflectionAbove: + result.move(0, -box.height() - reflectionOffset()); + break; + case ReflectionLeft: + result.move(-box.width() - reflectionOffset(), 0); + break; + case ReflectionRight: + result.move(box.width() + reflectionOffset(), 0); + break; + } + return result; +} + +int RenderObject::reflectionOffset() const +{ + if (!m_style->boxReflect()) + return 0; + if (m_style->boxReflect()->direction() == ReflectionLeft || m_style->boxReflect()->direction() == ReflectionRight) + return m_style->boxReflect()->offset().calcValue(borderBox().width()); + return m_style->boxReflect()->offset().calcValue(borderBox().height()); +} + +IntRect RenderObject::reflectedRect(const IntRect& r) const +{ + if (!m_style->boxReflect()) + return IntRect(); + + IntRect box = borderBox(); + IntRect result = r; + switch (m_style->boxReflect()->direction()) { + case ReflectionBelow: + result.setY(box.bottom() + reflectionOffset() + (box.bottom() - r.bottom())); + break; + case ReflectionAbove: + result.setY(box.y() - reflectionOffset() - box.height() + (box.bottom() - r.bottom())); + break; + case ReflectionLeft: + result.setX(box.x() - reflectionOffset() - box.width() + (box.right() - r.right())); + break; + case ReflectionRight: + result.setX(box.right() + reflectionOffset() + (box.right() - r.right())); + break; + } + return result; +} + +#if ENABLE(SVG) + +FloatRect RenderObject::relativeBBox(bool) const +{ + return FloatRect(); +} + +TransformationMatrix RenderObject::localTransform() const +{ + return TransformationMatrix(1, 0, 0, 1, xPos(), yPos()); +} + +TransformationMatrix RenderObject::absoluteTransform() const +{ + if (parent()) + return localTransform() * parent()->absoluteTransform(); + return localTransform(); +} + +#endif // ENABLE(SVG) + +} // namespace WebCore + +#ifndef NDEBUG + +void showTree(const WebCore::RenderObject* ro) +{ + if (ro) + ro->showTreeForThis(); +} + +#endif diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderObject.h b/src/3rdparty/webkit/WebCore/rendering/RenderObject.h new file mode 100644 index 0000000..70841b7 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderObject.h @@ -0,0 +1,991 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * (C) 2004 Allan Sandfeld Jensen (kde@carewolf.com) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef RenderObject_h +#define RenderObject_h + +#include "CachedResourceClient.h" +#include "Document.h" +#include "FloatQuad.h" +#include "RenderStyle.h" +#include "ScrollTypes.h" +#include "VisiblePosition.h" +#include <wtf/HashMap.h> + +namespace WebCore { + +class TransformationMatrix; +class AnimationController; +class Color; +class Document; +class Element; +class Event; +class FloatRect; +class FrameView; +class HTMLAreaElement; +class HitTestResult; +class InlineBox; +class InlineFlowBox; +class Position; +class RenderArena; +class RenderBlock; +class RenderFlow; +class RenderFrameSet; +class RenderLayer; +class RenderTable; +class RenderText; +class RenderView; +class String; + +struct HitTestRequest; + +/* + * The painting of a layer occurs in three distinct phases. Each phase involves + * a recursive descent into the layer's render objects. The first phase is the background phase. + * The backgrounds and borders of all blocks are painted. Inlines are not painted at all. + * Floats must paint above block backgrounds but entirely below inline content that can overlap them. + * In the foreground phase, all inlines are fully painted. Inline replaced elements will get all + * three phases invoked on them during this phase. + */ + +enum PaintPhase { + PaintPhaseBlockBackground, + PaintPhaseChildBlockBackground, + PaintPhaseChildBlockBackgrounds, + PaintPhaseFloat, + PaintPhaseForeground, + PaintPhaseOutline, + PaintPhaseChildOutlines, + PaintPhaseSelfOutline, + PaintPhaseSelection, + PaintPhaseCollapsedTableBorders, + PaintPhaseTextClip, + PaintPhaseMask +}; + +enum PaintRestriction { + PaintRestrictionNone, + PaintRestrictionSelectionOnly, + PaintRestrictionSelectionOnlyBlackText +}; + +enum HitTestFilter { + HitTestAll, + HitTestSelf, + HitTestDescendants +}; + +enum HitTestAction { + HitTestBlockBackground, + HitTestChildBlockBackground, + HitTestChildBlockBackgrounds, + HitTestFloat, + HitTestForeground +}; + +enum VerticalPositionHint { + PositionTop = -0x7fffffff, + PositionBottom = 0x7fffffff, + PositionUndefined = static_cast<int>(0x80000000) +}; + +#if ENABLE(DASHBOARD_SUPPORT) +struct DashboardRegionValue { + bool operator==(const DashboardRegionValue& o) const + { + return type == o.type && bounds == o.bounds && clip == o.clip && label == o.label; + } + bool operator!=(const DashboardRegionValue& o) const + { + return !(*this == o); + } + + String label; + IntRect bounds; + IntRect clip; + int type; +}; +#endif + +// FIXME: This should be a HashSequencedSet, but we don't have that data structure yet. +// This means the paint order of outlines will be wrong, although this is a minor issue. +typedef HashSet<RenderFlow*> RenderFlowSequencedSet; + +// Base class for all rendering tree objects. +class RenderObject : public CachedResourceClient { + friend class RenderContainer; + friend class RenderSVGContainer; + friend class RenderLayer; +public: + // Anonymous objects should pass the document as their node, and they will then automatically be + // marked as anonymous in the constructor. + RenderObject(Node*); + virtual ~RenderObject(); + + virtual const char* renderName() const { return "RenderObject"; } + + RenderObject* parent() const { return m_parent; } + bool isDescendantOf(const RenderObject*) const; + + RenderObject* previousSibling() const { return m_previous; } + RenderObject* nextSibling() const { return m_next; } + + virtual RenderObject* firstChild() const { return 0; } + virtual RenderObject* lastChild() const { return 0; } + + RenderObject* nextInPreOrder() const; + RenderObject* nextInPreOrder(RenderObject* stayWithin) const; + RenderObject* nextInPreOrderAfterChildren() const; + RenderObject* nextInPreOrderAfterChildren(RenderObject* stayWithin) const; + RenderObject* previousInPreOrder() const; + RenderObject* childAt(unsigned) const; + + RenderObject* firstLeafChild() const; + RenderObject* lastLeafChild() const; + + virtual RenderLayer* layer() const { return 0; } + RenderLayer* enclosingLayer() const; + void addLayers(RenderLayer* parentLayer, RenderObject* newObject); + void removeLayers(RenderLayer* parentLayer); + void moveLayers(RenderLayer* oldParent, RenderLayer* newParent); + RenderLayer* findNextLayer(RenderLayer* parentLayer, RenderObject* startPoint, bool checkParent = true); + virtual void positionChildLayers() { } + virtual bool requiresLayer(); + + virtual IntRect getOverflowClipRect(int /*tx*/, int /*ty*/) { return IntRect(0, 0, 0, 0); } + virtual IntRect getClipRect(int /*tx*/, int /*ty*/) { return IntRect(0, 0, 0, 0); } + bool hasClip() { return isPositioned() && style()->hasClip(); } + + virtual int getBaselineOfFirstLineBox() const { return -1; } + virtual int getBaselineOfLastLineBox() const { return -1; } + + virtual bool isEmpty() const { return firstChild() == 0; } + + virtual bool isEdited() const { return false; } + virtual void setEdited(bool) { } + +#ifndef NDEBUG + void setHasAXObject(bool flag) { m_hasAXObject = flag; } + bool hasAXObject() const { return m_hasAXObject; } +#endif + + // Obtains the nearest enclosing block (including this block) that contributes a first-line style to our inline + // children. + virtual RenderBlock* firstLineBlock() const; + + // Called when an object that was floating or positioned becomes a normal flow object + // again. We have to make sure the render tree updates as needed to accommodate the new + // normal flow object. + void handleDynamicFloatPositionChange(); + + // This function is a convenience helper for creating an anonymous block that inherits its + // style from this RenderObject. + RenderBlock* createAnonymousBlock(); + + // Whether or not a positioned element requires normal flow x/y to be computed + // to determine its position. + bool hasStaticX() const; + bool hasStaticY() const; + virtual void setStaticX(int /*staticX*/) { } + virtual void setStaticY(int /*staticY*/) { } + virtual int staticX() const { return 0; } + virtual int staticY() const { return 0; } + + // RenderObject tree manipulation + ////////////////////////////////////////// + virtual bool canHaveChildren() const; + virtual bool isChildAllowed(RenderObject*, RenderStyle*) const { return true; } + virtual void addChild(RenderObject* newChild, RenderObject* beforeChild = 0); + virtual void removeChild(RenderObject*); + virtual bool createsAnonymousWrapper() const { return false; } + + // raw tree manipulation + virtual RenderObject* removeChildNode(RenderObject*, bool fullRemove = true); + virtual void appendChildNode(RenderObject*, bool fullAppend = true); + virtual void insertChildNode(RenderObject* child, RenderObject* before, bool fullInsert = true); + // Designed for speed. Don't waste time doing a bunch of work like layer updating and repainting when we know that our + // change in parentage is not going to affect anything. + virtual void moveChildNode(RenderObject*); + ////////////////////////////////////////// + +protected: + ////////////////////////////////////////// + // Helper functions. Dangerous to use! + void setPreviousSibling(RenderObject* previous) { m_previous = previous; } + void setNextSibling(RenderObject* next) { m_next = next; } + void setParent(RenderObject* parent) { m_parent = parent; } + ////////////////////////////////////////// +private: + void addAbsoluteRectForLayer(IntRect& result); + +public: +#ifndef NDEBUG + void showTreeForThis() const; +#endif + + static RenderObject* createObject(Node*, RenderStyle*); + + // Overloaded new operator. Derived classes must override operator new + // in order to allocate out of the RenderArena. + void* operator new(size_t, RenderArena*) throw(); + + // Overridden to prevent the normal delete from being called. + void operator delete(void*, size_t); + +private: + // The normal operator new is disallowed on all render objects. + void* operator new(size_t) throw(); + +public: + RenderArena* renderArena() const { return document()->renderArena(); } + + virtual bool isApplet() const { return false; } + virtual bool isBR() const { return false; } + virtual bool isBlockFlow() const { return false; } + virtual bool isCounter() const { return false; } + virtual bool isFieldset() const { return false; } + virtual bool isFrame() const { return false; } + virtual bool isFrameSet() const { return false; } + virtual bool isImage() const { return false; } + virtual bool isInlineBlockOrInlineTable() const { return false; } + virtual bool isInlineContinuation() const; + virtual bool isInlineFlow() const { return false; } + virtual bool isListBox() const { return false; } + virtual bool isListItem() const { return false; } + virtual bool isListMarker() const { return false; } + virtual bool isMedia() const { return false; } + virtual bool isMenuList() const { return false; } + virtual bool isRenderBlock() const { return false; } + virtual bool isRenderImage() const { return false; } + virtual bool isRenderInline() const { return false; } + virtual bool isRenderPart() const { return false; } + virtual bool isRenderView() const { return false; } + virtual bool isSlider() const { return false; } + virtual bool isTable() const { return false; } + virtual bool isTableCell() const { return false; } + virtual bool isTableCol() const { return false; } + virtual bool isTableRow() const { return false; } + virtual bool isTableSection() const { return false; } + virtual bool isTextArea() const { return false; } + virtual bool isTextField() const { return false; } + virtual bool isWidget() const { return false; } + + + bool isRoot() const { return document()->documentElement() == node(); } + bool isBody() const; + bool isHR() const; + + bool isHTMLMarquee() const; + + virtual bool childrenInline() const { return false; } + virtual void setChildrenInline(bool) { } + + virtual RenderFlow* continuation() const; + +#if ENABLE(SVG) + virtual bool isSVGRoot() const { return false; } + virtual bool isSVGContainer() const { return false; } + virtual bool isSVGHiddenContainer() const { return false; } + virtual bool isRenderPath() const { return false; } + virtual bool isSVGText() const { return false; } + + virtual FloatRect relativeBBox(bool includeStroke = true) const; + + virtual TransformationMatrix localTransform() const; + virtual TransformationMatrix absoluteTransform() const; +#endif + + virtual bool isEditable() const; + + bool isAnonymous() const { return m_isAnonymous; } + void setIsAnonymous(bool b) { m_isAnonymous = b; } + bool isAnonymousBlock() const + { + return m_isAnonymous && style()->display() == BLOCK && style()->styleType() == RenderStyle::NOPSEUDO && !isListMarker(); + } + + bool isFloating() const { return m_floating; } + bool isPositioned() const { return m_positioned; } // absolute or fixed positioning + bool isRelPositioned() const { return m_relPositioned; } // relative positioning + bool isText() const { return m_isText; } + bool isInline() const { return m_inline; } // inline object + bool isCompact() const { return style()->display() == COMPACT; } // compact object + bool isRunIn() const { return style()->display() == RUN_IN; } // run-in object + bool isDragging() const { return m_isDragging; } + bool isReplaced() const { return m_replaced; } // a "replaced" element (see CSS) + + bool hasLayer() const { return m_hasLayer; } + + bool hasBoxDecorations() const { return m_paintBackground; } + bool mustRepaintBackgroundOrBorder() const; + + bool hasHorizontalBordersPaddingOrMargin() const { return hasHorizontalBordersOrPadding() || marginLeft() != 0 || marginRight() != 0; } + bool hasHorizontalBordersOrPadding() const { return borderLeft() != 0 || borderRight() != 0 || paddingLeft() != 0 || paddingRight() != 0; } + + bool needsLayout() const { return m_needsLayout || m_normalChildNeedsLayout || m_posChildNeedsLayout || m_needsPositionedMovementLayout; } + bool selfNeedsLayout() const { return m_needsLayout; } + bool needsPositionedMovementLayout() const { return m_needsPositionedMovementLayout; } + bool needsPositionedMovementLayoutOnly() const { return m_needsPositionedMovementLayout && !m_needsLayout && !m_normalChildNeedsLayout && !m_posChildNeedsLayout; } + bool posChildNeedsLayout() const { return m_posChildNeedsLayout; } + bool normalChildNeedsLayout() const { return m_normalChildNeedsLayout; } + + bool prefWidthsDirty() const { return m_prefWidthsDirty; } + + bool isSelectionBorder() const; + + bool hasOverflowClip() const { return m_hasOverflowClip; } + virtual bool hasControlClip() const { return false; } + virtual IntRect controlClipRect(int /*tx*/, int /*ty*/) const { return IntRect(); } + + bool hasAutoVerticalScrollbar() const { return hasOverflowClip() && (style()->overflowY() == OAUTO || style()->overflowY() == OOVERLAY); } + bool hasAutoHorizontalScrollbar() const { return hasOverflowClip() && (style()->overflowX() == OAUTO || style()->overflowX() == OOVERLAY); } + + bool scrollsOverflow() const { return scrollsOverflowX() || scrollsOverflowY(); } + bool scrollsOverflowX() const { return hasOverflowClip() && (style()->overflowX() == OSCROLL || hasAutoHorizontalScrollbar()); } + bool scrollsOverflowY() const { return hasOverflowClip() && (style()->overflowY() == OSCROLL || hasAutoVerticalScrollbar()); } + + virtual int verticalScrollbarWidth() const; + virtual int horizontalScrollbarHeight() const; + + bool hasTransform() const { return m_hasTransform; } + bool hasMask() const { return style() && style()->hasMask(); } + virtual IntRect maskClipRect() { return borderBox(); } + +private: + bool includeVerticalScrollbarSize() const { return hasOverflowClip() && (style()->overflowY() == OSCROLL || style()->overflowY() == OAUTO); } + bool includeHorizontalScrollbarSize() const { return hasOverflowClip() && (style()->overflowX() == OSCROLL || style()->overflowX() == OAUTO); } + +public: + // The pseudo element style can be cached or uncached. Use the cached method if the pseudo element doesn't respect + // any pseudo classes (and therefore has no concept of changing state). + RenderStyle* getCachedPseudoStyle(RenderStyle::PseudoId, RenderStyle* parentStyle = 0) const; + PassRefPtr<RenderStyle> getUncachedPseudoStyle(RenderStyle::PseudoId, RenderStyle* parentStyle = 0) const; + + void updateDragState(bool dragOn); + + RenderView* view() const; + + // don't even think about making this method virtual! + Node* element() const { return m_isAnonymous ? 0 : m_node; } + Document* document() const { return m_node->document(); } + void setNode(Node* node) { m_node = node; } + Node* node() const { return m_node; } + + bool hasOutlineAnnotation() const; + bool hasOutline() const { return style()->hasOutline() || hasOutlineAnnotation(); } + + /** + * returns the object containing this one. can be different from parent for + * positioned elements + */ + RenderObject* container() const; + RenderObject* hoverAncestor() const; + + virtual void markAllDescendantsWithFloatsForLayout(RenderObject* floatToRemove = 0); + void markContainingBlocksForLayout(bool scheduleRelayout = true, RenderObject* newRoot = 0); + void setNeedsLayout(bool b, bool markParents = true); + void setChildNeedsLayout(bool b, bool markParents = true); + void setNeedsPositionedMovementLayout(); + void setPrefWidthsDirty(bool, bool markParents = true); + void invalidateContainerPrefWidths(); + virtual void invalidateCounters() { } + + void setNeedsLayoutAndPrefWidthsRecalc() + { + setNeedsLayout(true); + setPrefWidthsDirty(true); + } + + void setPositioned(bool b = true) { m_positioned = b; } + void setRelPositioned(bool b = true) { m_relPositioned = b; } + void setFloating(bool b = true) { m_floating = b; } + void setInline(bool b = true) { m_inline = b; } + void setHasBoxDecorations(bool b = true) { m_paintBackground = b; } + void setRenderText() { m_isText = true; } + void setReplaced(bool b = true) { m_replaced = b; } + void setHasOverflowClip(bool b = true) { m_hasOverflowClip = b; } + void setHasLayer(bool b = true) { m_hasLayer = b; } + void setHasTransform(bool b = true) { m_hasTransform = b; } + void setHasReflection(bool b = true) { m_hasReflection = b; } + + void scheduleRelayout(); + + void updateFillImages(const FillLayer*, const FillLayer*); + void updateImage(StyleImage*, StyleImage*); + + virtual InlineBox* createInlineBox(bool makePlaceHolderBox, bool isRootLineBox, bool isOnlyRun = false); + virtual void dirtyLineBoxes(bool fullLayout, bool isRootLineBox = false); + + // For inline replaced elements, this function returns the inline box that owns us. Enables + // the replaced RenderObject to quickly determine what line it is contained on and to easily + // iterate over structures on the line. + virtual InlineBox* inlineBoxWrapper() const; + virtual void setInlineBoxWrapper(InlineBox*); + virtual void deleteLineBoxWrapper(); + + // for discussion of lineHeight see CSS2 spec + virtual int lineHeight(bool firstLine, bool isRootLineBox = false) const; + // for the vertical-align property of inline elements + // the difference between this objects baseline position and the lines baseline position. + virtual int verticalPositionHint(bool firstLine) const; + // the offset of baseline from the top of the object. + virtual int baselinePosition(bool firstLine, bool isRootLineBox = false) const; + + /* + * Paint the object and its children, clipped by (x|y|w|h). + * (tx|ty) is the calculated position of the parent + */ + struct PaintInfo { + PaintInfo(GraphicsContext* newContext, const IntRect& newRect, PaintPhase newPhase, bool newForceBlackText, + RenderObject* newPaintingRoot, RenderFlowSequencedSet* newOutlineObjects) + : context(newContext) + , rect(newRect) + , phase(newPhase) + , forceBlackText(newForceBlackText) + , paintingRoot(newPaintingRoot) + , outlineObjects(newOutlineObjects) + { + } + + GraphicsContext* context; + IntRect rect; + PaintPhase phase; + bool forceBlackText; + RenderObject* paintingRoot; // used to draw just one element and its visual kids + RenderFlowSequencedSet* outlineObjects; // used to list outlines that should be painted by a block with inline children + }; + + virtual void paint(PaintInfo&, int tx, int ty); + void paintBorder(GraphicsContext*, int tx, int ty, int w, int h, const RenderStyle*, bool begin = true, bool end = true); + bool paintNinePieceImage(GraphicsContext*, int tx, int ty, int w, int h, const RenderStyle*, const NinePieceImage&, CompositeOperator = CompositeSourceOver); + void paintOutline(GraphicsContext*, int tx, int ty, int w, int h, const RenderStyle*); + void paintBoxShadow(GraphicsContext*, int tx, int ty, int w, int h, const RenderStyle*, bool begin = true, bool end = true); + + // RenderBox implements this. + virtual void paintBoxDecorations(PaintInfo&, int /*tx*/, int /*ty*/) { } + virtual void paintMask(PaintInfo&, int /*tx*/, int /*ty*/) { } + virtual void paintFillLayerExtended(const PaintInfo&, const Color&, const FillLayer*, + int /*clipY*/, int /*clipH*/, int /*tx*/, int /*ty*/, int /*width*/, int /*height*/, + InlineFlowBox* = 0, CompositeOperator = CompositeSourceOver) { } + + + /* + * Calculates the actual width of the object (only for non inline + * objects) + */ + virtual void calcWidth() { } + + /* + * This function should cause the Element to calculate its + * width and height and the layout of its content + * + * when the Element calls setNeedsLayout(false), layout() is no + * longer called during relayouts, as long as there is no + * style sheet change. When that occurs, m_needsLayout will be + * set to true and the Element receives layout() calls + * again. + */ + virtual void layout() = 0; + + /* This function performs a layout only if one is needed. */ + void layoutIfNeeded() { if (needsLayout()) layout(); } + + // Called when a positioned object moves but doesn't necessarily change size. A simplified layout is attempted + // that just updates the object's position. If the size does change, the object remains dirty. + virtual void tryLayoutDoingPositionedMovementOnly() { } + + // used for element state updates that cannot be fixed with a + // repaint and do not need a relayout + virtual void updateFromElement() { } + + // Block flows subclass availableWidth to handle multi column layout (shrinking the width available to children when laying out.) + virtual int availableWidth() const { return contentWidth(); } + + virtual int availableHeight() const { return 0; } + + virtual void updateWidgetPosition(); + +#if ENABLE(DASHBOARD_SUPPORT) + void addDashboardRegions(Vector<DashboardRegionValue>&); + void collectDashboardRegions(Vector<DashboardRegionValue>&); +#endif + + bool hitTest(const HitTestRequest&, HitTestResult&, const IntPoint&, int tx, int ty, HitTestFilter = HitTestAll); + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); + void updateHitTestResult(HitTestResult&, const IntPoint&); + + virtual VisiblePosition positionForCoordinates(int x, int y); + VisiblePosition positionForPoint(const IntPoint& point) { return positionForCoordinates(point.x(), point.y()); } + + virtual void dirtyLinesFromChangedChild(RenderObject*); + + // Called to update a style that is allowed to trigger animations. + // FIXME: Right now this will typically be called only when updating happens from the DOM on explicit elements. + // We don't yet handle generated content animation such as first-letter or before/after (we'll worry about this later). + void setAnimatableStyle(PassRefPtr<RenderStyle>); + + // Set the style of the object and update the state of the object accordingly. + virtual void setStyle(PassRefPtr<RenderStyle>); + + // Updates only the local style ptr of the object. Does not update the state of the object, + // and so only should be called when the style is known not to have changed (or from setStyle). + void setStyleInternal(PassRefPtr<RenderStyle>); + + // returns the containing block level element for this element. + RenderBlock* containingBlock() const; + + // return just the width of the containing block + virtual int containingBlockWidth() const; + // return just the height of the containing block + virtual int containingBlockHeight() const; + + // content area (box minus padding/border) + IntRect contentBox() const; + // absolute coords of content area. Ignores transforms. + IntRect absoluteContentBox() const; + // content rect converted to absolute coords, taking transforms into account + FloatQuad absoluteContentQuad() const; + + int contentWidth() const { return clientWidth() - paddingLeft() - paddingRight(); } + int contentHeight() const { return clientHeight() - paddingTop() - paddingBottom(); } + + // used by flexible boxes to impose a flexed width/height override + virtual int overrideSize() const { return 0; } + virtual int overrideWidth() const { return 0; } + virtual int overrideHeight() const { return 0; } + virtual void setOverrideSize(int /*overrideSize*/) { } + + // relative to parent node + virtual void setPos(int /*xPos*/, int /*yPos*/) { } + virtual void setWidth(int /*width*/) { } + virtual void setHeight(int /*height*/) { } + virtual void setRect(const IntRect& rect) { setPos(rect.x(), rect.y()); setWidth(rect.width()); setHeight(rect.height()); } + + virtual int xPos() const { return 0; } + virtual int yPos() const { return 0; } + + // Convert the given local point to absolute coordinates + // FIXME: Temporary. If useTransforms is true, take transforms into account. Eventually localToAbsolute() will always be transform-aware. + virtual FloatPoint localToAbsolute(FloatPoint localPoint = FloatPoint(), bool fixed = false, bool useTransforms = false) const; + virtual FloatPoint absoluteToLocal(FloatPoint, bool fixed = false, bool useTransforms = false) const; + + // This function is used to deal with the extra top space that can occur in table cells (called borderTopExtra). + // The children of the cell do not factor this space in, so we have to add it in. Any code that wants to + // accurately deal with the contents of a cell must call this function instad of absolutePosition. + FloatPoint localToAbsoluteForContent(FloatPoint localPoint = FloatPoint(), bool fixed = false, bool useTransforms = false) const + { + localPoint.move(0.0f, static_cast<float>(borderTopExtra())); + return localToAbsolute(localPoint, fixed, useTransforms); + } + + // Convert a local quad to an absolute quad, taking transforms into account. + virtual FloatQuad localToAbsoluteQuad(const FloatQuad&, bool fixed = false) const; + + // Return the offset from the container() renderer (excluding transforms) + virtual IntSize offsetFromContainer(RenderObject*) const; + + // width and height are without margins but include paddings and borders + virtual int width() const { return 0; } + virtual int height() const { return 0; } + + virtual IntRect borderBox() const { return IntRect(0, 0, width(), height()); } + // Bounds of the outline box in absolute coords. Respects transforms + IntRect absoluteOutlineBounds() const; + + // The height of a block when you include normal flow overflow spillage out of the bottom + // of the block (e.g., a <div style="height:25px"> that has a 100px tall image inside + // it would have an overflow height of borderTop() + paddingTop() + 100px. + virtual int overflowHeight(bool /*includeInterior*/ = true) const { return height(); } + virtual int overflowWidth(bool /*includeInterior*/ = true) const { return width(); } + virtual void setOverflowHeight(int) { } + virtual void setOverflowWidth(int) { } + virtual int overflowLeft(bool /*includeInterior*/ = true) const { return 0; } + virtual int overflowTop(bool /*includeInterior*/ = true) const { return 0; } + virtual IntRect overflowRect(bool /*includeInterior*/ = true) const { return borderBox(); } + + // IE extensions. Used to calculate offsetWidth/Height. Overridden by inlines (RenderFlow) + // to return the remaining width on a given line (and the height of a single line). + virtual int offsetWidth() const { return width(); } + virtual int offsetHeight() const { return height() + borderTopExtra() + borderBottomExtra(); } + + // IE extensions. Also supported by Gecko. We override in render flow to get the + // left and top correct. -dwh + virtual int offsetLeft() const; + virtual int offsetTop() const; + virtual RenderObject* offsetParent() const; + + // More IE extensions. clientWidth and clientHeight represent the interior of an object + // excluding border and scrollbar. clientLeft/Top are just the borderLeftWidth and borderTopWidth. + int clientLeft() const { return borderLeft(); } + int clientTop() const { return borderTop(); } + int clientWidth() const; + int clientHeight() const; + + // scrollWidth/scrollHeight will be the same as clientWidth/clientHeight unless the + // object has overflow:hidden/scroll/auto specified and also has overflow. + // scrollLeft/Top return the current scroll position. These methods are virtual so that objects like + // textareas can scroll shadow content (but pretend that they are the objects that are + // scrolling). + virtual int scrollLeft() const; + virtual int scrollTop() const; + virtual int scrollWidth() const; + virtual int scrollHeight() const; + virtual void setScrollLeft(int); + virtual void setScrollTop(int); + + virtual bool scroll(ScrollDirection, ScrollGranularity, float multiplier = 1.0f); + virtual bool canBeProgramaticallyScrolled(bool) const; + virtual void autoscroll(); + virtual void stopAutoscroll() { } + + virtual void panScroll(const IntPoint&); + + virtual bool isScrollable() const; + + // The following seven functions are used to implement collapsing margins. + // All objects know their maximal positive and negative margins. The + // formula for computing a collapsed margin is |maxPosMargin|-|maxNegmargin|. + // For a non-collapsing, e.g., a leaf element, this formula will simply return + // the margin of the element. Blocks override the maxTopMargin and maxBottomMargin + // methods. + virtual bool isSelfCollapsingBlock() const { return false; } + virtual int collapsedMarginTop() const { return maxTopMargin(true) - maxTopMargin(false); } + virtual int collapsedMarginBottom() const { return maxBottomMargin(true) - maxBottomMargin(false); } + virtual bool isTopMarginQuirk() const { return false; } + virtual bool isBottomMarginQuirk() const { return false; } + + virtual int maxTopMargin(bool positive) const; + virtual int maxBottomMargin(bool positive) const; + + virtual int marginTop() const { return 0; } + virtual int marginBottom() const { return 0; } + virtual int marginLeft() const { return 0; } + virtual int marginRight() const { return 0; } + + // Virtual since table cells override + virtual int paddingTop() const; + virtual int paddingBottom() const; + virtual int paddingLeft() const; + virtual int paddingRight() const; + + virtual int borderTop() const { return style()->borderTopWidth(); } + virtual int borderBottom() const { return style()->borderBottomWidth(); } + virtual int borderTopExtra() const { return 0; } + virtual int borderBottomExtra() const { return 0; } + virtual int borderLeft() const { return style()->borderLeftWidth(); } + virtual int borderRight() const { return style()->borderRightWidth(); } + + virtual void addLineBoxRects(Vector<IntRect>&, unsigned startOffset = 0, unsigned endOffset = UINT_MAX, bool useSelectionHeight = false); + + virtual void absoluteRects(Vector<IntRect>&, int tx, int ty, bool topLevel = true); + // FIXME: useTransforms should go away eventually + IntRect absoluteBoundingBoxRect(bool useTransforms = false); + + // Build an array of quads in absolute coords for line boxes + virtual void collectAbsoluteLineBoxQuads(Vector<FloatQuad>&, unsigned startOffset = 0, unsigned endOffset = UINT_MAX, bool useSelectionHeight = false); + virtual void absoluteQuads(Vector<FloatQuad>&, bool topLevel = true); + + // the rect that will be painted if this object is passed as the paintingRoot + IntRect paintingRootRect(IntRect& topLevelRect); + + void addPDFURLRect(GraphicsContext*, const IntRect&); + + virtual void addFocusRingRects(GraphicsContext*, int tx, int ty); + + virtual int minPrefWidth() const { return 0; } + virtual int maxPrefWidth() const { return 0; } + + RenderStyle* style() const { return m_style.get(); } + RenderStyle* firstLineStyle() const; + RenderStyle* style(bool firstLine) const { return firstLine ? firstLineStyle() : style(); } + + void getTextDecorationColors(int decorations, Color& underline, Color& overline, + Color& linethrough, bool quirksMode = false); + + enum BorderSide { + BSTop, + BSBottom, + BSLeft, + BSRight + }; + + void drawBorderArc(GraphicsContext*, int x, int y, float thickness, IntSize radius, int angleStart, + int angleSpan, BorderSide, Color, const Color& textcolor, EBorderStyle, bool firstCorner); + void drawBorder(GraphicsContext*, int x1, int y1, int x2, int y2, BorderSide, + Color, const Color& textcolor, EBorderStyle, int adjbw1, int adjbw2); + + // Repaint the entire object. Called when, e.g., the color of a border changes, or when a border + // style changes. + void repaint(bool immediate = false); + + // Repaint a specific subrectangle within a given object. The rect |r| is in the object's coordinate space. + void repaintRectangle(const IntRect&, bool immediate = false); + + // Repaint only if our old bounds and new bounds are different. + bool repaintAfterLayoutIfNeeded(const IntRect& oldBounds, const IntRect& oldOutlineBox); + + // Repaint only if the object moved. + virtual void repaintDuringLayoutIfMoved(const IntRect& rect); + + // Called to repaint a block's floats. + virtual void repaintOverhangingFloats(bool paintAllDescendants = false); + + bool checkForRepaintDuringLayout() const; + + // Returns the rect that should be repainted whenever this object changes. The rect is in the view's + // coordinate space. This method deals with outlines and overflow. + virtual IntRect absoluteClippedOverflowRect(); + + IntRect getAbsoluteRepaintRectWithOutline(int ow); + + // Given a rect in the object's coordinate space, this method converts the rectangle to the view's + // coordinate space. + virtual void computeAbsoluteRepaintRect(IntRect&, bool fixed = false); + + virtual unsigned int length() const { return 1; } + + bool isFloatingOrPositioned() const { return (isFloating() || isPositioned()); } + virtual bool containsFloats() { return false; } + virtual bool containsFloat(RenderObject*) { return false; } + virtual bool hasOverhangingFloats() { return false; } + virtual bool expandsToEncloseOverhangingFloats() const { return isFloating() && style()->height().isAuto(); } + + virtual void removePositionedObjects(RenderBlock*) { } + + virtual bool avoidsFloats() const; + bool shrinkToAvoidFloats() const; + + // positioning of inline children (bidi) + virtual void position(InlineBox*) { } + + bool isTransparent() const { return style()->opacity() < 1.0f; } + float opacity() const { return style()->opacity(); } + + bool hasReflection() const { return m_hasReflection; } + IntRect reflectionBox() const; + int reflectionOffset() const; + // Given a rect in the object's coordinate space, returns the corresponding rect in the reflection. + IntRect reflectedRect(const IntRect&) const; + + // Applied as a "slop" to dirty rect checks during the outline painting phase's dirty-rect checks. + int maximalOutlineSize(PaintPhase) const; + + enum SelectionState { + SelectionNone, // The object is not selected. + SelectionStart, // The object either contains the start of a selection run or is the start of a run + SelectionInside, // The object is fully encompassed by a selection run + SelectionEnd, // The object either contains the end of a selection run or is the end of a run + SelectionBoth // The object contains an entire run or is the sole selected object in that run + }; + + // The current selection state for an object. For blocks, the state refers to the state of the leaf + // descendants (as described above in the SelectionState enum declaration). + virtual SelectionState selectionState() const { return SelectionNone; } + + // Sets the selection state for an object. + virtual void setSelectionState(SelectionState state) { if (parent()) parent()->setSelectionState(state); } + + // A single rectangle that encompasses all of the selected objects within this object. Used to determine the tightest + // possible bounding box for the selection. + virtual IntRect selectionRect(bool) { return IntRect(); } + + // Whether or not an object can be part of the leaf elements of the selection. + virtual bool canBeSelectionLeaf() const { return false; } + + // Whether or not a block has selected children. + virtual bool hasSelectedChildren() const { return false; } + + // Obtains the selection colors that should be used when painting a selection. + Color selectionBackgroundColor() const; + Color selectionForegroundColor() const; + + // Whether or not a given block needs to paint selection gaps. + virtual bool shouldPaintSelectionGaps() const { return false; } + + // This struct is used when the selection changes to cache the old and new state of the selection for each RenderObject. + struct SelectionInfo { + SelectionInfo() + : m_object(0) + , m_state(SelectionNone) + { + } + + SelectionInfo(RenderObject* o, bool clipToVisibleContent) + : m_object(o) + , m_rect(o->needsLayout() ? IntRect() : o->selectionRect(clipToVisibleContent)) + , m_state(o->selectionState()) + { + } + + RenderObject* object() const { return m_object; } + IntRect rect() const { return m_rect; } + SelectionState state() const { return m_state; } + + RenderObject* m_object; + IntRect m_rect; + SelectionState m_state; + }; + + Node* draggableNode(bool dhtmlOK, bool uaOK, int x, int y, bool& dhtmlWillDrag) const; + + /** + * Returns the local coordinates of the caret within this render object. + * @param caretOffset zero-based offset determining position within the render object. + * @param extraWidthToEndOfLine optional out arg to give extra width to end of line - + * useful for character range rect computations + */ + virtual IntRect localCaretRect(InlineBox*, int caretOffset, int* extraWidthToEndOfLine = 0); + + virtual int lowestPosition(bool /*includeOverflowInterior*/ = true, bool /*includeSelf*/ = true) const { return 0; } + virtual int rightmostPosition(bool /*includeOverflowInterior*/ = true, bool /*includeSelf*/ = true) const { return 0; } + virtual int leftmostPosition(bool /*includeOverflowInterior*/ = true, bool /*includeSelf*/ = true) const { return 0; } + + virtual void calcVerticalMargins() { } + void removeFromObjectLists(); + + // When performing a global document tear-down, the renderer of the document is cleared. We use this + // as a hook to detect the case of document destruction and don't waste time doing unnecessary work. + bool documentBeingDestroyed() const; + + virtual void destroy(); + + // Virtual function helpers for CSS3 Flexible Box Layout + virtual bool isFlexibleBox() const { return false; } + virtual bool isFlexingChildren() const { return false; } + virtual bool isStretchingChildren() const { return false; } + + // Convenience, to avoid repeating the code to dig down to get this. + UChar backslashAsCurrencySymbol() const; + + virtual int caretMinOffset() const; + virtual int caretMaxOffset() const; + virtual unsigned caretMaxRenderedOffset() const; + + virtual int previousOffset(int current) const; + virtual int nextOffset(int current) const; + + virtual void imageChanged(CachedImage*, const IntRect* = 0); + virtual void imageChanged(WrappedImagePtr, const IntRect* = 0) { } + virtual bool willRenderImage(CachedImage*); + + virtual void selectionStartEnd(int& spos, int& epos) const; + + RenderObject* paintingRootForChildren(PaintInfo& paintInfo) const + { + // if we're the painting root, kids draw normally, and see root of 0 + return (!paintInfo.paintingRoot || paintInfo.paintingRoot == this) ? 0 : paintInfo.paintingRoot; + } + + bool shouldPaintWithinRoot(PaintInfo& paintInfo) const + { + return !paintInfo.paintingRoot || paintInfo.paintingRoot == this; + } + + bool hasOverrideSize() const { return m_hasOverrideSize; } + void setHasOverrideSize(bool b) { m_hasOverrideSize = b; } + + void remove() { if (parent()) parent()->removeChild(this); } + + void invalidateVerticalPosition() { m_verticalPosition = PositionUndefined; } + + virtual void removeLeftoverAnonymousBlock(RenderBlock* child); + + virtual void capsLockStateMayHaveChanged() { } + + AnimationController* animation() const; + + bool visibleToHitTesting() const { return style()->visibility() == VISIBLE && style()->pointerEvents() != PE_NONE; } + +protected: + // Overrides should call the superclass at the end + virtual void styleWillChange(RenderStyle::Diff, const RenderStyle* newStyle); + // Overrides should call the superclass at the start + virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + + virtual void printBoxDecorations(GraphicsContext*, int /*x*/, int /*y*/, int /*w*/, int /*h*/, int /*tx*/, int /*ty*/) { } + + virtual IntRect viewRect() const; + + int getVerticalPosition(bool firstLine) const; + + void adjustRectForOutlineAndShadow(IntRect&) const; + + void arenaDelete(RenderArena*, void* objectBase); + +private: + RefPtr<RenderStyle> m_style; + + Node* m_node; + + RenderObject* m_parent; + RenderObject* m_previous; + RenderObject* m_next; + +#ifndef NDEBUG + bool m_hasAXObject; +#endif + mutable int m_verticalPosition; + + bool m_needsLayout : 1; + bool m_needsPositionedMovementLayout :1; + bool m_normalChildNeedsLayout : 1; + bool m_posChildNeedsLayout : 1; + bool m_prefWidthsDirty : 1; + bool m_floating : 1; + + bool m_positioned : 1; + bool m_relPositioned : 1; + bool m_paintBackground : 1; // if the box has something to paint in the + // background painting phase (background, border, etc) + + bool m_isAnonymous : 1; + bool m_isText : 1; + bool m_inline : 1; + bool m_replaced : 1; + bool m_isDragging : 1; + + bool m_hasLayer : 1; + bool m_hasOverflowClip : 1; + bool m_hasTransform : 1; + bool m_hasReflection : 1; + + bool m_hasOverrideSize : 1; + +public: + bool m_hasCounterNodeMap : 1; + bool m_everHadLayout : 1; + +private: + // Store state between styleWillChange and styleDidChange + static bool s_affectsParentBlock; +}; + +} // namespace WebCore + +#ifndef NDEBUG +// Outside the WebCore namespace for ease of invocation from gdb. +void showTree(const WebCore::RenderObject*); +#endif + +#endif // RenderObject_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderPart.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderPart.cpp new file mode 100644 index 0000000..d840418 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderPart.cpp @@ -0,0 +1,116 @@ +/** + * This file is part of the KDE project. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 2000 Simon Hausmann <hausmann@kde.org> + * (C) 2000 Stefan Schimanski (1Stein@gmx.de) + * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. + * + * 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" +#include "RenderPart.h" + +#include "Document.h" +#include "Frame.h" +#include "FrameTree.h" +#include "FrameView.h" +#include "HTMLFrameOwnerElement.h" +#include "HTMLNames.h" +#include "Page.h" + +namespace WebCore { + +using namespace HTMLNames; + +RenderPart::RenderPart(HTMLFrameOwnerElement* node) + : RenderWidget(node) +{ + // init RenderObject attributes + setInline(false); +} + +RenderPart::~RenderPart() +{ + // Since deref ends up calling setWidget back on us, need to make sure + // that widget is already 0 so it won't do any work. + Widget* widget = m_widget; + m_widget = 0; + if (widget && widget->isFrameView()) + static_cast<FrameView*>(widget)->deref(); + else + delete widget; +} + +void RenderPart::setWidget(Widget* widget) +{ + if (widget != m_widget) { + if (widget && widget->isFrameView()) + static_cast<FrameView*>(widget)->ref(); + RenderWidget::setWidget(widget); + + // make sure the scrollbars are set correctly for restore + // ### find better fix + viewCleared(); + } +} + +void RenderPart::viewCleared() +{ +} + +void RenderPart::deleteWidget() +{ + if (m_widget && m_widget->isFrameView()) + static_cast<FrameView*>(m_widget)->deref(); + else + delete m_widget; +} + +// FIXME: This should not be necessary. Remove this once WebKit knows to properly schedule +// layouts using WebCore when objects resize. +void RenderPart::updateWidgetPosition() +{ + if (!m_widget) + return; + + int width, height; + FloatPoint absPos = localToAbsolute(); + absPos.move(borderLeft() + paddingLeft(), borderTop() + paddingTop()); + width = m_width - borderLeft() - borderRight() - paddingLeft() - paddingRight(); + height = m_height - borderTop() - borderBottom() - paddingTop() - paddingBottom(); + IntRect newBounds(absPos.x(), absPos.y(), width, height); + bool boundsChanged = newBounds != m_widget->frameRect(); + if (boundsChanged) { + // The widget changed positions. Update the frame geometry. + RenderArena *arena = ref(); + element()->ref(); + m_widget->setFrameRect(newBounds); + element()->deref(); + deref(arena); + } + + // if the frame bounds got changed, or if view needs layout (possibly indicating + // content size is wrong) we have to do a layout to set the right widget size + if (m_widget && m_widget->isFrameView()) { + FrameView* frameView = static_cast<FrameView*>(m_widget); + if (boundsChanged || frameView->needsLayout()) + frameView->layout(); + } +} + +} diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderPart.h b/src/3rdparty/webkit/WebCore/rendering/RenderPart.h new file mode 100644 index 0000000..e339468 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderPart.h @@ -0,0 +1,62 @@ +/* + * This file is part of the KDE project. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 2000 Simon Hausmann <hausmann@kde.org> + * Copyright (C) 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef RenderPart_h +#define RenderPart_h + +#include "RenderWidget.h" + +namespace WebCore { + +class Frame; +class HTMLFrameOwnerElement; + +class RenderPart : public RenderWidget { +public: + RenderPart(HTMLFrameOwnerElement*); + virtual ~RenderPart(); + + virtual bool isRenderPart() const { return true; } + virtual const char* renderName() const { return "RenderPart"; } + + virtual void setWidget(Widget*); + + // FIXME: This should not be necessary. + // Remove this once WebKit knows to properly schedule layouts using WebCore when objects resize. + virtual void updateWidgetPosition(); + + bool hasFallbackContent() const { return m_hasFallbackContent; } + + virtual void viewCleared(); + +protected: + bool m_hasFallbackContent; + +private: + virtual void deleteWidget(); +}; + +} + +#endif diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderPartObject.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderPartObject.cpp new file mode 100644 index 0000000..e5200e2 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderPartObject.cpp @@ -0,0 +1,317 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 2000 Simon Hausmann <hausmann@kde.org> + * (C) 2000 Stefan Schimanski (1Stein@gmx.de) + * Copyright (C) 2004, 2005, 2006, 2008 Apple Inc. All rights reserved. + * + * 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" +#include "RenderPartObject.h" + +#include "Frame.h" +#include "FrameLoader.h" +#include "FrameLoaderClient.h" +#include "FrameTree.h" +#include "FrameView.h" +#include "HTMLEmbedElement.h" +#include "HTMLIFrameElement.h" +#include "HTMLNames.h" +#include "HTMLObjectElement.h" +#include "HTMLParamElement.h" +#include "MIMETypeRegistry.h" +#include "Page.h" +#include "PluginData.h" +#include "RenderView.h" +#include "Text.h" + +namespace WebCore { + +using namespace HTMLNames; + +RenderPartObject::RenderPartObject(HTMLFrameOwnerElement* element) + : RenderPart(element) +{ + // init RenderObject attributes + setInline(true); + m_hasFallbackContent = false; + + if (element->hasTagName(embedTag) || element->hasTagName(objectTag)) + view()->frameView()->setIsVisuallyNonEmpty(); +} + +RenderPartObject::~RenderPartObject() +{ + if (m_view) + m_view->removeWidgetToUpdate(this); +} + +static bool isURLAllowed(Document* doc, const String& url) +{ + if (doc->frame()->page()->frameCount() >= 200) + return false; + + // We allow one level of self-reference because some sites depend on that. + // But we don't allow more than one. + KURL completeURL = doc->completeURL(url); + bool foundSelfReference = false; + for (Frame* frame = doc->frame(); frame; frame = frame->tree()->parent()) { + if (equalIgnoringRef(frame->loader()->url(), completeURL)) { + if (foundSelfReference) + return false; + foundSelfReference = true; + } + } + return true; +} + +static inline void mapClassIdToServiceType(const String& classId, String& serviceType, const PluginData* pluginData) +{ + // Return early if classId is empty (since we won't do anything below). + // Furthermore, if classId is null, calling get() below will crash. + if (classId.isEmpty()) + return; + + typedef HashMap<String, String, CaseFoldingHash> ServiceTypeHashMap; + static ServiceTypeHashMap* serviceTypeFallbackForClassId = 0; + if (!serviceTypeFallbackForClassId) { + serviceTypeFallbackForClassId = new ServiceTypeHashMap; + serviceTypeFallbackForClassId->add("clsid:D27CDB6E-AE6D-11CF-96B8-444553540000", "application/x-shockwave-flash"); + serviceTypeFallbackForClassId->add("clsid:CFCDAA03-8BE4-11CF-B84B-0020AFBBCCFA", "audio/x-pn-realaudio-plugin"); + serviceTypeFallbackForClassId->add("clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B", "video/quicktime"); + serviceTypeFallbackForClassId->add("clsid:166B1BCA-3F9C-11CF-8075-444553540000", "application/x-director"); +#if ENABLE(ACTIVEX_TYPE_CONVERSION_WMPLAYER) + serviceTypeFallbackForClassId->add("clsid:6BF52A52-394A-11D3-B153-00C04F79FAA6", "application/x-mplayer2"); + serviceTypeFallbackForClassId->add("clsid:22D6F312-B0F6-11D0-94AB-0080C74C7E95", "application/x-mplayer2"); +#endif + } + + const String fallbackServiceType = serviceTypeFallbackForClassId->get(classId); + if (pluginData->supportsMimeType(fallbackServiceType)) + serviceType = fallbackServiceType; + else if (pluginData->supportsMimeType("application/x-oleobject")) + serviceType = "application/x-oleobject"; +} + +static bool shouldUseChildEmbedOfObject(HTMLObjectElement* o, const PluginData* pluginData) +{ + // An OBJECT tag with a classId is some kind of ActiveX control. The most + // common controls have parallel plugin versions and thus possibly nested + // EMBED tags. If this is the case, the OBJECT's classId should map to some + // known plugin MIME type. If it doesn't, either the control is unlikely to + // have a parallel plugin implementation (so there's no point looking + // inside), or we've purposefully disabled conversion for this classId, in + // which case we want to use the ActiveX OBJECT instead of the EMBED anyway. + String serviceType; + mapClassIdToServiceType(o->classId(), serviceType, pluginData); + return serviceType != "application/x-oleobject"; +} + +void RenderPartObject::updateWidget(bool onlyCreateNonNetscapePlugins) +{ + String url; + String serviceType; + Vector<String> paramNames; + Vector<String> paramValues; + Frame* frame = m_view->frame(); + + if (element()->hasTagName(objectTag)) { + HTMLObjectElement* o = static_cast<HTMLObjectElement*>(element()); + + o->setNeedWidgetUpdate(false); + if (!o->isFinishedParsingChildren()) + return; + + // Check for a child EMBED tag. + HTMLEmbedElement* embed = 0; + const PluginData* pluginData = frame->page()->pluginData(); + if (pluginData && shouldUseChildEmbedOfObject(o, pluginData)) { + for (Node* child = o->firstChild(); child;) { + if (child->hasTagName(embedTag)) { + embed = static_cast<HTMLEmbedElement*>(child); + break; + } else if (child->hasTagName(objectTag)) + child = child->nextSibling(); // Don't descend into nested OBJECT tags + else + child = child->traverseNextNode(o); // Otherwise descend (EMBEDs may be inside COMMENT tags) + } + } + + // Use the attributes from the EMBED tag instead of the OBJECT tag including WIDTH and HEIGHT. + HTMLElement *embedOrObject; + if (embed) { + embedOrObject = (HTMLElement *)embed; + url = embed->url(); + serviceType = embed->serviceType(); + } else + embedOrObject = (HTMLElement *)o; + + // If there was no URL or type defined in EMBED, try the OBJECT tag. + if (url.isEmpty()) + url = o->url(); + if (serviceType.isEmpty()) + serviceType = o->serviceType(); + + HashSet<StringImpl*, CaseFoldingHash> uniqueParamNames; + + // Scan the PARAM children. + // Get the URL and type from the params if we don't already have them. + // Get the attributes from the params if there is no EMBED tag. + Node *child = o->firstChild(); + while (child && (url.isEmpty() || serviceType.isEmpty() || !embed)) { + if (child->hasTagName(paramTag)) { + HTMLParamElement* p = static_cast<HTMLParamElement*>(child); + String name = p->name(); + if (url.isEmpty() && (equalIgnoringCase(name, "src") || equalIgnoringCase(name, "movie") || equalIgnoringCase(name, "code") || equalIgnoringCase(name, "url"))) + url = p->value(); + if (serviceType.isEmpty() && equalIgnoringCase(name, "type")) { + serviceType = p->value(); + int pos = serviceType.find(";"); + if (pos != -1) + serviceType = serviceType.left(pos); + } + if (!embed && !name.isEmpty()) { + uniqueParamNames.add(name.impl()); + paramNames.append(p->name()); + paramValues.append(p->value()); + } + } + child = child->nextSibling(); + } + + // When OBJECT is used for an applet via Sun's Java plugin, the CODEBASE attribute in the tag + // points to the Java plugin itself (an ActiveX component) while the actual applet CODEBASE is + // in a PARAM tag. See <http://java.sun.com/products/plugin/1.2/docs/tags.html>. This means + // we have to explicitly suppress the tag's CODEBASE attribute if there is none in a PARAM, + // else our Java plugin will misinterpret it. [4004531] + String codebase; + if (!embed && MIMETypeRegistry::isJavaAppletMIMEType(serviceType)) { + codebase = "codebase"; + uniqueParamNames.add(codebase.impl()); // pretend we found it in a PARAM already + } + + // Turn the attributes of either the EMBED tag or OBJECT tag into arrays, but don't override PARAM values. + NamedAttrMap* attributes = embedOrObject->attributes(); + if (attributes) { + for (unsigned i = 0; i < attributes->length(); ++i) { + Attribute* it = attributes->attributeItem(i); + const AtomicString& name = it->name().localName(); + if (embed || !uniqueParamNames.contains(name.impl())) { + paramNames.append(name.string()); + paramValues.append(it->value().string()); + } + } + } + + // If we still don't have a type, try to map from a specific CLASSID to a type. + if (pluginData && serviceType.isEmpty()) + mapClassIdToServiceType(o->classId(), serviceType, pluginData); + + if (!isURLAllowed(document(), url)) + return; + + // Find out if we support fallback content. + m_hasFallbackContent = false; + for (Node *child = o->firstChild(); child && !m_hasFallbackContent; child = child->nextSibling()) { + if ((!child->isTextNode() && !child->hasTagName(embedTag) && !child->hasTagName(paramTag)) || // Discount <embed> and <param> + (child->isTextNode() && !static_cast<Text*>(child)->containsOnlyWhitespace())) + m_hasFallbackContent = true; + } + + if (onlyCreateNonNetscapePlugins) { + KURL completedURL; + if (!url.isEmpty()) + completedURL = frame->loader()->completeURL(url); + + if (frame->loader()->client()->objectContentType(completedURL, serviceType) == ObjectContentNetscapePlugin) + return; + } + + bool success = frame->loader()->requestObject(this, url, AtomicString(o->name()), serviceType, paramNames, paramValues); + if (!success && m_hasFallbackContent) + o->renderFallbackContent(); + } else if (element()->hasTagName(embedTag)) { + HTMLEmbedElement *o = static_cast<HTMLEmbedElement*>(element()); + o->setNeedWidgetUpdate(false); + url = o->url(); + serviceType = o->serviceType(); + + if (url.isEmpty() && serviceType.isEmpty()) + return; + if (!isURLAllowed(document(), url)) + return; + + // add all attributes set on the embed object + NamedAttrMap* a = o->attributes(); + if (a) { + for (unsigned i = 0; i < a->length(); ++i) { + Attribute* it = a->attributeItem(i); + paramNames.append(it->name().localName().string()); + paramValues.append(it->value().string()); + } + } + + if (onlyCreateNonNetscapePlugins) { + KURL completedURL; + if (!url.isEmpty()) + completedURL = frame->loader()->completeURL(url); + + if (frame->loader()->client()->objectContentType(completedURL, serviceType) == ObjectContentNetscapePlugin) + return; + + } + + frame->loader()->requestObject(this, url, o->getAttribute(nameAttr), serviceType, paramNames, paramValues); + } +} + +void RenderPartObject::layout() +{ + ASSERT(needsLayout()); + + calcWidth(); + calcHeight(); + adjustOverflowForBoxShadow(); + + RenderPart::layout(); + + if (!m_widget && m_view) + m_view->addWidgetToUpdate(this); + + setNeedsLayout(false); +} + +void RenderPartObject::viewCleared() +{ + if (element() && m_widget && m_widget->isFrameView()) { + FrameView* view = static_cast<FrameView*>(m_widget); + int marginw = -1; + int marginh = -1; + if (element()->hasTagName(iframeTag)) { + HTMLIFrameElement* frame = static_cast<HTMLIFrameElement*>(element()); + marginw = frame->getMarginWidth(); + marginh = frame->getMarginHeight(); + } + if (marginw != -1) + view->setMarginWidth(marginw); + if (marginh != -1) + view->setMarginHeight(marginh); + } +} + +} diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderPartObject.h b/src/3rdparty/webkit/WebCore/rendering/RenderPartObject.h new file mode 100644 index 0000000..98de5b9 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderPartObject.h @@ -0,0 +1,47 @@ +/* + * This file is part of the KDE project. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 2000 Simon Hausmann <hausmann@kde.org> + * Copyright (C) 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef RenderPartObject_h +#define RenderPartObject_h + +#include "RenderPart.h" + +namespace WebCore { + +class RenderPartObject : public RenderPart { +public: + RenderPartObject(HTMLFrameOwnerElement*); + virtual ~RenderPartObject(); + + virtual const char* renderName() const { return "RenderPartObject"; } + + virtual void layout(); + void updateWidget(bool onlyCreateNonNetscapePlugins); + + virtual void viewCleared(); +}; + +} // namespace WebCore + +#endif // RenderPartObject_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderPath.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderPath.cpp new file mode 100644 index 0000000..e595745 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderPath.cpp @@ -0,0 +1,484 @@ +/* + Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org> + 2004, 2005, 2008 Rob Buis <buis@kde.org> + 2005, 2007 Eric Seidel <eric@webkit.org> + + This file is part of the KDE project + + 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 + aint 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(SVG) +#include "RenderPath.h" + +#include <math.h> + +#include "FloatPoint.h" +#include "GraphicsContext.h" +#include "PointerEventsHitRules.h" +#include "RenderSVGContainer.h" +#include "StrokeStyleApplier.h" +#include "SVGPaintServer.h" +#include "SVGRenderSupport.h" +#include "SVGResourceFilter.h" +#include "SVGResourceMarker.h" +#include "SVGResourceMasker.h" +#include "SVGStyledTransformableElement.h" +#include "SVGTransformList.h" +#include "SVGURIReference.h" + +#include <wtf/MathExtras.h> + +namespace WebCore { + +class BoundingRectStrokeStyleApplier : public StrokeStyleApplier { +public: + BoundingRectStrokeStyleApplier(const RenderObject* object, RenderStyle* style) + : m_object(object) + , m_style(style) + { + ASSERT(style); + ASSERT(object); + } + + void strokeStyle(GraphicsContext* gc) + { + applyStrokeStyleToContext(gc, m_style, m_object); + } + +private: + const RenderObject* m_object; + RenderStyle* m_style; +}; + +// RenderPath +RenderPath::RenderPath(RenderStyle* style, SVGStyledTransformableElement* node) + : RenderObject(node) +{ + ASSERT(style != 0); + ASSERT(static_cast<SVGElement*>(node)->isStyledTransformable()); +} + +RenderPath::~RenderPath() +{ +} + +TransformationMatrix RenderPath::localTransform() const +{ + return m_localTransform; +} + +FloatPoint RenderPath::mapAbsolutePointToLocal(const FloatPoint& point) const +{ + // FIXME: does it make sense to map incoming points with the inverse of the + // absolute transform? + double localX; + double localY; + absoluteTransform().inverse().map(point.x(), point.y(), &localX, &localY); + return FloatPoint::narrowPrecision(localX, localY); +} + +bool RenderPath::fillContains(const FloatPoint& point, bool requiresFill) const +{ + if (m_path.isEmpty()) + return false; + + if (requiresFill && !SVGPaintServer::fillPaintServer(style(), this)) + return false; + + return m_path.contains(point, style()->svgStyle()->fillRule()); +} + +FloatRect RenderPath::relativeBBox(bool includeStroke) const +{ + if (m_path.isEmpty()) + return FloatRect(); + + if (includeStroke) { + if (m_strokeBbox.isEmpty()) { + if (style()->svgStyle()->hasStroke()) { + BoundingRectStrokeStyleApplier strokeStyle(this, style()); + m_strokeBbox = m_path.strokeBoundingRect(&strokeStyle); + } else { + if (m_fillBBox.isEmpty()) + m_fillBBox = m_path.boundingRect(); + + m_strokeBbox = m_fillBBox; + } + } + + return m_strokeBbox; + } + + if (m_fillBBox.isEmpty()) + m_fillBBox = m_path.boundingRect(); + + return m_fillBBox; +} + +void RenderPath::setPath(const Path& newPath) +{ + m_path = newPath; + m_strokeBbox = FloatRect(); + m_fillBBox = FloatRect(); +} + +const Path& RenderPath::path() const +{ + return m_path; +} + +bool RenderPath::calculateLocalTransform() +{ + TransformationMatrix oldTransform = m_localTransform; + m_localTransform = static_cast<SVGStyledTransformableElement*>(element())->animatedLocalTransform(); + return (m_localTransform != oldTransform); +} + +void RenderPath::layout() +{ + IntRect oldBounds; + IntRect oldOutlineBox; + bool checkForRepaint = checkForRepaintDuringLayout() && selfNeedsLayout(); + if (checkForRepaint) { + oldBounds = m_absoluteBounds; + oldOutlineBox = absoluteOutlineBounds(); + } + + calculateLocalTransform(); + + setPath(static_cast<SVGStyledTransformableElement*>(element())->toPathData()); + + m_absoluteBounds = absoluteClippedOverflowRect(); + + setWidth(m_absoluteBounds.width()); + setHeight(m_absoluteBounds.height()); + + if (checkForRepaint) + repaintAfterLayoutIfNeeded(oldBounds, oldOutlineBox); + + setNeedsLayout(false); +} + +IntRect RenderPath::absoluteClippedOverflowRect() +{ + FloatRect repaintRect = absoluteTransform().mapRect(relativeBBox(true)); + + // Markers can expand the bounding box + repaintRect.unite(m_markerBounds); + +#if ENABLE(SVG_FILTERS) + // Filters can expand the bounding box + SVGResourceFilter* filter = getFilterById(document(), style()->svgStyle()->filter()); + if (filter) + repaintRect.unite(filter->filterBBoxForItemBBox(repaintRect)); +#endif + + if (!repaintRect.isEmpty()) + repaintRect.inflate(1); // inflate 1 pixel for antialiasing + + return enclosingIntRect(repaintRect); +} + +bool RenderPath::requiresLayer() +{ + return false; +} + +int RenderPath::lineHeight(bool, bool) const +{ + return relativeBBox(true).height(); +} + +int RenderPath::baselinePosition(bool, bool) const +{ + return relativeBBox(true).height(); +} + +static inline void fillAndStrokePath(const Path& path, GraphicsContext* context, RenderStyle* style, RenderPath* object) +{ + context->beginPath(); + + SVGPaintServer* fillPaintServer = SVGPaintServer::fillPaintServer(style, object); + if (fillPaintServer) { + context->addPath(path); + fillPaintServer->draw(context, object, ApplyToFillTargetType); + } + + SVGPaintServer* strokePaintServer = SVGPaintServer::strokePaintServer(style, object); + if (strokePaintServer) { + context->addPath(path); // path is cleared when filled. + strokePaintServer->draw(context, object, ApplyToStrokeTargetType); + } +} + +void RenderPath::paint(PaintInfo& paintInfo, int, int) +{ + if (paintInfo.context->paintingDisabled() || style()->visibility() == HIDDEN || m_path.isEmpty()) + return; + + paintInfo.context->save(); + paintInfo.context->concatCTM(localTransform()); + + SVGResourceFilter* filter = 0; + + FloatRect boundingBox = relativeBBox(true); + if (paintInfo.phase == PaintPhaseForeground) { + PaintInfo savedInfo(paintInfo); + + prepareToRenderSVGContent(this, paintInfo, boundingBox, filter); + if (style()->svgStyle()->shapeRendering() == SR_CRISPEDGES) + paintInfo.context->setUseAntialiasing(false); + fillAndStrokePath(m_path, paintInfo.context, style(), this); + + if (static_cast<SVGStyledElement*>(element())->supportsMarkers()) + m_markerBounds = drawMarkersIfNeeded(paintInfo.context, paintInfo.rect, m_path); + + finishRenderSVGContent(this, paintInfo, boundingBox, filter, savedInfo.context); + } + + if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth()) + paintOutline(paintInfo.context, static_cast<int>(boundingBox.x()), static_cast<int>(boundingBox.y()), + static_cast<int>(boundingBox.width()), static_cast<int>(boundingBox.height()), style()); + + paintInfo.context->restore(); +} + +void RenderPath::addFocusRingRects(GraphicsContext* graphicsContext, int, int) +{ + graphicsContext->addFocusRingRect(enclosingIntRect(relativeBBox(true))); +} + +void RenderPath::absoluteRects(Vector<IntRect>& rects, int, int, bool) +{ + rects.append(absoluteClippedOverflowRect()); +} + +void RenderPath::absoluteQuads(Vector<FloatQuad>& quads, bool) +{ + quads.append(absoluteClippedOverflowRect()); +} + +bool RenderPath::nodeAtPoint(const HitTestRequest&, HitTestResult& result, int _x, int _y, int, int, HitTestAction hitTestAction) +{ + // We only draw in the forground phase, so we only hit-test then. + if (hitTestAction != HitTestForeground) + return false; + + IntPoint absolutePoint(_x, _y); + + PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_PATH_HITTESTING, style()->pointerEvents()); + + bool isVisible = (style()->visibility() == VISIBLE); + if (isVisible || !hitRules.requireVisible) { + FloatPoint hitPoint = mapAbsolutePointToLocal(absolutePoint); + if ((hitRules.canHitStroke && (style()->svgStyle()->hasStroke() || !hitRules.requireStroke) && strokeContains(hitPoint, hitRules.requireStroke)) + || (hitRules.canHitFill && (style()->svgStyle()->hasFill() || !hitRules.requireFill) && fillContains(hitPoint, hitRules.requireFill))) { + updateHitTestResult(result, absolutePoint); + return true; + } + } + + return false; +} + +enum MarkerType { + Start, + Mid, + End +}; + +struct MarkerData { + FloatPoint origin; + FloatPoint subpathStart; + double strokeWidth; + FloatPoint inslopePoints[2]; + FloatPoint outslopePoints[2]; + MarkerType type; + SVGResourceMarker* marker; +}; + +struct DrawMarkersData { + DrawMarkersData(GraphicsContext*, SVGResourceMarker* startMarker, SVGResourceMarker* midMarker, double strokeWidth); + GraphicsContext* context; + int elementIndex; + MarkerData previousMarkerData; + SVGResourceMarker* midMarker; +}; + +DrawMarkersData::DrawMarkersData(GraphicsContext* c, SVGResourceMarker *start, SVGResourceMarker *mid, double strokeWidth) + : context(c) + , elementIndex(0) + , midMarker(mid) +{ + previousMarkerData.origin = FloatPoint(); + previousMarkerData.subpathStart = FloatPoint(); + previousMarkerData.strokeWidth = strokeWidth; + previousMarkerData.marker = start; + previousMarkerData.type = Start; +} + +static void drawMarkerWithData(GraphicsContext* context, MarkerData &data) +{ + if (!data.marker) + return; + + FloatPoint inslopeChange = data.inslopePoints[1] - FloatSize(data.inslopePoints[0].x(), data.inslopePoints[0].y()); + FloatPoint outslopeChange = data.outslopePoints[1] - FloatSize(data.outslopePoints[0].x(), data.outslopePoints[0].y()); + + double inslope = rad2deg(atan2(inslopeChange.y(), inslopeChange.x())); + double outslope = rad2deg(atan2(outslopeChange.y(), outslopeChange.x())); + + double angle = 0.0; + switch (data.type) { + case Start: + angle = outslope; + break; + case Mid: + angle = (inslope + outslope) / 2; + break; + case End: + angle = inslope; + } + + data.marker->draw(context, FloatRect(), data.origin.x(), data.origin.y(), data.strokeWidth, angle); +} + +static inline void updateMarkerDataForElement(MarkerData& previousMarkerData, const PathElement* element) +{ + FloatPoint* points = element->points; + + switch (element->type) { + case PathElementAddQuadCurveToPoint: + // TODO + previousMarkerData.origin = points[1]; + break; + case PathElementAddCurveToPoint: + previousMarkerData.inslopePoints[0] = points[1]; + previousMarkerData.inslopePoints[1] = points[2]; + previousMarkerData.origin = points[2]; + break; + case PathElementMoveToPoint: + previousMarkerData.subpathStart = points[0]; + case PathElementAddLineToPoint: + previousMarkerData.inslopePoints[0] = previousMarkerData.origin; + previousMarkerData.inslopePoints[1] = points[0]; + previousMarkerData.origin = points[0]; + break; + case PathElementCloseSubpath: + previousMarkerData.inslopePoints[0] = previousMarkerData.origin; + previousMarkerData.inslopePoints[1] = points[0]; + previousMarkerData.origin = previousMarkerData.subpathStart; + previousMarkerData.subpathStart = FloatPoint(); + } +} + +static void drawStartAndMidMarkers(void* info, const PathElement* element) +{ + DrawMarkersData& data = *reinterpret_cast<DrawMarkersData*>(info); + + int elementIndex = data.elementIndex; + MarkerData& previousMarkerData = data.previousMarkerData; + + FloatPoint* points = element->points; + + // First update the outslope for the previous element + previousMarkerData.outslopePoints[0] = previousMarkerData.origin; + previousMarkerData.outslopePoints[1] = points[0]; + + // Draw the marker for the previous element + if (elementIndex != 0) + drawMarkerWithData(data.context, previousMarkerData); + + // Update our marker data for this element + updateMarkerDataForElement(previousMarkerData, element); + + if (elementIndex == 1) { + // After drawing the start marker, switch to drawing mid markers + previousMarkerData.marker = data.midMarker; + previousMarkerData.type = Mid; + } + + data.elementIndex++; +} + +FloatRect RenderPath::drawMarkersIfNeeded(GraphicsContext* context, const FloatRect&, const Path& path) const +{ + Document* doc = document(); + + SVGElement* svgElement = static_cast<SVGElement*>(element()); + ASSERT(svgElement && svgElement->document() && svgElement->isStyled()); + + SVGStyledElement* styledElement = static_cast<SVGStyledElement*>(svgElement); + const SVGRenderStyle* svgStyle = style()->svgStyle(); + + AtomicString startMarkerId(svgStyle->startMarker()); + AtomicString midMarkerId(svgStyle->midMarker()); + AtomicString endMarkerId(svgStyle->endMarker()); + + SVGResourceMarker* startMarker = getMarkerById(doc, startMarkerId); + SVGResourceMarker* midMarker = getMarkerById(doc, midMarkerId); + SVGResourceMarker* endMarker = getMarkerById(doc, endMarkerId); + + if (!startMarker && !startMarkerId.isEmpty()) + svgElement->document()->accessSVGExtensions()->addPendingResource(startMarkerId, styledElement); + else if (startMarker) + startMarker->addClient(styledElement); + + if (!midMarker && !midMarkerId.isEmpty()) + svgElement->document()->accessSVGExtensions()->addPendingResource(midMarkerId, styledElement); + else if (midMarker) + midMarker->addClient(styledElement); + + if (!endMarker && !endMarkerId.isEmpty()) + svgElement->document()->accessSVGExtensions()->addPendingResource(endMarkerId, styledElement); + else if (endMarker) + endMarker->addClient(styledElement); + + if (!startMarker && !midMarker && !endMarker) + return FloatRect(); + + double strokeWidth = SVGRenderStyle::cssPrimitiveToLength(this, svgStyle->strokeWidth(), 1.0f); + DrawMarkersData data(context, startMarker, midMarker, strokeWidth); + + path.apply(&data, drawStartAndMidMarkers); + + data.previousMarkerData.marker = endMarker; + data.previousMarkerData.type = End; + drawMarkerWithData(context, data.previousMarkerData); + + // We know the marker boundaries, only after they're drawn! + // Otherwhise we'd need to do all the marker calculation twice + // once here (through paint()) and once in absoluteClippedOverflowRect(). + FloatRect bounds; + + if (startMarker) + bounds.unite(startMarker->cachedBounds()); + + if (midMarker) + bounds.unite(midMarker->cachedBounds()); + + if (endMarker) + bounds.unite(endMarker->cachedBounds()); + + return bounds; +} + +} + +#endif // ENABLE(SVG) diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderPath.h b/src/3rdparty/webkit/WebCore/rendering/RenderPath.h new file mode 100644 index 0000000..e96439d --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderPath.h @@ -0,0 +1,95 @@ +/* + Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org> + 2004, 2005 Rob Buis <buis@kde.org> + 2005 Eric Seidel <eric@webkit.org> + 2006 Apple Computer, Inc + + This file is part of the KDE project + + 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 + aint 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. +*/ + +#ifndef RenderPath_h +#define RenderPath_h + +#if ENABLE(SVG) + +#include "TransformationMatrix.h" +#include "FloatRect.h" + +#include "RenderObject.h" + +namespace WebCore { + +class FloatPoint; +class Path; +class RenderSVGContainer; +class SVGStyledTransformableElement; + +class RenderPath : public RenderObject +{ +public: + RenderPath(RenderStyle*, SVGStyledTransformableElement*); + virtual ~RenderPath(); + + // Hit-detection seperated for the fill and the stroke + bool fillContains(const FloatPoint&, bool requiresFill = true) const; + bool strokeContains(const FloatPoint&, bool requiresStroke = true) const; + + // Returns an unscaled bounding box (not even including localTransform()) for this vector path + virtual FloatRect relativeBBox(bool includeStroke = true) const; + + const Path& path() const; + void setPath(const Path& newPath); + + virtual bool isRenderPath() const { return true; } + virtual const char* renderName() const { return "RenderPath"; } + + bool calculateLocalTransform(); + virtual TransformationMatrix localTransform() const; + + virtual void layout(); + virtual IntRect absoluteClippedOverflowRect(); + virtual bool requiresLayer(); + virtual int lineHeight(bool b, bool isRootLineBox = false) const; + virtual int baselinePosition(bool b, bool isRootLineBox = false) const; + virtual void paint(PaintInfo&, int parentX, int parentY); + + virtual void absoluteRects(Vector<IntRect>&, int tx, int ty, bool topLevel = true); + virtual void absoluteQuads(Vector<FloatQuad>&, bool topLevel = true); + virtual void addFocusRingRects(GraphicsContext*, int tx, int ty); + + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); + + FloatRect drawMarkersIfNeeded(GraphicsContext*, const FloatRect&, const Path&) const; + +private: + FloatPoint mapAbsolutePointToLocal(const FloatPoint&) const; + + mutable Path m_path; + mutable FloatRect m_fillBBox; + mutable FloatRect m_strokeBbox; + FloatRect m_markerBounds; + TransformationMatrix m_localTransform; + IntRect m_absoluteBounds; +}; + +} + +#endif // ENABLE(SVG) +#endif + +// vim:ts=4:noet diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderReplaced.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderReplaced.cpp new file mode 100644 index 0000000..79df0f1 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderReplaced.cpp @@ -0,0 +1,416 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * Copyright (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved. + * + * 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" +#include "RenderReplaced.h" + +#include "GraphicsContext.h" +#include "RenderBlock.h" +#include "RenderLayer.h" +#include "RenderTheme.h" +#include "RenderView.h" + +using namespace std; + +namespace WebCore { + +typedef WTF::HashMap<const RenderReplaced*, IntRect> OverflowRectMap; +static OverflowRectMap* gOverflowRectMap = 0; + +const int cDefaultWidth = 300; +const int cDefaultHeight = 150; + +RenderReplaced::RenderReplaced(Node* node) + : RenderBox(node) + , m_intrinsicSize(cDefaultWidth, cDefaultHeight) + , m_selectionState(SelectionNone) + , m_hasOverflow(false) +{ + setReplaced(true); +} + +RenderReplaced::RenderReplaced(Node* node, const IntSize& intrinsicSize) + : RenderBox(node) + , m_intrinsicSize(intrinsicSize) + , m_selectionState(SelectionNone) + , m_hasOverflow(false) +{ + setReplaced(true); +} + +RenderReplaced::~RenderReplaced() +{ + if (m_hasOverflow) + gOverflowRectMap->remove(this); +} + +void RenderReplaced::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +{ + RenderBox::styleDidChange(diff, oldStyle); + + bool hadStyle = (oldStyle != 0); + float oldZoom = hadStyle ? oldStyle->effectiveZoom() : RenderStyle::initialZoom(); + if (hadStyle && style() && style()->effectiveZoom() != oldZoom) + intrinsicSizeChanged(); +} + +void RenderReplaced::layout() +{ + ASSERT(needsLayout()); + + IntRect oldBounds; + IntRect oldOutlineBox; + bool checkForRepaint = checkForRepaintDuringLayout(); + if (checkForRepaint) { + oldBounds = absoluteClippedOverflowRect(); + oldOutlineBox = absoluteOutlineBounds(); + } + + m_height = minimumReplacedHeight(); + + calcWidth(); + calcHeight(); + adjustOverflowForBoxShadow(); + + if (checkForRepaint) + repaintAfterLayoutIfNeeded(oldBounds, oldOutlineBox); + + setNeedsLayout(false); +} + +void RenderReplaced::intrinsicSizeChanged() +{ + int scaledWidth = static_cast<int>(cDefaultWidth * style()->effectiveZoom()); + int scaledHeight = static_cast<int>(cDefaultHeight * style()->effectiveZoom()); + m_intrinsicSize = IntSize(scaledWidth, scaledHeight); + setNeedsLayoutAndPrefWidthsRecalc(); +} + +void RenderReplaced::paint(PaintInfo& paintInfo, int tx, int ty) +{ + if (!shouldPaint(paintInfo, tx, ty)) + return; + + tx += m_x; + ty += m_y; + + if (hasBoxDecorations() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection)) + paintBoxDecorations(paintInfo, tx, ty); + + if (paintInfo.phase == PaintPhaseMask) { + paintMask(paintInfo, tx, ty); + return; + } + + if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth()) + paintOutline(paintInfo.context, tx, ty, width(), height(), style()); + + if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection) + return; + + if (!shouldPaintWithinRoot(paintInfo)) + return; + + bool drawSelectionTint = selectionState() != SelectionNone && !document()->printing(); + if (paintInfo.phase == PaintPhaseSelection) { + if (selectionState() == SelectionNone) + return; + drawSelectionTint = false; + } + + paintReplaced(paintInfo, tx, ty); + + if (drawSelectionTint) { + IntRect selectionPaintingRect = localSelectionRect(); + selectionPaintingRect.move(tx, ty); + paintInfo.context->fillRect(selectionPaintingRect, selectionBackgroundColor()); + } +} + +bool RenderReplaced::shouldPaint(PaintInfo& paintInfo, int& tx, int& ty) +{ + if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseOutline && paintInfo.phase != PaintPhaseSelfOutline + && paintInfo.phase != PaintPhaseSelection && paintInfo.phase != PaintPhaseMask) + return false; + + if (!shouldPaintWithinRoot(paintInfo)) + return false; + + // if we're invisible or haven't received a layout yet, then just bail. + if (style()->visibility() != VISIBLE) + return false; + + int currentTX = tx + m_x; + int currentTY = ty + m_y; + + // Early exit if the element touches the edges. + int top = currentTY + overflowTop(); + int bottom = currentTY + overflowHeight(); + if (isSelected() && m_inlineBoxWrapper) { + int selTop = ty + m_inlineBoxWrapper->root()->selectionTop(); + int selBottom = ty + selTop + m_inlineBoxWrapper->root()->selectionHeight(); + top = min(selTop, top); + bottom = max(selBottom, bottom); + } + + int os = 2 * maximalOutlineSize(paintInfo.phase); + if (currentTX + overflowLeft() >= paintInfo.rect.right() + os || currentTX + overflowWidth() <= paintInfo.rect.x() - os) + return false; + if (top >= paintInfo.rect.bottom() + os || bottom <= paintInfo.rect.y() - os) + return false; + + return true; +} + +void RenderReplaced::calcPrefWidths() +{ + ASSERT(prefWidthsDirty()); + + int paddingAndBorders = paddingLeft() + paddingRight() + borderLeft() + borderRight(); + int width = calcReplacedWidth(false) + paddingAndBorders; + + if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) + width = min(width, style()->maxWidth().value() + (style()->boxSizing() == CONTENT_BOX ? paddingAndBorders : 0)); + + if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent())) { + m_minPrefWidth = 0; + m_maxPrefWidth = width; + } else + m_minPrefWidth = m_maxPrefWidth = width; + + setPrefWidthsDirty(false); +} + +int RenderReplaced::lineHeight(bool, bool) const +{ + return height() + marginTop() + marginBottom(); +} + +int RenderReplaced::baselinePosition(bool, bool) const +{ + return height() + marginTop() + marginBottom(); +} + +unsigned RenderReplaced::caretMaxRenderedOffset() const +{ + return 1; +} + +VisiblePosition RenderReplaced::positionForCoordinates(int x, int y) +{ + InlineBox* box = inlineBoxWrapper(); + if (!box) + return VisiblePosition(element(), 0, DOWNSTREAM); + + // FIXME: This code is buggy if the replaced element is relative positioned. + + RootInlineBox* root = box->root(); + + int top = root->topOverflow(); + int bottom = root->nextRootBox() ? root->nextRootBox()->topOverflow() : root->bottomOverflow(); + + if (y + yPos() < top) + return VisiblePosition(element(), caretMinOffset(), DOWNSTREAM); // coordinates are above + + if (y + yPos() >= bottom) + return VisiblePosition(element(), caretMaxOffset(), DOWNSTREAM); // coordinates are below + + if (element()) { + if (x <= width() / 2) + return VisiblePosition(element(), 0, DOWNSTREAM); + return VisiblePosition(element(), 1, DOWNSTREAM); + } + + return RenderBox::positionForCoordinates(x, y); +} + +IntRect RenderReplaced::selectionRect(bool clipToVisibleContent) +{ + ASSERT(!needsLayout()); + + if (!isSelected()) + return IntRect(); + + IntRect rect = localSelectionRect(); + if (clipToVisibleContent) + computeAbsoluteRepaintRect(rect); + else { + FloatPoint absPos = localToAbsoluteForContent(FloatPoint()); + rect.move(absPos.x(), absPos.y()); + } + + return rect; +} + +IntRect RenderReplaced::localSelectionRect(bool checkWhetherSelected) const +{ + if (checkWhetherSelected && !isSelected()) + return IntRect(); + + if (!m_inlineBoxWrapper) + // We're a block-level replaced element. Just return our own dimensions. + return IntRect(0, 0, width(), height() + borderTopExtra() + borderBottomExtra()); + + RenderBlock* cb = containingBlock(); + if (!cb) + return IntRect(); + + RootInlineBox* root = m_inlineBoxWrapper->root(); + return IntRect(0, root->selectionTop() - yPos(), width(), root->selectionHeight()); +} + +void RenderReplaced::setSelectionState(SelectionState s) +{ + m_selectionState = s; + if (m_inlineBoxWrapper) { + RootInlineBox* line = m_inlineBoxWrapper->root(); + if (line) + line->setHasSelectedChildren(isSelected()); + } + + containingBlock()->setSelectionState(s); +} + +bool RenderReplaced::isSelected() const +{ + SelectionState s = selectionState(); + if (s == SelectionNone) + return false; + if (s == SelectionInside) + return true; + + int selectionStart, selectionEnd; + selectionStartEnd(selectionStart, selectionEnd); + if (s == SelectionStart) + return selectionStart == 0; + + int end = element()->hasChildNodes() ? element()->childNodeCount() : 1; + if (s == SelectionEnd) + return selectionEnd == end; + if (s == SelectionBoth) + return selectionStart == 0 && selectionEnd == end; + + ASSERT(0); + return false; +} + +IntSize RenderReplaced::intrinsicSize() const +{ + return m_intrinsicSize; +} + +void RenderReplaced::setIntrinsicSize(const IntSize& size) +{ + m_intrinsicSize = size; +} + +void RenderReplaced::adjustOverflowForBoxShadow() +{ + IntRect overflow; + for (ShadowData* boxShadow = style()->boxShadow(); boxShadow; boxShadow = boxShadow->next) { + IntRect shadow = borderBox(); + shadow.move(boxShadow->x, boxShadow->y); + shadow.inflate(boxShadow->blur); + overflow.unite(shadow); + } + + if (!overflow.isEmpty()) { + if (!gOverflowRectMap) + gOverflowRectMap = new OverflowRectMap(); + overflow.unite(borderBox()); + gOverflowRectMap->set(this, overflow); + m_hasOverflow = true; + } else if (m_hasOverflow) { + gOverflowRectMap->remove(this); + m_hasOverflow = false; + } +} + +int RenderReplaced::overflowHeight(bool) const +{ + if (m_hasOverflow) { + IntRect *r = &gOverflowRectMap->find(this)->second; + return r->height() + r->y(); + } + + return height(); +} + +int RenderReplaced::overflowWidth(bool) const +{ + if (m_hasOverflow) { + IntRect *r = &gOverflowRectMap->find(this)->second; + return r->width() + r->x(); + } + + return width(); +} + +int RenderReplaced::overflowLeft(bool) const +{ + if (m_hasOverflow) + return gOverflowRectMap->get(this).x(); + + return 0; +} + +int RenderReplaced::overflowTop(bool) const +{ + if (m_hasOverflow) + return gOverflowRectMap->get(this).y(); + + return 0; +} + +IntRect RenderReplaced::overflowRect(bool) const +{ + if (m_hasOverflow) + return gOverflowRectMap->find(this)->second; + + return borderBox(); +} + +IntRect RenderReplaced::absoluteClippedOverflowRect() +{ + if (style()->visibility() != VISIBLE && !enclosingLayer()->hasVisibleContent()) + return IntRect(); + + // The selectionRect can project outside of the overflowRect, so use + // that for repainting to avoid selection painting glitches + IntRect r = localSelectionRect(false); + + RenderView* v = view(); + if (v) + r.move(v->layoutDelta()); + + if (style()) { + if (style()->hasAppearance()) + // The theme may wish to inflate the rect used when repainting. + theme()->adjustRepaintRect(this, r); + if (v) + r.inflate(style()->outlineSize()); + } + computeAbsoluteRepaintRect(r); + return r; +} + +} diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderReplaced.h b/src/3rdparty/webkit/WebCore/rendering/RenderReplaced.h new file mode 100644 index 0000000..3186718 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderReplaced.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. + * + * 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. + * + */ + +#ifndef RenderReplaced_h +#define RenderReplaced_h + +#include "RenderBox.h" + +namespace WebCore { + +class RenderReplaced : public RenderBox { +public: + RenderReplaced(Node*); + RenderReplaced(Node*, const IntSize& intrinsicSize); + virtual ~RenderReplaced(); + + virtual const char* renderName() const { return "RenderReplaced"; } + + virtual int lineHeight(bool firstLine, bool isRootLineBox = false) const; + virtual int baselinePosition(bool firstLine, bool isRootLineBox = false) const; + + virtual void calcPrefWidths(); + + virtual void layout(); + virtual int minimumReplacedHeight() const { return 0; } + + virtual void paint(PaintInfo&, int tx, int ty); + virtual void paintReplaced(PaintInfo&, int /*tx*/, int /*ty*/) { } + + virtual IntSize intrinsicSize() const; + + virtual int overflowHeight(bool includeInterior = true) const; + virtual int overflowWidth(bool includeInterior = true) const; + virtual int overflowLeft(bool includeInterior = true) const; + virtual int overflowTop(bool includeInterior = true) const; + virtual IntRect overflowRect(bool includeInterior = true) const; + + virtual IntRect absoluteClippedOverflowRect(); + + virtual unsigned caretMaxRenderedOffset() const; + virtual VisiblePosition positionForCoordinates(int x, int y); + + virtual bool canBeSelectionLeaf() const { return true; } + virtual SelectionState selectionState() const { return static_cast<SelectionState>(m_selectionState); } + virtual void setSelectionState(SelectionState); + virtual IntRect selectionRect(bool clipToVisibleContent = true); + + bool isSelected() const; + +protected: + virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + + void setIntrinsicSize(const IntSize&); + virtual void intrinsicSizeChanged(); + + bool shouldPaint(PaintInfo&, int& tx, int& ty); + void adjustOverflowForBoxShadow(); + IntRect localSelectionRect(bool checkWhetherSelected = true) const; + +private: + IntSize m_intrinsicSize; + + unsigned m_selectionState : 3; // SelectionState + bool m_hasOverflow : 1; +}; + +} + +#endif diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderReplica.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderReplica.cpp new file mode 100644 index 0000000..9556980 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderReplica.cpp @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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 + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "RenderReplica.h" + +#include "RenderLayer.h" + +namespace WebCore { + +RenderReplica::RenderReplica(Node* n) +: RenderBox(n) +{} + +RenderReplica::~RenderReplica() +{} + +void RenderReplica::layout() +{ + IntRect box = parent()->borderBox(); + m_x = box.x(); + m_y = box.y(); + m_width = box.width(); + m_height = box.height(); + setNeedsLayout(false); +} + +void RenderReplica::calcPrefWidths() +{ + m_minPrefWidth = parent()->width(); + m_maxPrefWidth = m_minPrefWidth; + setPrefWidthsDirty(false); +} + +void RenderReplica::paint(PaintInfo& paintInfo, int tx, int ty) +{ + if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseMask) + return; + + tx += m_x; + ty += m_y; + + if (paintInfo.phase == PaintPhaseForeground) + // Turn around and paint the parent layer. Use temporary clipRects, so that the layer doesn't end up caching clip rects + // computing using the wrong rootLayer + layer()->parent()->paintLayer(layer()->transform() ? layer()->parent() : layer()->enclosingTransformedAncestor(), + paintInfo.context, paintInfo.rect, + true, PaintRestrictionNone, 0, + true, // appliedTransform + true); // temporaryClipRects + else if (paintInfo.phase == PaintPhaseMask) + paintMask(paintInfo, tx, ty); +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderReplica.h b/src/3rdparty/webkit/WebCore/rendering/RenderReplica.h new file mode 100644 index 0000000..d3a9026 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderReplica.h @@ -0,0 +1,53 @@ +/* + * Copyright (C) 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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 + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RenderReplica_h +#define RenderReplica_h + +#include "RenderBox.h" + +namespace WebCore { + +class RenderReplica : public RenderBox { +public: + RenderReplica(Node*); + virtual ~RenderReplica(); + + virtual const char* renderName() const { return "RenderReplica"; } + + virtual bool requiresLayer() { return true; } + + virtual void layout(); + virtual void calcPrefWidths(); + + virtual void paint(PaintInfo&, int tx, int ty); +}; + +} // namespace WebCore + +#endif // RenderReplica_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderSVGBlock.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderSVGBlock.cpp new file mode 100644 index 0000000..f065c44 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderSVGBlock.cpp @@ -0,0 +1,63 @@ +/* + * This file is part of the WebKit project. + * + * Copyright (C) 2006 Apple Computer, Inc. + * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> + * + * 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(SVG) +#include "RenderSVGBlock.h" + +#include "SVGElement.h" + +namespace WebCore { + +RenderSVGBlock::RenderSVGBlock(SVGElement* node) + : RenderBlock(node) +{ +} + +void RenderSVGBlock::setStyle(PassRefPtr<RenderStyle> style) +{ + RefPtr<RenderStyle> useStyle = style; + + // SVG text layout code expects us to be a block-level style element. + if (useStyle->display() == NONE) + setChildrenInline(false); + else if (useStyle->isDisplayInlineType()) { + RefPtr<RenderStyle> newStyle = RenderStyle::create(); + newStyle->inheritFrom(useStyle.get()); + newStyle->setDisplay(BLOCK); + useStyle = newStyle.release(); + } + + RenderBlock::setStyle(useStyle.release()); + setReplaced(false); + + //FIXME: Once overflow rules are supported by SVG we should + //probably map the CSS overflow rules rather than just ignoring + //them + setHasOverflowClip(false); +} + +} + +#endif // ENABLE(SVG) diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderSVGBlock.h b/src/3rdparty/webkit/WebCore/rendering/RenderSVGBlock.h new file mode 100644 index 0000000..d545fd0 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderSVGBlock.h @@ -0,0 +1,41 @@ +/* + * This file is part of the WebKit project. + * + * Copyright (C) 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef RenderSVGBlock_h +#define RenderSVGBlock_h +#if ENABLE(SVG) + +#include "RenderBlock.h" + +namespace WebCore { + +class SVGElement; + +class RenderSVGBlock : public RenderBlock { +public: + RenderSVGBlock(SVGElement*); + virtual void setStyle(PassRefPtr<RenderStyle>); +}; + +} +#endif // ENABLE(SVG) +#endif // !RenderSVGBlock_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderSVGContainer.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderSVGContainer.cpp new file mode 100644 index 0000000..2d6b57f --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderSVGContainer.cpp @@ -0,0 +1,438 @@ +/* + Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org> + 2004, 2005, 2007, 2008 Rob Buis <buis@kde.org> + 2007 Eric Seidel <eric@webkit.org> + + This file is part of the KDE project + + 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 + aint 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(SVG) +#include "RenderSVGContainer.h" + +#include "AXObjectCache.h" +#include "GraphicsContext.h" +#include "RenderView.h" +#include "SVGRenderSupport.h" +#include "SVGResourceFilter.h" +#include "SVGStyledElement.h" +#include "SVGURIReference.h" + +namespace WebCore { + +RenderSVGContainer::RenderSVGContainer(SVGStyledElement* node) + : RenderObject(node) + , m_firstChild(0) + , m_lastChild(0) + , m_width(0) + , m_height(0) + , m_drawsContents(true) +{ + setReplaced(true); +} + +RenderSVGContainer::~RenderSVGContainer() +{ +} + +bool RenderSVGContainer::canHaveChildren() const +{ + return true; +} + +void RenderSVGContainer::addChild(RenderObject* newChild, RenderObject* beforeChild) +{ + insertChildNode(newChild, beforeChild); +} + +void RenderSVGContainer::removeChild(RenderObject* oldChild) +{ + // We do this here instead of in removeChildNode, since the only extremely low-level uses of remove/appendChildNode + // cannot affect the positioned object list, and the floating object list is irrelevant (since the list gets cleared on + // layout anyway). + oldChild->removeFromObjectLists(); + + removeChildNode(oldChild); +} + +void RenderSVGContainer::destroy() +{ + destroyLeftoverChildren(); + RenderObject::destroy(); +} + +void RenderSVGContainer::destroyLeftoverChildren() +{ + while (m_firstChild) { + // Destroy any anonymous children remaining in the render tree, as well as implicit (shadow) DOM elements like those used in the engine-based text fields. + if (m_firstChild->element()) + m_firstChild->element()->setRenderer(0); + + m_firstChild->destroy(); + } +} + +RenderObject* RenderSVGContainer::removeChildNode(RenderObject* oldChild, bool fullRemove) +{ + ASSERT(oldChild->parent() == this); + + // So that we'll get the appropriate dirty bit set (either that a normal flow child got yanked or + // that a positioned child got yanked). We also repaint, so that the area exposed when the child + // disappears gets repainted properly. + if (!documentBeingDestroyed() && fullRemove) { + oldChild->setNeedsLayoutAndPrefWidthsRecalc(); + oldChild->repaint(); + } + + // If we have a line box wrapper, delete it. + oldChild->deleteLineBoxWrapper(); + + if (!documentBeingDestroyed() && fullRemove) { + // If oldChild is the start or end of the selection, then clear the selection to + // avoid problems of invalid pointers. + // FIXME: The SelectionController should be responsible for this when it + // is notified of DOM mutations. + if (oldChild->isSelectionBorder()) + view()->clearSelection(); + } + + // remove the child + if (oldChild->previousSibling()) + oldChild->previousSibling()->setNextSibling(oldChild->nextSibling()); + if (oldChild->nextSibling()) + oldChild->nextSibling()->setPreviousSibling(oldChild->previousSibling()); + + if (m_firstChild == oldChild) + m_firstChild = oldChild->nextSibling(); + if (m_lastChild == oldChild) + m_lastChild = oldChild->previousSibling(); + + oldChild->setPreviousSibling(0); + oldChild->setNextSibling(0); + oldChild->setParent(0); + + if (AXObjectCache::accessibilityEnabled()) + document()->axObjectCache()->childrenChanged(this); + + return oldChild; +} + +void RenderSVGContainer::appendChildNode(RenderObject* newChild, bool) +{ + ASSERT(!newChild->parent()); + ASSERT(newChild->element()->isSVGElement()); + + newChild->setParent(this); + RenderObject* lChild = m_lastChild; + + if (lChild) { + newChild->setPreviousSibling(lChild); + lChild->setNextSibling(newChild); + } else + m_firstChild = newChild; + + m_lastChild = newChild; + + newChild->setNeedsLayoutAndPrefWidthsRecalc(); // Goes up the containing block hierarchy. + if (!normalChildNeedsLayout()) + setChildNeedsLayout(true); // We may supply the static position for an absolute positioned child. + + if (AXObjectCache::accessibilityEnabled()) + document()->axObjectCache()->childrenChanged(this); +} + +void RenderSVGContainer::insertChildNode(RenderObject* child, RenderObject* beforeChild, bool) +{ + if (!beforeChild) { + appendChildNode(child); + return; + } + + ASSERT(!child->parent()); + ASSERT(beforeChild->parent() == this); + ASSERT(child->element()->isSVGElement()); + + if (beforeChild == m_firstChild) + m_firstChild = child; + + RenderObject* prev = beforeChild->previousSibling(); + child->setNextSibling(beforeChild); + beforeChild->setPreviousSibling(child); + if (prev) + prev->setNextSibling(child); + child->setPreviousSibling(prev); + + child->setParent(this); + + child->setNeedsLayoutAndPrefWidthsRecalc(); + if (!normalChildNeedsLayout()) + setChildNeedsLayout(true); // We may supply the static position for an absolute positioned child. + + if (AXObjectCache::accessibilityEnabled()) + document()->axObjectCache()->childrenChanged(this); +} + +bool RenderSVGContainer::drawsContents() const +{ + return m_drawsContents; +} + +void RenderSVGContainer::setDrawsContents(bool drawsContents) +{ + m_drawsContents = drawsContents; +} + +TransformationMatrix RenderSVGContainer::localTransform() const +{ + return m_localTransform; +} + +bool RenderSVGContainer::requiresLayer() +{ + // Only allow an <svg> element to generate a layer when it's positioned in a non-SVG context + return false; +} + +int RenderSVGContainer::lineHeight(bool, bool) const +{ + return height() + marginTop() + marginBottom(); +} + +int RenderSVGContainer::baselinePosition(bool, bool) const +{ + return height() + marginTop() + marginBottom(); +} + +bool RenderSVGContainer::calculateLocalTransform() +{ + // subclasses can override this to add transform support + return false; +} + +void RenderSVGContainer::layout() +{ + ASSERT(needsLayout()); + + // Arbitrary affine transforms are incompatible with LayoutState. + view()->disableLayoutState(); + + IntRect oldBounds; + IntRect oldOutlineBox; + bool checkForRepaint = checkForRepaintDuringLayout() && selfWillPaint(); + if (checkForRepaint) { + oldBounds = m_absoluteBounds; + oldOutlineBox = absoluteOutlineBounds(); + } + + calculateLocalTransform(); + + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { + // Only force our kids to layout if we're being asked to relayout as a result of a parent changing + // FIXME: We should be able to skip relayout of non-relative kids when only bounds size has changed + // that's a possible future optimization using LayoutState + // http://bugs.webkit.org/show_bug.cgi?id=15391 + if (selfNeedsLayout()) + child->setNeedsLayout(true); + + child->layoutIfNeeded(); + ASSERT(!child->needsLayout()); + } + + calcBounds(); + + if (checkForRepaint) + repaintAfterLayoutIfNeeded(oldBounds, oldOutlineBox); + + view()->enableLayoutState(); + setNeedsLayout(false); +} + +int RenderSVGContainer::calcReplacedWidth() const +{ + switch (style()->width().type()) { + case Fixed: + return max(0, style()->width().value()); + case Percent: + { + const int cw = containingBlockWidth(); + return cw > 0 ? max(0, style()->width().calcMinValue(cw)) : 0; + } + default: + return 0; + } +} + +int RenderSVGContainer::calcReplacedHeight() const +{ + switch (style()->height().type()) { + case Fixed: + return max(0, style()->height().value()); + case Percent: + { + RenderBlock* cb = containingBlock(); + return style()->height().calcValue(cb->availableHeight()); + } + default: + return 0; + } +} + +void RenderSVGContainer::applyContentTransforms(PaintInfo& paintInfo) +{ + if (!localTransform().isIdentity()) + paintInfo.context->concatCTM(localTransform()); +} + +void RenderSVGContainer::applyAdditionalTransforms(PaintInfo&) +{ + // no-op +} + +void RenderSVGContainer::calcBounds() +{ + m_width = calcReplacedWidth(); + m_height = calcReplacedHeight(); + m_absoluteBounds = absoluteClippedOverflowRect(); +} + +bool RenderSVGContainer::selfWillPaint() const +{ +#if ENABLE(SVG_FILTERS) + const SVGRenderStyle* svgStyle = style()->svgStyle(); + SVGResourceFilter* filter = getFilterById(document(), svgStyle->filter()); + if (filter) + return true; +#endif + return false; +} + +void RenderSVGContainer::paint(PaintInfo& paintInfo, int, int) +{ + if (paintInfo.context->paintingDisabled() || !drawsContents()) + return; + + // Spec: groups w/o children still may render filter content. + if (!firstChild() && !selfWillPaint()) + return; + + paintInfo.context->save(); + applyContentTransforms(paintInfo); + + SVGResourceFilter* filter = 0; + PaintInfo savedInfo(paintInfo); + + FloatRect boundingBox = relativeBBox(true); + if (paintInfo.phase == PaintPhaseForeground) + prepareToRenderSVGContent(this, paintInfo, boundingBox, filter); + + applyAdditionalTransforms(paintInfo); + + // default implementation. Just pass paint through to the children + PaintInfo childInfo(paintInfo); + childInfo.paintingRoot = paintingRootForChildren(paintInfo); + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) + child->paint(childInfo, 0, 0); + + if (paintInfo.phase == PaintPhaseForeground) + finishRenderSVGContent(this, paintInfo, boundingBox, filter, savedInfo.context); + + paintInfo.context->restore(); + + if ((paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth() && style()->visibility() == VISIBLE) + paintOutline(paintInfo.context, m_absoluteBounds.x(), m_absoluteBounds.y(), m_absoluteBounds.width(), m_absoluteBounds.height(), style()); +} + +TransformationMatrix RenderSVGContainer::viewportTransform() const +{ + return TransformationMatrix(); +} + +IntRect RenderSVGContainer::absoluteClippedOverflowRect() +{ + FloatRect repaintRect; + + for (RenderObject* current = firstChild(); current != 0; current = current->nextSibling()) + repaintRect.unite(current->absoluteClippedOverflowRect()); + +#if ENABLE(SVG_FILTERS) + // Filters can expand the bounding box + SVGResourceFilter* filter = getFilterById(document(), style()->svgStyle()->filter()); + if (filter) + repaintRect.unite(filter->filterBBoxForItemBBox(repaintRect)); +#endif + + if (!repaintRect.isEmpty()) + repaintRect.inflate(1); // inflate 1 pixel for antialiasing + + return enclosingIntRect(repaintRect); +} + +void RenderSVGContainer::addFocusRingRects(GraphicsContext* graphicsContext, int, int) +{ + graphicsContext->addFocusRingRect(m_absoluteBounds); +} + +void RenderSVGContainer::absoluteRects(Vector<IntRect>& rects, int, int, bool) +{ + rects.append(absoluteClippedOverflowRect()); +} + +void RenderSVGContainer::absoluteQuads(Vector<FloatQuad>& quads, bool) +{ + quads.append(absoluteClippedOverflowRect()); +} + +FloatRect RenderSVGContainer::relativeBBox(bool includeStroke) const +{ + FloatRect rect; + + RenderObject* current = firstChild(); + for (; current != 0; current = current->nextSibling()) { + FloatRect childBBox = current->relativeBBox(includeStroke); + FloatRect mappedBBox = current->localTransform().mapRect(childBBox); + + // <svg> can have a viewBox contributing to the bbox + if (current->isSVGContainer()) + mappedBBox = static_cast<RenderSVGContainer*>(current)->viewportTransform().mapRect(mappedBBox); + + rect.unite(mappedBBox); + } + + return rect; +} + +bool RenderSVGContainer::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction) +{ + for (RenderObject* child = lastChild(); child; child = child->previousSibling()) { + if (child->nodeAtPoint(request, result, _x, _y, _tx, _ty, hitTestAction)) { + updateHitTestResult(result, IntPoint(_x - _tx, _y - _ty)); + return true; + } + } + + // Spec: Only graphical elements can be targeted by the mouse, period. + // 16.4: "If there are no graphics elements whose relevant graphics content is under the pointer (i.e., there is no target element), the event is not dispatched." + return false; +} + +} + +#endif // ENABLE(SVG) + +// vim:ts=4:noet diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderSVGContainer.h b/src/3rdparty/webkit/WebCore/rendering/RenderSVGContainer.h new file mode 100644 index 0000000..48f5694 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderSVGContainer.h @@ -0,0 +1,122 @@ +/* + Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org> + 2004, 2005, 2007 Rob Buis <buis@kde.org> + + This file is part of the KDE project + + 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 + aint 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. +*/ + +#ifndef RenderSVGContainer_h +#define RenderSVGContainer_h + +#if ENABLE(SVG) + +#include "RenderPath.h" +#include "SVGPreserveAspectRatio.h" + +namespace WebCore { + +class SVGElement; + +class RenderSVGContainer : public RenderObject { +public: + RenderSVGContainer(SVGStyledElement*); + ~RenderSVGContainer(); + + virtual RenderObject* firstChild() const { return m_firstChild; } + virtual RenderObject* lastChild() const { return m_lastChild; } + + virtual int width() const { return m_width; } + virtual int height() const { return m_height; } + + virtual bool canHaveChildren() const; + virtual void addChild(RenderObject* newChild, RenderObject* beforeChild = 0); + virtual void removeChild(RenderObject*); + + virtual void destroy(); + void destroyLeftoverChildren(); + + virtual RenderObject* removeChildNode(RenderObject*, bool fullRemove = true); + virtual void appendChildNode(RenderObject*, bool fullAppend = true); + virtual void insertChildNode(RenderObject* child, RenderObject* before, bool fullInsert = true); + + // Designed for speed. Don't waste time doing a bunch of work like layer updating and repainting when we know that our + // change in parentage is not going to affect anything. + virtual void moveChildNode(RenderObject* child) { appendChildNode(child->parent()->removeChildNode(child, false), false); } + + virtual void calcPrefWidths() { setPrefWidthsDirty(false); } + + // Some containers do not want it's children + // to be drawn, because they may be 'referenced' + // Example: <marker> children in SVG + void setDrawsContents(bool); + bool drawsContents() const; + + virtual bool isSVGContainer() const { return true; } + virtual const char* renderName() const { return "RenderSVGContainer"; } + + virtual bool requiresLayer(); + virtual int lineHeight(bool b, bool isRootLineBox = false) const; + virtual int baselinePosition(bool b, bool isRootLineBox = false) const; + + virtual void layout(); + virtual void paint(PaintInfo&, int parentX, int parentY); + + virtual IntRect absoluteClippedOverflowRect(); + virtual void absoluteRects(Vector<IntRect>& rects, int tx, int ty, bool topLevel = true); + virtual void absoluteQuads(Vector<FloatQuad>&, bool topLevel = true); + virtual void addFocusRingRects(GraphicsContext*, int tx, int ty); + + FloatRect relativeBBox(bool includeStroke = true) const; + + virtual bool calculateLocalTransform(); + virtual TransformationMatrix localTransform() const; + virtual TransformationMatrix viewportTransform() const; + + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); + +protected: + virtual void applyContentTransforms(PaintInfo&); + virtual void applyAdditionalTransforms(PaintInfo&); + + void calcBounds(); + +private: + int calcReplacedWidth() const; + int calcReplacedHeight() const; + + RenderObject* m_firstChild; + RenderObject* m_lastChild; + + int m_width; + int m_height; + + bool selfWillPaint() const; + + bool m_drawsContents : 1; + +protected: + IntRect m_absoluteBounds; + TransformationMatrix m_localTransform; +}; + +} // namespace WebCore + +#endif // ENABLE(SVG) +#endif // RenderSVGContainer_h + +// vim:ts=4:noet diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderSVGGradientStop.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderSVGGradientStop.cpp new file mode 100644 index 0000000..d0dc881 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderSVGGradientStop.cpp @@ -0,0 +1,72 @@ +/* + * This file is part of the WebKit project. + * + * Copyright (C) 2007 Eric Seidel <eric@webkit.org> + * + * 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(SVG) +#include "RenderSVGGradientStop.h" + +#include "SVGGradientElement.h" +#include "SVGNames.h" +#include "SVGStopElement.h" + +namespace WebCore { + +using namespace SVGNames; + +RenderSVGGradientStop::RenderSVGGradientStop(SVGStopElement* element) + : RenderObject(element) +{ +} + +RenderSVGGradientStop::~RenderSVGGradientStop() +{ +} + +void RenderSVGGradientStop::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +{ + RenderObject::styleDidChange(diff, oldStyle); + + // <stop> elements should only be allowed to make renderers under gradient elements + // but I can imagine a few cases we might not be catching, so let's not crash if our parent isn't a gradient. + if (SVGGradientElement* gradient = gradientElement()) { + if (SVGResource* resource = gradient->canvasResource()) + resource->invalidate(); + } +} + +void RenderSVGGradientStop::layout() +{ + setNeedsLayout(false); +} + +SVGGradientElement* RenderSVGGradientStop::gradientElement() const +{ + Node* parentNode = element()->parent(); + if (parentNode->hasTagName(linearGradientTag) || parentNode->hasTagName(radialGradientTag)) + return static_cast<SVGGradientElement*>(parentNode); + return 0; +} + +} + +#endif // ENABLE(SVG) diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderSVGGradientStop.h b/src/3rdparty/webkit/WebCore/rendering/RenderSVGGradientStop.h new file mode 100644 index 0000000..86de6d0 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderSVGGradientStop.h @@ -0,0 +1,59 @@ +/* + * This file is part of the WebKit project. + * + * Copyright (C) 2007 Eric Seidel <eric@webkit.org> + * + * 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. + * + */ + +#ifndef RenderSVGGradientStop_h +#define RenderSVGGradientStop_h + +#if ENABLE(SVG) +#include "RenderObject.h" + +namespace WebCore { + + class SVGGradientElement; + class SVGStopElement; + + // This class exists mostly so we can hear about gradient stop style changes + class RenderSVGGradientStop : public RenderObject { + public: + RenderSVGGradientStop(SVGStopElement*); + virtual ~RenderSVGGradientStop(); + + virtual const char* renderName() const { return "RenderSVGGradientStop"; } + + virtual void layout(); + + // This override is needed to prevent crashing on <svg><stop /></svg> + // RenderObject's default impl asks the parent Object and RenderSVGRoot + // asks all child RenderObjects for overflow rects, thus infinite loop. + // https://bugs.webkit.org/show_bug.cgi?id=20400 + virtual IntRect absoluteClippedOverflowRect() { return IntRect(); } + + protected: + virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + + private: + SVGGradientElement* gradientElement() const; + }; +} + +#endif // ENABLE(SVG) +#endif // RenderSVGGradientStop_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderSVGHiddenContainer.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderSVGHiddenContainer.cpp new file mode 100644 index 0000000..4a7d6b2 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderSVGHiddenContainer.cpp @@ -0,0 +1,116 @@ +/* + * This file is part of the WebKit project. + * + * Copyright (C) 2007 Eric Seidel <eric@webkit.org> + * + * 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(SVG) +#include "RenderSVGHiddenContainer.h" + +#include "RenderPath.h" +#include "SVGStyledElement.h" + +namespace WebCore { + +RenderSVGHiddenContainer::RenderSVGHiddenContainer(SVGStyledElement* element) + : RenderSVGContainer(element) +{ +} + +RenderSVGHiddenContainer::~RenderSVGHiddenContainer() +{ +} + +bool RenderSVGHiddenContainer::requiresLayer() +{ + return false; +} + +int RenderSVGHiddenContainer::lineHeight(bool, bool) const +{ + return 0; +} + +int RenderSVGHiddenContainer::baselinePosition(bool, bool) const +{ + return 0; +} + +void RenderSVGHiddenContainer::layout() +{ + ASSERT(needsLayout()); + + // Layout our kids to prevent a kid from being marked as needing layout + // then never being asked to layout. + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { + if (selfNeedsLayout()) + child->setNeedsLayout(true); + + child->layoutIfNeeded(); + ASSERT(!child->needsLayout()); + } + + setNeedsLayout(false); +} + +void RenderSVGHiddenContainer::paint(PaintInfo&, int, int) +{ + // This subtree does not paint. +} + +IntRect RenderSVGHiddenContainer::absoluteClippedOverflowRect() +{ + return IntRect(); +} + +void RenderSVGHiddenContainer::absoluteRects(Vector<IntRect>&, int, int, bool) +{ + // This subtree does not take up space or paint +} + +void RenderSVGHiddenContainer::absoluteQuads(Vector<FloatQuad>&, bool) +{ + // This subtree does not take up space or paint +} + +TransformationMatrix RenderSVGHiddenContainer::absoluteTransform() const +{ + return TransformationMatrix(); +} + +TransformationMatrix RenderSVGHiddenContainer::localTransform() const +{ + return TransformationMatrix(); +} + +bool RenderSVGHiddenContainer::nodeAtPoint(const HitTestRequest&, HitTestResult&, int, int, int, int, HitTestAction) +{ + return false; +} + +FloatRect RenderSVGHiddenContainer::relativeBBox(bool) const +{ + return FloatRect(); +} + +} + +#endif // ENABLE(SVG) diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderSVGHiddenContainer.h b/src/3rdparty/webkit/WebCore/rendering/RenderSVGHiddenContainer.h new file mode 100644 index 0000000..24c7ddd --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderSVGHiddenContainer.h @@ -0,0 +1,67 @@ +/* + * This file is part of the WebKit project. + * + * Copyright (C) 2007 Eric Seidel <eric@webkit.org> + * + * 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. + * + */ + +#ifndef RenderSVGHiddenContainer_h +#define RenderSVGHiddenContainer_h + +#if ENABLE(SVG) + +#include "RenderSVGContainer.h" + +namespace WebCore { + + class SVGStyledElement; + + // This class is for containers which are never drawn, but do need to support style + // <defs>, <linearGradient>, <radialGradient> are all good examples + class RenderSVGHiddenContainer : public RenderSVGContainer { + public: + RenderSVGHiddenContainer(SVGStyledElement*); + virtual ~RenderSVGHiddenContainer(); + + virtual bool isSVGContainer() const { return true; } + virtual bool isSVGHiddenContainer() const { return true; } + + virtual const char* renderName() const { return "RenderSVGHiddenContainer"; } + + virtual bool requiresLayer(); + + virtual int lineHeight(bool b, bool isRootLineBox = false) const; + virtual int baselinePosition(bool b, bool isRootLineBox = false) const; + + virtual void layout(); + virtual void paint(PaintInfo&, int parentX, int parentY); + + virtual IntRect absoluteClippedOverflowRect(); + virtual void absoluteRects(Vector<IntRect>& rects, int tx, int ty, bool topLevel = true); + virtual void absoluteQuads(Vector<FloatQuad>&, bool topLevel = true); + + virtual TransformationMatrix absoluteTransform() const; + virtual TransformationMatrix localTransform() const; + + virtual FloatRect relativeBBox(bool includeStroke = true) const; + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); + }; +} + +#endif // ENABLE(SVG) +#endif // RenderSVGHiddenContainer_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderSVGImage.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderSVGImage.cpp new file mode 100644 index 0000000..61f65a0 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderSVGImage.cpp @@ -0,0 +1,280 @@ +/* + Copyright (C) 2006 Alexander Kellett <lypanov@kde.org> + Copyright (C) 2006 Apple Computer, Inc. + Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> + Copyright (C) 2007, 2008 Rob Buis <buis@kde.org> + + This file is part of the WebKit project + + 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(SVG) +#include "RenderSVGImage.h" + +#include "Attr.h" +#include "FloatConversion.h" +#include "GraphicsContext.h" +#include "PointerEventsHitRules.h" +#include "SVGImageElement.h" +#include "SVGLength.h" +#include "SVGPreserveAspectRatio.h" +#include "SVGRenderSupport.h" +#include "SVGResourceClipper.h" +#include "SVGResourceFilter.h" +#include "SVGResourceMasker.h" + +namespace WebCore { + +RenderSVGImage::RenderSVGImage(SVGImageElement* impl) + : RenderImage(impl) +{ +} + +RenderSVGImage::~RenderSVGImage() +{ +} + +void RenderSVGImage::adjustRectsForAspectRatio(FloatRect& destRect, FloatRect& srcRect, SVGPreserveAspectRatio* aspectRatio) +{ + float origDestWidth = destRect.width(); + float origDestHeight = destRect.height(); + if (aspectRatio->meetOrSlice() == SVGPreserveAspectRatio::SVG_MEETORSLICE_MEET) { + float widthToHeightMultiplier = srcRect.height() / srcRect.width(); + if (origDestHeight > (origDestWidth * widthToHeightMultiplier)) { + destRect.setHeight(origDestWidth * widthToHeightMultiplier); + switch(aspectRatio->align()) { + case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID: + case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID: + case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID: + destRect.setY(destRect.y() + origDestHeight / 2.0f - destRect.height() / 2.0f); + break; + case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX: + case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX: + case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX: + destRect.setY(destRect.y() + origDestHeight - destRect.height()); + break; + } + } + if (origDestWidth > (origDestHeight / widthToHeightMultiplier)) { + destRect.setWidth(origDestHeight / widthToHeightMultiplier); + switch(aspectRatio->align()) { + case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN: + case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID: + case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX: + destRect.setX(destRect.x() + origDestWidth / 2.0f - destRect.width() / 2.0f); + break; + case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN: + case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID: + case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX: + destRect.setX(destRect.x() + origDestWidth - destRect.width()); + break; + } + } + } else if (aspectRatio->meetOrSlice() == SVGPreserveAspectRatio::SVG_MEETORSLICE_SLICE) { + float widthToHeightMultiplier = srcRect.height() / srcRect.width(); + // if the destination height is less than the height of the image we'll be drawing + if (origDestHeight < (origDestWidth * widthToHeightMultiplier)) { + float destToSrcMultiplier = srcRect.width() / destRect.width(); + srcRect.setHeight(destRect.height() * destToSrcMultiplier); + switch(aspectRatio->align()) { + case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMID: + case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID: + case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID: + srcRect.setY(destRect.y() + image()->height() / 2.0f - srcRect.height() / 2.0f); + break; + case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMINYMAX: + case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX: + case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX: + srcRect.setY(destRect.y() + image()->height() - srcRect.height()); + break; + } + } + // if the destination width is less than the width of the image we'll be drawing + if (origDestWidth < (origDestHeight / widthToHeightMultiplier)) { + float destToSrcMultiplier = srcRect.height() / destRect.height(); + srcRect.setWidth(destRect.width() * destToSrcMultiplier); + switch(aspectRatio->align()) { + case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMIN: + case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMID: + case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMIDYMAX: + srcRect.setX(destRect.x() + image()->width() / 2.0f - srcRect.width() / 2.0f); + break; + case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMIN: + case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMID: + case SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_XMAXYMAX: + srcRect.setX(destRect.x() + image()->width() - srcRect.width()); + break; + } + } + } +} + +bool RenderSVGImage::calculateLocalTransform() +{ + TransformationMatrix oldTransform = m_localTransform; + m_localTransform = static_cast<SVGStyledTransformableElement*>(element())->animatedLocalTransform(); + return (m_localTransform != oldTransform); +} + +void RenderSVGImage::layout() +{ + ASSERT(needsLayout()); + + IntRect oldBounds; + IntRect oldOutlineBox; + bool checkForRepaint = checkForRepaintDuringLayout(); + if (checkForRepaint) { + oldBounds = absoluteClippedOverflowRect(); + oldOutlineBox = absoluteOutlineBounds(); + } + + calculateLocalTransform(); + + // minimum height + m_height = errorOccurred() ? intrinsicSize().height() : 0; + + calcWidth(); + calcHeight(); + + SVGImageElement* image = static_cast<SVGImageElement*>(node()); + m_localBounds = FloatRect(image->x().value(image), image->y().value(image), image->width().value(image), image->height().value(image)); + + calculateAbsoluteBounds(); + + if (checkForRepaint) + repaintAfterLayoutIfNeeded(oldBounds, oldOutlineBox); + + setNeedsLayout(false); +} + +void RenderSVGImage::paint(PaintInfo& paintInfo, int, int) +{ + if (paintInfo.context->paintingDisabled() || style()->visibility() == HIDDEN) + return; + + paintInfo.context->save(); + paintInfo.context->concatCTM(localTransform()); + + if (paintInfo.phase == PaintPhaseForeground) { + SVGResourceFilter* filter = 0; + + PaintInfo savedInfo(paintInfo); + + prepareToRenderSVGContent(this, paintInfo, m_localBounds, filter); + + FloatRect destRect = m_localBounds; + FloatRect srcRect(0, 0, image()->width(), image()->height()); + + SVGImageElement* imageElt = static_cast<SVGImageElement*>(node()); + if (imageElt->preserveAspectRatio()->align() != SVGPreserveAspectRatio::SVG_PRESERVEASPECTRATIO_NONE) + adjustRectsForAspectRatio(destRect, srcRect, imageElt->preserveAspectRatio()); + + paintInfo.context->drawImage(image(), destRect, srcRect); + + finishRenderSVGContent(this, paintInfo, m_localBounds, filter, savedInfo.context); + } + + paintInfo.context->restore(); +} + +bool RenderSVGImage::nodeAtPoint(const HitTestRequest&, HitTestResult& result, int _x, int _y, int, int, HitTestAction hitTestAction) +{ + // We only draw in the forground phase, so we only hit-test then. + if (hitTestAction != HitTestForeground) + return false; + + PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_IMAGE_HITTESTING, style()->pointerEvents()); + + bool isVisible = (style()->visibility() == VISIBLE); + if (isVisible || !hitRules.requireVisible) { + double localX, localY; + absoluteTransform().inverse().map(_x, _y, &localX, &localY); + + if (hitRules.canHitFill) { + if (m_localBounds.contains(narrowPrecisionToFloat(localX), narrowPrecisionToFloat(localY))) { + updateHitTestResult(result, IntPoint(_x, _y)); + return true; + } + } + } + + return false; +} + +bool RenderSVGImage::requiresLayer() +{ + return false; +} + +FloatRect RenderSVGImage::relativeBBox(bool) const +{ + return m_localBounds; +} + +void RenderSVGImage::imageChanged(WrappedImagePtr image, const IntRect* rect) +{ + RenderImage::imageChanged(image, rect); + + // We override to invalidate a larger rect, since SVG images can draw outside their "bounds" + repaintRectangle(absoluteClippedOverflowRect()); +} + +void RenderSVGImage::calculateAbsoluteBounds() +{ + // FIXME: broken with CSS transforms + FloatRect absoluteRect = absoluteTransform().mapRect(relativeBBox(true)); + +#if ENABLE(SVG_FILTERS) + // Filters can expand the bounding box + SVGResourceFilter* filter = getFilterById(document(), style()->svgStyle()->filter()); + if (filter) + absoluteRect.unite(filter->filterBBoxForItemBBox(absoluteRect)); +#endif + + if (!absoluteRect.isEmpty()) + absoluteRect.inflate(1); // inflate 1 pixel for antialiasing + + m_absoluteBounds = enclosingIntRect(absoluteRect); +} + +IntRect RenderSVGImage::absoluteClippedOverflowRect() +{ + return m_absoluteBounds; +} + +void RenderSVGImage::addFocusRingRects(GraphicsContext* graphicsContext, int, int) +{ + // this is called from paint() after the localTransform has already been applied + IntRect contentRect = enclosingIntRect(relativeBBox()); + graphicsContext->addFocusRingRect(contentRect); +} + +void RenderSVGImage::absoluteRects(Vector<IntRect>& rects, int, int, bool) +{ + rects.append(absoluteClippedOverflowRect()); +} + +void RenderSVGImage::absoluteQuads(Vector<FloatQuad>& quads, bool) +{ + quads.append(FloatRect(absoluteClippedOverflowRect())); +} + +} + +#endif // ENABLE(SVG) diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderSVGImage.h b/src/3rdparty/webkit/WebCore/rendering/RenderSVGImage.h new file mode 100644 index 0000000..de8a2d3 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderSVGImage.h @@ -0,0 +1,75 @@ +/* + Copyright (C) 2006 Alexander Kellett <lypanov@kde.org> + Copyright (C) 2006 Apple Computer, Inc. + Copyright (C) 2007 Rob Buis <buis@kde.org> + + This file is part of the WebKit project. + + 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. +*/ + +#ifndef RenderSVGImage_h +#define RenderSVGImage_h + +#if ENABLE(SVG) + +#include "TransformationMatrix.h" +#include "FloatRect.h" +#include "RenderImage.h" + +namespace WebCore { + + class SVGImageElement; + class SVGPreserveAspectRatio; + + class RenderSVGImage : public RenderImage { + public: + RenderSVGImage(SVGImageElement*); + virtual ~RenderSVGImage(); + + virtual TransformationMatrix localTransform() const { return m_localTransform; } + + virtual FloatRect relativeBBox(bool includeStroke = true) const; + virtual IntRect absoluteClippedOverflowRect(); + virtual void absoluteRects(Vector<IntRect>&, int tx, int ty, bool topLevel = true); + virtual void absoluteQuads(Vector<FloatQuad>&, bool topLevel = true); + virtual void addFocusRingRects(GraphicsContext*, int tx, int ty); + + virtual void imageChanged(WrappedImagePtr, const IntRect* = 0); + void adjustRectsForAspectRatio(FloatRect& destRect, FloatRect& srcRect, SVGPreserveAspectRatio*); + + virtual void layout(); + virtual void paint(PaintInfo&, int parentX, int parentY); + + bool requiresLayer(); + + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int _x, int _y, int _tx, int _ty, HitTestAction); + + bool calculateLocalTransform(); + + private: + void calculateAbsoluteBounds(); + TransformationMatrix m_localTransform; + FloatRect m_localBounds; + IntRect m_absoluteBounds; + }; + +} // namespace WebCore + +#endif // ENABLE(SVG) +#endif // RenderSVGImage_h + +// vim:ts=4:noet diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderSVGInline.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderSVGInline.cpp new file mode 100644 index 0000000..11da004 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderSVGInline.cpp @@ -0,0 +1,58 @@ +/* + * This file is part of the WebKit project. + * + * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz> + * (C) 2006 Apple Computer Inc. + * + * 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(SVG) +#include "RenderSVGInline.h" + +#include "SVGInlineFlowBox.h" + +namespace WebCore { + +RenderSVGInline::RenderSVGInline(Node* n) + : RenderInline(n) +{ +} + +InlineBox* RenderSVGInline::createInlineBox(bool makePlaceHolderBox, bool isRootLineBox, bool isOnlyRun) +{ + ASSERT(!(!isRootLineBox && (isReplaced() || makePlaceHolderBox))); + ASSERT(isInlineFlow()); + + InlineFlowBox* flowBox = new (renderArena()) SVGInlineFlowBox(this); + + if (!m_firstLineBox) + m_firstLineBox = m_lastLineBox = flowBox; + else { + m_lastLineBox->setNextLineBox(flowBox); + flowBox->setPreviousLineBox(m_lastLineBox); + m_lastLineBox = flowBox; + } + + return flowBox; +} + +} + +#endif // ENABLE(SVG) diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderSVGInline.h b/src/3rdparty/webkit/WebCore/rendering/RenderSVGInline.h new file mode 100644 index 0000000..42fdafc --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderSVGInline.h @@ -0,0 +1,41 @@ +/* + * This file is part of the WebKit project. + * + * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz> + * (C) 2006 Apple Computer Inc. + * + * 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. + * + */ + +#ifndef RenderSVGInline_h +#define RenderSVGInline_h + +#if ENABLE(SVG) +#include "RenderInline.h" + +namespace WebCore { +class RenderSVGInline : public RenderInline { +public: + RenderSVGInline(Node*); + virtual const char* renderName() const { return "RenderSVGInline"; } + virtual InlineBox* createInlineBox(bool makePlaceHolderBox, bool isRootLineBox, bool isOnlyRun = false); + virtual bool requiresLayer() { return false; } + }; +} + +#endif // ENABLE(SVG) +#endif // !RenderSVGTSpan_H diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderSVGInlineText.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderSVGInlineText.cpp new file mode 100644 index 0000000..2212d78 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderSVGInlineText.cpp @@ -0,0 +1,180 @@ +/* + * This file is part of the WebKit project. + * + * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz> + * (C) 2006 Apple Computer Inc. + * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> + * (C) 2008 Rob Buis <buis@kde.org> + * + * 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(SVG) +#include "RenderSVGInlineText.h" + +#include "FloatConversion.h" +#include "RenderBlock.h" +#include "RenderSVGRoot.h" +#include "SVGInlineTextBox.h" +#include "SVGRootInlineBox.h" + +namespace WebCore { + +static inline bool isChildOfHiddenContainer(RenderObject* start) +{ + while (start) { + if (start->isSVGHiddenContainer()) + return true; + + start = start->parent(); + } + + return false; +} + +RenderSVGInlineText::RenderSVGInlineText(Node* n, PassRefPtr<StringImpl> str) + : RenderText(n, str) +{ +} + +void RenderSVGInlineText::absoluteRects(Vector<IntRect>& rects, int, int, bool) +{ + rects.append(computeAbsoluteRectForRange(0, textLength())); +} + +void RenderSVGInlineText::absoluteQuads(Vector<FloatQuad>& quads, bool) +{ + quads.append(FloatRect(computeAbsoluteRectForRange(0, textLength()))); +} + +IntRect RenderSVGInlineText::selectionRect(bool) +{ + ASSERT(!needsLayout()); + + IntRect rect; + if (selectionState() == SelectionNone) + return rect; + + // Early exit if we're ie. a <text> within a <defs> section. + if (isChildOfHiddenContainer(this)) + return rect; + + // Now calculate startPos and endPos for painting selection. + // We include a selection while endPos > 0 + int startPos, endPos; + if (selectionState() == SelectionInside) { + // We are fully selected. + startPos = 0; + endPos = textLength(); + } else { + selectionStartEnd(startPos, endPos); + if (selectionState() == SelectionStart) + endPos = textLength(); + else if (selectionState() == SelectionEnd) + startPos = 0; + } + + if (startPos == endPos) + return rect; + + return computeAbsoluteRectForRange(startPos, endPos); +} + +IntRect RenderSVGInlineText::computeAbsoluteRectForRange(int startPos, int endPos) +{ + IntRect rect; + + RenderBlock* cb = containingBlock(); + if (!cb || !cb->container()) + return rect; + + RenderSVGRoot* root = findSVGRootObject(parent()); + if (!root) + return rect; + + for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) + rect.unite(box->selectionRect(0, 0, startPos, endPos)); + + // Mimic RenderBox::computeAbsoluteRepaintRect() functionality. But only the subset needed for SVG and respecting SVG transformations. + FloatPoint absPos = cb->container()->localToAbsolute(); + + // Remove HTML parent translation offsets here! These need to be retrieved from the RenderSVGRoot object. + // But do take the containingBlocks's container position into account, ie. SVG text in scrollable <div>. + TransformationMatrix htmlParentCtm = root->RenderContainer::absoluteTransform(); + + FloatRect fixedRect(narrowPrecisionToFloat(rect.x() + absPos.x() - xPos() - htmlParentCtm.e()), + narrowPrecisionToFloat(rect.y() + absPos.y() - yPos() - htmlParentCtm.f()), rect.width(), rect.height()); + // FIXME: broken with CSS transforms + return enclosingIntRect(absoluteTransform().mapRect(fixedRect)); +} + +InlineTextBox* RenderSVGInlineText::createInlineTextBox() +{ + return new (renderArena()) SVGInlineTextBox(this); +} + +IntRect RenderSVGInlineText::localCaretRect(InlineBox* inlineBox, int caretOffset, int* extraWidthToEndOfLine) +{ + // SVG doesn't have any editable content where a caret rect would be needed + return IntRect(); +} + +VisiblePosition RenderSVGInlineText::positionForCoordinates(int x, int y) +{ + SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(firstTextBox()); + + if (!textBox || textLength() == 0) + return VisiblePosition(element(), 0, DOWNSTREAM); + + SVGRootInlineBox* rootBox = textBox->svgRootInlineBox(); + RenderObject* object = rootBox ? rootBox->object() : 0; + + if (!object) + return VisiblePosition(element(), 0, DOWNSTREAM); + + int offset = 0; + + for (SVGInlineTextBox* box = textBox; box; box = static_cast<SVGInlineTextBox*>(box->nextTextBox())) { + if (box->svgCharacterHitsPosition(x + object->xPos(), y + object->yPos(), offset)) { + // If we're not at the end/start of the box, stop looking for other selected boxes. + if (box->direction() == LTR) { + if (offset <= (int) box->end() + 1) + break; + } else { + if (offset > (int) box->start()) + break; + } + } + } + + return VisiblePosition(element(), offset, DOWNSTREAM); +} + +void RenderSVGInlineText::destroy() +{ + if (!documentBeingDestroyed()) { + setNeedsLayoutAndPrefWidthsRecalc(); + repaint(); + } + RenderText::destroy(); +} + +} + +#endif // ENABLE(SVG) diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderSVGInlineText.h b/src/3rdparty/webkit/WebCore/rendering/RenderSVGInlineText.h new file mode 100644 index 0000000..cc37166 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderSVGInlineText.h @@ -0,0 +1,59 @@ +/* + * This file is part of the WebKit project. + * + * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz> + * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * (C) 2008 Rob Buis <buis@kde.org> + * + * 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. + * + */ + +#ifndef RenderSVGInlineText_h +#define RenderSVGInlineText_h + +#if ENABLE(SVG) + +#include "RenderText.h" + +namespace WebCore { +class RenderSVGInlineText : public RenderText { +public: + RenderSVGInlineText(Node*, PassRefPtr<StringImpl>); + virtual const char* renderName() const { return "RenderSVGInlineText"; } + + virtual void absoluteRects(Vector<IntRect>& rects, int tx, int ty, bool topLevel = true); + virtual void absoluteQuads(Vector<FloatQuad>&, bool topLevel = true); + + virtual bool requiresLayer() { return false; } + virtual IntRect selectionRect(bool clipToVisibleContent = true); + virtual bool isSVGText() const { return true; } + virtual InlineTextBox* createInlineTextBox(); + + virtual IntRect localCaretRect(InlineBox*, int caretOffset, int* extraWidthToEndOfLine = 0); + virtual VisiblePosition positionForCoordinates(int x, int y); + + virtual void destroy(); + +private: + IntRect computeAbsoluteRectForRange(int startPos, int endPos); +}; + +} + +#endif // ENABLE(SVG) + +#endif // !RenderSVGInlineText_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderSVGRoot.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderSVGRoot.cpp new file mode 100644 index 0000000..457ac02 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderSVGRoot.cpp @@ -0,0 +1,347 @@ +/* + Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org> + 2004, 2005, 2007, 2008, 2009 Rob Buis <buis@kde.org> + 2007 Eric Seidel <eric@webkit.org> + + This file is part of the KDE project + + 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 + aint 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(SVG) +#include "RenderSVGRoot.h" + +#include "GraphicsContext.h" +#include "RenderPath.h" +#include "RenderSVGContainer.h" +#include "RenderView.h" +#include "SVGLength.h" +#include "SVGRenderSupport.h" +#include "SVGResourceClipper.h" +#include "SVGResourceFilter.h" +#include "SVGResourceMasker.h" +#include "SVGSVGElement.h" +#include "SVGStyledElement.h" +#include "SVGURIReference.h" + +using namespace std; + +namespace WebCore { + +RenderSVGRoot::RenderSVGRoot(SVGStyledElement* node) + : RenderContainer(node) +{ + setReplaced(true); +} + +RenderSVGRoot::~RenderSVGRoot() +{ +} + +int RenderSVGRoot::lineHeight(bool, bool) const +{ + return height() + marginTop() + marginBottom(); +} + +int RenderSVGRoot::baselinePosition(bool, bool) const +{ + return height() + marginTop() + marginBottom(); +} + +void RenderSVGRoot::calcPrefWidths() +{ + ASSERT(prefWidthsDirty()); + + int paddingAndBorders = paddingLeft() + paddingRight() + borderLeft() + borderRight(); + int width = calcReplacedWidth(false) + paddingAndBorders; + + if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) + width = min(width, style()->maxWidth().value() + (style()->boxSizing() == CONTENT_BOX ? paddingAndBorders : 0)); + + if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent())) { + m_minPrefWidth = 0; + m_maxPrefWidth = width; + } else + m_minPrefWidth = m_maxPrefWidth = width; + + setPrefWidthsDirty(false); +} + +void RenderSVGRoot::layout() +{ + ASSERT(needsLayout()); + + calcViewport(); + + // Arbitrary affine transforms are incompatible with LayoutState. + view()->disableLayoutState(); + + IntRect oldBounds = m_absoluteBounds; + IntRect oldOutlineBox; + bool checkForRepaint = checkForRepaintDuringLayout() && selfNeedsLayout(); + if (checkForRepaint) + oldOutlineBox = absoluteOutlineBounds(); + + calcWidth(); + calcHeight(); + + m_absoluteBounds = absoluteClippedOverflowRect(); + SVGSVGElement* svg = static_cast<SVGSVGElement*>(element()); + m_width = static_cast<int>(m_width * svg->currentScale()); + m_height = static_cast<int>(m_height * svg->currentScale()); + + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { + if (selfNeedsLayout()) // either bounds or transform changed, force kids to relayout + child->setNeedsLayout(true); + + child->layoutIfNeeded(); + ASSERT(!child->needsLayout()); + } + + if (checkForRepaint) + repaintAfterLayoutIfNeeded(oldBounds, oldOutlineBox); + + view()->enableLayoutState(); + setNeedsLayout(false); +} + +void RenderSVGRoot::applyContentTransforms(PaintInfo& paintInfo, int parentX, int parentY) +{ + // Translate from parent offsets (html renderers) to a relative transform (svg renderers) + IntPoint origin; + origin.move(parentX, parentY); + origin.move(m_x, m_y); + origin.move(borderLeft(), borderTop()); + origin.move(paddingLeft(), paddingTop()); + + if (origin.x() || origin.y()) { + paintInfo.context->concatCTM(TransformationMatrix().translate(origin.x(), origin.y())); + paintInfo.rect.move(-origin.x(), -origin.y()); + } + + // Respect scroll offset caused by html parents + TransformationMatrix ctm = RenderContainer::absoluteTransform(); + paintInfo.rect.move(static_cast<int>(ctm.e()), static_cast<int>(ctm.f())); + + SVGSVGElement* svg = static_cast<SVGSVGElement*>(element()); + paintInfo.context->concatCTM(TransformationMatrix().scale(svg->currentScale())); + + if (!viewport().isEmpty()) { + if (style()->overflowX() != OVISIBLE) + paintInfo.context->clip(enclosingIntRect(viewport())); // FIXME: Eventually we'll want float-precision clipping + + paintInfo.context->concatCTM(TransformationMatrix().translate(viewport().x(), viewport().y())); + } + + paintInfo.context->concatCTM(TransformationMatrix().translate(svg->currentTranslate().x(), svg->currentTranslate().y())); +} + +void RenderSVGRoot::paint(PaintInfo& paintInfo, int parentX, int parentY) +{ + if (paintInfo.context->paintingDisabled()) + return; + + calcViewport(); + + SVGSVGElement* svg = static_cast<SVGSVGElement*>(element()); + // A value of zero disables rendering of the element. + if (viewport().width() <= 0. || viewport().height() <= 0.) + return; + + // This should only exist for <svg> renderers + if (hasBoxDecorations() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection)) + paintBoxDecorations(paintInfo, m_x + parentX, m_y + parentY); + + if (!firstChild()) { +#if ENABLE(SVG_FILTERS) + // Spec: groups w/o children still may render filter content. + const SVGRenderStyle* svgStyle = style()->svgStyle(); + SVGResourceFilter* filter = getFilterById(document(), svgStyle->filter()); + if (!filter) +#endif + return; + } + + RenderObject::PaintInfo childPaintInfo(paintInfo); + childPaintInfo.context->save(); + + applyContentTransforms(childPaintInfo, parentX, parentY); + + SVGResourceFilter* filter = 0; + + FloatRect boundingBox = relativeBBox(true); + if (childPaintInfo.phase == PaintPhaseForeground) + prepareToRenderSVGContent(this, childPaintInfo, boundingBox, filter); + + childPaintInfo.context->concatCTM(svg->viewBoxToViewTransform(width(), height())); + RenderContainer::paint(childPaintInfo, 0, 0); + + if (childPaintInfo.phase == PaintPhaseForeground) + finishRenderSVGContent(this, childPaintInfo, boundingBox, filter, paintInfo.context); + + childPaintInfo.context->restore(); + + if ((childPaintInfo.phase == PaintPhaseOutline || childPaintInfo.phase == PaintPhaseSelfOutline) && style()->outlineWidth() && style()->visibility() == VISIBLE) + paintOutline(childPaintInfo.context, m_absoluteBounds.x(), m_absoluteBounds.y(), m_absoluteBounds.width(), m_absoluteBounds.height(), style()); +} + +FloatRect RenderSVGRoot::viewport() const +{ + return m_viewport; +} + +void RenderSVGRoot::calcViewport() +{ + SVGElement* svgelem = static_cast<SVGElement*>(element()); + if (svgelem->hasTagName(SVGNames::svgTag)) { + SVGSVGElement* svg = static_cast<SVGSVGElement*>(element()); + + if (!selfNeedsLayout() && !svg->hasRelativeValues()) + return; + + float w, h; + SVGLength width = svg->width(); + if (width.unitType() == LengthTypePercentage && svg->hasSetContainerSize()) + w = svg->relativeWidthValue(); + else + w = width.value(svg); + + SVGLength height = svg->height(); + if (height.unitType() == LengthTypePercentage && svg->hasSetContainerSize()) + h = svg->relativeHeightValue(); + else + h = height.value(svg); + + m_viewport = FloatRect(0, 0, w, h); + } +} + +IntRect RenderSVGRoot::absoluteClippedOverflowRect() +{ + IntRect repaintRect; + + for (RenderObject* current = firstChild(); current != 0; current = current->nextSibling()) + repaintRect.unite(current->absoluteClippedOverflowRect()); + +#if ENABLE(SVG_FILTERS) + // Filters can expand the bounding box + SVGResourceFilter* filter = getFilterById(document(), style()->svgStyle()->filter()); + if (filter) + repaintRect.unite(enclosingIntRect(filter->filterBBoxForItemBBox(repaintRect))); +#endif + + return repaintRect; +} + +void RenderSVGRoot::addFocusRingRects(GraphicsContext* graphicsContext, int, int) +{ + graphicsContext->addFocusRingRect(m_absoluteBounds); +} + +void RenderSVGRoot::absoluteRects(Vector<IntRect>& rects, int, int) +{ + for (RenderObject* current = firstChild(); current != 0; current = current->nextSibling()) + current->absoluteRects(rects, 0, 0); +} + +void RenderSVGRoot::absoluteQuads(Vector<FloatQuad>& quads, bool) +{ + for (RenderObject* current = firstChild(); current != 0; current = current->nextSibling()) + current->absoluteQuads(quads); +} + +TransformationMatrix RenderSVGRoot::absoluteTransform() const +{ + TransformationMatrix ctm = RenderContainer::absoluteTransform(); + ctm.translate(m_x, m_y); + SVGSVGElement* svg = static_cast<SVGSVGElement*>(element()); + ctm.scale(svg->currentScale()); + ctm.translate(svg->currentTranslate().x(), svg->currentTranslate().y()); + ctm.translate(viewport().x(), viewport().y()); + return svg->viewBoxToViewTransform(width(), height()) * ctm; +} + +FloatRect RenderSVGRoot::relativeBBox(bool includeStroke) const +{ + FloatRect rect; + + RenderObject* current = firstChild(); + for (; current != 0; current = current->nextSibling()) { + FloatRect childBBox = current->relativeBBox(includeStroke); + FloatRect mappedBBox = current->localTransform().mapRect(childBBox); + // <svg> can have a viewBox contributing to the bbox + if (current->isSVGContainer()) + mappedBBox = static_cast<RenderSVGContainer*>(current)->viewportTransform().mapRect(mappedBBox); + rect.unite(mappedBBox); + } + + return rect; +} + +TransformationMatrix RenderSVGRoot::localTransform() const +{ + return TransformationMatrix(); +} + +bool RenderSVGRoot::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction) +{ + TransformationMatrix ctm = RenderContainer::absoluteTransform(); + + int sx = (_tx - static_cast<int>(ctm.e())); // scroll offset + int sy = (_ty - static_cast<int>(ctm.f())); // scroll offset + + if (!viewport().isEmpty() + && style()->overflowX() == OHIDDEN + && style()->overflowY() == OHIDDEN) { + int tx = m_x - _tx + sx; + int ty = m_y - _ty + sy; + + // Check if we need to do anything at all. + IntRect overflowBox = overflowRect(false); + overflowBox.move(tx, ty); + ctm.translate(viewport().x(), viewport().y()); + double localX, localY; + ctm.inverse().map(_x - _tx, _y - _ty, &localX, &localY); + if (!overflowBox.contains((int)localX, (int)localY)) + return false; + } + + for (RenderObject* child = lastChild(); child; child = child->previousSibling()) { + if (child->nodeAtPoint(request, result, _x - sx, _y - sy, 0, 0, hitTestAction)) { + updateHitTestResult(result, IntPoint(_x - _tx, _y - _ty)); + return true; + } + } + + // Spec: Only graphical elements can be targeted by the mouse, period. + // 16.4: "If there are no graphics elements whose relevant graphics content is under the pointer (i.e., there is no target element), the event is not dispatched." + return false; +} + +void RenderSVGRoot::position(InlineBox* box) +{ + RenderContainer::position(box); + if (m_absoluteBounds.isEmpty()) + setNeedsLayout(true, false); +} + +} + +#endif // ENABLE(SVG) + +// vim:ts=4:noet diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderSVGRoot.h b/src/3rdparty/webkit/WebCore/rendering/RenderSVGRoot.h new file mode 100644 index 0000000..acac78b --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderSVGRoot.h @@ -0,0 +1,82 @@ +/* + Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org> + 2004, 2005, 2007 Rob Buis <buis@kde.org> + + This file is part of the KDE project + + 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 + aint 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. +*/ + +#ifndef RenderSVGRoot_h +#define RenderSVGRoot_h + +#if ENABLE(SVG) +#include "RenderContainer.h" +#include "FloatRect.h" + +namespace WebCore { + +class SVGStyledElement; +class TransformationMatrix; + +class RenderSVGRoot : public RenderContainer { +public: + RenderSVGRoot(SVGStyledElement*); + ~RenderSVGRoot(); + + virtual bool isSVGRoot() const { return true; } + virtual const char* renderName() const { return "RenderSVGRoot"; } + + virtual int lineHeight(bool b, bool isRootLineBox = false) const; + virtual int baselinePosition(bool b, bool isRootLineBox = false) const; + virtual void calcPrefWidths(); + + virtual void layout(); + virtual void paint(PaintInfo&, int parentX, int parentY); + + virtual IntRect absoluteClippedOverflowRect(); + virtual void absoluteRects(Vector<IntRect>& rects, int tx, int ty); + virtual void absoluteQuads(Vector<FloatQuad>&, bool topLevel = true); + virtual void addFocusRingRects(GraphicsContext*, int tx, int ty); + + virtual TransformationMatrix absoluteTransform() const; + + bool fillContains(const FloatPoint&) const; + bool strokeContains(const FloatPoint&) const; + FloatRect relativeBBox(bool includeStroke = true) const; + + virtual TransformationMatrix localTransform() const; + + FloatRect viewport() const; + + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); + + virtual void position(InlineBox*); + +private: + void calcViewport(); + void applyContentTransforms(PaintInfo&, int parentX, int parentY); + + FloatRect m_viewport; + IntRect m_absoluteBounds; +}; + +} // namespace WebCore + +#endif // ENABLE(SVG) +#endif // RenderSVGRoot_h + +// vim:ts=4:noet diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderSVGTSpan.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderSVGTSpan.cpp new file mode 100644 index 0000000..fd2d3dd --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderSVGTSpan.cpp @@ -0,0 +1,82 @@ +/* + * This file is part of the WebKit project. + * + * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz> + * (C) 2006 Apple Computer Inc. + * + * 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(SVG) +#include "RenderSVGTSpan.h" + +#include "FloatRect.h" +#include "SVGInlineTextBox.h" +#include "SVGRootInlineBox.h" + +namespace WebCore { + +RenderSVGTSpan::RenderSVGTSpan(Node* n) + : RenderSVGInline(n) +{ +} + +void RenderSVGTSpan::absoluteRects(Vector<IntRect>& rects, int, int, bool) +{ + InlineRunBox* firstBox = firstLineBox(); + + SVGRootInlineBox* rootBox = firstBox ? static_cast<SVGInlineTextBox*>(firstBox)->svgRootInlineBox() : 0; + RenderObject* object = rootBox ? rootBox->object() : 0; + + if (!object) + return; + + int xRef = object->xPos() + xPos(); + int yRef = object->yPos() + yPos(); + + for (InlineRunBox* curr = firstBox; curr; curr = curr->nextLineBox()) { + FloatRect rect(xRef + curr->xPos(), yRef + curr->yPos(), curr->width(), curr->height()); + // FIXME: broken with CSS transforms + rects.append(enclosingIntRect(absoluteTransform().mapRect(rect))); + } +} + +void RenderSVGTSpan::absoluteQuads(Vector<FloatQuad>& quads, bool) +{ + InlineRunBox* firstBox = firstLineBox(); + + SVGRootInlineBox* rootBox = firstBox ? static_cast<SVGInlineTextBox*>(firstBox)->svgRootInlineBox() : 0; + RenderObject* object = rootBox ? rootBox->object() : 0; + + if (!object) + return; + + int xRef = object->xPos() + xPos(); + int yRef = object->yPos() + yPos(); + + for (InlineRunBox* curr = firstBox; curr; curr = curr->nextLineBox()) { + FloatRect rect(xRef + curr->xPos(), yRef + curr->yPos(), curr->width(), curr->height()); + // FIXME: broken with CSS transforms + quads.append(absoluteTransform().mapRect(rect)); + } +} + +} + +#endif // ENABLE(SVG) diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderSVGTSpan.h b/src/3rdparty/webkit/WebCore/rendering/RenderSVGTSpan.h new file mode 100644 index 0000000..d34cd2f --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderSVGTSpan.h @@ -0,0 +1,41 @@ +/* + * This file is part of the WebKit project. + * + * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz> + * (C) 2006 Apple Computer Inc. + * + * 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. + * + */ + +#ifndef RenderSVGTSpan_h +#define RenderSVGTSpan_h + +#if ENABLE(SVG) +#include "RenderSVGInline.h" + +namespace WebCore { +class RenderSVGTSpan : public RenderSVGInline { +public: + RenderSVGTSpan(Node*); + virtual const char* renderName() const { return "RenderSVGTSpan"; } + virtual void absoluteRects(Vector<IntRect>& rects, int tx, int ty, bool topLevel = true); + virtual void absoluteQuads(Vector<FloatQuad>&, bool topLevel = true); +}; +} + +#endif // ENABLE(SVG) +#endif // !RenderSVGTSpan_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderSVGText.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderSVGText.cpp new file mode 100644 index 0000000..1a1196b --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderSVGText.cpp @@ -0,0 +1,242 @@ +/* + * This file is part of the WebKit project. + * + * Copyright (C) 2006 Apple Computer, Inc. + * 2006 Alexander Kellett <lypanov@kde.org> + * 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz> + * 2007 Nikolas Zimmermann <zimmermann@kde.org> + * 2008 Rob Buis <buis@kde.org> + * + * 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(SVG) +#include "RenderSVGText.h" + +#include "FloatConversion.h" +#include "GraphicsContext.h" +#include "PointerEventsHitRules.h" +#include "RenderSVGRoot.h" +#include "SimpleFontData.h" +#include "SVGLengthList.h" +#include "SVGResourceFilter.h" +#include "SVGRootInlineBox.h" +#include "SVGTextElement.h" +#include "SVGTransformList.h" +#include "SVGURIReference.h" + +namespace WebCore { + +RenderSVGText::RenderSVGText(SVGTextElement* node) + : RenderSVGBlock(node) +{ +} + +IntRect RenderSVGText::absoluteClippedOverflowRect() +{ + FloatRect repaintRect = absoluteTransform().mapRect(relativeBBox(true)); + +#if ENABLE(SVG_FILTERS) + // Filters can expand the bounding box + SVGResourceFilter* filter = getFilterById(document(), style()->svgStyle()->filter()); + if (filter) + repaintRect.unite(filter->filterBBoxForItemBBox(repaintRect)); +#endif + + if (!repaintRect.isEmpty()) + repaintRect.inflate(1); // inflate 1 pixel for antialiasing + + return enclosingIntRect(repaintRect); +} + +bool RenderSVGText::requiresLayer() +{ + return false; +} + +bool RenderSVGText::calculateLocalTransform() +{ + TransformationMatrix oldTransform = m_localTransform; + m_localTransform = static_cast<SVGTextElement*>(element())->animatedLocalTransform(); + return (oldTransform != m_localTransform); +} + +void RenderSVGText::layout() +{ + ASSERT(needsLayout()); + + // FIXME: This is a hack to avoid the RenderBlock::layout() partial repainting code which is not (yet) SVG aware + setNeedsLayout(true); + + IntRect oldBounds; + IntRect oldOutlineBox; + bool checkForRepaint = checkForRepaintDuringLayout(); + if (checkForRepaint) { + oldBounds = m_absoluteBounds; + oldOutlineBox = absoluteOutlineBounds(); + } + + // Best guess for a relative starting point + SVGTextElement* text = static_cast<SVGTextElement*>(element()); + int xOffset = (int)(text->x()->getFirst().value(text)); + int yOffset = (int)(text->y()->getFirst().value(text)); + setPos(xOffset, yOffset); + + calculateLocalTransform(); + + RenderBlock::layout(); + + m_absoluteBounds = absoluteClippedOverflowRect(); + + bool repainted = false; + if (checkForRepaint) + repainted = repaintAfterLayoutIfNeeded(oldBounds, oldOutlineBox); + + setNeedsLayout(false); +} + +InlineBox* RenderSVGText::createInlineBox(bool, bool, bool) +{ + ASSERT(!isInlineFlow()); + InlineFlowBox* flowBox = new (renderArena()) SVGRootInlineBox(this); + + if (!m_firstLineBox) + m_firstLineBox = m_lastLineBox = flowBox; + else { + m_lastLineBox->setNextLineBox(flowBox); + flowBox->setPreviousLineBox(m_lastLineBox); + m_lastLineBox = flowBox; + } + + return flowBox; +} + +bool RenderSVGText::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction) +{ + PointerEventsHitRules hitRules(PointerEventsHitRules::SVG_TEXT_HITTESTING, style()->pointerEvents()); + bool isVisible = (style()->visibility() == VISIBLE); + if (isVisible || !hitRules.requireVisible) { + if ((hitRules.canHitStroke && (style()->svgStyle()->hasStroke() || !hitRules.requireStroke)) + || (hitRules.canHitFill && (style()->svgStyle()->hasFill() || !hitRules.requireFill))) { + TransformationMatrix totalTransform = absoluteTransform(); + double localX, localY; + totalTransform.inverse().map(_x, _y, &localX, &localY); + FloatPoint hitPoint(_x, _y); + return RenderBlock::nodeAtPoint(request, result, (int)localX, (int)localY, _tx, _ty, hitTestAction); + } + } + + return false; +} + +void RenderSVGText::absoluteRects(Vector<IntRect>& rects, int, int, bool) +{ + RenderSVGRoot* root = findSVGRootObject(parent()); + if (!root) + return; + + FloatPoint absPos = localToAbsolute(); + + TransformationMatrix htmlParentCtm = root->RenderContainer::absoluteTransform(); + + // Don't use relativeBBox here, as it's unites the selection rects. Makes it hard + // to spot errors, if there are any using WebInspector. Individually feed them into 'rects'. + for (InlineRunBox* runBox = firstLineBox(); runBox; runBox = runBox->nextLineBox()) { + ASSERT(runBox->isInlineFlowBox()); + + InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(runBox); + for (InlineBox* box = flowBox->firstChild(); box; box = box->nextOnLine()) { + FloatRect boxRect(box->xPos(), box->yPos(), box->width(), box->height()); + boxRect.move(narrowPrecisionToFloat(absPos.x() - htmlParentCtm.e()), narrowPrecisionToFloat(absPos.y() - htmlParentCtm.f())); + // FIXME: broken with CSS transforms + rects.append(enclosingIntRect(absoluteTransform().mapRect(boxRect))); + } + } +} + +void RenderSVGText::absoluteQuads(Vector<FloatQuad>& quads, bool) +{ + RenderSVGRoot* root = findSVGRootObject(parent()); + if (!root) + return; + + FloatPoint absPos = localToAbsolute(); + + TransformationMatrix htmlParentCtm = root->RenderContainer::absoluteTransform(); + + // Don't use relativeBBox here, as it's unites the selection rects. Makes it hard + // to spot errors, if there are any using WebInspector. Individually feed them into 'rects'. + for (InlineRunBox* runBox = firstLineBox(); runBox; runBox = runBox->nextLineBox()) { + ASSERT(runBox->isInlineFlowBox()); + + InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(runBox); + for (InlineBox* box = flowBox->firstChild(); box; box = box->nextOnLine()) { + FloatRect boxRect(box->xPos(), box->yPos(), box->width(), box->height()); + boxRect.move(narrowPrecisionToFloat(absPos.x() - htmlParentCtm.e()), narrowPrecisionToFloat(absPos.y() - htmlParentCtm.f())); + // FIXME: broken with CSS transforms + quads.append(absoluteTransform().mapRect(boxRect)); + } + } +} + +void RenderSVGText::paint(PaintInfo& paintInfo, int, int) +{ + RenderObject::PaintInfo pi(paintInfo); + pi.rect = absoluteTransform().inverse().mapRect(pi.rect); + RenderBlock::paint(pi, 0, 0); +} + +FloatRect RenderSVGText::relativeBBox(bool includeStroke) const +{ + FloatRect repaintRect; + + for (InlineRunBox* runBox = firstLineBox(); runBox; runBox = runBox->nextLineBox()) { + ASSERT(runBox->isInlineFlowBox()); + + InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(runBox); + for (InlineBox* box = flowBox->firstChild(); box; box = box->nextOnLine()) + repaintRect.unite(FloatRect(box->xPos(), box->yPos(), box->width(), box->height())); + } + + // SVG needs to include the strokeWidth(), not the textStrokeWidth(). + if (includeStroke && style()->svgStyle()->hasStroke()) { + float strokeWidth = SVGRenderStyle::cssPrimitiveToLength(this, style()->svgStyle()->strokeWidth(), 0.0f); + +#if ENABLE(SVG_FONTS) + const Font& font = style()->font(); + if (font.primaryFont()->isSVGFont()) { + float scale = font.unitsPerEm() > 0 ? font.size() / font.unitsPerEm() : 0.0f; + + if (scale != 0.0f) + strokeWidth /= scale; + } +#endif + + repaintRect.inflate(strokeWidth); + } + + repaintRect.move(xPos(), yPos()); + return repaintRect; +} + +} + +#endif // ENABLE(SVG) + +// vim:ts=4:noet diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderSVGText.h b/src/3rdparty/webkit/WebCore/rendering/RenderSVGText.h new file mode 100644 index 0000000..d76d451 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderSVGText.h @@ -0,0 +1,71 @@ +/* + * This file is part of the WebKit project. + * + * Copyright (C) 2006 Apple Computer, Inc. + * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> + * + * 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. + * + */ + +#ifndef RenderSVGText_h +#define RenderSVGText_h + +#if ENABLE(SVG) + +#include "TransformationMatrix.h" +#include "RenderSVGBlock.h" + +namespace WebCore { + +class SVGTextElement; + +class RenderSVGText : public RenderSVGBlock { +public: + RenderSVGText(SVGTextElement* node); + + virtual const char* renderName() const { return "RenderSVGText"; } + + virtual bool isSVGText() const { return true; } + + bool calculateLocalTransform(); + virtual TransformationMatrix localTransform() const { return m_localTransform; } + + virtual void paint(PaintInfo&, int tx, int ty); + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); + + virtual bool requiresLayer(); + virtual void layout(); + + virtual void absoluteRects(Vector<IntRect>&, int tx, int ty, bool topLevel = true); + virtual void absoluteQuads(Vector<FloatQuad>&, bool topLevel = true); + + virtual IntRect absoluteClippedOverflowRect(); + virtual FloatRect relativeBBox(bool includeStroke = true) const; + + virtual InlineBox* createInlineBox(bool makePlaceHolderBox, bool isRootLineBox, bool isOnlyRun = false); + +private: + TransformationMatrix m_localTransform; + IntRect m_absoluteBounds; +}; + +} + +#endif // ENABLE(SVG) +#endif + +// vim:ts=4:noet diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderSVGTextPath.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderSVGTextPath.cpp new file mode 100644 index 0000000..aa0209c --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderSVGTextPath.cpp @@ -0,0 +1,122 @@ +/* + * This file is part of the WebKit project. + * + * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> + * + * 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(SVG) +#include "RenderSVGTextPath.h" + +#include "FloatRect.h" +#include "SVGInlineTextBox.h" +#include "SVGPathElement.h" +#include "SVGRootInlineBox.h" +#include "SVGTextPathElement.h" +#include "SVGTransformList.h" + +namespace WebCore { + +RenderSVGTextPath::RenderSVGTextPath(Node* n) + : RenderSVGInline(n) + , m_startOffset(0.0f) + , m_exactAlignment(true) + , m_stretchMethod(false) +{ +} + +Path RenderSVGTextPath::layoutPath() const +{ + SVGTextPathElement* textPathElement = static_cast<SVGTextPathElement*>(element()); + String pathId = SVGURIReference::getTarget(textPathElement->href()); + Element* targetElement = textPathElement->document()->getElementById(pathId); + if (!targetElement || !targetElement->hasTagName(SVGNames::pathTag)) + return Path(); + + SVGPathElement* pathElement = static_cast<SVGPathElement*>(targetElement); + + Path pathData = pathElement->toPathData(); + // Spec: The transform attribute on the referenced 'path' element represents a + // supplemental transformation relative to the current user coordinate system for + // the current 'text' element, including any adjustments to the current user coordinate + // system due to a possible transform attribute on the current 'text' element. + // http://www.w3.org/TR/SVG/text.html#TextPathElement + pathData.transform(pathElement->animatedLocalTransform()); + return pathData; +} + +float RenderSVGTextPath::startOffset() const +{ + return static_cast<SVGTextPathElement*>(element())->startOffset().valueAsPercentage(); +} + +bool RenderSVGTextPath::exactAlignment() const +{ + return static_cast<SVGTextPathElement*>(element())->spacing() == SVG_TEXTPATH_SPACINGTYPE_EXACT; +} + +bool RenderSVGTextPath::stretchMethod() const +{ + return static_cast<SVGTextPathElement*>(element())->method() == SVG_TEXTPATH_METHODTYPE_STRETCH; +} + +void RenderSVGTextPath::absoluteRects(Vector<IntRect>& rects, int, int) +{ + InlineRunBox* firstBox = firstLineBox(); + + SVGRootInlineBox* rootBox = firstBox ? static_cast<SVGInlineTextBox*>(firstBox)->svgRootInlineBox() : 0; + RenderObject* object = rootBox ? rootBox->object() : 0; + + if (!object) + return; + + int xRef = object->xPos() + xPos(); + int yRef = object->yPos() + yPos(); + + for (InlineRunBox* curr = firstBox; curr; curr = curr->nextLineBox()) { + FloatRect rect(xRef + curr->xPos(), yRef + curr->yPos(), curr->width(), curr->height()); + // FIXME: broken with CSS transforms + rects.append(enclosingIntRect(absoluteTransform().mapRect(rect))); + } +} + +void RenderSVGTextPath::absoluteQuads(Vector<FloatQuad>& quads, bool) +{ + InlineRunBox* firstBox = firstLineBox(); + + SVGRootInlineBox* rootBox = firstBox ? static_cast<SVGInlineTextBox*>(firstBox)->svgRootInlineBox() : 0; + RenderObject* object = rootBox ? rootBox->object() : 0; + + if (!object) + return; + + int xRef = object->xPos() + xPos(); + int yRef = object->yPos() + yPos(); + + for (InlineRunBox* curr = firstBox; curr; curr = curr->nextLineBox()) { + FloatRect rect(xRef + curr->xPos(), yRef + curr->yPos(), curr->width(), curr->height()); + // FIXME: broken with CSS transforms + quads.append(absoluteTransform().mapRect(rect)); + } +} + +} + +#endif // ENABLE(SVG) diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderSVGTextPath.h b/src/3rdparty/webkit/WebCore/rendering/RenderSVGTextPath.h new file mode 100644 index 0000000..4fd4cc3 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderSVGTextPath.h @@ -0,0 +1,55 @@ +/* + * This file is part of the WebKit project. + * + * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> + * + * 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. + * + */ + +#ifndef RenderSVGTextPath_h +#define RenderSVGTextPath_h + +#if ENABLE(SVG) +#include "RenderSVGInline.h" + +namespace WebCore { + + class RenderSVGTextPath : public RenderSVGInline { + public: + RenderSVGTextPath(Node*); + + Path layoutPath() const; + float startOffset() const; + bool exactAlignment() const; + bool stretchMethod() const; + + virtual const char* renderName() const { return "RenderSVGTextPath"; } + virtual void absoluteRects(Vector<IntRect>& rects, int tx, int ty); + virtual void absoluteQuads(Vector<FloatQuad>&, bool topLevel = true); + + private: + float m_startOffset; + + bool m_exactAlignment : 1; + bool m_stretchMethod : 1; + + Path m_layoutPath; + }; +} + +#endif // ENABLE(SVG) +#endif // RenderSVGTextPath_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderSVGTransformableContainer.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderSVGTransformableContainer.cpp new file mode 100644 index 0000000..17d64f3 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderSVGTransformableContainer.cpp @@ -0,0 +1,47 @@ +/* + Copyright (C) 2004, 2005 Nikolas Zimmermann <wildfox@kde.org> + 2004, 2005, 2006 Rob Buis <buis@kde.org> + + This file is part of the KDE project + + 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(SVG) + +#include "RenderSVGTransformableContainer.h" + +#include "SVGStyledTransformableElement.h" +#include "SVGTransformList.h" + +namespace WebCore { + +RenderSVGTransformableContainer::RenderSVGTransformableContainer(SVGStyledTransformableElement* node) + : RenderSVGContainer(node) +{ +} + +bool RenderSVGTransformableContainer::calculateLocalTransform() +{ + TransformationMatrix oldTransform = m_localTransform; + m_localTransform = static_cast<SVGStyledTransformableElement*>(element())->animatedLocalTransform(); + return (m_localTransform != oldTransform); +} + +} + +#endif // ENABLE(SVG) diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderSVGTransformableContainer.h b/src/3rdparty/webkit/WebCore/rendering/RenderSVGTransformableContainer.h new file mode 100644 index 0000000..897cc63 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderSVGTransformableContainer.h @@ -0,0 +1,39 @@ +/* + Copyright (C) 2007 Eric Seidel <eric@webkit.org + + 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 + aint 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. + */ + + +#ifndef RenderSVGTransformableContainer_h +#define RenderSVGTransformableContainer_h + +#if ENABLE(SVG) +#include "RenderSVGContainer.h" + +namespace WebCore { + + class SVGStyledTransformableElement; + class RenderSVGTransformableContainer : public RenderSVGContainer { + public: + RenderSVGTransformableContainer(SVGStyledTransformableElement*); + + virtual bool calculateLocalTransform(); + }; +} + +#endif // ENABLE(SVG) +#endif // RenderSVGTransformableContainer_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderSVGViewportContainer.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderSVGViewportContainer.cpp new file mode 100644 index 0000000..f6ac424 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderSVGViewportContainer.cpp @@ -0,0 +1,198 @@ +/* + Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org> + 2004, 2005, 2007 Rob Buis <buis@kde.org> + 2007 Eric Seidel <eric@webkit.org> + + This file is part of the KDE project + + 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 + aint 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(SVG) +#include "RenderSVGViewportContainer.h" + +#include "GraphicsContext.h" + +#include "RenderView.h" +#include "SVGMarkerElement.h" +#include "SVGSVGElement.h" + +namespace WebCore { + +RenderSVGViewportContainer::RenderSVGViewportContainer(SVGStyledElement* node) + : RenderSVGContainer(node) +{ + setReplaced(true); +} + +RenderSVGViewportContainer::~RenderSVGViewportContainer() +{ +} + +void RenderSVGViewportContainer::layout() +{ + ASSERT(needsLayout()); + + calcViewport(); + + // Arbitrary affine transforms are incompatible with LayoutState. + view()->disableLayoutState(); + + IntRect oldBounds = m_absoluteBounds; + IntRect oldOutlineBox; + bool checkForRepaint = checkForRepaintDuringLayout() && selfNeedsLayout(); + if (checkForRepaint) + oldOutlineBox = absoluteOutlineBounds(); + + calcBounds(); + + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { + if (selfNeedsLayout()) + child->setNeedsLayout(true); + + child->layoutIfNeeded(); + ASSERT(!child->needsLayout()); + } + + if (checkForRepaint) + repaintAfterLayoutIfNeeded(oldBounds, oldOutlineBox); + + view()->enableLayoutState(); + setNeedsLayout(false); +} + +void RenderSVGViewportContainer::paint(PaintInfo& paintInfo, int parentX, int parentY) +{ + // A value of zero disables rendering of the element. + if (!viewport().isEmpty() && (viewport().width() <= 0. || viewport().height() <= 0.)) + return; + + RenderSVGContainer::paint(paintInfo, parentX, parentY); +} + +void RenderSVGViewportContainer::applyContentTransforms(PaintInfo& paintInfo) +{ + if (!viewport().isEmpty()) { + if (style()->overflowX() != OVISIBLE) + paintInfo.context->clip(enclosingIntRect(viewport())); // FIXME: Eventually we'll want float-precision clipping + + paintInfo.context->concatCTM(TransformationMatrix().translate(viewport().x(), viewport().y())); + } + + RenderSVGContainer::applyContentTransforms(paintInfo); +} + +void RenderSVGViewportContainer::applyAdditionalTransforms(PaintInfo& paintInfo) +{ + paintInfo.context->concatCTM(viewportTransform()); + RenderSVGContainer::applyAdditionalTransforms(paintInfo); +} + +FloatRect RenderSVGViewportContainer::viewport() const +{ + return m_viewport; +} + +void RenderSVGViewportContainer::calcViewport() +{ + SVGElement* svgelem = static_cast<SVGElement*>(element()); + if (svgelem->hasTagName(SVGNames::svgTag)) { + SVGSVGElement* svg = static_cast<SVGSVGElement*>(element()); + + if (!selfNeedsLayout() && !svg->hasRelativeValues()) + return; + + float x = svg->x().value(svg); + float y = svg->y().value(svg); + float w = svg->width().value(svg); + float h = svg->height().value(svg); + m_viewport = FloatRect(x, y, w, h); + } else if (svgelem->hasTagName(SVGNames::markerTag)) { + if (!selfNeedsLayout()) + return; + + SVGMarkerElement* svg = static_cast<SVGMarkerElement*>(element()); + float w = svg->markerWidth().value(svg); + float h = svg->markerHeight().value(svg); + m_viewport = FloatRect(0, 0, w, h); + } +} + +TransformationMatrix RenderSVGViewportContainer::viewportTransform() const +{ + if (element()->hasTagName(SVGNames::svgTag)) { + SVGSVGElement* svg = static_cast<SVGSVGElement*>(element()); + return svg->viewBoxToViewTransform(viewport().width(), viewport().height()); + } else if (element()->hasTagName(SVGNames::markerTag)) { + SVGMarkerElement* marker = static_cast<SVGMarkerElement*>(element()); + return marker->viewBoxToViewTransform(viewport().width(), viewport().height()); + } + + return TransformationMatrix(); +} + +TransformationMatrix RenderSVGViewportContainer::absoluteTransform() const +{ + TransformationMatrix ctm = RenderObject::absoluteTransform(); + ctm.translate(viewport().x(), viewport().y()); + return viewportTransform() * ctm; +} + +bool RenderSVGViewportContainer::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction) +{ + if (!viewport().isEmpty() + && style()->overflowX() == OHIDDEN + && style()->overflowY() == OHIDDEN) { + // Check if we need to do anything at all. + IntRect overflowBox = overflowRect(false); + overflowBox.move(_tx, _ty); + TransformationMatrix ctm = RenderObject::absoluteTransform(); + ctm.translate(viewport().x(), viewport().y()); + double localX, localY; + ctm.inverse().map(_x - _tx, _y - _ty, &localX, &localY); + if (!overflowBox.contains((int)localX, (int)localY)) + return false; + } + + int sx = 0; + int sy = 0; + + // Respect parent translation offset for non-outermost <svg> elements. + // Outermost <svg> element is handled by RenderSVGRoot. + if (element()->hasTagName(SVGNames::svgTag)) { + sx = _tx; + sy = _ty; + } + + for (RenderObject* child = lastChild(); child; child = child->previousSibling()) { + if (child->nodeAtPoint(request, result, _x - sx, _y - sy, _tx, _ty, hitTestAction)) { + updateHitTestResult(result, IntPoint(_x - _tx, _y - _ty)); + return true; + } + } + + // Spec: Only graphical elements can be targeted by the mouse, period. + // 16.4: "If there are no graphics elements whose relevant graphics content is under the pointer (i.e., there is no target element), the event is not dispatched." + return false; +} + +} + +#endif // ENABLE(SVG) + +// vim:ts=4:noet diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderSVGViewportContainer.h b/src/3rdparty/webkit/WebCore/rendering/RenderSVGViewportContainer.h new file mode 100644 index 0000000..2e4a49c --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderSVGViewportContainer.h @@ -0,0 +1,64 @@ +/* + Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org> + 2004, 2005, 2007 Rob Buis <buis@kde.org> + + This file is part of the KDE project + + 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 + aint 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. +*/ + +#ifndef RenderSVGViewportContainer_h +#define RenderSVGViewportContainer_h + +#if ENABLE(SVG) + +#include "RenderSVGContainer.h" + +namespace WebCore { + +class RenderSVGViewportContainer : public RenderSVGContainer { +public: + RenderSVGViewportContainer(SVGStyledElement*); + ~RenderSVGViewportContainer(); + + virtual bool isSVGContainer() const { return true; } + virtual const char* renderName() const { return "RenderSVGViewportContainer"; } + + virtual void layout(); + virtual void paint(PaintInfo&, int parentX, int parentY); + + virtual TransformationMatrix absoluteTransform() const; + virtual TransformationMatrix viewportTransform() const; + + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); + + FloatRect viewport() const; + +private: + void calcViewport(); + + virtual void applyContentTransforms(PaintInfo&); + virtual void applyAdditionalTransforms(PaintInfo&); + + FloatRect m_viewport; +}; + +} // namespace WebCore + +#endif // ENABLE(SVG) +#endif // RenderSVGViewportContainer_h + +// vim:ts=4:noet diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderScrollbar.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderScrollbar.cpp new file mode 100644 index 0000000..b7045d6 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderScrollbar.cpp @@ -0,0 +1,320 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "RenderScrollbar.h" +#include "RenderScrollbarPart.h" +#include "RenderScrollbarTheme.h" + +namespace WebCore { + +PassRefPtr<Scrollbar> RenderScrollbar::createCustomScrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, RenderObject* renderer) +{ + return adoptRef(new RenderScrollbar(client, orientation, renderer)); +} + +RenderScrollbar::RenderScrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, RenderObject* renderer) + : Scrollbar(client, orientation, RegularScrollbar, RenderScrollbarTheme::renderScrollbarTheme()) + , m_owner(renderer) +{ +} + +RenderScrollbar::~RenderScrollbar() +{ + ASSERT(m_parts.isEmpty()); +} + +void RenderScrollbar::setParent(ScrollView* parent) +{ + Scrollbar::setParent(parent); + if (!parent) { + // Destroy all of the scrollbar's RenderObjects. + updateScrollbarParts(true); + } +} + +void RenderScrollbar::setEnabled(bool e) +{ + bool wasEnabled = enabled(); + Scrollbar::setEnabled(e); + if (wasEnabled != e) + updateScrollbarParts(); +} + +void RenderScrollbar::styleChanged() +{ + updateScrollbarParts(); +} + +void RenderScrollbar::paint(GraphicsContext* context, const IntRect& damageRect) +{ + if (context->updatingControlTints()) { + updateScrollbarParts(); + return; + } + Scrollbar::paint(context, damageRect); +} + +void RenderScrollbar::setHoveredPart(ScrollbarPart part) +{ + if (part == m_hoveredPart) + return; + + ScrollbarPart oldPart = m_hoveredPart; + m_hoveredPart = part; + + updateScrollbarPart(oldPart); + updateScrollbarPart(m_hoveredPart); + + updateScrollbarPart(ScrollbarBGPart); + updateScrollbarPart(TrackBGPart); +} + +void RenderScrollbar::setPressedPart(ScrollbarPart part) +{ + ScrollbarPart oldPart = m_pressedPart; + Scrollbar::setPressedPart(part); + + updateScrollbarPart(oldPart); + updateScrollbarPart(part); + + updateScrollbarPart(ScrollbarBGPart); + updateScrollbarPart(TrackBGPart); +} + +static ScrollbarPart s_styleResolvePart; +static RenderScrollbar* s_styleResolveScrollbar; + +RenderScrollbar* RenderScrollbar::scrollbarForStyleResolve() +{ + return s_styleResolveScrollbar; +} + +ScrollbarPart RenderScrollbar::partForStyleResolve() +{ + return s_styleResolvePart; +} + +PassRefPtr<RenderStyle> RenderScrollbar::getScrollbarPseudoStyle(ScrollbarPart partType, RenderStyle::PseudoId pseudoId) +{ + s_styleResolvePart = partType; + s_styleResolveScrollbar = this; + RefPtr<RenderStyle> result = m_owner->getUncachedPseudoStyle(pseudoId, m_owner->style()); + s_styleResolvePart = NoPart; + s_styleResolveScrollbar = 0; + return result; +} + +void RenderScrollbar::updateScrollbarParts(bool destroy) +{ + updateScrollbarPart(ScrollbarBGPart, destroy); + updateScrollbarPart(BackButtonStartPart, destroy); + updateScrollbarPart(ForwardButtonStartPart, destroy); + updateScrollbarPart(BackTrackPart, destroy); + updateScrollbarPart(ThumbPart, destroy); + updateScrollbarPart(ForwardTrackPart, destroy); + updateScrollbarPart(BackButtonEndPart, destroy); + updateScrollbarPart(ForwardButtonEndPart, destroy); + updateScrollbarPart(TrackBGPart, destroy); + + if (destroy) + return; + + // See if the scrollbar's thickness changed. If so, we need to mark our owning object as needing a layout. + bool isHorizontal = orientation() == HorizontalScrollbar; + int oldThickness = isHorizontal ? height() : width(); + int newThickness = 0; + RenderScrollbarPart* part = m_parts.get(ScrollbarBGPart); + if (part) { + part->layout(); + newThickness = isHorizontal ? part->height() : part->width(); + } + + if (newThickness != oldThickness) { + setFrameRect(IntRect(x(), y(), isHorizontal ? width() : newThickness, isHorizontal ? newThickness : height())); + m_owner->setChildNeedsLayout(true); + } +} + +static RenderStyle::PseudoId pseudoForScrollbarPart(ScrollbarPart part) +{ + switch (part) { + case BackButtonStartPart: + case ForwardButtonStartPart: + case BackButtonEndPart: + case ForwardButtonEndPart: + return RenderStyle::SCROLLBAR_BUTTON; + case BackTrackPart: + case ForwardTrackPart: + return RenderStyle::SCROLLBAR_TRACK_PIECE; + case ThumbPart: + return RenderStyle::SCROLLBAR_THUMB; + case TrackBGPart: + return RenderStyle::SCROLLBAR_TRACK; + default: + return RenderStyle::SCROLLBAR; + } +} + +void RenderScrollbar::updateScrollbarPart(ScrollbarPart partType, bool destroy) +{ + if (partType == NoPart) + return; + + RefPtr<RenderStyle> partStyle = !destroy ? getScrollbarPseudoStyle(partType, pseudoForScrollbarPart(partType)) : 0; + + bool needRenderer = !destroy && partStyle && partStyle->display() != NONE && partStyle->visibility() == VISIBLE; + + if (needRenderer && partStyle->display() != BLOCK) { + // See if we are a button that should not be visible according to OS settings. + ScrollbarButtonsPlacement buttonsPlacement = theme()->buttonsPlacement(); + switch (partType) { + case BackButtonStartPart: + needRenderer = (buttonsPlacement == ScrollbarButtonsSingle || buttonsPlacement == ScrollbarButtonsDoubleStart || + buttonsPlacement == ScrollbarButtonsDoubleBoth); + break; + case ForwardButtonStartPart: + needRenderer = (buttonsPlacement == ScrollbarButtonsDoubleStart || buttonsPlacement == ScrollbarButtonsDoubleBoth); + break; + case BackButtonEndPart: + needRenderer = (buttonsPlacement == ScrollbarButtonsDoubleEnd || buttonsPlacement == ScrollbarButtonsDoubleBoth); + break; + case ForwardButtonEndPart: + needRenderer = (buttonsPlacement == ScrollbarButtonsSingle || buttonsPlacement == ScrollbarButtonsDoubleEnd || + buttonsPlacement == ScrollbarButtonsDoubleBoth); + break; + default: + break; + } + } + + RenderScrollbarPart* partRenderer = m_parts.get(partType); + if (!partRenderer && needRenderer) { + partRenderer = new (m_owner->renderArena()) RenderScrollbarPart(m_owner->document(), this, partType); + m_parts.set(partType, partRenderer); + } else if (partRenderer && !needRenderer) { + m_parts.remove(partType); + partRenderer->destroy(); + partRenderer = 0; + } + + if (partRenderer) + partRenderer->setStyle(partStyle.release()); +} + +void RenderScrollbar::paintPart(GraphicsContext* graphicsContext, ScrollbarPart partType, const IntRect& rect) +{ + RenderScrollbarPart* partRenderer = m_parts.get(partType); + if (!partRenderer) + return; + partRenderer->paintIntoRect(graphicsContext, x(), y(), rect); +} + +IntRect RenderScrollbar::buttonRect(ScrollbarPart partType) +{ + RenderScrollbarPart* partRenderer = m_parts.get(partType); + if (!partRenderer) + return IntRect(); + + partRenderer->layout(); + + bool isHorizontal = orientation() == HorizontalScrollbar; + if (partType == BackButtonStartPart) + return IntRect(x(), y(), isHorizontal ? partRenderer->width() : width(), isHorizontal ? height() : partRenderer->height()); + if (partType == ForwardButtonEndPart) + return IntRect(isHorizontal ? x() + width() - partRenderer->width() : x(), + + isHorizontal ? y() : y() + height() - partRenderer->height(), + isHorizontal ? partRenderer->width() : width(), + isHorizontal ? height() : partRenderer->height()); + + if (partType == ForwardButtonStartPart) { + IntRect previousButton = buttonRect(BackButtonStartPart); + return IntRect(isHorizontal ? x() + previousButton.width() : x(), + isHorizontal ? y() : y() + previousButton.height(), + isHorizontal ? partRenderer->width() : width(), + isHorizontal ? height() : partRenderer->height()); + } + + IntRect followingButton = buttonRect(ForwardButtonEndPart); + return IntRect(isHorizontal ? x() + width() - followingButton.width() - partRenderer->width() : x(), + isHorizontal ? y() : y() + height() - followingButton.height() - partRenderer->height(), + isHorizontal ? partRenderer->width() : width(), + isHorizontal ? height() : partRenderer->height()); +} + +IntRect RenderScrollbar::trackRect(int startLength, int endLength) +{ + RenderScrollbarPart* part = m_parts.get(TrackBGPart); + if (part) + part->layout(); + + if (orientation() == HorizontalScrollbar) { + int marginLeft = part ? part->marginLeft() : 0; + int marginRight = part ? part->marginRight() : 0; + startLength += marginLeft; + endLength += marginRight; + int totalLength = startLength + endLength; + return IntRect(x() + startLength, y(), width() - totalLength, height()); + } + + int marginTop = part ? part->marginTop() : 0; + int marginBottom = part ? part->marginBottom() : 0; + startLength += marginTop; + endLength += marginBottom; + int totalLength = startLength + endLength; + + return IntRect(x(), y() + startLength, width(), height() - totalLength); +} + +IntRect RenderScrollbar::trackPieceRectWithMargins(ScrollbarPart partType, const IntRect& oldRect) +{ + RenderScrollbarPart* partRenderer = m_parts.get(partType); + if (!partRenderer) + return oldRect; + + partRenderer->layout(); + + IntRect rect = oldRect; + if (orientation() == HorizontalScrollbar) { + rect.setX(rect.x() + partRenderer->marginLeft()); + rect.setWidth(rect.width() - (partRenderer->marginLeft() + partRenderer->marginRight())); + } else { + rect.setY(rect.y() + partRenderer->marginTop()); + rect.setHeight(rect.height() - (partRenderer->marginTop() + partRenderer->marginBottom())); + } + return rect; +} + +int RenderScrollbar::minimumThumbLength() +{ + RenderScrollbarPart* partRenderer = m_parts.get(ThumbPart); + if (!partRenderer) + return 0; + partRenderer->layout(); + return orientation() == HorizontalScrollbar ? partRenderer->width() : partRenderer->height(); +} + +} diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderScrollbar.h b/src/3rdparty/webkit/WebCore/rendering/RenderScrollbar.h new file mode 100644 index 0000000..ad97001 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderScrollbar.h @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RenderScrollbar_h +#define RenderScrollbar_h + +#include "Scrollbar.h" +#include "RenderStyle.h" +#include <wtf/HashMap.h> + +namespace WebCore { + +class RenderObject; +class RenderScrollbarPart; +class RenderStyle; + +class RenderScrollbar : public Scrollbar { +protected: + RenderScrollbar(ScrollbarClient*, ScrollbarOrientation, RenderObject*); + +public: + friend class Scrollbar; + static PassRefPtr<Scrollbar> createCustomScrollbar(ScrollbarClient*, ScrollbarOrientation, RenderObject*); + virtual ~RenderScrollbar(); + + virtual void setParent(ScrollView*); + virtual void setEnabled(bool); + + virtual void paint(GraphicsContext*, const IntRect& damageRect); + + virtual void setHoveredPart(ScrollbarPart); + virtual void setPressedPart(ScrollbarPart); + + void updateScrollbarParts(bool destroy = false); + + static ScrollbarPart partForStyleResolve(); + static RenderScrollbar* scrollbarForStyleResolve(); + + virtual void styleChanged(); + + RenderObject* owningRenderer() const { return m_owner; } + + void paintPart(GraphicsContext*, ScrollbarPart, const IntRect&); + + IntRect buttonRect(ScrollbarPart); + IntRect trackRect(int startLength, int endLength); + IntRect trackPieceRectWithMargins(ScrollbarPart, const IntRect&); + + int minimumThumbLength(); + +private: + PassRefPtr<RenderStyle> getScrollbarPseudoStyle(ScrollbarPart, RenderStyle::PseudoId); + void updateScrollbarPart(ScrollbarPart, bool destroy = false); + + RenderObject* m_owner; + HashMap<unsigned, RenderScrollbarPart*> m_parts; +}; + +} // namespace WebCore + +#endif // RenderScrollbar_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderScrollbarPart.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderScrollbarPart.cpp new file mode 100644 index 0000000..3f69ba6 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderScrollbarPart.cpp @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "RenderScrollbarPart.h" +#include "RenderScrollbar.h" +#include "RenderScrollbarTheme.h" + +using namespace std; + +namespace WebCore { + +RenderScrollbarPart::RenderScrollbarPart(Node* node, RenderScrollbar* scrollbar, ScrollbarPart part) + : RenderBlock(node) + , m_scrollbar(scrollbar) + , m_part(part) +{ +} + +RenderScrollbarPart::~RenderScrollbarPart() +{ +} + +void RenderScrollbarPart::layout() +{ + setPos(0, 0); // We don't worry about positioning ourselves. We're just determining our minimum width/height. + if (m_scrollbar->orientation() == HorizontalScrollbar) + layoutHorizontalPart(); + else + layoutVerticalPart(); + + m_overflowWidth = max(m_width, m_overflowWidth); + m_overflowHeight = max(m_height, m_overflowHeight); + + setNeedsLayout(false); +} + +void RenderScrollbarPart::layoutHorizontalPart() +{ + if (m_part == ScrollbarBGPart) { + m_width = m_scrollbar->width(); + computeScrollbarHeight(); + } else { + computeScrollbarWidth(); + m_height = m_scrollbar->height(); + } +} + +void RenderScrollbarPart::layoutVerticalPart() +{ + if (m_part == ScrollbarBGPart) { + computeScrollbarWidth(); + m_height = m_scrollbar->height(); + } else { + m_width = m_scrollbar->width(); + computeScrollbarHeight(); + } +} + +static int calcScrollbarThicknessUsing(const Length& l, int containingLength) +{ + if (l.isIntrinsicOrAuto()) + return ScrollbarTheme::nativeTheme()->scrollbarThickness(); + return l.calcMinValue(containingLength); +} + +void RenderScrollbarPart::computeScrollbarWidth() +{ + int visibleSize = m_scrollbar->owningRenderer()->width() - m_scrollbar->owningRenderer()->borderLeft() - m_scrollbar->owningRenderer()->borderRight(); + int width = calcScrollbarThicknessUsing(style()->width(), visibleSize); + int minWidth = calcScrollbarThicknessUsing(style()->minWidth(), visibleSize); + int maxWidth = style()->maxWidth().isUndefined() ? width : calcScrollbarThicknessUsing(style()->maxWidth(), visibleSize); + m_width = max(minWidth, min(maxWidth, width)); + + // Buttons and track pieces can all have margins along the axis of the scrollbar. + m_marginLeft = style()->marginLeft().calcMinValue(visibleSize); + m_marginRight = style()->marginRight().calcMinValue(visibleSize); +} + +void RenderScrollbarPart::computeScrollbarHeight() +{ + int visibleSize = m_scrollbar->owningRenderer()->height() - m_scrollbar->owningRenderer()->borderTop() - m_scrollbar->owningRenderer()->borderBottom(); + int height = calcScrollbarThicknessUsing(style()->height(), visibleSize); + int minHeight = calcScrollbarThicknessUsing(style()->minHeight(), visibleSize); + int maxHeight = style()->maxHeight().isUndefined() ? height : calcScrollbarThicknessUsing(style()->maxHeight(), visibleSize); + m_height = max(minHeight, min(maxHeight, height)); + + // Buttons and track pieces can all have margins along the axis of the scrollbar. + m_marginTop = style()->marginTop().calcMinValue(visibleSize); + m_marginBottom = style()->marginBottom().calcMinValue(visibleSize); +} + +void RenderScrollbarPart::calcPrefWidths() +{ + if (!prefWidthsDirty()) + return; + + m_minPrefWidth = m_maxPrefWidth = 0; + + setPrefWidthsDirty(false); +} + +void RenderScrollbarPart::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +{ + RenderBlock::styleDidChange(diff, oldStyle); + setInline(false); + setPositioned(false); + setFloating(false); + setHasOverflowClip(false); + if (oldStyle && m_scrollbar && m_part != NoPart && diff >= RenderStyle::Repaint) + m_scrollbar->theme()->invalidatePart(m_scrollbar, m_part); +} + +void RenderScrollbarPart::imageChanged(WrappedImagePtr image, const IntRect* rect) +{ + if (m_scrollbar && m_part != NoPart) + m_scrollbar->theme()->invalidatePart(m_scrollbar, m_part); + else + RenderBlock::imageChanged(image, rect); +} + +void RenderScrollbarPart::paintIntoRect(GraphicsContext* graphicsContext, int tx, int ty, const IntRect& rect) +{ + // Make sure our dimensions match the rect. + setPos(rect.x() - tx, rect.y() - ty); + setWidth(rect.width()); + setHeight(rect.height()); + setOverflowWidth(max(rect.width(), overflowWidth())); + setOverflowHeight(max(rect.height(), overflowHeight())); + + if (graphicsContext->paintingDisabled()) + return; + + // Now do the paint. + RenderObject::PaintInfo paintInfo(graphicsContext, rect, PaintPhaseBlockBackground, false, 0, 0); + paint(paintInfo, tx, ty); + paintInfo.phase = PaintPhaseChildBlockBackgrounds; + paint(paintInfo, tx, ty); + paintInfo.phase = PaintPhaseFloat; + paint(paintInfo, tx, ty); + paintInfo.phase = PaintPhaseForeground; + paint(paintInfo, tx, ty); + paintInfo.phase = PaintPhaseOutline; + paint(paintInfo, tx, ty); +} + +} diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderScrollbarPart.h b/src/3rdparty/webkit/WebCore/rendering/RenderScrollbarPart.h new file mode 100644 index 0000000..2010f97 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderScrollbarPart.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RenderScrollbarPart_h +#define RenderScrollbarPart_h + +#include "RenderBlock.h" +#include "ScrollTypes.h" + +namespace WebCore { + +class RenderScrollbar; + +class RenderScrollbarPart : public RenderBlock { +public: + RenderScrollbarPart(Node*, RenderScrollbar* = 0, ScrollbarPart = NoPart); + virtual ~RenderScrollbarPart(); + + virtual const char* renderName() const { return "RenderScrollbarPart"; } + + virtual bool requiresLayer() { return false; } + + virtual void layout(); + virtual void calcPrefWidths(); + + void paintIntoRect(GraphicsContext*, int tx, int ty, const IntRect&); + +protected: + virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + virtual void imageChanged(WrappedImagePtr, const IntRect* = 0); + +private: + void layoutHorizontalPart(); + void layoutVerticalPart(); + + void computeScrollbarWidth(); + void computeScrollbarHeight(); + + RenderScrollbar* m_scrollbar; + ScrollbarPart m_part; +}; + +} // namespace WebCore + +#endif // RenderScrollbarPart_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderScrollbarTheme.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderScrollbarTheme.cpp new file mode 100644 index 0000000..d7cfb2b --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderScrollbarTheme.cpp @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "RenderScrollbarTheme.h" +#include "RenderScrollbar.h" +#include <wtf/StdLibExtras.h> + +namespace WebCore { + +RenderScrollbarTheme* RenderScrollbarTheme::renderScrollbarTheme() +{ + DEFINE_STATIC_LOCAL(RenderScrollbarTheme, theme, ()); + return &theme; +} + +void RenderScrollbarTheme::buttonSizesAlongTrackAxis(Scrollbar* scrollbar, int& beforeSize, int& afterSize) +{ + IntRect firstButton = backButtonRect(scrollbar, BackButtonStartPart); + IntRect secondButton = forwardButtonRect(scrollbar, ForwardButtonStartPart); + IntRect thirdButton = backButtonRect(scrollbar, BackButtonEndPart); + IntRect fourthButton = forwardButtonRect(scrollbar, ForwardButtonEndPart); + if (scrollbar->orientation() == HorizontalScrollbar) { + beforeSize = firstButton.width() + secondButton.width(); + afterSize = thirdButton.width() + fourthButton.width(); + } else { + beforeSize = firstButton.height() + secondButton.height(); + afterSize = thirdButton.height() + fourthButton.height(); + } +} + +bool RenderScrollbarTheme::hasButtons(Scrollbar* scrollbar) +{ + int startSize; + int endSize; + buttonSizesAlongTrackAxis(scrollbar, startSize, endSize); + return (startSize + endSize) <= (scrollbar->orientation() == HorizontalScrollbar ? scrollbar->width() : scrollbar->height()); +} + +bool RenderScrollbarTheme::hasThumb(Scrollbar* scrollbar) +{ + return trackLength(scrollbar) - thumbLength(scrollbar) >= 0; +} + +int RenderScrollbarTheme::minimumThumbLength(Scrollbar* scrollbar) +{ + return static_cast<RenderScrollbar*>(scrollbar)->minimumThumbLength(); +} + +IntRect RenderScrollbarTheme::backButtonRect(Scrollbar* scrollbar, ScrollbarPart partType, bool) +{ + return static_cast<RenderScrollbar*>(scrollbar)->buttonRect(partType); +} + +IntRect RenderScrollbarTheme::forwardButtonRect(Scrollbar* scrollbar, ScrollbarPart partType, bool) +{ + return static_cast<RenderScrollbar*>(scrollbar)->buttonRect(partType); +} + +IntRect RenderScrollbarTheme::trackRect(Scrollbar* scrollbar, bool) +{ + if (!hasButtons(scrollbar)) + return scrollbar->frameRect(); + + int startLength; + int endLength; + buttonSizesAlongTrackAxis(scrollbar, startLength, endLength); + + return static_cast<RenderScrollbar*>(scrollbar)->trackRect(startLength, endLength); +} + +IntRect RenderScrollbarTheme::constrainTrackRectToTrackPieces(Scrollbar* scrollbar, const IntRect& rect) +{ + IntRect backRect = static_cast<RenderScrollbar*>(scrollbar)->trackPieceRectWithMargins(BackTrackPart, rect); + IntRect forwardRect = static_cast<RenderScrollbar*>(scrollbar)->trackPieceRectWithMargins(ForwardTrackPart, rect); + IntRect result = rect; + if (scrollbar->orientation() == HorizontalScrollbar) { + result.setX(backRect.x()); + result.setWidth(forwardRect.right() - backRect.x()); + } else { + result.setY(backRect.y()); + result.setHeight(forwardRect.bottom() - backRect.y()); + } + return result; +} + +void RenderScrollbarTheme::paintScrollCorner(ScrollView*, GraphicsContext* context, const IntRect& cornerRect) +{ + // FIXME: Implement. + context->fillRect(cornerRect, Color::white); +} + +void RenderScrollbarTheme::paintScrollbarBackground(GraphicsContext* context, Scrollbar* scrollbar) +{ + static_cast<RenderScrollbar*>(scrollbar)->paintPart(context, ScrollbarBGPart, scrollbar->frameRect()); +} + +void RenderScrollbarTheme::paintTrackBackground(GraphicsContext* context, Scrollbar* scrollbar, const IntRect& rect) +{ + static_cast<RenderScrollbar*>(scrollbar)->paintPart(context, TrackBGPart, rect); +} + +void RenderScrollbarTheme::paintTrackPiece(GraphicsContext* context, Scrollbar* scrollbar, const IntRect& rect, ScrollbarPart part) +{ + static_cast<RenderScrollbar*>(scrollbar)->paintPart(context, part, rect); +} + +void RenderScrollbarTheme::paintButton(GraphicsContext* context, Scrollbar* scrollbar, const IntRect& rect, ScrollbarPart part) +{ + static_cast<RenderScrollbar*>(scrollbar)->paintPart(context, part, rect); +} + +void RenderScrollbarTheme::paintThumb(GraphicsContext* context, Scrollbar* scrollbar, const IntRect& rect) +{ + static_cast<RenderScrollbar*>(scrollbar)->paintPart(context, ThumbPart, rect); +} + +} diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderScrollbarTheme.h b/src/3rdparty/webkit/WebCore/rendering/RenderScrollbarTheme.h new file mode 100644 index 0000000..9b8b2c1 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderScrollbarTheme.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2008 Apple Inc. All Rights Reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RenderScrollbarTheme_h +#define RenderScrollbarTheme_h + +#include "ScrollbarThemeComposite.h" + +namespace WebCore { + +class PlatformMouseEvent; +class Scrollbar; +class ScrollView; + +class RenderScrollbarTheme : public ScrollbarThemeComposite { +public: + virtual ~RenderScrollbarTheme() {}; + + virtual int scrollbarThickness(ScrollbarControlSize controlSize) { return ScrollbarTheme::nativeTheme()->scrollbarThickness(controlSize); } + + virtual ScrollbarButtonsPlacement buttonsPlacement() const { return ScrollbarTheme::nativeTheme()->buttonsPlacement(); } + + virtual bool supportsControlTints() const { return true; } + + virtual void paintScrollCorner(ScrollView*, GraphicsContext* context, const IntRect& cornerRect); + + virtual bool shouldCenterOnThumb(Scrollbar* scrollbar, const PlatformMouseEvent& event) { return ScrollbarTheme::nativeTheme()->shouldCenterOnThumb(scrollbar, event); } + + virtual double initialAutoscrollTimerDelay() { return ScrollbarTheme::nativeTheme()->initialAutoscrollTimerDelay(); } + virtual double autoscrollTimerDelay() { return ScrollbarTheme::nativeTheme()->autoscrollTimerDelay(); } + + virtual void registerScrollbar(Scrollbar* scrollbar) { return ScrollbarTheme::nativeTheme()->registerScrollbar(scrollbar); } + virtual void unregisterScrollbar(Scrollbar* scrollbar) { return ScrollbarTheme::nativeTheme()->unregisterScrollbar(scrollbar); } + + virtual int minimumThumbLength(Scrollbar*); + + void buttonSizesAlongTrackAxis(Scrollbar* scrollbar, int& beforeSize, int& afterSize); + + static RenderScrollbarTheme* renderScrollbarTheme(); + +protected: + virtual bool hasButtons(Scrollbar*); + virtual bool hasThumb(Scrollbar*); + + virtual IntRect backButtonRect(Scrollbar*, ScrollbarPart, bool painting = false); + virtual IntRect forwardButtonRect(Scrollbar*, ScrollbarPart, bool painting = false); + virtual IntRect trackRect(Scrollbar*, bool painting = false); + + virtual void paintScrollbarBackground(GraphicsContext*, Scrollbar*); + virtual void paintTrackBackground(GraphicsContext*, Scrollbar*, const IntRect&); + virtual void paintTrackPiece(GraphicsContext*, Scrollbar*, const IntRect&, ScrollbarPart); + virtual void paintButton(GraphicsContext*, Scrollbar*, const IntRect&, ScrollbarPart); + virtual void paintThumb(GraphicsContext*, Scrollbar*, const IntRect&); + + virtual IntRect constrainTrackRectToTrackPieces(Scrollbar*, const IntRect&); +}; + +} // namespace WebCore + +#endif // RenderScrollbarTheme_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderSlider.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderSlider.cpp new file mode 100644 index 0000000..040fa31 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderSlider.cpp @@ -0,0 +1,402 @@ +/** + * + * Copyright (C) 2006, 2007, 2008 Apple Computer, Inc. + * + * 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" +#include "RenderSlider.h" + +#include "CSSPropertyNames.h" +#include "Document.h" +#include "Event.h" +#include "EventHandler.h" +#include "EventNames.h" +#include "Frame.h" +#include "HTMLInputElement.h" +#include "HTMLDivElement.h" +#include "HTMLNames.h" +#include "MouseEvent.h" +#include "RenderTheme.h" +#include <wtf/MathExtras.h> + +using std::min; + +namespace WebCore { + +using namespace HTMLNames; + +const int defaultTrackLength = 129; + +class HTMLSliderThumbElement : public HTMLDivElement { +public: + HTMLSliderThumbElement(Document*, Node* shadowParent = 0); + + virtual void defaultEventHandler(Event*); + virtual bool isShadowNode() const { return true; } + virtual Node* shadowParentNode() { return m_shadowParent; } + + bool inDragMode() const { return m_inDragMode; } +private: + Node* m_shadowParent; + FloatPoint m_initialClickPoint; // initial click point in RenderSlider-local coordinates + int m_initialPosition; + bool m_inDragMode; +}; + +HTMLSliderThumbElement::HTMLSliderThumbElement(Document* doc, Node* shadowParent) + : HTMLDivElement(divTag, doc) + , m_shadowParent(shadowParent) + , m_initialClickPoint(IntPoint()) + , m_initialPosition(0) + , m_inDragMode(false) +{ +} + +void HTMLSliderThumbElement::defaultEventHandler(Event* event) +{ + const AtomicString& eventType = event->type(); + if (eventType == eventNames().mousedownEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) { + MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); + RenderSlider* slider; + if (document()->frame() && renderer() && renderer()->parent() && + (slider = static_cast<RenderSlider*>(renderer()->parent())) && + slider->mouseEventIsInThumb(mouseEvent)) { + // Cache the initial point where the mouse down occurred, in slider coordinates + m_initialClickPoint = slider->absoluteToLocal(FloatPoint(mouseEvent->pageX(), mouseEvent->pageY()), false, true); + // Cache the initial position of the thumb. + m_initialPosition = slider->currentPosition(); + m_inDragMode = true; + + document()->frame()->eventHandler()->setCapturingMouseEventsNode(m_shadowParent); + + event->setDefaultHandled(); + return; + } + } else if (eventType == eventNames().mouseupEvent && event->isMouseEvent() && static_cast<MouseEvent*>(event)->button() == LeftButton) { + if (m_inDragMode) { + if (Frame* frame = document()->frame()) + frame->eventHandler()->setCapturingMouseEventsNode(0); + m_inDragMode = false; + event->setDefaultHandled(); + return; + } + } else if (eventType == eventNames().mousemoveEvent && event->isMouseEvent()) { + if (m_inDragMode && renderer() && renderer()->parent()) { + // Move the slider + MouseEvent* mouseEvent = static_cast<MouseEvent*>(event); + RenderSlider* slider = static_cast<RenderSlider*>(renderer()->parent()); + FloatPoint curPoint = slider->absoluteToLocal(FloatPoint(mouseEvent->pageX(), mouseEvent->pageY()), false, true); + int newPosition = slider->positionForOffset( + IntPoint(m_initialPosition + curPoint.x() - m_initialClickPoint.x() + + (renderer()->width() / 2), + m_initialPosition + curPoint.y() - m_initialClickPoint.y() + + (renderer()->height() / 2))); + if (slider->currentPosition() != newPosition) { + slider->setCurrentPosition(newPosition); + slider->valueChanged(); + } + event->setDefaultHandled(); + return; + } + } + + HTMLDivElement::defaultEventHandler(event); +} + +RenderSlider::RenderSlider(HTMLInputElement* element) + : RenderBlock(element) + , m_thumb(0) +{ +} + +RenderSlider::~RenderSlider() +{ + if (m_thumb) + m_thumb->detach(); +} + +int RenderSlider::baselinePosition(bool, bool) const +{ + return height() + marginTop(); +} + +void RenderSlider::calcPrefWidths() +{ + m_minPrefWidth = 0; + m_maxPrefWidth = 0; + + if (style()->width().isFixed() && style()->width().value() > 0) + m_minPrefWidth = m_maxPrefWidth = calcContentBoxWidth(style()->width().value()); + else + m_maxPrefWidth = defaultTrackLength * style()->effectiveZoom(); + + if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) { + m_maxPrefWidth = max(m_maxPrefWidth, calcContentBoxWidth(style()->minWidth().value())); + m_minPrefWidth = max(m_minPrefWidth, calcContentBoxWidth(style()->minWidth().value())); + } else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent())) + m_minPrefWidth = 0; + else + m_minPrefWidth = m_maxPrefWidth; + + if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) { + m_maxPrefWidth = min(m_maxPrefWidth, calcContentBoxWidth(style()->maxWidth().value())); + m_minPrefWidth = min(m_minPrefWidth, calcContentBoxWidth(style()->maxWidth().value())); + } + + int toAdd = paddingLeft() + paddingRight() + borderLeft() + borderRight(); + m_minPrefWidth += toAdd; + m_maxPrefWidth += toAdd; + + setPrefWidthsDirty(false); +} + +void RenderSlider::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +{ + RenderBlock::styleDidChange(diff, oldStyle); + + if (m_thumb) + m_thumb->renderer()->setStyle(createThumbStyle(style(), m_thumb->renderer()->style())); + + setReplaced(isInline()); +} + +PassRefPtr<RenderStyle> RenderSlider::createThumbStyle(const RenderStyle* parentStyle, const RenderStyle* oldStyle) +{ + RefPtr<RenderStyle> style; + RenderStyle* pseudoStyle = getCachedPseudoStyle(RenderStyle::SLIDER_THUMB); + if (pseudoStyle) + // We may be sharing style with another slider, but we must not share the thumb style. + style = RenderStyle::clone(pseudoStyle); + else + style = RenderStyle::create(); + + if (parentStyle) + style->inheritFrom(parentStyle); + + style->setDisplay(BLOCK); + style->setPosition(RelativePosition); + if (oldStyle) { + style->setLeft(oldStyle->left()); + style->setTop(oldStyle->top()); + } + + if (parentStyle->appearance() == SliderVerticalPart) + style->setAppearance(SliderThumbVerticalPart); + else if (parentStyle->appearance() == SliderHorizontalPart) + style->setAppearance(SliderThumbHorizontalPart); + else if (parentStyle->appearance() == MediaSliderPart) + style->setAppearance(MediaSliderThumbPart); + + return style.release(); +} + +void RenderSlider::layout() +{ + bool relayoutChildren = false; + + if (m_thumb && m_thumb->renderer()) { + + int oldWidth = m_width; + calcWidth(); + int oldHeight = m_height; + calcHeight(); + + if (oldWidth != m_width || oldHeight != m_height) + relayoutChildren = true; + + // Allow the theme to set the size of the thumb + if (m_thumb->renderer()->style()->hasAppearance()) + theme()->adjustSliderThumbSize(m_thumb->renderer()); + + if (style()->appearance() == SliderVerticalPart) { + // FIXME: Handle percentage widths correctly. See http://bugs.webkit.org/show_bug.cgi?id=12104 + m_thumb->renderer()->style()->setLeft(Length(contentWidth() / 2 - m_thumb->renderer()->style()->width().value() / 2, Fixed)); + } else { + // FIXME: Handle percentage heights correctly. See http://bugs.webkit.org/show_bug.cgi?id=12104 + m_thumb->renderer()->style()->setTop(Length(contentHeight() / 2 - m_thumb->renderer()->style()->height().value() / 2, Fixed)); + } + + if (relayoutChildren) + setPositionFromValue(true); + } + + RenderBlock::layoutBlock(relayoutChildren); +} + +void RenderSlider::updateFromElement() +{ + if (!m_thumb) { + m_thumb = new HTMLSliderThumbElement(document(), node()); + RefPtr<RenderStyle> thumbStyle = createThumbStyle(style()); + m_thumb->setRenderer(m_thumb->createRenderer(renderArena(), thumbStyle.get())); + m_thumb->renderer()->setStyle(thumbStyle.release()); + m_thumb->setAttached(); + m_thumb->setInDocument(true); + addChild(m_thumb->renderer()); + } + setPositionFromValue(); + setNeedsLayout(true, false); +} + +bool RenderSlider::mouseEventIsInThumb(MouseEvent* evt) +{ + if (!m_thumb || !m_thumb->renderer()) + return false; + + FloatPoint localPoint = m_thumb->renderer()->absoluteToLocal(FloatPoint(evt->pageX(), evt->pageY()), false, true); + IntRect thumbBounds = m_thumb->renderer()->borderBox(); + return thumbBounds.contains(roundedIntPoint(localPoint)); +} + +void RenderSlider::setValueForPosition(int position) +{ + if (!m_thumb || !m_thumb->renderer()) + return; + + const AtomicString& minStr = static_cast<HTMLInputElement*>(node())->getAttribute(minAttr); + const AtomicString& maxStr = static_cast<HTMLInputElement*>(node())->getAttribute(maxAttr); + const AtomicString& precision = static_cast<HTMLInputElement*>(node())->getAttribute(precisionAttr); + + double minVal = minStr.isNull() ? 0.0 : minStr.toDouble(); + double maxVal = maxStr.isNull() ? 100.0 : maxStr.toDouble(); + minVal = min(minVal, maxVal); // Make sure the range is sane. + + // Calculate the new value based on the position + double factor = (double)position / (double)trackSize(); + if (style()->appearance() == SliderVerticalPart) + factor = 1.0 - factor; + double val = minVal + factor * (maxVal - minVal); + + val = max(minVal, min(val, maxVal)); // Make sure val is within min/max. + + // Force integer value if not float. + if (!equalIgnoringCase(precision, "float")) + val = lround(val); + + static_cast<HTMLInputElement*>(node())->setValueFromRenderer(String::number(val)); + + if (position != currentPosition()) { + setCurrentPosition(position); + static_cast<HTMLInputElement*>(node())->onChange(); + } +} + +double RenderSlider::setPositionFromValue(bool inLayout) +{ + if (!m_thumb || !m_thumb->renderer()) + return 0; + + if (!inLayout) + document()->updateLayout(); + + String value = static_cast<HTMLInputElement*>(node())->value(); + const AtomicString& minStr = static_cast<HTMLInputElement*>(node())->getAttribute(minAttr); + const AtomicString& maxStr = static_cast<HTMLInputElement*>(node())->getAttribute(maxAttr); + const AtomicString& precision = static_cast<HTMLInputElement*>(node())->getAttribute(precisionAttr); + + double minVal = minStr.isNull() ? 0.0 : minStr.toDouble(); + double maxVal = maxStr.isNull() ? 100.0 : maxStr.toDouble(); + minVal = min(minVal, maxVal); // Make sure the range is sane. + + double oldVal = value.isNull() ? (maxVal + minVal)/2.0 : value.toDouble(); + double val = max(minVal, min(oldVal, maxVal)); // Make sure val is within min/max. + + // Force integer value if not float. + if (!equalIgnoringCase(precision, "float")) + val = lround(val); + + // Calculate the new position based on the value + double factor = (val - minVal) / (maxVal - minVal); + if (style()->appearance() == SliderVerticalPart) + factor = 1.0 - factor; + + setCurrentPosition((int)(factor * trackSize())); + + if (value.isNull() || val != oldVal) + static_cast<HTMLInputElement*>(node())->setValueFromRenderer(String::number(val)); + + return val; +} + +int RenderSlider::positionForOffset(const IntPoint& p) +{ + if (!m_thumb || !m_thumb->renderer()) + return 0; + + int position; + if (style()->appearance() == SliderVerticalPart) + position = p.y() - m_thumb->renderer()->height() / 2; + else + position = p.x() - m_thumb->renderer()->width() / 2; + + return max(0, min(position, trackSize())); +} + +void RenderSlider::valueChanged() +{ + setValueForPosition(currentPosition()); + static_cast<HTMLInputElement*>(node())->onChange(); +} + +int RenderSlider::currentPosition() +{ + if (!m_thumb || !m_thumb->renderer()) + return 0; + + if (style()->appearance() == SliderVerticalPart) + return m_thumb->renderer()->style()->top().value(); + return m_thumb->renderer()->style()->left().value(); +} + +void RenderSlider::setCurrentPosition(int pos) +{ + if (!m_thumb || !m_thumb->renderer()) + return; + + if (style()->appearance() == SliderVerticalPart) + m_thumb->renderer()->style()->setTop(Length(pos, Fixed)); + else + m_thumb->renderer()->style()->setLeft(Length(pos, Fixed)); + + m_thumb->renderer()->layer()->updateLayerPosition(); + repaint(); + m_thumb->renderer()->repaint(); +} + +int RenderSlider::trackSize() +{ + if (!m_thumb || !m_thumb->renderer()) + return 0; + + if (style()->appearance() == SliderVerticalPart) + return contentHeight() - m_thumb->renderer()->height(); + return contentWidth() - m_thumb->renderer()->width(); +} + +void RenderSlider::forwardEvent(Event* evt) +{ + m_thumb->defaultEventHandler(evt); +} + +bool RenderSlider::inDragMode() const +{ + return m_thumb->inDragMode(); +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderSlider.h b/src/3rdparty/webkit/WebCore/rendering/RenderSlider.h new file mode 100644 index 0000000..2667672 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderSlider.h @@ -0,0 +1,73 @@ +/** + * + * Copyright (C) 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef RenderSlider_h +#define RenderSlider_h + +#include "RenderBlock.h" + +namespace WebCore { + + class HTMLDivElement; + class HTMLInputElement; + class HTMLSliderThumbElement; + class MouseEvent; + + class RenderSlider : public RenderBlock { + public: + RenderSlider(HTMLInputElement*); + ~RenderSlider(); + + virtual const char* renderName() const { return "RenderSlider"; } + virtual bool isSlider() const { return true; } + + virtual int baselinePosition( bool, bool ) const; + virtual void calcPrefWidths(); + virtual void layout(); + virtual void updateFromElement(); + + bool mouseEventIsInThumb(MouseEvent*); + + void setValueForPosition(int position); + double setPositionFromValue(bool inLayout = false); + int positionForOffset(const IntPoint&); + + void valueChanged(); + + int currentPosition(); + void setCurrentPosition(int pos); + + void forwardEvent(Event*); + bool inDragMode() const; + + protected: + virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + + private: + PassRefPtr<RenderStyle> createThumbStyle(const RenderStyle* parentStyle, const RenderStyle* oldStyle = 0); + int trackSize(); + + RefPtr<HTMLSliderThumbElement> m_thumb; +}; + +} // namespace WebCore + +#endif // RenderSlider_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderTable.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderTable.cpp new file mode 100644 index 0000000..4561d57 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderTable.cpp @@ -0,0 +1,1152 @@ +/* + * Copyright (C) 1997 Martin Jones (mjones@kde.org) + * (C) 1997 Torben Weis (weis@kde.org) + * (C) 1998 Waldo Bastian (bastian@kde.org) + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.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" +#include "RenderTable.h" + +#include "AutoTableLayout.h" +#include "DeleteButtonController.h" +#include "Document.h" +#include "FixedTableLayout.h" +#include "FrameView.h" +#include "HTMLNames.h" +#include "RenderLayer.h" +#include "RenderTableCell.h" +#include "RenderTableCol.h" +#include "RenderTableSection.h" +#include "RenderView.h" + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +RenderTable::RenderTable(Node* node) + : RenderBlock(node) + , m_caption(0) + , m_head(0) + , m_foot(0) + , m_firstBody(0) + , m_tableLayout(0) + , m_currentBorder(0) + , m_frame(Void) + , m_rules(None) + , m_hasColElements(false) + , m_needsSectionRecalc(0) + , m_hSpacing(0) + , m_vSpacing(0) + , m_borderLeft(0) + , m_borderRight(0) +{ + m_columnPos.fill(0, 2); + m_columns.fill(ColumnStruct(), 1); +} + +RenderTable::~RenderTable() +{ + delete m_tableLayout; +} + +void RenderTable::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +{ + RenderBlock::styleDidChange(diff, oldStyle); + + ETableLayout oldTableLayout = oldStyle ? oldStyle->tableLayout() : TAUTO; + + // In the collapsed border model, there is no cell spacing. + m_hSpacing = collapseBorders() ? 0 : style()->horizontalBorderSpacing(); + m_vSpacing = collapseBorders() ? 0 : style()->verticalBorderSpacing(); + m_columnPos[0] = m_hSpacing; + + if (!m_tableLayout || style()->tableLayout() != oldTableLayout) { + delete m_tableLayout; + + // According to the CSS2 spec, you only use fixed table layout if an + // explicit width is specified on the table. Auto width implies auto table layout. + if (style()->tableLayout() == TFIXED && !style()->width().isAuto()) + m_tableLayout = new FixedTableLayout(this); + else + m_tableLayout = new AutoTableLayout(this); + } +} + +static inline void resetSectionPointerIfNotBefore(RenderTableSection*& ptr, RenderObject* before) +{ + if (!before || !ptr) + return; + RenderObject* o = before->previousSibling(); + while (o && o != ptr) + o = o->previousSibling(); + if (!o) + ptr = 0; +} + +void RenderTable::addChild(RenderObject* child, RenderObject* beforeChild) +{ + // Make sure we don't append things after :after-generated content if we have it. + if (!beforeChild && isAfterContent(lastChild())) + beforeChild = lastChild(); + + bool wrapInAnonymousSection = true; + bool isTableElement = element() && element()->hasTagName(tableTag); + + if (child->isRenderBlock() && child->style()->display() == TABLE_CAPTION) { + // First caption wins. + if (beforeChild && m_caption) { + RenderObject* o = beforeChild->previousSibling(); + while (o && o != m_caption) + o = o->previousSibling(); + if (!o) + m_caption = 0; + } + if (!m_caption) + m_caption = static_cast<RenderBlock*>(child); + wrapInAnonymousSection = false; + } else if (child->isTableCol()) { + m_hasColElements = true; + wrapInAnonymousSection = false; + } else if (child->isTableSection()) { + switch (child->style()->display()) { + case TABLE_HEADER_GROUP: + if (child->isTableSection()) { + resetSectionPointerIfNotBefore(m_head, beforeChild); + if (!m_head) { + m_head = static_cast<RenderTableSection*>(child); + } else { + resetSectionPointerIfNotBefore(m_firstBody, beforeChild); + if (!m_firstBody) + m_firstBody = static_cast<RenderTableSection*>(child); + } + } + wrapInAnonymousSection = false; + break; + case TABLE_FOOTER_GROUP: + if (child->isTableSection()) { + resetSectionPointerIfNotBefore(m_foot, beforeChild); + if (!m_foot) { + m_foot = static_cast<RenderTableSection*>(child); + wrapInAnonymousSection = false; + break; + } + } + // Fall through. + case TABLE_ROW_GROUP: + if (child->isTableSection()) { + resetSectionPointerIfNotBefore(m_firstBody, beforeChild); + if (!m_firstBody) + m_firstBody = static_cast<RenderTableSection*>(child); + } + wrapInAnonymousSection = false; + break; + default: + ASSERT_NOT_REACHED(); + } + } else if (child->isTableCell() || child->isTableRow()) { + wrapInAnonymousSection = true; + } else { + // Allow a form to just sit at the top level. + wrapInAnonymousSection = !isTableElement || !child->element() || !(child->element()->hasTagName(formTag) && document()->isHTMLDocument()); + + // FIXME: Allow the delete button container element to sit at the top level. This is needed until http://bugs.webkit.org/show_bug.cgi?id=11363 is fixed. + if (wrapInAnonymousSection && child->element() && child->element()->isHTMLElement() && static_cast<HTMLElement*>(child->element())->id() == DeleteButtonController::containerElementIdentifier) + wrapInAnonymousSection = false; + } + + if (!wrapInAnonymousSection) { + // If the next renderer is actually wrapped in an anonymous table section, we need to go up and find that. + while (beforeChild && !beforeChild->isTableSection() && !beforeChild->isTableCol() && beforeChild->style()->display() != TABLE_CAPTION) + beforeChild = beforeChild->parent(); + + RenderContainer::addChild(child, beforeChild); + return; + } + + if (!beforeChild && lastChild() && lastChild()->isTableSection() && lastChild()->isAnonymous()) { + lastChild()->addChild(child); + return; + } + + RenderObject* lastBox = beforeChild; + while (lastBox && lastBox->parent()->isAnonymous() && !lastBox->isTableSection() && lastBox->style()->display() != TABLE_CAPTION && lastBox->style()->display() != TABLE_COLUMN_GROUP) + lastBox = lastBox->parent(); + if (lastBox && lastBox->isAnonymous() && !isAfterContent(lastBox)) { + lastBox->addChild(child, beforeChild); + return; + } + + if (beforeChild && !beforeChild->isTableSection() && beforeChild->style()->display() != TABLE_CAPTION && beforeChild->style()->display() != TABLE_COLUMN_GROUP) + beforeChild = 0; + RenderTableSection* section = new (renderArena()) RenderTableSection(document() /* anonymous */); + RefPtr<RenderStyle> newStyle = RenderStyle::create(); + newStyle->inheritFrom(style()); + newStyle->setDisplay(TABLE_ROW_GROUP); + section->setStyle(newStyle.release()); + addChild(section, beforeChild); + section->addChild(child); +} + +void RenderTable::calcWidth() +{ + if (isPositioned()) + calcAbsoluteHorizontal(); + + RenderBlock* cb = containingBlock(); + int availableWidth = cb->availableWidth(); + + LengthType widthType = style()->width().type(); + if (widthType > Relative && style()->width().isPositive()) { + // Percent or fixed table + m_width = style()->width().calcMinValue(availableWidth); + m_width = max(minPrefWidth(), m_width); + } else { + // An auto width table should shrink to fit within the line width if necessary in order to + // avoid overlapping floats. + availableWidth = cb->lineWidth(m_y); + + // Subtract out any fixed margins from our available width for auto width tables. + int marginTotal = 0; + if (!style()->marginLeft().isAuto()) + marginTotal += style()->marginLeft().calcValue(availableWidth); + if (!style()->marginRight().isAuto()) + marginTotal += style()->marginRight().calcValue(availableWidth); + + // Subtract out our margins to get the available content width. + int availContentWidth = max(0, availableWidth - marginTotal); + + // Ensure we aren't bigger than our max width or smaller than our min width. + m_width = min(availContentWidth, maxPrefWidth()); + } + + m_width = max(m_width, minPrefWidth()); + + // Finally, with our true width determined, compute our margins for real. + m_marginRight = 0; + m_marginLeft = 0; + calcHorizontalMargins(style()->marginLeft(), style()->marginRight(), availableWidth); +} + +void RenderTable::layout() +{ + ASSERT(needsLayout()); + + if (layoutOnlyPositionedObjects()) + return; + + recalcSectionsIfNeeded(); + + IntRect oldBounds; + IntRect oldOutlineBox; + bool checkForRepaint = checkForRepaintDuringLayout(); + if (checkForRepaint) { + oldBounds = absoluteClippedOverflowRect(); + oldOutlineBox = absoluteOutlineBounds(); + } + + LayoutStateMaintainer statePusher(view(), this, IntSize(m_x, m_y)); + + m_height = 0; + m_overflowHeight = 0; + m_overflowTop = 0; + initMaxMarginValues(); + + int oldWidth = m_width; + calcWidth(); + + if (m_caption && m_width != oldWidth) + m_caption->setNeedsLayout(true, false); + + // FIXME: The optimisation below doesn't work since the internal table + // layout could have changed. we need to add a flag to the table + // layout that tells us if something has changed in the min max + // calculations to do it correctly. +// if ( oldWidth != m_width || columns.size() + 1 != columnPos.size() ) + m_tableLayout->layout(); + + setCellWidths(); + + // layout child objects + int calculatedHeight = 0; + int oldTableTop = m_caption ? m_caption->height() + m_caption->marginTop() + m_caption->marginBottom() : 0; + + bool collapsing = collapseBorders(); + + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { + // FIXME: What about a form that has a display value that makes it a table section? + if (child->needsLayout() && !(child->element() && child->element()->hasTagName(formTag))) + child->layout(); + if (child->isTableSection()) { + RenderTableSection* section = static_cast<RenderTableSection*>(child); + calculatedHeight += section->calcRowHeight(); + if (collapsing) + section->recalcOuterBorder(); + } + } + + m_overflowWidth = m_width + (collapsing ? outerBorderRight() - borderRight() : 0); + m_overflowLeft = collapsing ? borderLeft() - outerBorderLeft() : 0; + + // If any table section moved vertically, we will just repaint everything from that + // section down (it is quite unlikely that any of the following sections + // did not shift). + bool sectionMoved = false; + int movedSectionTop = 0; + + // FIXME: Collapse caption margin. + if (m_caption && m_caption->style()->captionSide() != CAPBOTTOM) { + IntRect captionRect(m_caption->xPos(), m_caption->yPos(), m_caption->width(), m_caption->height()); + + m_caption->setPos(m_caption->marginLeft(), m_height); + if (!selfNeedsLayout() && m_caption->checkForRepaintDuringLayout()) + m_caption->repaintDuringLayoutIfMoved(captionRect); + + m_height += m_caption->height() + m_caption->marginTop() + m_caption->marginBottom(); + m_overflowLeft = min(m_overflowLeft, m_caption->xPos() + m_caption->overflowLeft(false)); + m_overflowWidth = max(m_overflowWidth, m_caption->xPos() + m_caption->overflowWidth(false)); + m_overflowTop = min(m_overflowTop, m_caption->yPos() + m_caption->overflowTop(false)); + m_overflowHeight = max(m_overflowHeight, m_caption->yPos() + m_caption->overflowHeight(false)); + + if (m_height != oldTableTop) { + sectionMoved = true; + movedSectionTop = min(m_height, oldTableTop); + } + } + + int bpTop = borderTop() + (collapsing ? 0 : paddingTop()); + int bpBottom = borderBottom() + (collapsing ? 0 : paddingBottom()); + + m_height += bpTop; + + if (!isPositioned()) + calcHeight(); + + Length h = style()->height(); + int th = 0; + if (h.isFixed()) + // Tables size as though CSS height includes border/padding. + th = h.value() - (bpTop + bpBottom); + else if (h.isPercent()) + th = calcPercentageHeight(h); + th = max(0, th); + + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { + if (!child->isTableSection()) + continue; + // FIXME: Distribute extra height between all table body sections instead of giving it all to the first one. + static_cast<RenderTableSection*>(child)->layoutRows(child == m_firstBody ? max(0, th - calculatedHeight) : 0); + } + + if (!m_firstBody && th > calculatedHeight && !style()->htmlHacks()) { + // Completely empty tables (with no sections or anything) should at least honor specified height + // in strict mode. + m_height += th; + } + + int bl = borderLeft(); + if (!collapsing) + bl += paddingLeft(); + + // position the table sections + RenderTableSection* section = m_head ? m_head : (m_firstBody ? m_firstBody : m_foot); + while (section) { + if (!sectionMoved && section->yPos() != m_height) { + sectionMoved = true; + movedSectionTop = min(m_height, section->yPos()) + section->overflowTop(false); + } + section->setPos(bl, m_height); + + m_height += section->height(); + m_overflowLeft = min(m_overflowLeft, section->xPos() + section->overflowLeft(false)); + m_overflowWidth = max(m_overflowWidth, section->xPos() + section->overflowWidth(false)); + m_overflowTop = min(m_overflowTop, section->yPos() + section->overflowTop(false)); + m_overflowHeight = max(m_overflowHeight, section->yPos() + section->overflowHeight(false)); + section = sectionBelow(section); + } + + m_height += bpBottom; + + if (m_caption && m_caption->style()->captionSide() == CAPBOTTOM) { + IntRect captionRect(m_caption->xPos(), m_caption->yPos(), m_caption->width(), m_caption->height()); + + m_caption->setPos(m_caption->marginLeft(), m_height); + if (!selfNeedsLayout() && m_caption->checkForRepaintDuringLayout()) + m_caption->repaintDuringLayoutIfMoved(captionRect); + + m_height += m_caption->height() + m_caption->marginTop() + m_caption->marginBottom(); + m_overflowLeft = min(m_overflowLeft, m_caption->xPos() + m_caption->overflowLeft(false)); + m_overflowWidth = max(m_overflowWidth, m_caption->xPos() + m_caption->overflowWidth(false)); + } + + if (isPositioned()) + calcHeight(); + + m_overflowHeight = max(m_overflowHeight, m_height); + + // table can be containing block of positioned elements. + // FIXME: Only pass true if width or height changed. + layoutPositionedObjects(true); + + if (!hasOverflowClip()) { + for (ShadowData* boxShadow = style()->boxShadow(); boxShadow; boxShadow = boxShadow->next) { + m_overflowLeft = min(m_overflowLeft, boxShadow->x - boxShadow->blur); + m_overflowWidth = max(m_overflowWidth, m_width + boxShadow->x + boxShadow->blur); + m_overflowTop = min(m_overflowTop, boxShadow->y - boxShadow->blur); + m_overflowHeight = max(m_overflowHeight, m_height + boxShadow->y + boxShadow->blur); + } + + if (hasReflection()) { + IntRect reflection(reflectionBox()); + m_overflowTop = min(m_overflowTop, reflection.y()); + m_overflowHeight = max(m_overflowHeight, reflection.bottom()); + m_overflowLeft = min(m_overflowLeft, reflection.x()); + m_overflowHeight = max(m_overflowWidth, reflection.right()); + } + } + + statePusher.pop(); + + bool didFullRepaint = true; + // Repaint with our new bounds if they are different from our old bounds. + if (checkForRepaint) + didFullRepaint = repaintAfterLayoutIfNeeded(oldBounds, oldOutlineBox); + if (!didFullRepaint && sectionMoved) + repaintRectangle(IntRect(m_overflowLeft, movedSectionTop, m_overflowWidth - m_overflowLeft, m_overflowHeight - movedSectionTop)); + + setNeedsLayout(false); +} + +void RenderTable::setCellWidths() +{ + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { + if (child->isTableSection()) + static_cast<RenderTableSection*>(child)->setCellWidths(); + } +} + +void RenderTable::paint(PaintInfo& paintInfo, int tx, int ty) +{ + tx += xPos(); + ty += yPos(); + + PaintPhase paintPhase = paintInfo.phase; + + int os = 2 * maximalOutlineSize(paintPhase); + if (ty + overflowTop(false) >= paintInfo.rect.bottom() + os || ty + overflowHeight(false) <= paintInfo.rect.y() - os) + return; + if (tx + overflowLeft(false) >= paintInfo.rect.right() + os || tx + overflowWidth(false) <= paintInfo.rect.x() - os) + return; + + if ((paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) && hasBoxDecorations() && style()->visibility() == VISIBLE) + paintBoxDecorations(paintInfo, tx, ty); + + if (paintPhase == PaintPhaseMask) { + paintMask(paintInfo, tx, ty); + return; + } + + // We're done. We don't bother painting any children. + if (paintPhase == PaintPhaseBlockBackground) + return; + + // We don't paint our own background, but we do let the kids paint their backgrounds. + if (paintPhase == PaintPhaseChildBlockBackgrounds) + paintPhase = PaintPhaseChildBlockBackground; + PaintInfo info(paintInfo); + info.phase = paintPhase; + info.paintingRoot = paintingRootForChildren(paintInfo); + + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { + if (!child->hasLayer() && (child->isTableSection() || child == m_caption)) + child->paint(info, tx, ty); + } + + if (collapseBorders() && paintPhase == PaintPhaseChildBlockBackground && style()->visibility() == VISIBLE) { + // Collect all the unique border styles that we want to paint in a sorted list. Once we + // have all the styles sorted, we then do individual passes, painting each style of border + // from lowest precedence to highest precedence. + info.phase = PaintPhaseCollapsedTableBorders; + RenderTableCell::CollapsedBorderStyles borderStyles; + RenderObject* stop = nextInPreOrderAfterChildren(); + for (RenderObject* o = firstChild(); o && o != stop; o = o->nextInPreOrder()) + if (o->isTableCell()) + static_cast<RenderTableCell*>(o)->collectBorderStyles(borderStyles); + RenderTableCell::sortBorderStyles(borderStyles); + size_t count = borderStyles.size(); + for (size_t i = 0; i < count; ++i) { + m_currentBorder = &borderStyles[i]; + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) + if (child->isTableSection()) + child->paint(info, tx, ty); + } + m_currentBorder = 0; + } +} + +void RenderTable::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty) +{ + int w = width(); + int h = height(); + + // Account for the caption. + if (m_caption) { + int captionHeight = (m_caption->height() + m_caption->marginBottom() + m_caption->marginTop()); + h -= captionHeight; + if (m_caption->style()->captionSide() != CAPBOTTOM) + ty += captionHeight; + } + + int my = max(ty, paintInfo.rect.y()); + int mh; + if (ty < paintInfo.rect.y()) + mh = max(0, h - (paintInfo.rect.y() - ty)); + else + mh = min(paintInfo.rect.height(), h); + + paintBoxShadow(paintInfo.context, tx, ty, w, h, style()); + + paintFillLayers(paintInfo, style()->backgroundColor(), style()->backgroundLayers(), my, mh, tx, ty, w, h); + + if (style()->hasBorder() && !collapseBorders()) + paintBorder(paintInfo.context, tx, ty, w, h, style()); +} + +void RenderTable::paintMask(PaintInfo& paintInfo, int tx, int ty) +{ + if (style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) + return; + + int w = width(); + int h = height(); + + // Account for the caption. + if (m_caption) { + int captionHeight = (m_caption->height() + m_caption->marginBottom() + m_caption->marginTop()); + h -= captionHeight; + if (m_caption->style()->captionSide() != CAPBOTTOM) + ty += captionHeight; + } + + int my = max(ty, paintInfo.rect.y()); + int mh; + if (ty < paintInfo.rect.y()) + mh = max(0, h - (paintInfo.rect.y() - ty)); + else + mh = min(paintInfo.rect.height(), h); + + paintMaskImages(paintInfo, my, mh, tx, ty, w, h); +} + +void RenderTable::calcPrefWidths() +{ + ASSERT(prefWidthsDirty()); + + recalcSectionsIfNeeded(); + recalcHorizontalBorders(); + + m_tableLayout->calcPrefWidths(m_minPrefWidth, m_maxPrefWidth); + + if (m_caption) + m_minPrefWidth = max(m_minPrefWidth, m_caption->minPrefWidth()); + + setPrefWidthsDirty(false); +} + +void RenderTable::splitColumn(int pos, int firstSpan) +{ + // we need to add a new columnStruct + int oldSize = m_columns.size(); + m_columns.grow(oldSize + 1); + int oldSpan = m_columns[pos].span; + ASSERT(oldSpan > firstSpan); + m_columns[pos].span = firstSpan; + memmove(m_columns.data() + pos + 1, m_columns.data() + pos, (oldSize - pos) * sizeof(ColumnStruct)); + m_columns[pos + 1].span = oldSpan - firstSpan; + + // change width of all rows. + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { + if (child->isTableSection()) + static_cast<RenderTableSection*>(child)->splitColumn(pos, oldSize + 1); + } + + m_columnPos.grow(numEffCols() + 1); + setNeedsLayoutAndPrefWidthsRecalc(); +} + +void RenderTable::appendColumn(int span) +{ + // easy case. + int pos = m_columns.size(); + int newSize = pos + 1; + m_columns.grow(newSize); + m_columns[pos].span = span; + + // change width of all rows. + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { + if (child->isTableSection()) + static_cast<RenderTableSection*>(child)->appendColumn(pos); + } + + m_columnPos.grow(numEffCols() + 1); + setNeedsLayoutAndPrefWidthsRecalc(); +} + +RenderTableCol* RenderTable::colElement(int col, bool* startEdge, bool* endEdge) const +{ + if (!m_hasColElements) + return 0; + RenderObject* child = firstChild(); + int cCol = 0; + + while (child) { + if (child->isTableCol()) { + RenderTableCol* colElem = static_cast<RenderTableCol*>(child); + int span = colElem->span(); + if (!colElem->firstChild()) { + int startCol = cCol; + int endCol = cCol + span - 1; + cCol += span; + if (cCol > col) { + if (startEdge) + *startEdge = startCol == col; + if (endEdge) + *endEdge = endCol == col; + return colElem; + } + } + + RenderObject* next = child->firstChild(); + if (!next) + next = child->nextSibling(); + if (!next && child->parent()->isTableCol()) + next = child->parent()->nextSibling(); + child = next; + } else if (child == m_caption) + child = child->nextSibling(); + else + break; + } + + return 0; +} + +void RenderTable::recalcSections() const +{ + m_caption = 0; + m_head = 0; + m_foot = 0; + m_firstBody = 0; + m_hasColElements = false; + + // We need to get valid pointers to caption, head, foot and first body again + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { + switch (child->style()->display()) { + case TABLE_CAPTION: + if (!m_caption && child->isRenderBlock()) { + m_caption = static_cast<RenderBlock*>(child); + m_caption->setNeedsLayout(true); + } + break; + case TABLE_COLUMN: + case TABLE_COLUMN_GROUP: + m_hasColElements = true; + break; + case TABLE_HEADER_GROUP: + if (child->isTableSection()) { + RenderTableSection* section = static_cast<RenderTableSection*>(child); + if (!m_head) + m_head = section; + else if (!m_firstBody) + m_firstBody = section; + section->recalcCellsIfNeeded(); + } + break; + case TABLE_FOOTER_GROUP: + if (child->isTableSection()) { + RenderTableSection* section = static_cast<RenderTableSection*>(child); + if (!m_foot) + m_foot = section; + else if (!m_firstBody) + m_firstBody = section; + section->recalcCellsIfNeeded(); + } + break; + case TABLE_ROW_GROUP: + if (child->isTableSection()) { + RenderTableSection* section = static_cast<RenderTableSection*>(child); + if (!m_firstBody) + m_firstBody = section; + section->recalcCellsIfNeeded(); + } + break; + default: + break; + } + } + + // repair column count (addChild can grow it too much, because it always adds elements to the last row of a section) + int maxCols = 0; + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { + if (child->isTableSection()) { + RenderTableSection* section = static_cast<RenderTableSection*>(child); + int sectionCols = section->numColumns(); + if (sectionCols > maxCols) + maxCols = sectionCols; + } + } + + m_columns.resize(maxCols); + m_columnPos.resize(maxCols + 1); + + ASSERT(selfNeedsLayout()); + + m_needsSectionRecalc = false; +} + +RenderObject* RenderTable::removeChildNode(RenderObject* child, bool fullRemove) +{ + setNeedsSectionRecalc(); + return RenderContainer::removeChildNode(child, fullRemove); +} + +int RenderTable::calcBorderLeft() const +{ + if (collapseBorders()) { + // Determined by the first cell of the first row. See the CSS 2.1 spec, section 17.6.2. + if (!numEffCols()) + return 0; + + unsigned borderWidth = 0; + + const BorderValue& tb = style()->borderLeft(); + if (tb.style() == BHIDDEN) + return 0; + if (tb.style() > BHIDDEN) + borderWidth = tb.width; + + int leftmostColumn = style()->direction() == RTL ? numEffCols() - 1 : 0; + RenderTableCol* colGroup = colElement(leftmostColumn); + if (colGroup) { + const BorderValue& gb = style()->borderLeft(); + if (gb.style() == BHIDDEN) + return 0; + if (gb.style() > BHIDDEN) + borderWidth = max(borderWidth, static_cast<unsigned>(gb.width)); + } + + RenderTableSection* firstNonEmptySection = m_head ? m_head : (m_firstBody ? m_firstBody : m_foot); + if (firstNonEmptySection && !firstNonEmptySection->numRows()) + firstNonEmptySection = sectionBelow(firstNonEmptySection, true); + + if (firstNonEmptySection) { + const BorderValue& sb = firstNonEmptySection->style()->borderLeft(); + if (sb.style() == BHIDDEN) + return 0; + + if (sb.style() > BHIDDEN) + borderWidth = max(borderWidth, static_cast<unsigned>(sb.width)); + + const RenderTableSection::CellStruct& cs = firstNonEmptySection->cellAt(0, leftmostColumn); + + if (cs.cell) { + const BorderValue& cb = cs.cell->style()->borderLeft(); + if (cb.style() == BHIDDEN) + return 0; + + const BorderValue& rb = cs.cell->parent()->style()->borderLeft(); + if (rb.style() == BHIDDEN) + return 0; + + if (cb.style() > BHIDDEN) + borderWidth = max(borderWidth, static_cast<unsigned>(cb.width)); + if (rb.style() > BHIDDEN) + borderWidth = max(borderWidth, static_cast<unsigned>(rb.width)); + } + } + return borderWidth / 2; + } + return RenderBlock::borderLeft(); +} + +int RenderTable::calcBorderRight() const +{ + if (collapseBorders()) { + // Determined by the last cell of the first row. See the CSS 2.1 spec, section 17.6.2. + if (!numEffCols()) + return 0; + + unsigned borderWidth = 0; + + const BorderValue& tb = style()->borderRight(); + if (tb.style() == BHIDDEN) + return 0; + if (tb.style() > BHIDDEN) + borderWidth = tb.width; + + int rightmostColumn = style()->direction() == RTL ? 0 : numEffCols() - 1; + RenderTableCol* colGroup = colElement(rightmostColumn); + if (colGroup) { + const BorderValue& gb = style()->borderRight(); + if (gb.style() == BHIDDEN) + return 0; + if (gb.style() > BHIDDEN) + borderWidth = max(borderWidth, static_cast<unsigned>(gb.width)); + } + + RenderTableSection* firstNonEmptySection = m_head ? m_head : (m_firstBody ? m_firstBody : m_foot); + if (firstNonEmptySection && !firstNonEmptySection->numRows()) + firstNonEmptySection = sectionBelow(firstNonEmptySection, true); + + if (firstNonEmptySection) { + const BorderValue& sb = firstNonEmptySection->style()->borderRight(); + if (sb.style() == BHIDDEN) + return 0; + + if (sb.style() > BHIDDEN) + borderWidth = max(borderWidth, static_cast<unsigned>(sb.width)); + + const RenderTableSection::CellStruct& cs = firstNonEmptySection->cellAt(0, rightmostColumn); + + if (cs.cell) { + const BorderValue& cb = cs.cell->style()->borderRight(); + if (cb.style() == BHIDDEN) + return 0; + + const BorderValue& rb = cs.cell->parent()->style()->borderRight(); + if (rb.style() == BHIDDEN) + return 0; + + if (cb.style() > BHIDDEN) + borderWidth = max(borderWidth, static_cast<unsigned>(cb.width)); + if (rb.style() > BHIDDEN) + borderWidth = max(borderWidth, static_cast<unsigned>(rb.width)); + } + } + return (borderWidth + 1) / 2; + } + return RenderBlock::borderRight(); +} + +void RenderTable::recalcHorizontalBorders() +{ + m_borderLeft = calcBorderLeft(); + m_borderRight = calcBorderRight(); +} + +int RenderTable::borderTop() const +{ + if (collapseBorders()) + return outerBorderTop(); + return RenderBlock::borderTop(); +} + +int RenderTable::borderBottom() const +{ + if (collapseBorders()) + return outerBorderBottom(); + return RenderBlock::borderBottom(); +} + +int RenderTable::outerBorderTop() const +{ + if (!collapseBorders()) + return 0; + int borderWidth = 0; + RenderTableSection* topSection; + if (m_head) + topSection = m_head; + else if (m_firstBody) + topSection = m_firstBody; + else if (m_foot) + topSection = m_foot; + else + topSection = 0; + if (topSection) { + borderWidth = topSection->outerBorderTop(); + if (borderWidth == -1) + return 0; // Overridden by hidden + } + const BorderValue& tb = style()->borderTop(); + if (tb.style() == BHIDDEN) + return 0; + if (tb.style() > BHIDDEN) + borderWidth = max(borderWidth, static_cast<int>(tb.width / 2)); + return borderWidth; +} + +int RenderTable::outerBorderBottom() const +{ + if (!collapseBorders()) + return 0; + int borderWidth = 0; + RenderTableSection* bottomSection; + if (m_foot) + bottomSection = m_foot; + else { + RenderObject* child; + for (child = lastChild(); child && !child->isTableSection(); child = child->previousSibling()) + ; + bottomSection = child ? static_cast<RenderTableSection*>(child) : 0; + } + if (bottomSection) { + borderWidth = bottomSection->outerBorderBottom(); + if (borderWidth == -1) + return 0; // Overridden by hidden + } + const BorderValue& tb = style()->borderBottom(); + if (tb.style() == BHIDDEN) + return 0; + if (tb.style() > BHIDDEN) + borderWidth = max(borderWidth, static_cast<int>((tb.width + 1) / 2)); + return borderWidth; +} + +int RenderTable::outerBorderLeft() const +{ + if (!collapseBorders()) + return 0; + + int borderWidth = 0; + + const BorderValue& tb = style()->borderLeft(); + if (tb.style() == BHIDDEN) + return 0; + if (tb.style() > BHIDDEN) + borderWidth = tb.width / 2; + + bool allHidden = true; + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { + if (!child->isTableSection()) + continue; + int sw = static_cast<RenderTableSection*>(child)->outerBorderLeft(); + if (sw == -1) + continue; + else + allHidden = false; + borderWidth = max(borderWidth, sw); + } + if (allHidden) + return 0; + + return borderWidth; +} + +int RenderTable::outerBorderRight() const +{ + if (!collapseBorders()) + return 0; + + int borderWidth = 0; + + const BorderValue& tb = style()->borderRight(); + if (tb.style() == BHIDDEN) + return 0; + if (tb.style() > BHIDDEN) + borderWidth = (tb.width + 1) / 2; + + bool allHidden = true; + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { + if (!child->isTableSection()) + continue; + int sw = static_cast<RenderTableSection*>(child)->outerBorderRight(); + if (sw == -1) + continue; + else + allHidden = false; + borderWidth = max(borderWidth, sw); + } + if (allHidden) + return 0; + + return borderWidth; +} + +RenderTableSection* RenderTable::sectionAbove(const RenderTableSection* section, bool skipEmptySections) const +{ + recalcSectionsIfNeeded(); + + if (section == m_head) + return 0; + + RenderObject* prevSection = section == m_foot ? lastChild() : section->previousSibling(); + while (prevSection) { + if (prevSection->isTableSection() && prevSection != m_head && prevSection != m_foot && (!skipEmptySections || static_cast<RenderTableSection*>(prevSection)->numRows())) + break; + prevSection = prevSection->previousSibling(); + } + if (!prevSection && m_head && (!skipEmptySections || m_head->numRows())) + prevSection = m_head; + return static_cast<RenderTableSection*>(prevSection); +} + +RenderTableSection* RenderTable::sectionBelow(const RenderTableSection* section, bool skipEmptySections) const +{ + recalcSectionsIfNeeded(); + + if (section == m_foot) + return 0; + + RenderObject* nextSection = section == m_head ? firstChild() : section->nextSibling(); + while (nextSection) { + if (nextSection->isTableSection() && nextSection != m_head && nextSection != m_foot && (!skipEmptySections || static_cast<RenderTableSection*>(nextSection)->numRows())) + break; + nextSection = nextSection->nextSibling(); + } + if (!nextSection && m_foot && (!skipEmptySections || m_foot->numRows())) + nextSection = m_foot; + return static_cast<RenderTableSection*>(nextSection); +} + +RenderTableCell* RenderTable::cellAbove(const RenderTableCell* cell) const +{ + recalcSectionsIfNeeded(); + + // Find the section and row to look in + int r = cell->row(); + RenderTableSection* section = 0; + int rAbove = 0; + if (r > 0) { + // cell is not in the first row, so use the above row in its own section + section = cell->section(); + rAbove = r - 1; + } else { + section = sectionAbove(cell->section(), true); + if (section) + rAbove = section->numRows() - 1; + } + + // Look up the cell in the section's grid, which requires effective col index + if (section) { + int effCol = colToEffCol(cell->col()); + RenderTableSection::CellStruct aboveCell; + // If we hit a span back up to a real cell. + do { + aboveCell = section->cellAt(rAbove, effCol); + effCol--; + } while (!aboveCell.cell && aboveCell.inColSpan && effCol >= 0); + return aboveCell.cell; + } else + return 0; +} + +RenderTableCell* RenderTable::cellBelow(const RenderTableCell* cell) const +{ + recalcSectionsIfNeeded(); + + // Find the section and row to look in + int r = cell->row() + cell->rowSpan() - 1; + RenderTableSection* section = 0; + int rBelow = 0; + if (r < cell->section()->numRows() - 1) { + // The cell is not in the last row, so use the next row in the section. + section = cell->section(); + rBelow = r + 1; + } else { + section = sectionBelow(cell->section(), true); + if (section) + rBelow = 0; + } + + // Look up the cell in the section's grid, which requires effective col index + if (section) { + int effCol = colToEffCol(cell->col()); + RenderTableSection::CellStruct belowCell; + // If we hit a colspan back up to a real cell. + do { + belowCell = section->cellAt(rBelow, effCol); + effCol--; + } while (!belowCell.cell && belowCell.inColSpan && effCol >= 0); + return belowCell.cell; + } else + return 0; +} + +RenderTableCell* RenderTable::cellBefore(const RenderTableCell* cell) const +{ + recalcSectionsIfNeeded(); + + RenderTableSection* section = cell->section(); + int effCol = colToEffCol(cell->col()); + if (!effCol) + return 0; + + // If we hit a colspan back up to a real cell. + RenderTableSection::CellStruct prevCell; + do { + prevCell = section->cellAt(cell->row(), effCol - 1); + effCol--; + } while (!prevCell.cell && prevCell.inColSpan && effCol >= 0); + return prevCell.cell; +} + +RenderTableCell* RenderTable::cellAfter(const RenderTableCell* cell) const +{ + recalcSectionsIfNeeded(); + + int effCol = colToEffCol(cell->col() + cell->colSpan()); + if (effCol >= numEffCols()) + return 0; + return cell->section()->cellAt(cell->row(), effCol).cell; +} + +RenderBlock* RenderTable::firstLineBlock() const +{ + return 0; +} + +void RenderTable::updateFirstLetter() +{ +} + +int RenderTable::getBaselineOfFirstLineBox() const +{ + RenderTableSection* firstNonEmptySection = m_head ? m_head : (m_firstBody ? m_firstBody : m_foot); + if (firstNonEmptySection && !firstNonEmptySection->numRows()) + firstNonEmptySection = sectionBelow(firstNonEmptySection, true); + + if (!firstNonEmptySection) + return -1; + + return firstNonEmptySection->yPos() + firstNonEmptySection->getBaselineOfFirstLineBox(); +} + +IntRect RenderTable::getOverflowClipRect(int tx, int ty) +{ + IntRect rect = RenderBlock::getOverflowClipRect(tx, ty); + + // If we have a caption, expand the clip to include the caption. + // FIXME: Technically this is wrong, but it's virtually impossible to fix this + // for real until captions have been re-written. + // FIXME: This code assumes (like all our other caption code) that only top/bottom are + // supported. When we actually support left/right and stop mapping them to top/bottom, + // we might have to hack this code first (depending on what order we do these bug fixes in). + if (m_caption) { + rect.setHeight(height()); + rect.setY(ty); + } + + return rect; +} + +} diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderTable.h b/src/3rdparty/webkit/WebCore/rendering/RenderTable.h new file mode 100644 index 0000000..59cb00e --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderTable.h @@ -0,0 +1,226 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1997 Martin Jones (mjones@kde.org) + * (C) 1997 Torben Weis (weis@kde.org) + * (C) 1998 Waldo Bastian (bastian@kde.org) + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. + * + * 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. + */ + +#ifndef RenderTable_h +#define RenderTable_h + +#include "RenderBlock.h" +#include <wtf/Vector.h> + +namespace WebCore { + +class RenderTableCol; +class RenderTableCell; +class RenderTableSection; +class TableLayout; + +class RenderTable : public RenderBlock { +public: + enum Rules { + None = 0x00, + RGroups = 0x01, + CGroups = 0x02, + Groups = 0x03, + Rows = 0x05, + Cols = 0x0a, + All = 0x0f + }; + enum Frame { + Void = 0x00, + Above = 0x01, + Below = 0x02, + Lhs = 0x04, + Rhs = 0x08, + Hsides = 0x03, + Vsides = 0x0c, + Box = 0x0f + }; + + RenderTable(Node*); + ~RenderTable(); + + virtual const char* renderName() const { return "RenderTable"; } + + virtual bool isTable() const { return true; } + + virtual bool avoidsFloats() const { return true; } + + int getColumnPos(int col) const { return m_columnPos[col]; } + + int hBorderSpacing() const { return m_hSpacing; } + int vBorderSpacing() const { return m_vSpacing; } + + bool collapseBorders() const { return style()->borderCollapse(); } + int borderLeft() const { return m_borderLeft; } + int borderRight() const { return m_borderRight; } + int borderTop() const; + int borderBottom() const; + + Rules getRules() const { return static_cast<Rules>(m_rules); } + + const Color& bgColor() const { return style()->backgroundColor(); } + + int outerBorderTop() const; + int outerBorderBottom() const; + int outerBorderLeft() const; + int outerBorderRight() const; + + int calcBorderLeft() const; + int calcBorderRight() const; + void recalcHorizontalBorders(); + + // overrides + virtual void addChild(RenderObject* child, RenderObject* beforeChild = 0); + virtual void paint(PaintInfo&, int tx, int ty); + virtual void paintBoxDecorations(PaintInfo&, int tx, int ty); + virtual void paintMask(PaintInfo& paintInfo, int tx, int ty); + virtual void layout(); + virtual void calcPrefWidths(); + + virtual int getBaselineOfFirstLineBox() const; + + virtual RenderBlock* firstLineBlock() const; + virtual void updateFirstLetter(); + + virtual void setCellWidths(); + + virtual void calcWidth(); + + struct ColumnStruct { + enum { + WidthUndefined = 0xffff + }; + + ColumnStruct() + : span(1) + , width(WidthUndefined) + { + } + + unsigned short span; + unsigned width; // the calculated position of the column + }; + + Vector<ColumnStruct>& columns() { return m_columns; } + Vector<int>& columnPositions() { return m_columnPos; } + RenderTableSection* header() const { return m_head; } + RenderTableSection* footer() const { return m_foot; } + RenderTableSection* firstBody() const { return m_firstBody; } + + void splitColumn(int pos, int firstSpan); + void appendColumn(int span); + int numEffCols() const { return m_columns.size(); } + int spanOfEffCol(int effCol) const { return m_columns[effCol].span; } + + int colToEffCol(int col) const + { + int i = 0; + int effCol = numEffCols(); + for (int c = 0; c < col && i < effCol; ++i) + c += m_columns[i].span; + return i; + } + + int effColToCol(int effCol) const + { + int c = 0; + for (int i = 0; i < effCol; i++) + c += m_columns[i].span; + return c; + } + + int bordersPaddingAndSpacing() const + { + return borderLeft() + borderRight() + + (collapseBorders() ? 0 : (paddingLeft() + paddingRight() + (numEffCols() + 1) * hBorderSpacing())); + } + + RenderTableCol* colElement(int col, bool* startEdge = 0, bool* endEdge = 0) const; + + bool needsSectionRecalc() const { return m_needsSectionRecalc; } + void setNeedsSectionRecalc() + { + if (documentBeingDestroyed()) + return; + m_needsSectionRecalc = true; + setNeedsLayout(true); + } + + virtual RenderObject* removeChildNode(RenderObject*, bool fullRemove = true); + + RenderTableSection* sectionAbove(const RenderTableSection*, bool skipEmptySections = false) const; + RenderTableSection* sectionBelow(const RenderTableSection*, bool skipEmptySections = false) const; + + RenderTableCell* cellAbove(const RenderTableCell*) const; + RenderTableCell* cellBelow(const RenderTableCell*) const; + RenderTableCell* cellBefore(const RenderTableCell*) const; + RenderTableCell* cellAfter(const RenderTableCell*) const; + + const CollapsedBorderValue* currentBorderStyle() const { return m_currentBorder; } + + bool hasSections() const { return m_head || m_foot || m_firstBody; } + + virtual IntRect getOverflowClipRect(int tx, int ty); + + void recalcSectionsIfNeeded() const + { + if (m_needsSectionRecalc) + recalcSections(); + } + +protected: + virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + +private: + void recalcSections() const; + + mutable Vector<int> m_columnPos; + mutable Vector<ColumnStruct> m_columns; + + mutable RenderBlock* m_caption; + mutable RenderTableSection* m_head; + mutable RenderTableSection* m_foot; + mutable RenderTableSection* m_firstBody; + + TableLayout* m_tableLayout; + + const CollapsedBorderValue* m_currentBorder; + + unsigned m_frame : 4; // Frame + unsigned m_rules : 4; // Rules + + mutable bool m_hasColElements : 1; + mutable bool m_needsSectionRecalc : 1; + + short m_hSpacing; + short m_vSpacing; + int m_borderLeft; + int m_borderRight; +}; + +} // namespace WebCore + +#endif // RenderTable_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderTableCell.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderTableCell.cpp new file mode 100644 index 0000000..02e7729 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderTableCell.cpp @@ -0,0 +1,882 @@ +/** + * Copyright (C) 1997 Martin Jones (mjones@kde.org) + * (C) 1997 Torben Weis (weis@kde.org) + * (C) 1998 Waldo Bastian (bastian@kde.org) + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * 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" +#include "RenderTableCell.h" + +#include "GraphicsContext.h" +#include "HTMLNames.h" +#include "HTMLTableCellElement.h" +#include "RenderTableCol.h" +#include "RenderView.h" + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +RenderTableCell::RenderTableCell(Node* node) + : RenderBlock(node) + , m_row(-1) + , m_column(-1) + , m_rowSpan(1) + , m_columnSpan(1) + , m_topExtra(0) + , m_bottomExtra(0) + , m_widthChanged(false) + , m_percentageHeight(0) +{ + updateFromElement(); +} + +void RenderTableCell::destroy() +{ + RenderTableSection* recalcSection = parent() ? section() : 0; + + RenderBlock::destroy(); + + if (recalcSection) + recalcSection->setNeedsCellRecalc(); +} + +void RenderTableCell::updateFromElement() +{ + Node* node = element(); + if (node && (node->hasTagName(tdTag) || node->hasTagName(thTag))) { + HTMLTableCellElement* tc = static_cast<HTMLTableCellElement*>(node); + int oldRSpan = m_rowSpan; + int oldCSpan = m_columnSpan; + + m_columnSpan = tc->colSpan(); + m_rowSpan = tc->rowSpan(); + if ((oldRSpan != m_rowSpan || oldCSpan != m_columnSpan) && style() && parent()) { + setNeedsLayoutAndPrefWidthsRecalc(); + if (section()) + section()->setNeedsCellRecalc(); + } + } +} + +Length RenderTableCell::styleOrColWidth() const +{ + Length w = style()->width(); + if (colSpan() > 1 || !w.isAuto()) + return w; + RenderTableCol* tableCol = table()->colElement(col()); + if (tableCol) { + w = tableCol->style()->width(); + + // Column widths specified on <col> apply to the border box of the cell. + // Percentages don't need to be handled since they're always treated this way (even when specified on the cells). + // See Bugzilla bug 8126 for details. + if (w.isFixed() && w.value() > 0) + w = Length(max(0, w.value() - borderLeft() - borderRight() - paddingLeft() - paddingRight()), Fixed); + } + return w; +} + +void RenderTableCell::calcPrefWidths() +{ + // The child cells rely on the grids up in the sections to do their calcPrefWidths work. Normally the sections are set up early, as table + // cells are added, but relayout can cause the cells to be freed, leaving stale pointers in the sections' + // grids. We must refresh those grids before the child cells try to use them. + table()->recalcSectionsIfNeeded(); + + RenderBlock::calcPrefWidths(); + if (element() && style()->autoWrap()) { + // See if nowrap was set. + Length w = styleOrColWidth(); + String nowrap = static_cast<Element*>(element())->getAttribute(nowrapAttr); + if (!nowrap.isNull() && w.isFixed()) + // Nowrap is set, but we didn't actually use it because of the + // fixed width set on the cell. Even so, it is a WinIE/Moz trait + // to make the minwidth of the cell into the fixed width. They do this + // even in strict mode, so do not make this a quirk. Affected the top + // of hiptop.com. + m_minPrefWidth = max(w.value(), m_minPrefWidth); + } +} + +void RenderTableCell::calcWidth() +{ +} + +void RenderTableCell::setWidth(int width) +{ + if (width != m_width) { + m_width = width; + m_widthChanged = true; + } +} + +void RenderTableCell::layout() +{ + layoutBlock(m_widthChanged); + m_widthChanged = false; +} + +IntRect RenderTableCell::absoluteClippedOverflowRect() +{ + // If the table grid is dirty, we cannot get reliable information about adjoining cells, + // so we ignore outside borders. This should not be a problem because it means that + // the table is going to recalculate the grid, relayout and repaint its current rect, which + // includes any outside borders of this cell. + if (!table()->collapseBorders() || table()->needsSectionRecalc()) + return RenderBlock::absoluteClippedOverflowRect(); + + bool rtl = table()->style()->direction() == RTL; + int outlineSize = style()->outlineSize(); + int left = max(borderHalfLeft(true), outlineSize); + int right = max(borderHalfRight(true), outlineSize); + int top = max(borderHalfTop(true), outlineSize); + int bottom = max(borderHalfBottom(true), outlineSize); + if (left && !rtl || right && rtl) { + if (RenderTableCell* before = table()->cellBefore(this)) { + top = max(top, before->borderHalfTop(true)); + bottom = max(bottom, before->borderHalfBottom(true)); + } + } + if (left && rtl || right && !rtl) { + if (RenderTableCell* after = table()->cellAfter(this)) { + top = max(top, after->borderHalfTop(true)); + bottom = max(bottom, after->borderHalfBottom(true)); + } + } + if (top) { + if (RenderTableCell* above = table()->cellAbove(this)) { + left = max(left, above->borderHalfLeft(true)); + right = max(right, above->borderHalfRight(true)); + } + } + if (bottom) { + if (RenderTableCell* below = table()->cellBelow(this)) { + left = max(left, below->borderHalfLeft(true)); + right = max(right, below->borderHalfRight(true)); + } + } + left = max(left, -overflowLeft(false)); + top = max(top, -overflowTop(false) - borderTopExtra()); + IntRect r(-left, -borderTopExtra() - top, left + max(width() + right, overflowWidth(false)), borderTopExtra() + top + max(height() + bottom + borderBottomExtra(), overflowHeight(false))); + + if (RenderView* v = view()) + r.move(v->layoutDelta()); + + computeAbsoluteRepaintRect(r); + return r; +} + +void RenderTableCell::computeAbsoluteRepaintRect(IntRect& r, bool fixed) +{ + r.setY(r.y() + m_topExtra); + RenderView* v = view(); + if ((!v || !v->layoutState()) && parent()) + r.move(-parent()->xPos(), -parent()->yPos()); // Rows are in the same coordinate space, so don't add their offset in. + RenderBlock::computeAbsoluteRepaintRect(r, fixed); +} + +FloatPoint RenderTableCell::localToAbsolute(FloatPoint localPoint, bool fixed, bool useTransforms) const +{ + RenderView* v = view(); + if ((!v || !v->layoutState()) && parent()) { + // Rows are in the same coordinate space, so don't add their offset in. + localPoint.move(-parent()->xPos(), -parent()->yPos()); + } + return RenderBlock::localToAbsolute(localPoint, fixed, useTransforms); +} + +FloatPoint RenderTableCell::absoluteToLocal(FloatPoint containerPoint, bool fixed, bool useTransforms) const +{ + FloatPoint localPoint = RenderBlock::absoluteToLocal(containerPoint, fixed, useTransforms); + if (parent()) { + // Rows are in the same coordinate space, so add their offset back in. + localPoint.move(parent()->xPos(), parent()->yPos()); + } + return localPoint; +} + +FloatQuad RenderTableCell::localToAbsoluteQuad(const FloatQuad& localQuad, bool fixed) const +{ + FloatQuad quad = localQuad; + if (parent()) { + // Rows are in the same coordinate space, so don't add their offset in. + quad.move(-parent()->xPos(), -parent()->yPos()); + } + return RenderBlock::localToAbsoluteQuad(quad, fixed); +} + +int RenderTableCell::baselinePosition(bool /*firstLine*/, bool /*isRootLineBox*/) const +{ + // <http://www.w3.org/TR/2007/CR-CSS21-20070719/tables.html#height-layout>: The baseline of a cell is the baseline of + // the first in-flow line box in the cell, or the first in-flow table-row in the cell, whichever comes first. If there + // is no such line box or table-row, the baseline is the bottom of content edge of the cell box. + + int firstLineBaseline = getBaselineOfFirstLineBox(); + if (firstLineBaseline != -1) + return firstLineBaseline; + + return paddingTop() + borderTop() + contentHeight(); +} + +void RenderTableCell::styleWillChange(RenderStyle::Diff diff, const RenderStyle* newStyle) +{ + if (parent() && section() && style() && style()->height() != newStyle->height()) + section()->setNeedsCellRecalc(); + + ASSERT(newStyle->display() == TABLE_CELL); + + RenderBlock::styleWillChange(diff, newStyle); +} + +void RenderTableCell::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +{ + RenderBlock::styleDidChange(diff, oldStyle); + setHasBoxDecorations(true); +} + +bool RenderTableCell::requiresLayer() +{ + return isPositioned() || isTransparent() || hasOverflowClip() || hasTransform() || hasMask() || hasReflection(); +} + +// The following rules apply for resolving conflicts and figuring out which border +// to use. +// (1) Borders with the 'border-style' of 'hidden' take precedence over all other conflicting +// borders. Any border with this value suppresses all borders at this location. +// (2) Borders with a style of 'none' have the lowest priority. Only if the border properties of all +// the elements meeting at this edge are 'none' will the border be omitted (but note that 'none' is +// the default value for the border style.) +// (3) If none of the styles are 'hidden' and at least one of them is not 'none', then narrow borders +// are discarded in favor of wider ones. If several have the same 'border-width' then styles are preferred +// in this order: 'double', 'solid', 'dashed', 'dotted', 'ridge', 'outset', 'groove', and the lowest: 'inset'. +// (4) If border styles differ only in color, then a style set on a cell wins over one on a row, +// which wins over a row group, column, column group and, lastly, table. It is undefined which color +// is used when two elements of the same type disagree. +static CollapsedBorderValue compareBorders(const CollapsedBorderValue& border1, const CollapsedBorderValue& border2) +{ + // Sanity check the values passed in. If either is null, return the other. + if (!border2.exists()) + return border1; + if (!border1.exists()) + return border2; + + // Rule #1 above. + if (border1.style() == BHIDDEN || border2.style() == BHIDDEN) + return CollapsedBorderValue(); // No border should exist at this location. + + // Rule #2 above. A style of 'none' has lowest priority and always loses to any other border. + if (border2.style() == BNONE) + return border1; + if (border1.style() == BNONE) + return border2; + + // The first part of rule #3 above. Wider borders win. + if (border1.width() != border2.width()) + return border1.width() > border2.width() ? border1 : border2; + + // The borders have equal width. Sort by border style. + if (border1.style() != border2.style()) + return border1.style() > border2.style() ? border1 : border2; + + // The border have the same width and style. Rely on precedence (cell over row over row group, etc.) + return border1.precedence >= border2.precedence ? border1 : border2; +} + +CollapsedBorderValue RenderTableCell::collapsedLeftBorder(bool rtl) const +{ + RenderTable* tableElt = table(); + bool leftmostColumn; + if (!rtl) + leftmostColumn = col() == 0; + else { + int effCol = tableElt->colToEffCol(col() + colSpan() - 1); + leftmostColumn = effCol == tableElt->numEffCols() - 1; + } + + // For border left, we need to check, in order of precedence: + // (1) Our left border. + CollapsedBorderValue result(&style()->borderLeft(), BCELL); + + // (2) The right border of the cell to the left. + RenderTableCell* prevCell = rtl ? tableElt->cellAfter(this) : tableElt->cellBefore(this); + if (prevCell) { + result = rtl ? compareBorders(result, CollapsedBorderValue(&prevCell->style()->borderRight(), BCELL)) : compareBorders(CollapsedBorderValue(&prevCell->style()->borderRight(), BCELL), result); + if (!result.exists()) + return result; + } else if (leftmostColumn) { + // (3) Our row's left border. + result = compareBorders(result, CollapsedBorderValue(&parent()->style()->borderLeft(), BROW)); + if (!result.exists()) + return result; + + // (4) Our row group's left border. + result = compareBorders(result, CollapsedBorderValue(§ion()->style()->borderLeft(), BROWGROUP)); + if (!result.exists()) + return result; + } + + // (5) Our column and column group's left borders. + bool startColEdge; + bool endColEdge; + RenderTableCol* colElt = tableElt->colElement(col() + (rtl ? colSpan() - 1 : 0), &startColEdge, &endColEdge); + if (colElt && (!rtl ? startColEdge : endColEdge)) { + result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderLeft(), BCOL)); + if (!result.exists()) + return result; + if (colElt->parent()->isTableCol() && (!rtl ? !colElt->previousSibling() : !colElt->nextSibling())) { + result = compareBorders(result, CollapsedBorderValue(&colElt->parent()->style()->borderLeft(), BCOLGROUP)); + if (!result.exists()) + return result; + } + } + + // (6) The right border of the column to the left. + if (!leftmostColumn) { + colElt = tableElt->colElement(col() + (rtl ? colSpan() : -1), &startColEdge, &endColEdge); + if (colElt && (!rtl ? endColEdge : startColEdge)) { + result = rtl ? compareBorders(result, CollapsedBorderValue(&colElt->style()->borderRight(), BCOL)) : compareBorders(CollapsedBorderValue(&colElt->style()->borderRight(), BCOL), result); + if (!result.exists()) + return result; + } + } else { + // (7) The table's left border. + result = compareBorders(result, CollapsedBorderValue(&tableElt->style()->borderLeft(), BTABLE)); + if (!result.exists()) + return result; + } + + return result; +} + +CollapsedBorderValue RenderTableCell::collapsedRightBorder(bool rtl) const +{ + RenderTable* tableElt = table(); + bool rightmostColumn; + if (rtl) + rightmostColumn = col() == 0; + else { + int effCol = tableElt->colToEffCol(col() + colSpan() - 1); + rightmostColumn = effCol == tableElt->numEffCols() - 1; + } + + // For border right, we need to check, in order of precedence: + // (1) Our right border. + CollapsedBorderValue result = CollapsedBorderValue(&style()->borderRight(), BCELL); + + // (2) The left border of the cell to the right. + if (!rightmostColumn) { + RenderTableCell* nextCell = rtl ? tableElt->cellBefore(this) : tableElt->cellAfter(this); + if (nextCell && nextCell->style()) { + result = rtl ? compareBorders(CollapsedBorderValue(&nextCell->style()->borderLeft(), BCELL), result) : compareBorders(result, CollapsedBorderValue(&nextCell->style()->borderLeft(), BCELL)); + if (!result.exists()) + return result; + } + } else { + // (3) Our row's right border. + result = compareBorders(result, CollapsedBorderValue(&parent()->style()->borderRight(), BROW)); + if (!result.exists()) + return result; + + // (4) Our row group's right border. + result = compareBorders(result, CollapsedBorderValue(§ion()->style()->borderRight(), BROWGROUP)); + if (!result.exists()) + return result; + } + + // (5) Our column and column group's right borders. + bool startColEdge; + bool endColEdge; + RenderTableCol* colElt = tableElt->colElement(col() + (rtl ? 0 : colSpan() - 1), &startColEdge, &endColEdge); + if (colElt && (!rtl ? endColEdge : startColEdge)) { + result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderRight(), BCOL)); + if (!result.exists()) + return result; + if (colElt->parent()->isTableCol() && (!rtl ? !colElt->nextSibling() : !colElt->previousSibling())) { + result = compareBorders(result, CollapsedBorderValue(&colElt->parent()->style()->borderRight(), BCOLGROUP)); + if (!result.exists()) + return result; + } + } + + // (6) The left border of the column to the right. + if (!rightmostColumn) { + colElt = tableElt->colElement(col() + (rtl ? -1 : colSpan()), &startColEdge, &endColEdge); + if (colElt && (!rtl ? startColEdge : endColEdge)) { + result = rtl ? compareBorders(CollapsedBorderValue(&colElt->style()->borderLeft(), BCOL), result) : compareBorders(result, CollapsedBorderValue(&colElt->style()->borderLeft(), BCOL)); + if (!result.exists()) + return result; + } + } else { + // (7) The table's right border. + result = compareBorders(result, CollapsedBorderValue(&tableElt->style()->borderRight(), BTABLE)); + if (!result.exists()) + return result; + } + + return result; +} + +CollapsedBorderValue RenderTableCell::collapsedTopBorder() const +{ + // For border top, we need to check, in order of precedence: + // (1) Our top border. + CollapsedBorderValue result = CollapsedBorderValue(&style()->borderTop(), BCELL); + + RenderTableCell* prevCell = table()->cellAbove(this); + if (prevCell) { + // (2) A previous cell's bottom border. + result = compareBorders(CollapsedBorderValue(&prevCell->style()->borderBottom(), BCELL), result); + if (!result.exists()) + return result; + } + + // (3) Our row's top border. + result = compareBorders(result, CollapsedBorderValue(&parent()->style()->borderTop(), BROW)); + if (!result.exists()) + return result; + + // (4) The previous row's bottom border. + if (prevCell) { + RenderObject* prevRow = 0; + if (prevCell->section() == section()) + prevRow = parent()->previousSibling(); + else + prevRow = prevCell->section()->lastChild(); + + if (prevRow) { + result = compareBorders(CollapsedBorderValue(&prevRow->style()->borderBottom(), BROW), result); + if (!result.exists()) + return result; + } + } + + // Now check row groups. + RenderTableSection* currSection = section(); + if (!row()) { + // (5) Our row group's top border. + result = compareBorders(result, CollapsedBorderValue(&currSection->style()->borderTop(), BROWGROUP)); + if (!result.exists()) + return result; + + // (6) Previous row group's bottom border. + currSection = table()->sectionAbove(currSection); + if (currSection) { + result = compareBorders(CollapsedBorderValue(&currSection->style()->borderBottom(), BROWGROUP), result); + if (!result.exists()) + return result; + } + } + + if (!currSection) { + // (8) Our column and column group's top borders. + RenderTableCol* colElt = table()->colElement(col()); + if (colElt) { + result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderTop(), BCOL)); + if (!result.exists()) + return result; + if (colElt->parent()->isTableCol()) { + result = compareBorders(result, CollapsedBorderValue(&colElt->parent()->style()->borderTop(), BCOLGROUP)); + if (!result.exists()) + return result; + } + } + + // (9) The table's top border. + result = compareBorders(result, CollapsedBorderValue(&table()->style()->borderTop(), BTABLE)); + if (!result.exists()) + return result; + } + + return result; +} + +CollapsedBorderValue RenderTableCell::collapsedBottomBorder() const +{ + // For border top, we need to check, in order of precedence: + // (1) Our bottom border. + CollapsedBorderValue result = CollapsedBorderValue(&style()->borderBottom(), BCELL); + + RenderTableCell* nextCell = table()->cellBelow(this); + if (nextCell) { + // (2) A following cell's top border. + result = compareBorders(result, CollapsedBorderValue(&nextCell->style()->borderTop(), BCELL)); + if (!result.exists()) + return result; + } + + // (3) Our row's bottom border. (FIXME: Deal with rowspan!) + result = compareBorders(result, CollapsedBorderValue(&parent()->style()->borderBottom(), BROW)); + if (!result.exists()) + return result; + + // (4) The next row's top border. + if (nextCell) { + result = compareBorders(result, CollapsedBorderValue(&nextCell->parent()->style()->borderTop(), BROW)); + if (!result.exists()) + return result; + } + + // Now check row groups. + RenderTableSection* currSection = section(); + if (row() + rowSpan() >= static_cast<RenderTableSection*>(currSection)->numRows()) { + // (5) Our row group's bottom border. + result = compareBorders(result, CollapsedBorderValue(&currSection->style()->borderBottom(), BROWGROUP)); + if (!result.exists()) + return result; + + // (6) Following row group's top border. + currSection = table()->sectionBelow(currSection); + if (currSection) { + result = compareBorders(result, CollapsedBorderValue(&currSection->style()->borderTop(), BROWGROUP)); + if (!result.exists()) + return result; + } + } + + if (!currSection) { + // (8) Our column and column group's bottom borders. + RenderTableCol* colElt = table()->colElement(col()); + if (colElt) { + result = compareBorders(result, CollapsedBorderValue(&colElt->style()->borderBottom(), BCOL)); + if (!result.exists()) return result; + if (colElt->parent()->isTableCol()) { + result = compareBorders(result, CollapsedBorderValue(&colElt->parent()->style()->borderBottom(), BCOLGROUP)); + if (!result.exists()) + return result; + } + } + + // (9) The table's bottom border. + result = compareBorders(result, CollapsedBorderValue(&table()->style()->borderBottom(), BTABLE)); + if (!result.exists()) + return result; + } + + return result; +} + +int RenderTableCell::borderLeft() const +{ + return table()->collapseBorders() ? borderHalfLeft(false) : RenderBlock::borderLeft(); +} + +int RenderTableCell::borderRight() const +{ + return table()->collapseBorders() ? borderHalfRight(false) : RenderBlock::borderRight(); +} + +int RenderTableCell::borderTop() const +{ + return table()->collapseBorders() ? borderHalfTop(false) : RenderBlock::borderTop(); +} + +int RenderTableCell::borderBottom() const +{ + return table()->collapseBorders() ? borderHalfBottom(false) : RenderBlock::borderBottom(); +} + +int RenderTableCell::borderHalfLeft(bool outer) const +{ + CollapsedBorderValue border = collapsedLeftBorder(table()->style()->direction() == RTL); + if (border.exists()) + return (border.width() + (outer ? 0 : 1)) / 2; // Give the extra pixel to top and left. + return 0; +} + +int RenderTableCell::borderHalfRight(bool outer) const +{ + CollapsedBorderValue border = collapsedRightBorder(table()->style()->direction() == RTL); + if (border.exists()) + return (border.width() + (outer ? 1 : 0)) / 2; + return 0; +} + +int RenderTableCell::borderHalfTop(bool outer) const +{ + CollapsedBorderValue border = collapsedTopBorder(); + if (border.exists()) + return (border.width() + (outer ? 0 : 1)) / 2; // Give the extra pixel to top and left. + return 0; +} + +int RenderTableCell::borderHalfBottom(bool outer) const +{ + CollapsedBorderValue border = collapsedBottomBorder(); + if (border.exists()) + return (border.width() + (outer ? 1 : 0)) / 2; + return 0; +} + +void RenderTableCell::paint(PaintInfo& paintInfo, int tx, int ty) +{ + tx += m_x; + ty += m_y; + + // check if we need to do anything at all... + int os = 2 * maximalOutlineSize(paintInfo.phase); + + if (paintInfo.phase == PaintPhaseCollapsedTableBorders && style()->visibility() == VISIBLE) { + if (ty - table()->outerBorderTop() >= paintInfo.rect.bottom() + os || + ty + m_topExtra + m_height + m_bottomExtra + table()->outerBorderBottom() <= paintInfo.rect.y() - os) + return; + int w = width(); + int h = height() + borderTopExtra() + borderBottomExtra(); + paintCollapsedBorder(paintInfo.context, tx, ty, w, h); + } else { + if (ty + overflowTop(false) >= paintInfo.rect.bottom() + os || ty + m_topExtra + overflowHeight(false) + m_bottomExtra <= paintInfo.rect.y() - os) + return; + RenderBlock::paintObject(paintInfo, tx, ty + m_topExtra); + } +} + +static EBorderStyle collapsedBorderStyle(EBorderStyle style) +{ + if (style == OUTSET) + return GROOVE; + if (style == INSET) + return RIDGE; + return style; +} + +struct CollapsedBorder { + CollapsedBorderValue borderValue; + RenderObject::BorderSide side; + bool shouldPaint; + int x1; + int y1; + int x2; + int y2; + EBorderStyle style; +}; + +class CollapsedBorders { +public: + CollapsedBorders() + : m_count(0) + { + } + + void addBorder(const CollapsedBorderValue& borderValue, RenderObject::BorderSide borderSide, bool shouldPaint, + int x1, int y1, int x2, int y2, EBorderStyle borderStyle) + { + if (borderValue.exists() && shouldPaint) { + m_borders[m_count].borderValue = borderValue; + m_borders[m_count].side = borderSide; + m_borders[m_count].shouldPaint = shouldPaint; + m_borders[m_count].x1 = x1; + m_borders[m_count].x2 = x2; + m_borders[m_count].y1 = y1; + m_borders[m_count].y2 = y2; + m_borders[m_count].style = borderStyle; + m_count++; + } + } + + CollapsedBorder* nextBorder() + { + for (int i = 0; i < m_count; i++) { + if (m_borders[i].borderValue.exists() && m_borders[i].shouldPaint) { + m_borders[i].shouldPaint = false; + return &m_borders[i]; + } + } + + return 0; + } + + CollapsedBorder m_borders[4]; + int m_count; +}; + +static void addBorderStyle(RenderTableCell::CollapsedBorderStyles& borderStyles, CollapsedBorderValue borderValue) +{ + if (!borderValue.exists()) + return; + size_t count = borderStyles.size(); + for (size_t i = 0; i < count; ++i) + if (borderStyles[i] == borderValue) + return; + borderStyles.append(borderValue); +} + +void RenderTableCell::collectBorderStyles(CollapsedBorderStyles& borderStyles) const +{ + bool rtl = table()->style()->direction() == RTL; + addBorderStyle(borderStyles, collapsedLeftBorder(rtl)); + addBorderStyle(borderStyles, collapsedRightBorder(rtl)); + addBorderStyle(borderStyles, collapsedTopBorder()); + addBorderStyle(borderStyles, collapsedBottomBorder()); +} + +static int compareBorderStylesForQSort(const void* pa, const void* pb) +{ + const CollapsedBorderValue* a = static_cast<const CollapsedBorderValue*>(pa); + const CollapsedBorderValue* b = static_cast<const CollapsedBorderValue*>(pb); + if (*a == *b) + return 0; + CollapsedBorderValue borderWithHigherPrecedence = compareBorders(*a, *b); + if (*a == borderWithHigherPrecedence) + return 1; + return -1; +} + +void RenderTableCell::sortBorderStyles(CollapsedBorderStyles& borderStyles) +{ + qsort(borderStyles.data(), borderStyles.size(), sizeof(CollapsedBorderValue), + compareBorderStylesForQSort); +} + +void RenderTableCell::paintCollapsedBorder(GraphicsContext* graphicsContext, int tx, int ty, int w, int h) +{ + if (!table()->currentBorderStyle()) + return; + + bool rtl = table()->style()->direction() == RTL; + CollapsedBorderValue leftVal = collapsedLeftBorder(rtl); + CollapsedBorderValue rightVal = collapsedRightBorder(rtl); + CollapsedBorderValue topVal = collapsedTopBorder(); + CollapsedBorderValue bottomVal = collapsedBottomBorder(); + + // Adjust our x/y/width/height so that we paint the collapsed borders at the correct location. + int topWidth = topVal.width(); + int bottomWidth = bottomVal.width(); + int leftWidth = leftVal.width(); + int rightWidth = rightVal.width(); + + tx -= leftWidth / 2; + ty -= topWidth / 2; + w += leftWidth / 2 + (rightWidth + 1) / 2; + h += topWidth / 2 + (bottomWidth + 1) / 2; + + EBorderStyle topStyle = collapsedBorderStyle(topVal.style()); + EBorderStyle bottomStyle = collapsedBorderStyle(bottomVal.style()); + EBorderStyle leftStyle = collapsedBorderStyle(leftVal.style()); + EBorderStyle rightStyle = collapsedBorderStyle(rightVal.style()); + + bool renderTop = topStyle > BHIDDEN && !topVal.isTransparent(); + bool renderBottom = bottomStyle > BHIDDEN && !bottomVal.isTransparent(); + bool renderLeft = leftStyle > BHIDDEN && !leftVal.isTransparent(); + bool renderRight = rightStyle > BHIDDEN && !rightVal.isTransparent(); + + // We never paint diagonals at the joins. We simply let the border with the highest + // precedence paint on top of borders with lower precedence. + CollapsedBorders borders; + borders.addBorder(topVal, BSTop, renderTop, tx, ty, tx + w, ty + topWidth, topStyle); + borders.addBorder(bottomVal, BSBottom, renderBottom, tx, ty + h - bottomWidth, tx + w, ty + h, bottomStyle); + borders.addBorder(leftVal, BSLeft, renderLeft, tx, ty, tx + leftWidth, ty + h, leftStyle); + borders.addBorder(rightVal, BSRight, renderRight, tx + w - rightWidth, ty, tx + w, ty + h, rightStyle); + + for (CollapsedBorder* border = borders.nextBorder(); border; border = borders.nextBorder()) { + if (border->borderValue == *table()->currentBorderStyle()) + drawBorder(graphicsContext, border->x1, border->y1, border->x2, border->y2, border->side, + border->borderValue.color(), style()->color(), border->style, 0, 0); + } +} + +void RenderTableCell::paintBackgroundsBehindCell(PaintInfo& paintInfo, int tx, int ty, RenderObject* backgroundObject) +{ + if (!backgroundObject) + return; + + if (style()->visibility() != VISIBLE) + return; + + RenderTable* tableElt = table(); + if (!tableElt->collapseBorders() && style()->emptyCells() == HIDE && !firstChild()) + return; + + if (backgroundObject != this) { + tx += m_x; + ty += m_y + m_topExtra; + } + + int w = width(); + int h = height() + borderTopExtra() + borderBottomExtra(); + ty -= borderTopExtra(); + + int my = max(ty, paintInfo.rect.y()); + int end = min(paintInfo.rect.bottom(), ty + h); + int mh = end - my; + + Color c = backgroundObject->style()->backgroundColor(); + const FillLayer* bgLayer = backgroundObject->style()->backgroundLayers(); + + if (bgLayer->hasImage() || c.isValid()) { + // We have to clip here because the background would paint + // on top of the borders otherwise. This only matters for cells and rows. + bool shouldClip = backgroundObject->hasLayer() && (backgroundObject == this || backgroundObject == parent()) && tableElt->collapseBorders(); + if (shouldClip) { + IntRect clipRect(tx + borderLeft(), ty + borderTop(), + w - borderLeft() - borderRight(), h - borderTop() - borderBottom()); + paintInfo.context->save(); + paintInfo.context->clip(clipRect); + } + paintFillLayers(paintInfo, c, bgLayer, my, mh, tx, ty, w, h); + if (shouldClip) + paintInfo.context->restore(); + } +} + +void RenderTableCell::paintBoxDecorations(PaintInfo& paintInfo, int tx, int ty) +{ + RenderTable* tableElt = table(); + if (!tableElt->collapseBorders() && style()->emptyCells() == HIDE && !firstChild()) + return; + + int w = width(); + int h = height() + borderTopExtra() + borderBottomExtra(); + + if (style()->boxShadow()) + paintBoxShadow(paintInfo.context, tx, ty - borderTopExtra(), w, h, style()); + + // Paint our cell background. + paintBackgroundsBehindCell(paintInfo, tx, ty, this); + + if (!style()->hasBorder() || tableElt->collapseBorders()) + return; + + ty -= borderTopExtra(); + paintBorder(paintInfo.context, tx, ty, w, h, style()); +} + +void RenderTableCell::paintMask(PaintInfo& paintInfo, int tx, int ty) +{ + if (style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) + return; + + RenderTable* tableElt = table(); + if (!tableElt->collapseBorders() && style()->emptyCells() == HIDE && !firstChild()) + return; + + int w = width(); + int h = height() + borderTopExtra() + borderBottomExtra(); + + int my = max(ty, paintInfo.rect.y()); + int end = min(paintInfo.rect.bottom(), ty + h); + int mh = end - my; + + paintMaskImages(paintInfo, my, mh, tx, ty, w, h); +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderTableCell.h b/src/3rdparty/webkit/WebCore/rendering/RenderTableCell.h new file mode 100644 index 0000000..20b7da7 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderTableCell.h @@ -0,0 +1,133 @@ +/* + * Copyright (C) 1997 Martin Jones (mjones@kde.org) + * (C) 1997 Torben Weis (weis@kde.org) + * (C) 1998 Waldo Bastian (bastian@kde.org) + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * 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. + */ + +#ifndef RenderTableCell_h +#define RenderTableCell_h + +#include "RenderTableSection.h" + +namespace WebCore { + +class RenderTableCell : public RenderBlock { +public: + RenderTableCell(Node*); + + virtual const char* renderName() const { return isAnonymous() ? "RenderTableCell (anonymous)" : "RenderTableCell"; } + + virtual bool isTableCell() const { return true; } + + virtual void destroy(); + + // FIXME: need to implement cellIndex + int cellIndex() const { return 0; } + void setCellIndex(int) { } + + int colSpan() const { return m_columnSpan; } + void setColSpan(int c) { m_columnSpan = c; } + + int rowSpan() const { return m_rowSpan; } + void setRowSpan(int r) { m_rowSpan = r; } + + int col() const { return m_column; } + void setCol(int col) { m_column = col; } + int row() const { return m_row; } + void setRow(int row) { m_row = row; } + + RenderTableSection* section() const { return static_cast<RenderTableSection*>(parent()->parent()); } + RenderTable* table() const { return static_cast<RenderTable*>(parent()->parent()->parent()); } + + Length styleOrColWidth() const; + + virtual bool requiresLayer(); + + virtual void calcPrefWidths(); + virtual void calcWidth(); + virtual void setWidth(int); + + virtual bool expandsToEncloseOverhangingFloats() const { return true; } + + int borderLeft() const; + int borderRight() const; + int borderTop() const; + int borderBottom() const; + + int borderHalfLeft(bool outer) const; + int borderHalfRight(bool outer) const; + int borderHalfTop(bool outer) const; + int borderHalfBottom(bool outer) const; + + CollapsedBorderValue collapsedLeftBorder(bool rtl) const; + CollapsedBorderValue collapsedRightBorder(bool rtl) const; + CollapsedBorderValue collapsedTopBorder() const; + CollapsedBorderValue collapsedBottomBorder() const; + + typedef Vector<CollapsedBorderValue, 100> CollapsedBorderStyles; + void collectBorderStyles(CollapsedBorderStyles&) const; + static void sortBorderStyles(CollapsedBorderStyles&); + + virtual void updateFromElement(); + + virtual void layout(); + + virtual void paint(PaintInfo&, int tx, int ty); + virtual void paintBoxDecorations(PaintInfo&, int tx, int ty); + virtual void paintMask(PaintInfo& paintInfo, int tx, int ty); + void paintCollapsedBorder(GraphicsContext*, int x, int y, int w, int h); + void paintBackgroundsBehindCell(PaintInfo&, int tx, int ty, RenderObject* backgroundObject); + + // Lie about position to outside observers. + virtual int yPos() const { return m_y + m_topExtra; } + + virtual IntRect absoluteClippedOverflowRect(); + virtual void computeAbsoluteRepaintRect(IntRect&, bool fixed = false); + virtual FloatPoint localToAbsolute(FloatPoint localPoint = FloatPoint(), bool fixed = false, bool useTransforms = false) const; + virtual FloatPoint absoluteToLocal(FloatPoint containerPoint, bool fixed = false, bool useTransforms = false) const; + virtual FloatQuad localToAbsoluteQuad(const FloatQuad&, bool fixed = false) const; + + virtual int baselinePosition(bool firstLine = false, bool isRootLineBox = false) const; + + void setCellTopExtra(int p) { m_topExtra = p; } + void setCellBottomExtra(int p) { m_bottomExtra = p; } + + virtual int borderTopExtra() const { return m_topExtra; } + virtual int borderBottomExtra() const { return m_bottomExtra; } + +protected: + virtual void styleWillChange(RenderStyle::Diff, const RenderStyle* newStyle); + virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + +private: + int m_row; + int m_column; + int m_rowSpan; + int m_columnSpan; + int m_topExtra : 31; + int m_bottomExtra : 31; + bool m_widthChanged : 1; + int m_percentageHeight; +}; + +} // namespace WebCore + +#endif // RenderTableCell_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderTableCol.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderTableCol.cpp new file mode 100644 index 0000000..5c2a049 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderTableCol.cpp @@ -0,0 +1,92 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1997 Martin Jones (mjones@kde.org) + * (C) 1997 Torben Weis (weis@kde.org) + * (C) 1998 Waldo Bastian (bastian@kde.org) + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * + * 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" +#include "RenderTableCol.h" + +#include "CachedImage.h" +#include "HTMLNames.h" +#include "HTMLTableColElement.h" + +namespace WebCore { + +using namespace HTMLNames; + +RenderTableCol::RenderTableCol(Node* node) + : RenderContainer(node), m_span(1) +{ + // init RenderObject attributes + setInline(true); // our object is not Inline + updateFromElement(); +} + +void RenderTableCol::updateFromElement() +{ + int oldSpan = m_span; + Node* node = element(); + if (node && (node->hasTagName(colTag) || node->hasTagName(colgroupTag))) { + HTMLTableColElement* tc = static_cast<HTMLTableColElement*>(node); + m_span = tc->span(); + } else + m_span = !(style() && style()->display() == TABLE_COLUMN_GROUP); + if (m_span != oldSpan && style() && parent()) + setNeedsLayoutAndPrefWidthsRecalc(); +} + +bool RenderTableCol::isChildAllowed(RenderObject* child, RenderStyle* style) const +{ + return !child->isText() && style && (style->display() == TABLE_COLUMN); +} + +bool RenderTableCol::canHaveChildren() const +{ + // Cols cannot have children. This is actually necessary to fix a bug + // with libraries.uc.edu, which makes a <p> be a table-column. + return style()->display() == TABLE_COLUMN_GROUP; +} + +IntRect RenderTableCol::absoluteClippedOverflowRect() +{ + // For now, just repaint the whole table. + // FIXME: Find a better way to do this, e.g., need to repaint all the cells that we + // might have propagated a background color or borders into. + RenderObject* table = parent(); + if (table && !table->isTable()) + table = table->parent(); + if (table && table->isTable()) + return table->absoluteClippedOverflowRect(); + + return IntRect(); +} + +void RenderTableCol::imageChanged(WrappedImagePtr, const IntRect*) +{ + // FIXME: Repaint only the rect the image paints in. + repaint(); +} + +} diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderTableCol.h b/src/3rdparty/webkit/WebCore/rendering/RenderTableCol.h new file mode 100644 index 0000000..6752bd8 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderTableCol.h @@ -0,0 +1,61 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1997 Martin Jones (mjones@kde.org) + * (C) 1997 Torben Weis (weis@kde.org) + * (C) 1998 Waldo Bastian (bastian@kde.org) + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. + * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies) + * + * 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. + */ + +#ifndef RenderTableCol_h +#define RenderTableCol_h + +#include "RenderContainer.h" + +namespace WebCore { + +class RenderTableCol : public RenderContainer +{ +public: + RenderTableCol(Node*); + + virtual const char* renderName() const { return "RenderTableCol"; } + virtual bool isTableCol() const { return true; } + virtual int lineHeight(bool) const { return 0; } + virtual void updateFromElement(); + + virtual bool isChildAllowed(RenderObject*, RenderStyle*) const; + virtual bool canHaveChildren() const; + virtual bool requiresLayer() { return false; } + + virtual IntRect absoluteClippedOverflowRect(); + virtual void imageChanged(WrappedImagePtr, const IntRect* = 0); + + int span() const { return m_span; } + void setSpan(int s) { m_span = s; } + +private: + int m_span; +}; + +} + +#endif diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderTableRow.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderTableRow.cpp new file mode 100644 index 0000000..21ba91a --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderTableRow.cpp @@ -0,0 +1,219 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1997 Martin Jones (mjones@kde.org) + * (C) 1997 Torben Weis (weis@kde.org) + * (C) 1998 Waldo Bastian (bastian@kde.org) + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. + * + * 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" +#include "RenderTableRow.h" + +#include "CachedImage.h" +#include "Document.h" +#include "HTMLNames.h" +#include "RenderTableCell.h" +#include "RenderView.h" + +#if ENABLE(WML) +#include "WMLNames.h" +#endif + +namespace WebCore { + +using namespace HTMLNames; + +RenderTableRow::RenderTableRow(Node* node) + : RenderContainer(node) +{ + // init RenderObject attributes + setInline(false); // our object is not Inline +} + +void RenderTableRow::destroy() +{ + RenderTableSection* recalcSection = section(); + + RenderContainer::destroy(); + + if (recalcSection) + recalcSection->setNeedsCellRecalc(); +} + +void RenderTableRow::styleWillChange(RenderStyle::Diff diff, const RenderStyle* newStyle) +{ + if (section() && style() && style()->height() != newStyle->height()) + section()->setNeedsCellRecalc(); + + ASSERT(newStyle->display() == TABLE_ROW); + + RenderContainer::styleWillChange(diff, newStyle); +} + +void RenderTableRow::addChild(RenderObject* child, RenderObject* beforeChild) +{ + // Make sure we don't append things after :after-generated content if we have it. + if (!beforeChild && isAfterContent(lastChild())) + beforeChild = lastChild(); + + bool isTableRow = element() && element()->hasTagName(trTag); + +#if ENABLE(WML) + if (!isTableRow && element() && element()->isWMLElement()) + isTableRow = element()->hasTagName(WMLNames::trTag); +#endif + + if (!child->isTableCell()) { + if (isTableRow && child->element() && child->element()->hasTagName(formTag) && document()->isHTMLDocument()) { + RenderContainer::addChild(child, beforeChild); + return; + } + + RenderObject* last = beforeChild; + if (!last) + last = lastChild(); + if (last && last->isAnonymous() && last->isTableCell()) { + last->addChild(child); + return; + } + + // If beforeChild is inside an anonymous cell, insert into the cell. + if (last && !last->isTableCell() && last->parent() && last->parent()->isAnonymous()) { + last->parent()->addChild(child, beforeChild); + return; + } + + RenderTableCell* cell = new (renderArena()) RenderTableCell(document() /* anonymous object */); + RefPtr<RenderStyle> newStyle = RenderStyle::create(); + newStyle->inheritFrom(style()); + newStyle->setDisplay(TABLE_CELL); + cell->setStyle(newStyle.release()); + addChild(cell, beforeChild); + cell->addChild(child); + return; + } + + // If the next renderer is actually wrapped in an anonymous table cell, we need to go up and find that. + while (beforeChild && beforeChild->parent() != this) + beforeChild = beforeChild->parent(); + + RenderTableCell* cell = static_cast<RenderTableCell*>(child); + + // Generated content can result in us having a null section so make sure to null check our parent. + if (parent()) + section()->addCell(cell, this); + + ASSERT(!beforeChild || beforeChild->isTableCell() || isTableRow && beforeChild->element() && beforeChild->element()->hasTagName(formTag) && document()->isHTMLDocument()); + RenderContainer::addChild(cell, beforeChild); + + if (beforeChild || nextSibling()) + section()->setNeedsCellRecalc(); +} + +void RenderTableRow::layout() +{ + ASSERT(needsLayout()); + + // Table rows do not add translation. + LayoutStateMaintainer statePusher(view(), this, IntSize()); + + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { + if (child->isTableCell()) { + RenderTableCell* cell = static_cast<RenderTableCell*>(child); + if (child->needsLayout()) { + cell->calcVerticalMargins(); + cell->layout(); + } + } + } + + // We only ever need to repaint if our cells didn't, which menas that they didn't need + // layout, so we know that our bounds didn't change. This code is just making up for + // the fact that we did not repaint in setStyle() because we had a layout hint. + // We cannot call repaint() because our absoluteClippedOverflowRect() is taken from the + // parent table, and being mid-layout, that is invalid. Instead, we repaint our cells. + if (selfNeedsLayout() && checkForRepaintDuringLayout()) { + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { + if (child->isTableCell()) + child->repaint(); + } + } + + statePusher.pop(); + setNeedsLayout(false); +} + +IntRect RenderTableRow::absoluteClippedOverflowRect() +{ + // For now, just repaint the whole table. + // FIXME: Find a better way to do this, e.g., need to repaint all the cells that we + // might have propagated a background color into. + if (RenderTable* parentTable = table()) + return parentTable->absoluteClippedOverflowRect(); + + return IntRect(); +} + +// Hit Testing +bool RenderTableRow::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction action) +{ + // Table rows cannot ever be hit tested. Effectively they do not exist. + // Just forward to our children always. + for (RenderObject* child = lastChild(); child; child = child->previousSibling()) { + // FIXME: We have to skip over inline flows, since they can show up inside table rows + // at the moment (a demoted inline <form> for example). If we ever implement a + // table-specific hit-test method (which we should do for performance reasons anyway), + // then we can remove this check. + if (!child->hasLayer() && !child->isInlineFlow() && child->nodeAtPoint(request, result, x, y, tx, ty, action)) { + updateHitTestResult(result, IntPoint(x - tx, y - ty)); + return true; + } + } + + return false; +} + +void RenderTableRow::paint(PaintInfo& paintInfo, int tx, int ty) +{ + ASSERT(m_layer); + if (!m_layer) + return; + + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { + if (child->isTableCell()) { + // Paint the row background behind the cell. + if (paintInfo.phase == PaintPhaseBlockBackground || paintInfo.phase == PaintPhaseChildBlockBackground) { + RenderTableCell* cell = static_cast<RenderTableCell*>(child); + cell->paintBackgroundsBehindCell(paintInfo, tx, ty, this); + } + if (!child->hasLayer()) + child->paint(paintInfo, tx, ty); + } + } +} + +void RenderTableRow::imageChanged(WrappedImagePtr, const IntRect*) +{ + // FIXME: Examine cells and repaint only the rect the image paints in. + repaint(); +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderTableRow.h b/src/3rdparty/webkit/WebCore/rendering/RenderTableRow.h new file mode 100644 index 0000000..a65d0e9 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderTableRow.h @@ -0,0 +1,67 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1997 Martin Jones (mjones@kde.org) + * (C) 1997 Torben Weis (weis@kde.org) + * (C) 1998 Waldo Bastian (bastian@kde.org) + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. + * + * 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. + */ + +#ifndef RenderTableRow_h +#define RenderTableRow_h + +#include "RenderTableSection.h" + +namespace WebCore { + +class RenderTableRow : public RenderContainer { +public: + RenderTableRow(Node*); + + RenderTableSection* section() const { return static_cast<RenderTableSection*>(parent()); } + RenderTable* table() const { return static_cast<RenderTable*>(parent()->parent()); } + +private: + virtual const char* renderName() const { return isAnonymous() ? "RenderTableRow (anonymous)" : "RenderTableRow"; } + + virtual bool isTableRow() const { return true; } + + virtual void destroy(); + + virtual void addChild(RenderObject* child, RenderObject* beforeChild = 0); + virtual int lineHeight(bool, bool) const { return 0; } + virtual void position(InlineBox*) { } + virtual void layout(); + virtual IntRect absoluteClippedOverflowRect(); + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); + + // The only time rows get a layer is when they have transparency. + virtual bool requiresLayer() { return isTransparent() || hasOverflowClip(); } + + virtual void paint(PaintInfo&, int tx, int ty); + virtual void imageChanged(WrappedImagePtr, const IntRect* = 0); + + virtual void styleWillChange(RenderStyle::Diff, const RenderStyle* newStyle); + +}; + +} // namespace WebCore + +#endif // RenderTableRow_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderTableSection.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderTableSection.cpp new file mode 100644 index 0000000..be43ab8 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderTableSection.cpp @@ -0,0 +1,1080 @@ +/* + * Copyright (C) 1997 Martin Jones (mjones@kde.org) + * (C) 1997 Torben Weis (weis@kde.org) + * (C) 1998 Waldo Bastian (bastian@kde.org) + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Alexey Proskuryakov (ap@nypop.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" +#include "RenderTableSection.h" + +#include "CachedImage.h" +#include "Document.h" +#include "HTMLNames.h" +#include "RenderTableCell.h" +#include "RenderTableCol.h" +#include "RenderTableRow.h" +#include "RenderView.h" +#include <limits> +#include <wtf/Vector.h> + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +RenderTableSection::RenderTableSection(Node* node) + : RenderContainer(node) + , m_gridRows(0) + , m_cCol(0) + , m_cRow(-1) + , m_needsCellRecalc(false) + , m_outerBorderLeft(0) + , m_outerBorderRight(0) + , m_outerBorderTop(0) + , m_outerBorderBottom(0) + , m_overflowLeft(0) + , m_overflowWidth(0) + , m_overflowTop(0) + , m_overflowHeight(0) + , m_hasOverflowingCell(false) +{ + // init RenderObject attributes + setInline(false); // our object is not Inline +} + +RenderTableSection::~RenderTableSection() +{ + clearGrid(); +} + +void RenderTableSection::destroy() +{ + RenderTable* recalcTable = table(); + + RenderContainer::destroy(); + + // recalc cell info because RenderTable has unguarded pointers + // stored that point to this RenderTableSection. + if (recalcTable) + recalcTable->setNeedsSectionRecalc(); +} + +void RenderTableSection::addChild(RenderObject* child, RenderObject* beforeChild) +{ + // Make sure we don't append things after :after-generated content if we have it. + if (!beforeChild && isAfterContent(lastChild())) + beforeChild = lastChild(); + + bool isTableSection = element() && (element()->hasTagName(theadTag) || element()->hasTagName(tbodyTag) || element()->hasTagName(tfootTag)); + + if (!child->isTableRow()) { + if (isTableSection && child->element() && child->element()->hasTagName(formTag) && document()->isHTMLDocument()) { + RenderContainer::addChild(child, beforeChild); + return; + } + + RenderObject* last = beforeChild; + if (!last) + last = lastChild(); + if (last && last->isAnonymous()) { + last->addChild(child); + return; + } + + // If beforeChild is inside an anonymous cell/row, insert into the cell or into + // the anonymous row containing it, if there is one. + RenderObject* lastBox = last; + while (lastBox && lastBox->parent()->isAnonymous() && !lastBox->isTableRow()) + lastBox = lastBox->parent(); + if (lastBox && lastBox->isAnonymous()) { + lastBox->addChild(child, beforeChild); + return; + } + + RenderObject* row = new (renderArena()) RenderTableRow(document() /* anonymous table */); + RefPtr<RenderStyle> newStyle = RenderStyle::create(); + newStyle->inheritFrom(style()); + newStyle->setDisplay(TABLE_ROW); + row->setStyle(newStyle.release()); + addChild(row, beforeChild); + row->addChild(child); + return; + } + + if (beforeChild) + setNeedsCellRecalc(); + + ++m_cRow; + m_cCol = 0; + + // make sure we have enough rows + if (!ensureRows(m_cRow + 1)) + return; + + m_grid[m_cRow].rowRenderer = child; + + if (!beforeChild) { + m_grid[m_cRow].height = child->style()->height(); + if (m_grid[m_cRow].height.isRelative()) + m_grid[m_cRow].height = Length(); + } + + // If the next renderer is actually wrapped in an anonymous table row, we need to go up and find that. + while (beforeChild && beforeChild->parent() != this) + beforeChild = beforeChild->parent(); + + ASSERT(!beforeChild || beforeChild->isTableRow() || isTableSection && beforeChild->element() && beforeChild->element()->hasTagName(formTag) && document()->isHTMLDocument()); + RenderContainer::addChild(child, beforeChild); +} + +bool RenderTableSection::ensureRows(int numRows) +{ + int nRows = m_gridRows; + if (numRows > nRows) { + if (numRows > static_cast<int>(m_grid.size())) { + size_t maxSize = numeric_limits<size_t>::max() / sizeof(RowStruct); + if (static_cast<size_t>(numRows) > maxSize) + return false; + m_grid.grow(numRows); + } + m_gridRows = numRows; + int nCols = max(1, table()->numEffCols()); + CellStruct emptyCellStruct; + emptyCellStruct.cell = 0; + emptyCellStruct.inColSpan = false; + for (int r = nRows; r < numRows; r++) { + m_grid[r].row = new Row(nCols); + m_grid[r].row->fill(emptyCellStruct); + m_grid[r].rowRenderer = 0; + m_grid[r].baseline = 0; + m_grid[r].height = Length(); + } + } + + return true; +} + +void RenderTableSection::addCell(RenderTableCell* cell, RenderObject* row) +{ + int rSpan = cell->rowSpan(); + int cSpan = cell->colSpan(); + Vector<RenderTable::ColumnStruct>& columns = table()->columns(); + int nCols = columns.size(); + + // ### mozilla still seems to do the old HTML way, even for strict DTD + // (see the annotation on table cell layouting in the CSS specs and the testcase below: + // <TABLE border> + // <TR><TD>1 <TD rowspan="2">2 <TD>3 <TD>4 + // <TR><TD colspan="2">5 + // </TABLE> + + while (m_cCol < nCols && (cellAt(m_cRow, m_cCol).cell || cellAt(m_cRow, m_cCol).inColSpan)) + m_cCol++; + + if (rSpan == 1) { + // we ignore height settings on rowspan cells + Length height = cell->style()->height(); + if (height.isPositive() || (height.isRelative() && height.value() >= 0)) { + Length cRowHeight = m_grid[m_cRow].height; + switch (height.type()) { + case Percent: + if (!(cRowHeight.isPercent()) || + (cRowHeight.isPercent() && cRowHeight.rawValue() < height.rawValue())) + m_grid[m_cRow].height = height; + break; + case Fixed: + if (cRowHeight.type() < Percent || + (cRowHeight.isFixed() && cRowHeight.value() < height.value())) + m_grid[m_cRow].height = height; + break; + case Relative: + default: + break; + } + } + } + + // make sure we have enough rows + if (!ensureRows(m_cRow + rSpan)) + return; + + m_grid[m_cRow].rowRenderer = row; + + int col = m_cCol; + // tell the cell where it is + CellStruct currentCell; + currentCell.cell = cell; + currentCell.inColSpan = false; + while (cSpan) { + int currentSpan; + if (m_cCol >= nCols) { + table()->appendColumn(cSpan); + currentSpan = cSpan; + } else { + if (cSpan < columns[m_cCol].span) + table()->splitColumn(m_cCol, cSpan); + currentSpan = columns[m_cCol].span; + } + + for (int r = 0; r < rSpan; r++) { + CellStruct& c = cellAt(m_cRow + r, m_cCol); + if (currentCell.cell && !c.cell) + c.cell = currentCell.cell; + if (currentCell.inColSpan) + c.inColSpan = true; + } + m_cCol++; + cSpan -= currentSpan; + currentCell.cell = 0; + currentCell.inColSpan = true; + } + if (cell) { + cell->setRow(m_cRow); + cell->setCol(table()->effColToCol(col)); + } +} + +void RenderTableSection::setCellWidths() +{ + Vector<int>& columnPos = table()->columnPositions(); + + LayoutStateMaintainer statePusher(view()); + + for (int i = 0; i < m_gridRows; i++) { + Row& row = *m_grid[i].row; + int cols = row.size(); + for (int j = 0; j < cols; j++) { + CellStruct current = row[j]; + RenderTableCell* cell = current.cell; + + if (!cell) + continue; + int endCol = j; + int cspan = cell->colSpan(); + while (cspan && endCol < cols) { + cspan -= table()->columns()[endCol].span; + endCol++; + } + int w = columnPos[endCol] - columnPos[j] - table()->hBorderSpacing(); + int oldWidth = cell->width(); + if (w != oldWidth) { + cell->setNeedsLayout(true); + if (!table()->selfNeedsLayout() && cell->checkForRepaintDuringLayout()) { + if (!statePusher.didPush()) { + // Technically, we should also push state for the row, but since + // rows don't push a coordinate transform, that's not necessary. + statePusher.push(this, IntSize(m_x, m_y)); + } + cell->repaint(); + } + cell->setWidth(w); + } + } + } + + statePusher.pop(); // only pops if we pushed +} + +int RenderTableSection::calcRowHeight() +{ + RenderTableCell* cell; + + int spacing = table()->vBorderSpacing(); + + LayoutStateMaintainer statePusher(view()); + + m_rowPos.resize(m_gridRows + 1); + m_rowPos[0] = spacing; + + for (int r = 0; r < m_gridRows; r++) { + m_rowPos[r + 1] = 0; + m_grid[r].baseline = 0; + int baseline = 0; + int bdesc = 0; + int ch = m_grid[r].height.calcMinValue(0); + int pos = m_rowPos[r] + ch + (m_grid[r].rowRenderer ? spacing : 0); + + m_rowPos[r + 1] = max(m_rowPos[r + 1], pos); + + Row* row = m_grid[r].row; + int totalCols = row->size(); + + for (int c = 0; c < totalCols; c++) { + CellStruct current = cellAt(r, c); + cell = current.cell; + if (!cell || current.inColSpan) + continue; + if (r < m_gridRows - 1 && cellAt(r + 1, c).cell == cell) + continue; + + int indx = max(r - cell->rowSpan() + 1, 0); + + if (cell->overrideSize() != -1) { + if (!statePusher.didPush()) { + // Technically, we should also push state for the row, but since + // rows don't push a coordinate transform, that's not necessary. + statePusher.push(this, IntSize(m_x, m_y)); + } + cell->setOverrideSize(-1); + cell->setChildNeedsLayout(true, false); + cell->layoutIfNeeded(); + } + + // Explicit heights use the border box in quirks mode. In strict mode do the right + // thing and actually add in the border and padding. + ch = cell->style()->height().calcValue(0) + + (cell->style()->htmlHacks() ? 0 : (cell->paddingTop() + cell->paddingBottom() + + cell->borderTop() + cell->borderBottom())); + ch = max(ch, cell->height()); + + pos = m_rowPos[indx] + ch + (m_grid[r].rowRenderer ? spacing : 0); + + m_rowPos[r + 1] = max(m_rowPos[r + 1], pos); + + // find out the baseline + EVerticalAlign va = cell->style()->verticalAlign(); + if (va == BASELINE || va == TEXT_BOTTOM || va == TEXT_TOP || va == SUPER || va == SUB) { + int b = cell->baselinePosition(); + if (b > cell->borderTop() + cell->paddingTop()) { + baseline = max(baseline, b); + bdesc = max(bdesc, m_rowPos[indx] + ch - b); + } + } + } + + //do we have baseline aligned elements? + if (baseline) { + // increase rowheight if baseline requires + m_rowPos[r + 1] = max(m_rowPos[r + 1], baseline + bdesc + (m_grid[r].rowRenderer ? spacing : 0)); + m_grid[r].baseline = baseline; + } + + m_rowPos[r + 1] = max(m_rowPos[r + 1], m_rowPos[r]); + } + + statePusher.pop(); + + return m_rowPos[m_gridRows]; +} + +int RenderTableSection::layoutRows(int toAdd) +{ + int rHeight; + int rindx; + int totalRows = m_gridRows; + + // Set the width of our section now. The rows will also be this width. + m_width = table()->contentWidth(); + m_overflowLeft = 0; + m_overflowWidth = m_width; + m_overflowTop = 0; + m_overflowHeight = 0; + m_hasOverflowingCell = false; + + if (toAdd && totalRows && (m_rowPos[totalRows] || !nextSibling())) { + int totalHeight = m_rowPos[totalRows] + toAdd; + + int dh = toAdd; + int totalPercent = 0; + int numAuto = 0; + for (int r = 0; r < totalRows; r++) { + if (m_grid[r].height.isAuto()) + numAuto++; + else if (m_grid[r].height.isPercent()) + totalPercent += m_grid[r].height.rawValue(); + } + if (totalPercent) { + // try to satisfy percent + int add = 0; + totalPercent = min(totalPercent, 100 * percentScaleFactor); + int rh = m_rowPos[1] - m_rowPos[0]; + for (int r = 0; r < totalRows; r++) { + if (totalPercent > 0 && m_grid[r].height.isPercent()) { + int toAdd = min(dh, (totalHeight * m_grid[r].height.rawValue() / (100 * percentScaleFactor)) - rh); + // If toAdd is negative, then we don't want to shrink the row (this bug + // affected Outlook Web Access). + toAdd = max(0, toAdd); + add += toAdd; + dh -= toAdd; + totalPercent -= m_grid[r].height.rawValue(); + } + if (r < totalRows - 1) + rh = m_rowPos[r + 2] - m_rowPos[r + 1]; + m_rowPos[r + 1] += add; + } + } + if (numAuto) { + // distribute over variable cols + int add = 0; + for (int r = 0; r < totalRows; r++) { + if (numAuto > 0 && m_grid[r].height.isAuto()) { + int toAdd = dh / numAuto; + add += toAdd; + dh -= toAdd; + numAuto--; + } + m_rowPos[r + 1] += add; + } + } + if (dh > 0 && m_rowPos[totalRows]) { + // if some left overs, distribute equally. + int tot = m_rowPos[totalRows]; + int add = 0; + int prev = m_rowPos[0]; + for (int r = 0; r < totalRows; r++) { + //weight with the original height + add += dh * (m_rowPos[r + 1] - prev) / tot; + prev = m_rowPos[r + 1]; + m_rowPos[r + 1] += add; + } + } + } + + int hspacing = table()->hBorderSpacing(); + int vspacing = table()->vBorderSpacing(); + int nEffCols = table()->numEffCols(); + + LayoutStateMaintainer statePusher(view(), this, IntSize(m_x, m_y)); + + for (int r = 0; r < totalRows; r++) { + // Set the row's x/y position and width/height. + if (RenderObject* rowRenderer = m_grid[r].rowRenderer) { + rowRenderer->setPos(0, m_rowPos[r]); + rowRenderer->setWidth(m_width); + rowRenderer->setHeight(m_rowPos[r + 1] - m_rowPos[r] - vspacing); + } + + for (int c = 0; c < nEffCols; c++) { + RenderTableCell* cell = cellAt(r, c).cell; + + if (!cell) + continue; + if (r < totalRows - 1 && cell == cellAt(r + 1, c).cell) + continue; + + rindx = max(0, r - cell->rowSpan() + 1); + + rHeight = m_rowPos[r + 1] - m_rowPos[rindx] - vspacing; + + // Force percent height children to lay themselves out again. + // This will cause these children to grow to fill the cell. + // FIXME: There is still more work to do here to fully match WinIE (should + // it become necessary to do so). In quirks mode, WinIE behaves like we + // do, but it will clip the cells that spill out of the table section. In + // strict mode, Mozilla and WinIE both regrow the table to accommodate the + // new height of the cell (thus letting the percentages cause growth one + // time only). We may also not be handling row-spanning cells correctly. + // + // Note also the oddity where replaced elements always flex, and yet blocks/tables do + // not necessarily flex. WinIE is crazy and inconsistent, and we can't hope to + // match the behavior perfectly, but we'll continue to refine it as we discover new + // bugs. :) + bool cellChildrenFlex = false; + bool flexAllChildren = cell->style()->height().isFixed() || + (!table()->style()->height().isAuto() && rHeight != cell->height()); + + for (RenderObject* o = cell->firstChild(); o; o = o->nextSibling()) { + if (!o->isText() && o->style()->height().isPercent() && (o->isReplaced() || o->scrollsOverflow() || flexAllChildren)) { + // Tables with no sections do not flex. + if (!o->isTable() || static_cast<RenderTable*>(o)->hasSections()) { + o->setNeedsLayout(true, false); + cell->setChildNeedsLayout(true, false); + cellChildrenFlex = true; + } + } + } + if (cellChildrenFlex) { + // Alignment within a cell is based off the calculated + // height, which becomes irrelevant once the cell has + // been resized based off its percentage. -dwh + cell->setOverrideSize(max(0, + rHeight - cell->borderTop() - cell->paddingTop() - + cell->borderBottom() - cell->paddingBottom())); + cell->layoutIfNeeded(); + + // If the baseline moved, we may have to update the data for our row. Find out the new baseline. + EVerticalAlign va = cell->style()->verticalAlign(); + if (va == BASELINE || va == TEXT_BOTTOM || va == TEXT_TOP || va == SUPER || va == SUB) { + int b = cell->baselinePosition(); + if (b > cell->borderTop() + cell->paddingTop()) + m_grid[r].baseline = max(m_grid[r].baseline, b); + } + } + + int te = 0; + switch (cell->style()->verticalAlign()) { + case SUB: + case SUPER: + case TEXT_TOP: + case TEXT_BOTTOM: + case BASELINE: + te = getBaseline(r) - cell->baselinePosition(); + break; + case TOP: + te = 0; + break; + case MIDDLE: + te = (rHeight - cell->height()) / 2; + break; + case BOTTOM: + te = rHeight - cell->height(); + break; + default: + break; + } + + int oldTe = cell->borderTopExtra(); + int oldBe = cell->borderBottomExtra(); + + int be = rHeight - cell->height() - te; + cell->setCellTopExtra(te); + cell->setCellBottomExtra(be); + if ((te != oldTe || be > oldBe) && !table()->selfNeedsLayout() && cell->checkForRepaintDuringLayout()) + cell->repaint(); + + IntRect oldCellRect(cell->xPos(), cell->yPos() - cell->borderTopExtra() , cell->width(), cell->height()); + + if (style()->direction() == RTL) { + cell->setPos(table()->columnPositions()[nEffCols] - table()->columnPositions()[table()->colToEffCol(cell->col() + cell->colSpan())] + hspacing, m_rowPos[rindx]); + } else + cell->setPos(table()->columnPositions()[c] + hspacing, m_rowPos[rindx]); + + m_overflowLeft = min(m_overflowLeft, cell->xPos() + cell->overflowLeft(false)); + m_overflowWidth = max(m_overflowWidth, cell->xPos() + cell->overflowWidth(false)); + m_overflowTop = min(m_overflowTop, cell->yPos() + cell->overflowTop(false)); + m_overflowHeight = max(m_overflowHeight, cell->yPos() + cell->overflowHeight(false)); + m_hasOverflowingCell |= cell->overflowLeft(false) || cell->overflowWidth(false) > cell->width() || cell->overflowTop(false) || cell->overflowHeight(false) > cell->height(); + + // If the cell moved, we have to repaint it as well as any floating/positioned + // descendants. An exception is if we need a layout. In this case, we know we're going to + // repaint ourselves (and the cell) anyway. + if (!table()->selfNeedsLayout() && cell->checkForRepaintDuringLayout()) + cell->repaintDuringLayoutIfMoved(oldCellRect); + } + } + + statePusher.pop(); + + m_height = m_rowPos[totalRows]; + m_overflowHeight = max(m_overflowHeight, m_height); + return m_height; +} + +int RenderTableSection::lowestPosition(bool includeOverflowInterior, bool includeSelf) const +{ + int bottom = RenderContainer::lowestPosition(includeOverflowInterior, includeSelf); + if (!includeOverflowInterior && hasOverflowClip()) + return bottom; + + for (RenderObject* row = firstChild(); row; row = row->nextSibling()) { + for (RenderObject* cell = row->firstChild(); cell; cell = cell->nextSibling()) { + if (cell->isTableCell()) + bottom = max(bottom, cell->yPos() + cell->lowestPosition(false)); + } + } + + return bottom; +} + +int RenderTableSection::rightmostPosition(bool includeOverflowInterior, bool includeSelf) const +{ + int right = RenderContainer::rightmostPosition(includeOverflowInterior, includeSelf); + if (!includeOverflowInterior && hasOverflowClip()) + return right; + + for (RenderObject* row = firstChild(); row; row = row->nextSibling()) { + for (RenderObject* cell = row->firstChild(); cell; cell = cell->nextSibling()) { + if (cell->isTableCell()) + right = max(right, cell->xPos() + cell->rightmostPosition(false)); + } + } + + return right; +} + +int RenderTableSection::leftmostPosition(bool includeOverflowInterior, bool includeSelf) const +{ + int left = RenderContainer::leftmostPosition(includeOverflowInterior, includeSelf); + if (!includeOverflowInterior && hasOverflowClip()) + return left; + + for (RenderObject* row = firstChild(); row; row = row->nextSibling()) { + for (RenderObject* cell = row->firstChild(); cell; cell = cell->nextSibling()) { + if (cell->isTableCell()) + left = min(left, cell->xPos() + cell->leftmostPosition(false)); + } + } + + return left; +} + +int RenderTableSection::calcOuterBorderTop() const +{ + int totalCols = table()->numEffCols(); + if (!m_gridRows || !totalCols) + return 0; + + unsigned borderWidth = 0; + + const BorderValue& sb = style()->borderTop(); + if (sb.style() == BHIDDEN) + return -1; + if (sb.style() > BHIDDEN) + borderWidth = sb.width; + + const BorderValue& rb = firstChild()->style()->borderTop(); + if (rb.style() == BHIDDEN) + return -1; + if (rb.style() > BHIDDEN && rb.width > borderWidth) + borderWidth = rb.width; + + bool allHidden = true; + for (int c = 0; c < totalCols; c++) { + const CellStruct& current = cellAt(0, c); + if (current.inColSpan || !current.cell) + continue; + const BorderValue& cb = current.cell->style()->borderTop(); + // FIXME: Don't repeat for the same col group + RenderTableCol* colGroup = table()->colElement(c); + if (colGroup) { + const BorderValue& gb = colGroup->style()->borderTop(); + if (gb.style() == BHIDDEN || cb.style() == BHIDDEN) + continue; + else + allHidden = false; + if (gb.style() > BHIDDEN && gb.width > borderWidth) + borderWidth = gb.width; + if (cb.style() > BHIDDEN && cb.width > borderWidth) + borderWidth = cb.width; + } else { + if (cb.style() == BHIDDEN) + continue; + else + allHidden = false; + if (cb.style() > BHIDDEN && cb.width > borderWidth) + borderWidth = cb.width; + } + } + if (allHidden) + return -1; + + return borderWidth / 2; +} + +int RenderTableSection::calcOuterBorderBottom() const +{ + int totalCols = table()->numEffCols(); + if (!m_gridRows || !totalCols) + return 0; + + unsigned borderWidth = 0; + + const BorderValue& sb = style()->borderBottom(); + if (sb.style() == BHIDDEN) + return -1; + if (sb.style() > BHIDDEN) + borderWidth = sb.width; + + const BorderValue& rb = lastChild()->style()->borderBottom(); + if (rb.style() == BHIDDEN) + return -1; + if (rb.style() > BHIDDEN && rb.width > borderWidth) + borderWidth = rb.width; + + bool allHidden = true; + for (int c = 0; c < totalCols; c++) { + const CellStruct& current = cellAt(m_gridRows - 1, c); + if (current.inColSpan || !current.cell) + continue; + const BorderValue& cb = current.cell->style()->borderBottom(); + // FIXME: Don't repeat for the same col group + RenderTableCol* colGroup = table()->colElement(c); + if (colGroup) { + const BorderValue& gb = colGroup->style()->borderBottom(); + if (gb.style() == BHIDDEN || cb.style() == BHIDDEN) + continue; + else + allHidden = false; + if (gb.style() > BHIDDEN && gb.width > borderWidth) + borderWidth = gb.width; + if (cb.style() > BHIDDEN && cb.width > borderWidth) + borderWidth = cb.width; + } else { + if (cb.style() == BHIDDEN) + continue; + else + allHidden = false; + if (cb.style() > BHIDDEN && cb.width > borderWidth) + borderWidth = cb.width; + } + } + if (allHidden) + return -1; + + return (borderWidth + 1) / 2; +} + +int RenderTableSection::calcOuterBorderLeft(bool rtl) const +{ + int totalCols = table()->numEffCols(); + if (!m_gridRows || !totalCols) + return 0; + + unsigned borderWidth = 0; + + const BorderValue& sb = style()->borderLeft(); + if (sb.style() == BHIDDEN) + return -1; + if (sb.style() > BHIDDEN) + borderWidth = sb.width; + + int leftmostColumn = rtl ? totalCols - 1 : 0; + RenderTableCol* colGroup = table()->colElement(leftmostColumn); + if (colGroup) { + const BorderValue& gb = colGroup->style()->borderLeft(); + if (gb.style() == BHIDDEN) + return -1; + if (gb.style() > BHIDDEN && gb.width > borderWidth) + borderWidth = gb.width; + } + + bool allHidden = true; + for (int r = 0; r < m_gridRows; r++) { + const CellStruct& current = cellAt(r, leftmostColumn); + if (!current.cell) + continue; + // FIXME: Don't repeat for the same cell + const BorderValue& cb = current.cell->style()->borderLeft(); + const BorderValue& rb = current.cell->parent()->style()->borderLeft(); + if (cb.style() == BHIDDEN || rb.style() == BHIDDEN) + continue; + else + allHidden = false; + if (cb.style() > BHIDDEN && cb.width > borderWidth) + borderWidth = cb.width; + if (rb.style() > BHIDDEN && rb.width > borderWidth) + borderWidth = rb.width; + } + if (allHidden) + return -1; + + return borderWidth / 2; +} + +int RenderTableSection::calcOuterBorderRight(bool rtl) const +{ + int totalCols = table()->numEffCols(); + if (!m_gridRows || !totalCols) + return 0; + + unsigned borderWidth = 0; + + const BorderValue& sb = style()->borderRight(); + if (sb.style() == BHIDDEN) + return -1; + if (sb.style() > BHIDDEN) + borderWidth = sb.width; + + int rightmostColumn = rtl ? 0 : totalCols - 1; + RenderTableCol* colGroup = table()->colElement(rightmostColumn); + if (colGroup) { + const BorderValue& gb = colGroup->style()->borderRight(); + if (gb.style() == BHIDDEN) + return -1; + if (gb.style() > BHIDDEN && gb.width > borderWidth) + borderWidth = gb.width; + } + + bool allHidden = true; + for (int r = 0; r < m_gridRows; r++) { + const CellStruct& current = cellAt(r, rightmostColumn); + if (!current.cell) + continue; + // FIXME: Don't repeat for the same cell + const BorderValue& cb = current.cell->style()->borderRight(); + const BorderValue& rb = current.cell->parent()->style()->borderRight(); + if (cb.style() == BHIDDEN || rb.style() == BHIDDEN) + continue; + else + allHidden = false; + if (cb.style() > BHIDDEN && cb.width > borderWidth) + borderWidth = cb.width; + if (rb.style() > BHIDDEN && rb.width > borderWidth) + borderWidth = rb.width; + } + if (allHidden) + return -1; + + return (borderWidth + 1) / 2; +} + +void RenderTableSection::recalcOuterBorder() +{ + bool rtl = table()->style()->direction() == RTL; + m_outerBorderTop = calcOuterBorderTop(); + m_outerBorderBottom = calcOuterBorderBottom(); + m_outerBorderLeft = calcOuterBorderLeft(rtl); + m_outerBorderRight = calcOuterBorderRight(rtl); +} + +int RenderTableSection::getBaselineOfFirstLineBox() const +{ + if (!m_gridRows) + return -1; + + int firstLineBaseline = m_grid[0].baseline; + if (firstLineBaseline) + return firstLineBaseline + m_rowPos[0]; + + firstLineBaseline = -1; + Row* firstRow = m_grid[0].row; + for (size_t i = 0; i < firstRow->size(); ++i) { + RenderTableCell* cell = firstRow->at(i).cell; + if (cell) + firstLineBaseline = max(firstLineBaseline, cell->yPos() + cell->paddingTop() + cell->borderTop() + cell->contentHeight()); + } + + return firstLineBaseline; +} + +void RenderTableSection::paint(PaintInfo& paintInfo, int tx, int ty) +{ + // put this back in when all layout tests can handle it + // ASSERT(!needsLayout()); + // avoid crashing on bugs that cause us to paint with dirty layout + if (needsLayout()) + return; + + unsigned totalRows = m_gridRows; + unsigned totalCols = table()->columns().size(); + + if (!totalRows || !totalCols) + return; + + tx += m_x; + ty += m_y; + + // Check which rows and cols are visible and only paint these. + // FIXME: Could use a binary search here. + PaintPhase paintPhase = paintInfo.phase; + int x = paintInfo.rect.x(); + int y = paintInfo.rect.y(); + int w = paintInfo.rect.width(); + int h = paintInfo.rect.height(); + + int os = 2 * maximalOutlineSize(paintPhase); + unsigned startrow = 0; + unsigned endrow = totalRows; + + // If some cell overflows, just paint all of them. + if (!m_hasOverflowingCell) { + for (; startrow < totalRows; startrow++) { + if (ty + m_rowPos[startrow + 1] >= y - os) + break; + } + if (startrow == totalRows && ty + m_rowPos[totalRows] + table()->outerBorderBottom() >= y - os) + startrow--; + + for (; endrow > 0; endrow--) { + if (ty + m_rowPos[endrow - 1] <= y + h + os) + break; + } + if (!endrow && ty + m_rowPos[0] - table()->outerBorderTop() <= y + h + os) + endrow++; + } + + unsigned startcol = 0; + unsigned endcol = totalCols; + // FIXME: Implement RTL. + if (!m_hasOverflowingCell && style()->direction() == LTR) { + for (; startcol < totalCols; startcol++) { + if (tx + table()->columnPositions()[startcol + 1] >= x - os) + break; + } + if (startcol == totalCols && tx + table()->columnPositions()[totalCols] + table()->outerBorderRight() >= x - os) + startcol--; + + for (; endcol > 0; endcol--) { + if (tx + table()->columnPositions()[endcol - 1] <= x + w + os) + break; + } + if (!endcol && tx + table()->columnPositions()[0] - table()->outerBorderLeft() <= y + w + os) + endcol++; + } + + if (startcol < endcol) { + // draw the cells + for (unsigned r = startrow; r < endrow; r++) { + unsigned c = startcol; + // since a cell can be -1 (indicating a colspan) we might have to search backwards to include it + while (c && cellAt(r, c).inColSpan) + c--; + for (; c < endcol; c++) { + CellStruct current = cellAt(r, c); + RenderTableCell* cell = current.cell; + + // Cells must always paint in the order in which they appear taking into account + // their upper left originating row/column. For cells with rowspans, avoid repainting + // if we've already seen the cell. + if (!cell || (r > startrow && (cellAt(r - 1, c).cell == cell))) + continue; + + RenderTableRow* row = static_cast<RenderTableRow*>(cell->parent()); + + if (paintPhase == PaintPhaseBlockBackground || paintPhase == PaintPhaseChildBlockBackground) { + // We need to handle painting a stack of backgrounds. This stack (from bottom to top) consists of + // the column group, column, row group, row, and then the cell. + RenderObject* col = table()->colElement(c); + RenderObject* colGroup = 0; + if (col && col->parent()->style()->display() == TABLE_COLUMN_GROUP) + colGroup = col->parent(); + + // Column groups and columns first. + // FIXME: Columns and column groups do not currently support opacity, and they are being painted "too late" in + // the stack, since we have already opened a transparency layer (potentially) for the table row group. + // Note that we deliberately ignore whether or not the cell has a layer, since these backgrounds paint "behind" the + // cell. + cell->paintBackgroundsBehindCell(paintInfo, tx, ty, colGroup); + cell->paintBackgroundsBehindCell(paintInfo, tx, ty, col); + + // Paint the row group next. + cell->paintBackgroundsBehindCell(paintInfo, tx, ty, this); + + // Paint the row next, but only if it doesn't have a layer. If a row has a layer, it will be responsible for + // painting the row background for the cell. + if (!row->hasLayer()) + cell->paintBackgroundsBehindCell(paintInfo, tx, ty, row); + } + + if ((!cell->hasLayer() && !row->hasLayer()) || paintInfo.phase == PaintPhaseCollapsedTableBorders) + cell->paint(paintInfo, tx, ty); + } + } + } +} + +void RenderTableSection::imageChanged(WrappedImagePtr, const IntRect*) +{ + // FIXME: Examine cells and repaint only the rect the image paints in. + repaint(); +} + +void RenderTableSection::recalcCells() +{ + m_cCol = 0; + m_cRow = -1; + clearGrid(); + m_gridRows = 0; + + for (RenderObject* row = firstChild(); row; row = row->nextSibling()) { + if (row->isTableRow()) { + m_cRow++; + m_cCol = 0; + if (!ensureRows(m_cRow + 1)) + break; + m_grid[m_cRow].rowRenderer = row; + + for (RenderObject* cell = row->firstChild(); cell; cell = cell->nextSibling()) { + if (cell->isTableCell()) + addCell(static_cast<RenderTableCell*>(cell), row); + } + } + } + m_needsCellRecalc = false; + setNeedsLayout(true); +} + +void RenderTableSection::clearGrid() +{ + int rows = m_gridRows; + while (rows--) + delete m_grid[rows].row; +} + +int RenderTableSection::numColumns() const +{ + int result = 0; + + for (int r = 0; r < m_gridRows; ++r) { + for (int c = result; c < table()->numEffCols(); ++c) { + const CellStruct& cell = cellAt(r, c); + if (cell.cell || cell.inColSpan) + result = c; + } + } + + return result + 1; +} + +void RenderTableSection::appendColumn(int pos) +{ + for (int row = 0; row < m_gridRows; ++row) { + m_grid[row].row->resize(pos + 1); + CellStruct& c = cellAt(row, pos); + c.cell = 0; + c.inColSpan = false; + } +} + +void RenderTableSection::splitColumn(int pos, int newSize) +{ + if (m_cCol > pos) + m_cCol++; + for (int row = 0; row < m_gridRows; ++row) { + m_grid[row].row->resize(newSize); + Row& r = *m_grid[row].row; + memmove(r.data() + pos + 1, r.data() + pos, (newSize - 1 - pos) * sizeof(CellStruct)); + r[pos + 1].cell = 0; + r[pos + 1].inColSpan = r[pos].inColSpan || r[pos].cell; + } +} + +RenderObject* RenderTableSection::removeChildNode(RenderObject* child, bool fullRemove) +{ + setNeedsCellRecalc(); + return RenderContainer::removeChildNode(child, fullRemove); +} + +// Hit Testing +bool RenderTableSection::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction action) +{ + // Table sections cannot ever be hit tested. Effectively they do not exist. + // Just forward to our children always. + tx += m_x; + ty += m_y; + + for (RenderObject* child = lastChild(); child; child = child->previousSibling()) { + // FIXME: We have to skip over inline flows, since they can show up inside table rows + // at the moment (a demoted inline <form> for example). If we ever implement a + // table-specific hit-test method (which we should do for performance reasons anyway), + // then we can remove this check. + if (!child->hasLayer() && !child->isInlineFlow() && child->nodeAtPoint(request, result, x, y, tx, ty, action)) { + updateHitTestResult(result, IntPoint(x - tx, y - ty)); + return true; + } + } + + return false; +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderTableSection.h b/src/3rdparty/webkit/WebCore/rendering/RenderTableSection.h new file mode 100644 index 0000000..8d460cb --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderTableSection.h @@ -0,0 +1,154 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1997 Martin Jones (mjones@kde.org) + * (C) 1997 Torben Weis (weis@kde.org) + * (C) 1998 Waldo Bastian (bastian@kde.org) + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. + * + * 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. + */ + +#ifndef RenderTableSection_h +#define RenderTableSection_h + +#include "RenderTable.h" +#include <wtf/Vector.h> + +namespace WebCore { + +class RenderTableCell; + +class RenderTableSection : public RenderContainer { +public: + RenderTableSection(Node*); + ~RenderTableSection(); + + virtual const char* renderName() const { return isAnonymous() ? "RenderTableSection (anonymous)" : "RenderTableSection"; } + + virtual bool isTableSection() const { return true; } + + virtual void destroy(); + + virtual void addChild(RenderObject* child, RenderObject* beforeChild = 0); + + virtual int getBaselineOfFirstLineBox() const; + + void addCell(RenderTableCell*, RenderObject* row); + + void setCellWidths(); + int calcRowHeight(); + int layoutRows(int height); + + RenderTable* table() const { return static_cast<RenderTable*>(parent()); } + + struct CellStruct { + RenderTableCell* cell; + bool inColSpan; // true for columns after the first in a colspan + }; + + typedef Vector<CellStruct> Row; + + struct RowStruct { + Row* row; + RenderObject* rowRenderer; + int baseline; + Length height; + }; + + CellStruct& cellAt(int row, int col) { return (*m_grid[row].row)[col]; } + const CellStruct& cellAt(int row, int col) const { return (*m_grid[row].row)[col]; } + + void appendColumn(int pos); + void splitColumn(int pos, int newSize); + + virtual int overflowWidth(bool includeInterior = true) const { return (!includeInterior && hasOverflowClip()) ? m_width : m_overflowWidth; } + virtual int overflowLeft(bool includeInterior = true) const { return (!includeInterior && hasOverflowClip()) ? 0 : m_overflowLeft; } + virtual int overflowHeight(bool includeInterior = true) const { return (!includeInterior && hasOverflowClip()) ? m_height : m_overflowHeight; } + virtual int overflowTop(bool includeInterior = true) const { return (!includeInterior && hasOverflowClip()) ? 0 : m_overflowTop; } + + virtual int lowestPosition(bool includeOverflowInterior, bool includeSelf) const; + virtual int rightmostPosition(bool includeOverflowInterior, bool includeSelf) const; + virtual int leftmostPosition(bool includeOverflowInterior, bool includeSelf) const; + + int calcOuterBorderTop() const; + int calcOuterBorderBottom() const; + int calcOuterBorderLeft(bool rtl) const; + int calcOuterBorderRight(bool rtl) const; + void recalcOuterBorder(); + + int outerBorderTop() const { return m_outerBorderTop; } + int outerBorderBottom() const { return m_outerBorderBottom; } + int outerBorderLeft() const { return m_outerBorderLeft; } + int outerBorderRight() const { return m_outerBorderRight; } + + virtual void paint(PaintInfo&, int tx, int ty); + virtual void imageChanged(WrappedImagePtr, const IntRect* = 0); + + int numRows() const { return m_gridRows; } + int numColumns() const; + void recalcCells(); + void recalcCellsIfNeeded() + { + if (m_needsCellRecalc) + recalcCells(); + } + + bool needsCellRecalc() const { return m_needsCellRecalc; } + void setNeedsCellRecalc() + { + m_needsCellRecalc = true; + table()->setNeedsSectionRecalc(); + } + + int getBaseline(int row) { return m_grid[row].baseline; } + + virtual RenderObject* removeChildNode(RenderObject*, bool fullRemove = true); + + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); + +private: + virtual int lineHeight(bool, bool) const { return 0; } + virtual void position(InlineBox*) { } + + bool ensureRows(int); + void clearGrid(); + + Vector<RowStruct> m_grid; + int m_gridRows; + Vector<int> m_rowPos; + + // the current insertion position + int m_cCol; + int m_cRow; + bool m_needsCellRecalc; + + int m_outerBorderLeft; + int m_outerBorderRight; + int m_outerBorderTop; + int m_outerBorderBottom; + int m_overflowLeft; + int m_overflowWidth; + int m_overflowTop; + int m_overflowHeight; + bool m_hasOverflowingCell; +}; + +} // namespace WebCore + +#endif // RenderTableSection_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderText.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderText.cpp new file mode 100644 index 0000000..d7fdbb9 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderText.cpp @@ -0,0 +1,1216 @@ +/** + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net) + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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" +#include "RenderText.h" + +#include "CharacterNames.h" +#include "FrameView.h" +#include "InlineTextBox.h" +#include "Range.h" +#include "RenderArena.h" +#include "RenderBlock.h" +#include "RenderLayer.h" +#include "RenderView.h" +#include "Text.h" +#include "TextBreakIterator.h" +#include "break_lines.h" +#include <wtf/AlwaysInline.h> + +using namespace std; +using namespace WTF; +using namespace Unicode; + +namespace WebCore { + +// FIXME: Move to StringImpl.h eventually. +static inline bool charactersAreAllASCII(StringImpl* text) +{ + return charactersAreAllASCII(text->characters(), text->length()); +} + +RenderText::RenderText(Node* node, PassRefPtr<StringImpl> str) + : RenderObject(node) + , m_text(str) + , m_firstTextBox(0) + , m_lastTextBox(0) + , m_minWidth(-1) + , m_maxWidth(-1) + , m_beginMinWidth(0) + , m_endMinWidth(0) + , m_selectionState(SelectionNone) + , m_hasTab(false) + , m_linesDirty(false) + , m_containsReversedText(false) + , m_isAllASCII(charactersAreAllASCII(m_text.get())) +{ + ASSERT(m_text); + setRenderText(); + m_text = m_text->replace('\\', backslashAsCurrencySymbol()); + + view()->frameView()->setIsVisuallyNonEmpty(); +} + +#ifndef NDEBUG + +RenderText::~RenderText() +{ + ASSERT(!m_firstTextBox); + ASSERT(!m_lastTextBox); +} + +#endif + +const char* RenderText::renderName() const +{ + return "RenderText"; +} + +bool RenderText::isTextFragment() const +{ + return false; +} + +bool RenderText::isWordBreak() const +{ + return false; +} + +void RenderText::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +{ + RenderObject::styleDidChange(diff, oldStyle); + + ETextTransform oldTransform = oldStyle ? oldStyle->textTransform() : TTNONE; + ETextSecurity oldSecurity = oldStyle ? oldStyle->textSecurity() : TSNONE; + + if (oldTransform != style()->textTransform() || oldSecurity != style()->textSecurity() +#if ENABLE(SVG) + || isSVGText() /* All SVG text has to be transformed */ +#endif + ) { + if (RefPtr<StringImpl> textToTransform = originalText()) + setText(textToTransform.release(), true); + } +} + +void RenderText::destroy() +{ + if (!documentBeingDestroyed()) { + if (firstTextBox()) { + if (isBR()) { + RootInlineBox* next = firstTextBox()->root()->nextRootBox(); + if (next) + next->markDirty(); + } + for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) + box->remove(); + } else if (parent()) + parent()->dirtyLinesFromChangedChild(this); + } + deleteTextBoxes(); + RenderObject::destroy(); +} + +void RenderText::extractTextBox(InlineTextBox* box) +{ + checkConsistency(); + + m_lastTextBox = box->prevTextBox(); + if (box == m_firstTextBox) + m_firstTextBox = 0; + if (box->prevTextBox()) + box->prevTextBox()->setNextLineBox(0); + box->setPreviousLineBox(0); + for (InlineRunBox* curr = box; curr; curr = curr->nextLineBox()) + curr->setExtracted(); + + checkConsistency(); +} + +void RenderText::attachTextBox(InlineTextBox* box) +{ + checkConsistency(); + + if (m_lastTextBox) { + m_lastTextBox->setNextLineBox(box); + box->setPreviousLineBox(m_lastTextBox); + } else + m_firstTextBox = box; + InlineTextBox* last = box; + for (InlineTextBox* curr = box; curr; curr = curr->nextTextBox()) { + curr->setExtracted(false); + last = curr; + } + m_lastTextBox = last; + + checkConsistency(); +} + +void RenderText::removeTextBox(InlineTextBox* box) +{ + checkConsistency(); + + if (box == m_firstTextBox) + m_firstTextBox = box->nextTextBox(); + if (box == m_lastTextBox) + m_lastTextBox = box->prevTextBox(); + if (box->nextTextBox()) + box->nextTextBox()->setPreviousLineBox(box->prevTextBox()); + if (box->prevTextBox()) + box->prevTextBox()->setNextLineBox(box->nextTextBox()); + + checkConsistency(); +} + +void RenderText::deleteTextBoxes() +{ + if (firstTextBox()) { + RenderArena* arena = renderArena(); + InlineTextBox* next; + for (InlineTextBox* curr = firstTextBox(); curr; curr = next) { + next = curr->nextTextBox(); + curr->destroy(arena); + } + m_firstTextBox = m_lastTextBox = 0; + } +} + +PassRefPtr<StringImpl> RenderText::originalText() const +{ + Node* e = element(); + return e ? static_cast<Text*>(e)->string() : 0; +} + +void RenderText::absoluteRects(Vector<IntRect>& rects, int tx, int ty, bool) +{ + for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) + rects.append(IntRect(tx + box->xPos(), ty + box->yPos(), box->width(), box->height())); +} + +void RenderText::addLineBoxRects(Vector<IntRect>& rects, unsigned start, unsigned end, bool useSelectionHeight) +{ + // Work around signed/unsigned issues. This function takes unsigneds, and is often passed UINT_MAX + // to mean "all the way to the end". InlineTextBox coordinates are unsigneds, so changing this + // function to take ints causes various internal mismatches. But selectionRect takes ints, and + // passing UINT_MAX to it causes trouble. Ideally we'd change selectionRect to take unsigneds, but + // that would cause many ripple effects, so for now we'll just clamp our unsigned parameters to INT_MAX. + ASSERT(end == UINT_MAX || end <= INT_MAX); + ASSERT(start <= INT_MAX); + start = min(start, static_cast<unsigned>(INT_MAX)); + end = min(end, static_cast<unsigned>(INT_MAX)); + + FloatPoint absPos = localToAbsoluteForContent(FloatPoint()); + + for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) { + // Note: box->end() returns the index of the last character, not the index past it + if (start <= box->start() && box->end() < end) { + IntRect r = IntRect(absPos.x() + box->xPos(), absPos.y() + box->yPos(), box->width(), box->height()); + if (useSelectionHeight) { + IntRect selectionRect = box->selectionRect(absPos.x(), absPos.y(), start, end); + r.setHeight(selectionRect.height()); + r.setY(selectionRect.y()); + } + rects.append(r); + } else { + unsigned realEnd = min(box->end() + 1, end); + IntRect r = box->selectionRect(absPos.x(), absPos.y(), start, realEnd); + if (!r.isEmpty()) { + if (!useSelectionHeight) { + // change the height and y position because selectionRect uses selection-specific values + r.setHeight(box->height()); + r.setY(absPos.y() + box->yPos()); + } + rects.append(r); + } + } + } +} + +void RenderText::absoluteQuads(Vector<FloatQuad>& quads, bool) +{ + for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) + quads.append(localToAbsoluteQuad(FloatRect(box->xPos(), box->yPos(), box->width(), box->height()))); +} + +void RenderText::collectAbsoluteLineBoxQuads(Vector<FloatQuad>& quads, unsigned start, unsigned end, bool useSelectionHeight) +{ + // Work around signed/unsigned issues. This function takes unsigneds, and is often passed UINT_MAX + // to mean "all the way to the end". InlineTextBox coordinates are unsigneds, so changing this + // function to take ints causes various internal mismatches. But selectionRect takes ints, and + // passing UINT_MAX to it causes trouble. Ideally we'd change selectionRect to take unsigneds, but + // that would cause many ripple effects, so for now we'll just clamp our unsigned parameters to INT_MAX. + ASSERT(end == UINT_MAX || end <= INT_MAX); + ASSERT(start <= INT_MAX); + start = min(start, static_cast<unsigned>(INT_MAX)); + end = min(end, static_cast<unsigned>(INT_MAX)); + + for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) { + // Note: box->end() returns the index of the last character, not the index past it + if (start <= box->start() && box->end() < end) { + IntRect r = IntRect(box->xPos(), box->yPos(), box->width(), box->height()); + if (useSelectionHeight) { + IntRect selectionRect = box->selectionRect(0, 0, start, end); + r.setHeight(selectionRect.height()); + r.setY(selectionRect.y()); + } + quads.append(localToAbsoluteQuad(FloatRect(r))); + } else { + unsigned realEnd = min(box->end() + 1, end); + IntRect r = box->selectionRect(0, 0, start, realEnd); + if (!r.isEmpty()) { + if (!useSelectionHeight) { + // change the height and y position because selectionRect uses selection-specific values + r.setHeight(box->height()); + r.setY(box->yPos()); + } + quads.append(localToAbsoluteQuad(FloatRect(r))); + } + } + } +} + +InlineTextBox* RenderText::findNextInlineTextBox(int offset, int& pos) const +{ + // The text runs point to parts of the RenderText's m_text + // (they don't include '\n') + // Find the text run that includes the character at offset + // and return pos, which is the position of the char in the run. + + if (!m_firstTextBox) + return 0; + + InlineTextBox* s = m_firstTextBox; + int off = s->m_len; + while (offset > off && s->nextTextBox()) { + s = s->nextTextBox(); + off = s->m_start + s->m_len; + } + // we are now in the correct text run + pos = (offset > off ? s->m_len : s->m_len - (off - offset) ); + return s; +} + +VisiblePosition RenderText::positionForCoordinates(int x, int y) +{ + if (!firstTextBox() || textLength() == 0) + return VisiblePosition(element(), 0, DOWNSTREAM); + + // Get the offset for the position, since this will take rtl text into account. + int offset; + + // FIXME: We should be able to roll these special cases into the general cases in the loop below. + if (firstTextBox() && y < firstTextBox()->root()->bottomOverflow() && x < firstTextBox()->m_x) { + // at the y coordinate of the first line or above + // and the x coordinate is to the left of the first text box left edge + offset = firstTextBox()->offsetForPosition(x); + return VisiblePosition(element(), offset + firstTextBox()->m_start, DOWNSTREAM); + } + if (lastTextBox() && y >= lastTextBox()->root()->topOverflow() && x >= lastTextBox()->m_x + lastTextBox()->m_width) { + // at the y coordinate of the last line or below + // and the x coordinate is to the right of the last text box right edge + offset = lastTextBox()->offsetForPosition(x); + return VisiblePosition(element(), offset + lastTextBox()->m_start, DOWNSTREAM); + } + + InlineTextBox* lastBoxAbove = 0; + for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) { + if (y >= box->root()->topOverflow()) { + int bottom = box->root()->nextRootBox() ? box->root()->nextRootBox()->topOverflow() : box->root()->bottomOverflow(); + if (y < bottom) { + offset = box->offsetForPosition(x); + + if (x == box->m_x) + // the x coordinate is equal to the left edge of this box + // the affinity must be downstream so the position doesn't jump back to the previous line + return VisiblePosition(element(), offset + box->m_start, DOWNSTREAM); + + if (x < box->m_x + box->m_width) + // and the x coordinate is to the left of the right edge of this box + // check to see if position goes in this box + return VisiblePosition(element(), offset + box->m_start, offset > 0 ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM); + + if (!box->prevOnLine() && x < box->m_x) + // box is first on line + // and the x coordinate is to the left of the first text box left edge + return VisiblePosition(element(), offset + box->m_start, DOWNSTREAM); + + if (!box->nextOnLine()) + // box is last on line + // and the x coordinate is to the right of the last text box right edge + // generate VisiblePosition, use UPSTREAM affinity if possible + return VisiblePosition(element(), offset + box->m_start, offset > 0 ? VP_UPSTREAM_IF_POSSIBLE : DOWNSTREAM); + } + lastBoxAbove = box; + } + } + + return VisiblePosition(element(), lastBoxAbove ? lastBoxAbove->m_start + lastBoxAbove->m_len : 0, DOWNSTREAM); +} + +IntRect RenderText::localCaretRect(InlineBox* inlineBox, int caretOffset, int* extraWidthToEndOfLine) +{ + if (!inlineBox) + return IntRect(); + + ASSERT(inlineBox->isInlineTextBox()); + if (!inlineBox->isInlineTextBox()) + return IntRect(); + + InlineTextBox* box = static_cast<InlineTextBox*>(inlineBox); + + int height = box->root()->bottomOverflow() - box->root()->topOverflow(); + int top = box->root()->topOverflow(); + + int left = box->positionForOffset(caretOffset); + + int rootLeft = box->root()->xPos(); + // FIXME: should we use the width of the root inline box or the + // width of the containing block for this? + if (extraWidthToEndOfLine) + *extraWidthToEndOfLine = (box->root()->width() + rootLeft) - (left + 1); + + RenderBlock* cb = containingBlock(); + if (style()->autoWrap()) { + int availableWidth = cb->lineWidth(top); + if (box->direction() == LTR) + left = min(left, rootLeft + availableWidth - 1); + else + left = max(left, rootLeft); + } + + const int caretWidth = 1; + return IntRect(left, top, caretWidth, height); +} + +ALWAYS_INLINE int RenderText::widthFromCache(const Font& f, int start, int len, int xPos) const +{ + if (f.isFixedPitch() && !f.isSmallCaps() && m_isAllASCII) { + int monospaceCharacterWidth = f.spaceWidth(); + int tabWidth = allowTabs() ? monospaceCharacterWidth * 8 : 0; + int w = 0; + bool isSpace; + bool previousCharWasSpace = true; // FIXME: Preserves historical behavior, but seems wrong for start > 0. + for (int i = start; i < start + len; i++) { + char c = (*m_text)[i]; + if (c <= ' ') { + if (c == ' ' || c == '\n') { + w += monospaceCharacterWidth; + isSpace = true; + } else if (c == '\t') { + w += tabWidth ? tabWidth - ((xPos + w) % tabWidth) : monospaceCharacterWidth; + isSpace = true; + } else + isSpace = false; + } else { + w += monospaceCharacterWidth; + isSpace = false; + } + if (isSpace && !previousCharWasSpace) + w += f.wordSpacing(); + previousCharWasSpace = isSpace; + } + return w; + } + + return f.width(TextRun(text()->characters() + start, len, allowTabs(), xPos)); +} + +void RenderText::trimmedPrefWidths(int leadWidth, + int& beginMinW, bool& beginWS, + int& endMinW, bool& endWS, + bool& hasBreakableChar, bool& hasBreak, + int& beginMaxW, int& endMaxW, + int& minW, int& maxW, bool& stripFrontSpaces) +{ + bool collapseWhiteSpace = style()->collapseWhiteSpace(); + if (!collapseWhiteSpace) + stripFrontSpaces = false; + + if (m_hasTab || prefWidthsDirty()) + calcPrefWidths(leadWidth); + + beginWS = !stripFrontSpaces && m_hasBeginWS; + endWS = m_hasEndWS; + + int len = textLength(); + + if (!len || (stripFrontSpaces && m_text->containsOnlyWhitespace())) { + beginMinW = 0; + endMinW = 0; + beginMaxW = 0; + endMaxW = 0; + minW = 0; + maxW = 0; + hasBreak = false; + return; + } + + minW = m_minWidth; + maxW = m_maxWidth; + + beginMinW = m_beginMinWidth; + endMinW = m_endMinWidth; + + hasBreakableChar = m_hasBreakableChar; + hasBreak = m_hasBreak; + + if ((*m_text)[0] == ' ' || ((*m_text)[0] == '\n' && !style()->preserveNewline()) || (*m_text)[0] == '\t') { + const Font& f = style()->font(); // FIXME: This ignores first-line. + if (stripFrontSpaces) { + const UChar space = ' '; + int spaceWidth = f.width(TextRun(&space, 1)); + maxW -= spaceWidth; + } else + maxW += f.wordSpacing(); + } + + stripFrontSpaces = collapseWhiteSpace && m_hasEndWS; + + if (!style()->autoWrap() || minW > maxW) + minW = maxW; + + // Compute our max widths by scanning the string for newlines. + if (hasBreak) { + const Font& f = style()->font(); // FIXME: This ignores first-line. + bool firstLine = true; + beginMaxW = maxW; + endMaxW = maxW; + for (int i = 0; i < len; i++) { + int linelen = 0; + while (i + linelen < len && (*m_text)[i + linelen] != '\n') + linelen++; + + if (linelen) { + endMaxW = widthFromCache(f, i, linelen, leadWidth + endMaxW); + if (firstLine) { + firstLine = false; + leadWidth = 0; + beginMaxW = endMaxW; + } + i += linelen; + } else if (firstLine) { + beginMaxW = 0; + firstLine = false; + leadWidth = 0; + } + + if (i == len - 1) + // A <pre> run that ends with a newline, as in, e.g., + // <pre>Some text\n\n<span>More text</pre> + endMaxW = 0; + } + } +} + +static inline bool isSpaceAccordingToStyle(UChar c, RenderStyle* style) +{ + return c == ' ' || (c == noBreakSpace && style->nbspMode() == SPACE); +} + +int RenderText::minPrefWidth() const +{ + if (prefWidthsDirty()) + const_cast<RenderText*>(this)->calcPrefWidths(0); + + return m_minWidth; +} + +int RenderText::maxPrefWidth() const +{ + if (prefWidthsDirty()) + const_cast<RenderText*>(this)->calcPrefWidths(0); + + return m_maxWidth; +} + +void RenderText::calcPrefWidths(int leadWidth) +{ + ASSERT(m_hasTab || prefWidthsDirty()); + + m_minWidth = 0; + m_beginMinWidth = 0; + m_endMinWidth = 0; + m_maxWidth = 0; + + if (isBR()) + return; + + int currMinWidth = 0; + int currMaxWidth = 0; + m_hasBreakableChar = false; + m_hasBreak = false; + m_hasTab = false; + m_hasBeginWS = false; + m_hasEndWS = false; + + const Font& f = style()->font(); // FIXME: This ignores first-line. + int wordSpacing = style()->wordSpacing(); + int len = textLength(); + const UChar* txt = characters(); + bool needsWordSpacing = false; + bool ignoringSpaces = false; + bool isSpace = false; + bool firstWord = true; + bool firstLine = true; + int nextBreakable = -1; + int lastWordBoundary = 0; + + bool breakNBSP = style()->autoWrap() && style()->nbspMode() == SPACE; + bool breakAll = (style()->wordBreak() == BreakAllWordBreak || style()->wordBreak() == BreakWordBreak) && style()->autoWrap(); + + for (int i = 0; i < len; i++) { + UChar c = txt[i]; + + bool previousCharacterIsSpace = isSpace; + + bool isNewline = false; + if (c == '\n') { + if (style()->preserveNewline()) { + m_hasBreak = true; + isNewline = true; + isSpace = false; + } else + isSpace = true; + } else if (c == '\t') { + if (!style()->collapseWhiteSpace()) { + m_hasTab = true; + isSpace = false; + } else + isSpace = true; + } else + isSpace = c == ' '; + + if ((isSpace || isNewline) && !i) + m_hasBeginWS = true; + if ((isSpace || isNewline) && i == len - 1) + m_hasEndWS = true; + + if (!ignoringSpaces && style()->collapseWhiteSpace() && previousCharacterIsSpace && isSpace) + ignoringSpaces = true; + + if (ignoringSpaces && !isSpace) + ignoringSpaces = false; + + // Ignore spaces and soft hyphens + if (ignoringSpaces) { + ASSERT(lastWordBoundary == i); + lastWordBoundary++; + continue; + } else if (c == softHyphen) { + currMaxWidth += widthFromCache(f, lastWordBoundary, i - lastWordBoundary, leadWidth + currMaxWidth); + lastWordBoundary = i + 1; + continue; + } + + bool hasBreak = breakAll || isBreakable(txt, i, len, nextBreakable, breakNBSP); + bool betweenWords = true; + int j = i; + while (c != '\n' && !isSpaceAccordingToStyle(c, style()) && c != '\t' && c != softHyphen) { + j++; + if (j == len) + break; + c = txt[j]; + if (isBreakable(txt, j, len, nextBreakable, breakNBSP)) + break; + if (breakAll) { + betweenWords = false; + break; + } + } + + int wordLen = j - i; + if (wordLen) { + int w = widthFromCache(f, i, wordLen, leadWidth + currMaxWidth); + currMinWidth += w; + if (betweenWords) { + if (lastWordBoundary == i) + currMaxWidth += w; + else + currMaxWidth += widthFromCache(f, lastWordBoundary, j - lastWordBoundary, leadWidth + currMaxWidth); + lastWordBoundary = j; + } + + bool isSpace = (j < len) && isSpaceAccordingToStyle(c, style()); + bool isCollapsibleWhiteSpace = (j < len) && style()->isCollapsibleWhiteSpace(c); + if (j < len && style()->autoWrap()) + m_hasBreakableChar = true; + + // Add in wordSpacing to our currMaxWidth, but not if this is the last word on a line or the + // last word in the run. + if (wordSpacing && (isSpace || isCollapsibleWhiteSpace) && !containsOnlyWhitespace(j, len-j)) + currMaxWidth += wordSpacing; + + if (firstWord) { + firstWord = false; + // If the first character in the run is breakable, then we consider ourselves to have a beginning + // minimum width of 0, since a break could occur right before our run starts, preventing us from ever + // being appended to a previous text run when considering the total minimum width of the containing block. + if (hasBreak) + m_hasBreakableChar = true; + m_beginMinWidth = hasBreak ? 0 : w; + } + m_endMinWidth = w; + + if (currMinWidth > m_minWidth) + m_minWidth = currMinWidth; + currMinWidth = 0; + + i += wordLen - 1; + } else { + // Nowrap can never be broken, so don't bother setting the + // breakable character boolean. Pre can only be broken if we encounter a newline. + if (style()->autoWrap() || isNewline) + m_hasBreakableChar = true; + + if (currMinWidth > m_minWidth) + m_minWidth = currMinWidth; + currMinWidth = 0; + + if (isNewline) { // Only set if preserveNewline was true and we saw a newline. + if (firstLine) { + firstLine = false; + leadWidth = 0; + if (!style()->autoWrap()) + m_beginMinWidth = currMaxWidth; + } + + if (currMaxWidth > m_maxWidth) + m_maxWidth = currMaxWidth; + currMaxWidth = 0; + } else { + currMaxWidth += f.width(TextRun(txt + i, 1, allowTabs(), leadWidth + currMaxWidth)); + needsWordSpacing = isSpace && !previousCharacterIsSpace && i == len - 1; + } + ASSERT(lastWordBoundary == i); + lastWordBoundary++; + } + } + + if (needsWordSpacing && len > 1 || ignoringSpaces && !firstWord) + currMaxWidth += wordSpacing; + + m_minWidth = max(currMinWidth, m_minWidth); + m_maxWidth = max(currMaxWidth, m_maxWidth); + + if (!style()->autoWrap()) + m_minWidth = m_maxWidth; + + if (style()->whiteSpace() == PRE) { + if (firstLine) + m_beginMinWidth = m_maxWidth; + m_endMinWidth = currMaxWidth; + } + + setPrefWidthsDirty(false); +} + +bool RenderText::containsOnlyWhitespace(unsigned from, unsigned len) const +{ + unsigned currPos; + for (currPos = from; + currPos < from + len && ((*m_text)[currPos] == '\n' || (*m_text)[currPos] == ' ' || (*m_text)[currPos] == '\t'); + currPos++) { } + return currPos >= (from + len); +} + +int RenderText::minXPos() const +{ + if (!m_firstTextBox) + return 0; + + // FIXME: we should not use an arbitrary value like this. Perhaps we should use INT_MAX. + int minXPos = 6666666; + for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) + minXPos = min(minXPos, static_cast<int>(box->m_x)); + return minXPos; +} + +int RenderText::xPos() const +{ + return m_firstTextBox ? m_firstTextBox->m_x : 0; +} + +int RenderText::yPos() const +{ + return m_firstTextBox ? m_firstTextBox->m_y : 0; +} + +void RenderText::setSelectionState(SelectionState state) +{ + InlineTextBox* box; + + m_selectionState = state; + if (state == SelectionStart || state == SelectionEnd || state == SelectionBoth) { + int startPos, endPos; + selectionStartEnd(startPos, endPos); + if (selectionState() == SelectionStart) { + endPos = textLength(); + + // to handle selection from end of text to end of line + if (startPos != 0 && startPos == endPos) + startPos = endPos - 1; + } else if (selectionState() == SelectionEnd) + startPos = 0; + + for (box = firstTextBox(); box; box = box->nextTextBox()) { + if (box->isSelected(startPos, endPos)) { + RootInlineBox* line = box->root(); + if (line) + line->setHasSelectedChildren(true); + } + } + } else { + for (box = firstTextBox(); box; box = box->nextTextBox()) { + RootInlineBox* line = box->root(); + if (line) + line->setHasSelectedChildren(state == SelectionInside); + } + } + + containingBlock()->setSelectionState(state); +} + +void RenderText::setTextWithOffset(PassRefPtr<StringImpl> text, unsigned offset, unsigned len, bool force) +{ + unsigned oldLen = textLength(); + unsigned newLen = text->length(); + int delta = newLen - oldLen; + unsigned end = len ? offset + len - 1 : offset; + + RootInlineBox* firstRootBox = 0; + RootInlineBox* lastRootBox = 0; + + bool dirtiedLines = false; + + // Dirty all text boxes that include characters in between offset and offset+len. + for (InlineTextBox* curr = firstTextBox(); curr; curr = curr->nextTextBox()) { + // Text run is entirely before the affected range. + if (curr->end() < offset) + continue; + + // Text run is entirely after the affected range. + if (curr->start() > end) { + curr->offsetRun(delta); + RootInlineBox* root = curr->root(); + if (!firstRootBox) { + firstRootBox = root; + if (!dirtiedLines) { + // The affected area was in between two runs. Go ahead and mark the root box of + // the run after the affected area as dirty. + firstRootBox->markDirty(); + dirtiedLines = true; + } + } + lastRootBox = root; + } else if (curr->end() >= offset && curr->end() <= end) { + // Text run overlaps with the left end of the affected range. + curr->dirtyLineBoxes(); + dirtiedLines = true; + } else if (curr->start() <= offset && curr->end() >= end) { + // Text run subsumes the affected range. + curr->dirtyLineBoxes(); + dirtiedLines = true; + } else if (curr->start() <= end && curr->end() >= end) { + // Text run overlaps with right end of the affected range. + curr->dirtyLineBoxes(); + dirtiedLines = true; + } + } + + // Now we have to walk all of the clean lines and adjust their cached line break information + // to reflect our updated offsets. + if (lastRootBox) + lastRootBox = lastRootBox->nextRootBox(); + if (firstRootBox) { + RootInlineBox* prev = firstRootBox->prevRootBox(); + if (prev) + firstRootBox = prev; + } + for (RootInlineBox* curr = firstRootBox; curr && curr != lastRootBox; curr = curr->nextRootBox()) { + if (curr->lineBreakObj() == this && curr->lineBreakPos() > end) + curr->setLineBreakPos(curr->lineBreakPos() + delta); + } + + // If the text node is empty, dirty the line where new text will be inserted. + if (!firstTextBox() && parent()) { + parent()->dirtyLinesFromChangedChild(this); + dirtiedLines = true; + } + + m_linesDirty = dirtiedLines; + setText(text, force); +} + +static inline bool isInlineFlowOrEmptyText(RenderObject* o) +{ + if (o->isInlineFlow()) + return true; + if (!o->isText()) + return false; + StringImpl* text = static_cast<RenderText*>(o)->text(); + if (!text) + return true; + return !text->length(); +} + +UChar RenderText::previousCharacter() +{ + // find previous text renderer if one exists + RenderObject* previousText = this; + while ((previousText = previousText->previousInPreOrder())) + if (!isInlineFlowOrEmptyText(previousText)) + break; + UChar prev = ' '; + if (previousText && previousText->isText()) + if (StringImpl* previousString = static_cast<RenderText*>(previousText)->text()) + prev = (*previousString)[previousString->length() - 1]; + return prev; +} + +void RenderText::setTextInternal(PassRefPtr<StringImpl> text) +{ + m_text = text; + ASSERT(m_text); + + m_text = m_text->replace('\\', backslashAsCurrencySymbol()); + +#if ENABLE(SVG) + if (isSVGText()) { + if (style() && style()->whiteSpace() == PRE) { + // Spec: When xml:space="preserve", the SVG user agent will do the following using a + // copy of the original character data content. It will convert all newline and tab + // characters into space characters. Then, it will draw all space characters, including + // leading, trailing and multiple contiguous space characters. + + m_text = m_text->replace('\n', ' '); + + // If xml:space="preserve" is set, white-space is set to "pre", which + // preserves leading, trailing & contiguous space character for us. + } else { + // Spec: When xml:space="default", the SVG user agent will do the following using a + // copy of the original character data content. First, it will remove all newline + // characters. Then it will convert all tab characters into space characters. + // Then, it will strip off all leading and trailing space characters. + // Then, all contiguous space characters will be consolidated. + + m_text = m_text->replace('\n', StringImpl::empty()); + + // If xml:space="default" is set, white-space is set to "nowrap", which handles + // leading, trailing & contiguous space character removal for us. + } + + m_text = m_text->replace('\t', ' '); + } +#endif + + if (style()) { + switch (style()->textTransform()) { + case TTNONE: + break; + case CAPITALIZE: { + m_text = m_text->capitalize(previousCharacter()); + break; + } + case UPPERCASE: + m_text = m_text->upper(); + break; + case LOWERCASE: + m_text = m_text->lower(); + break; + } + + // We use the same characters here as for list markers. + // See the listMarkerText function in RenderListMarker.cpp. + switch (style()->textSecurity()) { + case TSNONE: + break; + case TSCIRCLE: + m_text = m_text->secure(whiteBullet); + break; + case TSDISC: + m_text = m_text->secure(bullet); + break; + case TSSQUARE: + m_text = m_text->secure(blackSquare); + } + } + + ASSERT(m_text); + ASSERT(!isBR() || (textLength() == 1 && (*m_text)[0] == '\n')); + + m_isAllASCII = charactersAreAllASCII(m_text.get()); +} + +void RenderText::setText(PassRefPtr<StringImpl> text, bool force) +{ + ASSERT(text); + + if (!force && equal(m_text.get(), text.get())) + return; + + setTextInternal(text); + setNeedsLayoutAndPrefWidthsRecalc(); +} + +int RenderText::height() const +{ + int retval = 0; + if (firstTextBox()) + retval = lastTextBox()->m_y + lastTextBox()->height() - firstTextBox()->m_y; + return retval; +} + +int RenderText::lineHeight(bool firstLine, bool) const +{ + // Always use the interior line height of the parent (e.g., if our parent is an inline block). + return parent()->lineHeight(firstLine, true); +} + +void RenderText::dirtyLineBoxes(bool fullLayout, bool) +{ + if (fullLayout) + deleteTextBoxes(); + else if (!m_linesDirty) { + for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) + box->dirtyLineBoxes(); + } + m_linesDirty = false; +} + +InlineTextBox* RenderText::createInlineTextBox() +{ + return new (renderArena()) InlineTextBox(this); +} + +InlineBox* RenderText::createInlineBox(bool, bool isRootLineBox, bool) +{ + ASSERT(!isRootLineBox); + InlineTextBox* textBox = createInlineTextBox(); + if (!m_firstTextBox) + m_firstTextBox = m_lastTextBox = textBox; + else { + m_lastTextBox->setNextLineBox(textBox); + textBox->setPreviousLineBox(m_lastTextBox); + m_lastTextBox = textBox; + } + return textBox; +} + +void RenderText::position(InlineBox* box) +{ + InlineTextBox* s = static_cast<InlineTextBox*>(box); + + // FIXME: should not be needed!!! + if (!s->m_len) { + // We want the box to be destroyed. + s->remove(); + s->destroy(renderArena()); + m_firstTextBox = m_lastTextBox = 0; + return; + } + + m_containsReversedText |= s->direction() == RTL; +} + +unsigned int RenderText::width(unsigned int from, unsigned int len, int xPos, bool firstLine) const +{ + if (from >= textLength()) + return 0; + + if (from + len > textLength()) + len = textLength() - from; + + return width(from, len, style(firstLine)->font(), xPos); +} + +unsigned int RenderText::width(unsigned int from, unsigned int len, const Font& f, int xPos) const +{ + if (!characters() || from > textLength()) + return 0; + + if (from + len > textLength()) + len = textLength() - from; + + int w; + if (&f == &style()->font()) { + if (!style()->preserveNewline() && !from && len == textLength()) + w = maxPrefWidth(); + else + w = widthFromCache(f, from, len, xPos); + } else + w = f.width(TextRun(text()->characters() + from, len, allowTabs(), xPos)); + + return w; +} + +int RenderText::width() const +{ + // FIXME: we should not use an arbitrary value like this. Perhaps we should use INT_MAX. + int minx = 100000000; + int maxx = 0; + // slooow + for (InlineTextBox* s = firstTextBox(); s; s = s->nextTextBox()) { + if (s->m_x < minx) + minx = s->m_x; + if (s->m_x + s->m_width > maxx) + maxx = s->m_x + s->m_width; + } + + return max(0, maxx - minx); +} + +IntRect RenderText::absoluteClippedOverflowRect() +{ + RenderObject* cb = containingBlock(); + return cb->absoluteClippedOverflowRect(); +} + +IntRect RenderText::selectionRect(bool clipToVisibleContent) +{ + ASSERT(!needsLayout()); + + IntRect rect; + if (selectionState() == SelectionNone) + return rect; + RenderBlock* cb = containingBlock(); + if (!cb) + return rect; + + // Now calculate startPos and endPos for painting selection. + // We include a selection while endPos > 0 + int startPos, endPos; + if (selectionState() == SelectionInside) { + // We are fully selected. + startPos = 0; + endPos = textLength(); + } else { + selectionStartEnd(startPos, endPos); + if (selectionState() == SelectionStart) + endPos = textLength(); + else if (selectionState() == SelectionEnd) + startPos = 0; + } + + if (startPos == endPos) + return rect; + + for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) + rect.unite(box->selectionRect(0, 0, startPos, endPos)); + + if (clipToVisibleContent) + computeAbsoluteRepaintRect(rect); + else { + if (cb->hasColumns()) + cb->adjustRectForColumns(rect); + // FIXME: This doesn't work correctly with transforms. + FloatPoint absPos = localToAbsolute(); + rect.move(absPos.x(), absPos.y()); + } + + return rect; +} + +int RenderText::verticalPositionHint(bool firstLine) const +{ + if (parent()->isReplaced()) + return 0; // Treat inline blocks just like blocks. There can't be any vertical position hint. + return parent()->verticalPositionHint(firstLine); +} + +int RenderText::caretMinOffset() const +{ + InlineTextBox* box = firstTextBox(); + if (!box) + return 0; + int minOffset = box->m_start; + for (box = box->nextTextBox(); box; box = box->nextTextBox()) + minOffset = min(minOffset, box->m_start); + return minOffset; +} + +int RenderText::caretMaxOffset() const +{ + InlineTextBox* box = lastTextBox(); + if (!box) + return textLength(); + int maxOffset = box->m_start + box->m_len; + for (box = box->prevTextBox(); box; box = box->prevTextBox()) + maxOffset = max(maxOffset, box->m_start + box->m_len); + return maxOffset; +} + +unsigned RenderText::caretMaxRenderedOffset() const +{ + int l = 0; + for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) + l += box->m_len; + return l; +} + +int RenderText::previousOffset(int current) const +{ + StringImpl* si = m_text.get(); + TextBreakIterator* iterator = characterBreakIterator(si->characters(), si->length()); + if (!iterator) + return current - 1; + + long result = textBreakPreceding(iterator, current); + if (result == TextBreakDone) + result = current - 1; + + return result; +} + +int RenderText::nextOffset(int current) const +{ + StringImpl* si = m_text.get(); + TextBreakIterator* iterator = characterBreakIterator(si->characters(), si->length()); + if (!iterator) + return current + 1; + + long result = textBreakFollowing(iterator, current); + if (result == TextBreakDone) + result = current + 1; + + return result; +} + +#ifndef NDEBUG + +void RenderText::checkConsistency() const +{ +#ifdef CHECK_CONSISTENCY + const InlineTextBox* prev = 0; + for (const InlineTextBox* child = m_firstTextBox; child != 0; child = child->nextTextBox()) { + ASSERT(child->object() == this); + ASSERT(child->prevTextBox() == prev); + prev = child; + } + ASSERT(prev == m_lastTextBox); +#endif +} + +#endif + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderText.h b/src/3rdparty/webkit/WebCore/rendering/RenderText.h new file mode 100644 index 0000000..4c62638 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderText.h @@ -0,0 +1,183 @@ +/* + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef RenderText_h +#define RenderText_h + +#include "RenderObject.h" + +namespace WebCore { + +class InlineTextBox; +class StringImpl; + +class RenderText : public RenderObject { +public: + RenderText(Node*, PassRefPtr<StringImpl>); +#ifndef NDEBUG + virtual ~RenderText(); +#endif + + virtual const char* renderName() const; + + virtual bool isTextFragment() const; + virtual bool isWordBreak() const; + + virtual PassRefPtr<StringImpl> originalText() const; + + void extractTextBox(InlineTextBox*); + void attachTextBox(InlineTextBox*); + void removeTextBox(InlineTextBox*); + + virtual void destroy(); + + StringImpl* text() const { return m_text.get(); } + + virtual InlineBox* createInlineBox(bool makePlaceHolderBox, bool isRootLineBox, bool isOnlyRun = false); + virtual InlineTextBox* createInlineTextBox(); + virtual void dirtyLineBoxes(bool fullLayout, bool isRootInlineBox = false); + + virtual void absoluteRects(Vector<IntRect>&, int tx, int ty, bool topLevel = true); + virtual void addLineBoxRects(Vector<IntRect>&, unsigned startOffset = 0, unsigned endOffset = UINT_MAX, bool useSelectionHeight = false); + + virtual void absoluteQuads(Vector<FloatQuad>&, bool topLevel = true); + virtual void collectAbsoluteLineBoxQuads(Vector<FloatQuad>&, unsigned startOffset = 0, unsigned endOffset = UINT_MAX, bool useSelectionHeight = false); + + virtual VisiblePosition positionForCoordinates(int x, int y); + + const UChar* characters() const { return m_text->characters(); } + unsigned textLength() const { return m_text->length(); } // non virtual implementation of length() + virtual void position(InlineBox*); + + virtual unsigned width(unsigned from, unsigned len, const Font&, int xPos) const; + virtual unsigned width(unsigned from, unsigned len, int xPos, bool firstLine = false) const; + virtual int width() const; + virtual int height() const; + + virtual int lineHeight(bool firstLine, bool isRootLineBox = false) const; + + virtual int minPrefWidth() const; + virtual int maxPrefWidth() const; + + void trimmedPrefWidths(int leadWidth, + int& beginMinW, bool& beginWS, + int& endMinW, bool& endWS, + bool& hasBreakableChar, bool& hasBreak, + int& beginMaxW, int& endMaxW, + int& minW, int& maxW, bool& stripFrontSpaces); + + // returns the minimum x position of all runs relative to the parent. + // defaults to 0. + int minXPos() const; + + virtual int xPos() const; + virtual int yPos() const; + + virtual int verticalPositionHint(bool firstLine) const; + + void setText(PassRefPtr<StringImpl>, bool force = false); + void setTextWithOffset(PassRefPtr<StringImpl>, unsigned offset, unsigned len, bool force = false); + + virtual bool canBeSelectionLeaf() const { return true; } + virtual SelectionState selectionState() const { return static_cast<SelectionState>(m_selectionState); } + virtual void setSelectionState(SelectionState s); + virtual IntRect selectionRect(bool clipToVisibleContent = true); + virtual IntRect localCaretRect(InlineBox*, int caretOffset, int* extraWidthToEndOfLine = 0); + + virtual int marginLeft() const { return style()->marginLeft().calcMinValue(0); } + virtual int marginRight() const { return style()->marginRight().calcMinValue(0); } + + virtual IntRect absoluteClippedOverflowRect(); + + InlineTextBox* firstTextBox() const { return m_firstTextBox; } + InlineTextBox* lastTextBox() const { return m_lastTextBox; } + + virtual int caretMinOffset() const; + virtual int caretMaxOffset() const; + virtual unsigned caretMaxRenderedOffset() const; + + virtual int previousOffset(int current) const; + virtual int nextOffset(int current) const; + + bool containsReversedText() const { return m_containsReversedText; } + + InlineTextBox* findNextInlineTextBox(int offset, int& pos) const; + + bool allowTabs() const { return !style()->collapseWhiteSpace(); } + + void checkConsistency() const; + +protected: + virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + + virtual void setTextInternal(PassRefPtr<StringImpl>); + virtual void calcPrefWidths(int leadWidth); + virtual UChar previousCharacter(); + +private: + // Make length() private so that callers that have a RenderText* + // will use the more efficient textLength() instead, while + // callers with a RenderObject* can continue to use length(). + virtual unsigned length() const { return textLength(); } + + virtual void paint(PaintInfo&, int, int) { ASSERT_NOT_REACHED(); } + virtual void layout() { ASSERT_NOT_REACHED(); } + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int, int, int, int, HitTestAction) { ASSERT_NOT_REACHED(); return false; } + + void deleteTextBoxes(); + bool containsOnlyWhitespace(unsigned from, unsigned len) const; + int widthFromCache(const Font&, int start, int len, int xPos) const; + bool isAllASCII() const { return m_isAllASCII; } + + RefPtr<StringImpl> m_text; + + InlineTextBox* m_firstTextBox; + InlineTextBox* m_lastTextBox; + + int m_minWidth; + int m_maxWidth; + int m_beginMinWidth; + int m_endMinWidth; + + unsigned m_selectionState : 3; // enums on Windows are signed, so this needs to be unsigned to prevent it turning negative. + bool m_hasBreakableChar : 1; // Whether or not we can be broken into multiple lines. + bool m_hasBreak : 1; // Whether or not we have a hard break (e.g., <pre> with '\n'). + bool m_hasTab : 1; // Whether or not we have a variable width tab character (e.g., <pre> with '\t'). + bool m_hasBeginWS : 1; // Whether or not we begin with WS (only true if we aren't pre) + bool m_hasEndWS : 1; // Whether or not we end with WS (only true if we aren't pre) + bool m_linesDirty : 1; // This bit indicates that the text run has already dirtied specific + // line boxes, and this hint will enable layoutInlineChildren to avoid + // just dirtying everything when character data is modified (e.g., appended/inserted + // or removed). + bool m_containsReversedText : 1; + bool m_isAllASCII : 1; +}; + +#ifdef NDEBUG +inline void RenderText::checkConsistency() const +{ +} +#endif + +} // namespace WebCore + +#endif // RenderText_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderTextControl.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderTextControl.cpp new file mode 100644 index 0000000..8f853f8 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderTextControl.cpp @@ -0,0 +1,591 @@ +/** + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * (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" +#include "RenderTextControl.h" + +#include "CharacterNames.h" +#include "Editor.h" +#include "Event.h" +#include "EventNames.h" +#include "Frame.h" +#include "HTMLBRElement.h" +#include "HTMLFormControlElement.h" +#include "HTMLNames.h" +#include "HitTestResult.h" +#include "ScrollbarTheme.h" +#include "SelectionController.h" +#include "TextControlInnerElements.h" +#include "Text.h" +#include "TextIterator.h" + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +// Value chosen by observation. This can be tweaked. +static const int minColorContrastValue = 1300; + +static Color disabledTextColor(const Color& textColor, const Color& backgroundColor) +{ + // The explicit check for black is an optimization for the 99% case (black on white). + // This also means that black on black will turn into grey on black when disabled. + Color disabledColor; + if (textColor.rgb() == Color::black || differenceSquared(textColor, Color::white) > differenceSquared(backgroundColor, Color::white)) + disabledColor = textColor.light(); + else + disabledColor = textColor.dark(); + + // If there's not very much contrast between the disabled color and the background color, + // just leave the text color alone. We don't want to change a good contrast color scheme so that it has really bad contrast. + // If the the contrast was already poor, then it doesn't do any good to change it to a different poor contrast color scheme. + if (differenceSquared(disabledColor, backgroundColor) < minColorContrastValue) + return textColor; + + return disabledColor; +} + +RenderTextControl::RenderTextControl(Node* node) + : RenderBlock(node) + , m_edited(false) + , m_userEdited(false) +{ +} + +RenderTextControl::~RenderTextControl() +{ + // The children renderers have already been destroyed by destroyLeftoverChildren + if (m_innerText) + m_innerText->detach(); +} + +void RenderTextControl::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +{ + RenderBlock::styleDidChange(diff, oldStyle); + + if (m_innerText) { + RenderBlock* textBlockRenderer = static_cast<RenderBlock*>(m_innerText->renderer()); + RefPtr<RenderStyle> textBlockStyle = createInnerTextStyle(style()); + // We may have set the width and the height in the old style in layout(). + // Reset them now to avoid getting a spurious layout hint. + textBlockRenderer->style()->setHeight(Length()); + textBlockRenderer->style()->setWidth(Length()); + textBlockRenderer->setStyle(textBlockStyle); + for (Node* n = m_innerText->firstChild(); n; n = n->traverseNextNode(m_innerText.get())) { + if (n->renderer()) + n->renderer()->setStyle(textBlockStyle); + } + } + + setHasOverflowClip(false); + setReplaced(isInline()); +} + +void RenderTextControl::adjustInnerTextStyle(const RenderStyle* startStyle, RenderStyle* textBlockStyle) const +{ + // The inner block, if present, always has its direction set to LTR, + // so we need to inherit the direction from the element. + textBlockStyle->setDirection(style()->direction()); + textBlockStyle->setUserModify((node()->isReadOnlyControl() || !node()->isEnabled()) ? READ_ONLY : READ_WRITE_PLAINTEXT_ONLY); + + if (!node()->isEnabled()) + textBlockStyle->setColor(disabledTextColor(textBlockStyle->color(), startStyle->backgroundColor())); +} + +void RenderTextControl::createSubtreeIfNeeded(TextControlInnerElement* innerBlock) +{ + if (!m_innerText) { + // Create the text block element + // For non-search fields, there is no intermediate innerBlock as the shadow node. + // m_innerText will be the shadow node in that case. + RenderStyle* parentStyle = innerBlock ? innerBlock->renderer()->style() : style(); + m_innerText = new TextControlInnerTextElement(document(), innerBlock ? 0 : node()); + m_innerText->attachInnerElement(innerBlock ? innerBlock : node(), createInnerTextStyle(parentStyle), renderArena()); + } +} + +int RenderTextControl::textBlockHeight() const +{ + return m_height - paddingTop() - paddingBottom() - borderTop() - borderBottom(); +} + +int RenderTextControl::textBlockWidth() const +{ + return m_width - paddingLeft() - paddingRight() - borderLeft() - borderRight() + - m_innerText->renderer()->paddingLeft() - m_innerText->renderer()->paddingRight(); +} + +void RenderTextControl::updateFromElement() +{ + m_innerText->renderer()->style()->setUserModify((node()->isReadOnlyControl() || !node()->isEnabled()) ? READ_ONLY : READ_WRITE_PLAINTEXT_ONLY); +} + +void RenderTextControl::setInnerTextValue(const String& innerTextValue) +{ + String value; + + if (innerTextValue.isNull()) + value = ""; + else { + value = innerTextValue; + value = value.replace('\\', backslashAsCurrencySymbol()); + } + + if (value != text() || !m_innerText->hasChildNodes()) { + if (value != text()) { + if (Frame* frame = document()->frame()) + frame->editor()->clearUndoRedoOperations(); + } + + ExceptionCode ec = 0; + m_innerText->setInnerText(value, ec); + ASSERT(!ec); + + if (value.endsWith("\n") || value.endsWith("\r")) { + m_innerText->appendChild(new HTMLBRElement(brTag, document()), ec); + ASSERT(!ec); + } + + m_edited = false; + m_userEdited = false; + } + + formControlElement()->setValueMatchesRenderer(); +} + +void RenderTextControl::setUserEdited(bool isUserEdited) +{ + m_userEdited = isUserEdited; + document()->setIgnoreAutofocus(isUserEdited); +} + +int RenderTextControl::selectionStart() +{ + Frame* frame = document()->frame(); + if (!frame) + return 0; + return indexForVisiblePosition(frame->selection()->start()); +} + +int RenderTextControl::selectionEnd() +{ + Frame* frame = document()->frame(); + if (!frame) + return 0; + return indexForVisiblePosition(frame->selection()->end()); +} + +void RenderTextControl::setSelectionStart(int start) +{ + setSelectionRange(start, max(start, selectionEnd())); +} + +void RenderTextControl::setSelectionEnd(int end) +{ + setSelectionRange(min(end, selectionStart()), end); +} + +void RenderTextControl::select() +{ + setSelectionRange(0, text().length()); +} + +void RenderTextControl::setSelectionRange(int start, int end) +{ + end = max(end, 0); + start = min(max(start, 0), end); + + document()->updateLayout(); + + if (style()->visibility() == HIDDEN || !m_innerText || !m_innerText->renderer() || !m_innerText->renderer()->height()) { + cacheSelection(start, end); + return; + } + VisiblePosition startPosition = visiblePositionForIndex(start); + VisiblePosition endPosition; + if (start == end) + endPosition = startPosition; + else + endPosition = visiblePositionForIndex(end); + + ASSERT(startPosition.isNotNull() && endPosition.isNotNull()); + ASSERT(startPosition.deepEquivalent().node()->shadowAncestorNode() == node() && endPosition.deepEquivalent().node()->shadowAncestorNode() == node()); + + Selection newSelection = Selection(startPosition, endPosition); + + if (Frame* frame = document()->frame()) + frame->selection()->setSelection(newSelection); + + // FIXME: Granularity is stored separately on the frame, but also in the selection controller. + // The granularity in the selection controller should be used, and then this line of code would not be needed. + if (Frame* frame = document()->frame()) + frame->setSelectionGranularity(CharacterGranularity); +} + +Selection RenderTextControl::selection(int start, int end) const +{ + return Selection(VisiblePosition(m_innerText.get(), start, VP_DEFAULT_AFFINITY), + VisiblePosition(m_innerText.get(), end, VP_DEFAULT_AFFINITY)); +} + +VisiblePosition RenderTextControl::visiblePositionForIndex(int index) +{ + if (index <= 0) + return VisiblePosition(m_innerText.get(), 0, DOWNSTREAM); + ExceptionCode ec = 0; + RefPtr<Range> range = Range::create(document()); + range->selectNodeContents(m_innerText.get(), ec); + ASSERT(!ec); + CharacterIterator it(range.get()); + it.advance(index - 1); + Node* endContainer = it.range()->endContainer(ec); + ASSERT(!ec); + int endOffset = it.range()->endOffset(ec); + ASSERT(!ec); + return VisiblePosition(endContainer, endOffset, UPSTREAM); +} + +int RenderTextControl::indexForVisiblePosition(const VisiblePosition& pos) +{ + Position indexPosition = pos.deepEquivalent(); + if (!indexPosition.node() || indexPosition.node()->rootEditableElement() != m_innerText) + return 0; + ExceptionCode ec = 0; + RefPtr<Range> range = Range::create(document()); + range->setStart(m_innerText.get(), 0, ec); + ASSERT(!ec); + range->setEnd(indexPosition.node(), indexPosition.offset(), ec); + ASSERT(!ec); + return TextIterator::rangeLength(range.get()); +} + +void RenderTextControl::subtreeHasChanged() +{ + m_edited = true; + m_userEdited = true; +} + +String RenderTextControl::finishText(Vector<UChar>& result) const +{ + // Remove one trailing newline; there's always one that's collapsed out by rendering. + size_t size = result.size(); + if (size && result[size - 1] == '\n') + result.shrink(--size); + + // Convert backslash to currency symbol. + UChar symbol = backslashAsCurrencySymbol(); + if (symbol != '\\') { + for (size_t i = 0; i < size; ++i) { + if (result[i] == '\\') + result[i] = symbol; + } + } + + return String::adopt(result); +} + +String RenderTextControl::text() +{ + if (!m_innerText) + return ""; + + Frame* frame = document()->frame(); + Text* compositionNode = frame ? frame->editor()->compositionNode() : 0; + + Vector<UChar> result; + + for (Node* n = m_innerText.get(); n; n = n->traverseNextNode(m_innerText.get())) { + if (n->hasTagName(brTag)) + result.append(&newlineCharacter, 1); + else if (n->isTextNode()) { + Text* text = static_cast<Text*>(n); + String data = text->data(); + unsigned length = data.length(); + if (text != compositionNode) + result.append(data.characters(), length); + else { + unsigned compositionStart = min(frame->editor()->compositionStart(), length); + unsigned compositionEnd = min(max(compositionStart, frame->editor()->compositionEnd()), length); + result.append(data.characters(), compositionStart); + result.append(data.characters() + compositionEnd, length - compositionEnd); + } + } + } + + return finishText(result); +} + +static void getNextSoftBreak(RootInlineBox*& line, Node*& breakNode, unsigned& breakOffset) +{ + RootInlineBox* next; + for (; line; line = next) { + next = line->nextRootBox(); + if (next && !line->endsWithBreak()) { + ASSERT(line->lineBreakObj()); + breakNode = line->lineBreakObj()->node(); + breakOffset = line->lineBreakPos(); + line = next; + return; + } + } + breakNode = 0; +} + +String RenderTextControl::textWithHardLineBreaks() +{ + if (!m_innerText) + return ""; + Node* firstChild = m_innerText->firstChild(); + if (!firstChild) + return ""; + + document()->updateLayout(); + + RenderObject* renderer = firstChild->renderer(); + if (!renderer) + return ""; + + InlineBox* box = renderer->isText() ? static_cast<RenderText*>(renderer)->firstTextBox() : renderer->inlineBoxWrapper(); + if (!box) + return ""; + + Frame* frame = document()->frame(); + Text* compositionNode = frame ? frame->editor()->compositionNode() : 0; + + Node* breakNode; + unsigned breakOffset; + RootInlineBox* line = box->root(); + getNextSoftBreak(line, breakNode, breakOffset); + + Vector<UChar> result; + + for (Node* n = firstChild; n; n = n->traverseNextNode(m_innerText.get())) { + if (n->hasTagName(brTag)) + result.append(&newlineCharacter, 1); + else if (n->isTextNode()) { + Text* text = static_cast<Text*>(n); + String data = text->data(); + unsigned length = data.length(); + unsigned compositionStart = (text == compositionNode) + ? min(frame->editor()->compositionStart(), length) : 0; + unsigned compositionEnd = (text == compositionNode) + ? min(max(compositionStart, frame->editor()->compositionEnd()), length) : 0; + unsigned position = 0; + while (breakNode == n && breakOffset < compositionStart) { + result.append(data.characters() + position, breakOffset - position); + position = breakOffset; + result.append(&newlineCharacter, 1); + getNextSoftBreak(line, breakNode, breakOffset); + } + result.append(data.characters() + position, compositionStart - position); + position = compositionEnd; + while (breakNode == n && breakOffset <= length) { + if (breakOffset > position) { + result.append(data.characters() + position, breakOffset - position); + position = breakOffset; + result.append(&newlineCharacter, 1); + } + getNextSoftBreak(line, breakNode, breakOffset); + } + result.append(data.characters() + position, length - position); + } + while (breakNode == n) + getNextSoftBreak(line, breakNode, breakOffset); + } + + return finishText(result); +} + +int RenderTextControl::scrollbarThickness() const +{ + // FIXME: We should get the size of the scrollbar from the RenderTheme instead. + return ScrollbarTheme::nativeTheme()->scrollbarThickness(); +} + +void RenderTextControl::calcHeight() +{ + m_height = m_innerText->renderer()->borderTop() + m_innerText->renderer()->borderBottom() + + m_innerText->renderer()->paddingTop() + m_innerText->renderer()->paddingBottom() + + m_innerText->renderer()->marginTop() + m_innerText->renderer()->marginBottom(); + + adjustControlHeightBasedOnLineHeight(m_innerText->renderer()->lineHeight(true, true)); + m_height += paddingTop() + paddingBottom() + borderTop() + borderBottom(); + + // We are able to have a horizontal scrollbar if the overflow style is scroll, or if its auto and there's no word wrap. + if (m_innerText->renderer()->style()->overflowX() == OSCROLL || (m_innerText->renderer()->style()->overflowX() == OAUTO && m_innerText->renderer()->style()->wordWrap() == NormalWordWrap)) + m_height += scrollbarThickness(); + + RenderBlock::calcHeight(); +} + +void RenderTextControl::hitInnerTextBlock(HitTestResult& result, int x, int y, int tx, int ty) +{ + result.setInnerNode(m_innerText.get()); + result.setLocalPoint(IntPoint(x - tx - m_x - m_innerText->renderer()->xPos(), + y - ty - m_y - m_innerText->renderer()->yPos())); +} + +void RenderTextControl::forwardEvent(Event* event) +{ + if (event->type() == eventNames().blurEvent || event->type() == eventNames().focusEvent) + return; + m_innerText->defaultEventHandler(event); +} + +IntRect RenderTextControl::controlClipRect(int tx, int ty) const +{ + IntRect clipRect = contentBox(); + clipRect.move(tx, ty); + return clipRect; +} + +void RenderTextControl::calcPrefWidths() +{ + ASSERT(prefWidthsDirty()); + + m_minPrefWidth = 0; + m_maxPrefWidth = 0; + + if (style()->width().isFixed() && style()->width().value() > 0) + m_minPrefWidth = m_maxPrefWidth = calcContentBoxWidth(style()->width().value()); + else { + // Figure out how big a text control needs to be for a given number of characters + // (using "0" as the nominal character). + const UChar ch = '0'; + float charWidth = style()->font().floatWidth(TextRun(&ch, 1, false, 0, 0, false, false, false)); + m_maxPrefWidth = preferredContentWidth(charWidth) + m_innerText->renderer()->paddingLeft() + m_innerText->renderer()->paddingRight(); + } + + if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) { + m_maxPrefWidth = max(m_maxPrefWidth, calcContentBoxWidth(style()->minWidth().value())); + m_minPrefWidth = max(m_minPrefWidth, calcContentBoxWidth(style()->minWidth().value())); + } else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent())) + m_minPrefWidth = 0; + else + m_minPrefWidth = m_maxPrefWidth; + + if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) { + m_maxPrefWidth = min(m_maxPrefWidth, calcContentBoxWidth(style()->maxWidth().value())); + m_minPrefWidth = min(m_minPrefWidth, calcContentBoxWidth(style()->maxWidth().value())); + } + + int toAdd = paddingLeft() + paddingRight() + borderLeft() + borderRight(); + + m_minPrefWidth += toAdd; + m_maxPrefWidth += toAdd; + + setPrefWidthsDirty(false); +} + +void RenderTextControl::selectionChanged(bool userTriggered) +{ + cacheSelection(selectionStart(), selectionEnd()); + + if (Frame* frame = document()->frame()) { + if (frame->selection()->isRange() && userTriggered) + static_cast<EventTargetNode*>(node())->dispatchEventForType(eventNames().selectEvent, true, false); + } +} + +void RenderTextControl::addFocusRingRects(GraphicsContext* graphicsContext, int tx, int ty) +{ + graphicsContext->addFocusRingRect(IntRect(tx, ty, width(), height())); +} + +void RenderTextControl::autoscroll() +{ + RenderLayer* layer = m_innerText->renderer()->layer(); + if (layer) + layer->autoscroll(); +} + +int RenderTextControl::scrollWidth() const +{ + if (m_innerText) + return m_innerText->scrollWidth(); + return RenderBlock::scrollWidth(); +} + +int RenderTextControl::scrollHeight() const +{ + if (m_innerText) + return m_innerText->scrollHeight(); + return RenderBlock::scrollHeight(); +} + +int RenderTextControl::scrollLeft() const +{ + if (m_innerText) + return m_innerText->scrollLeft(); + return RenderBlock::scrollLeft(); +} + +int RenderTextControl::scrollTop() const +{ + if (m_innerText) + return m_innerText->scrollTop(); + return RenderBlock::scrollTop(); +} + +void RenderTextControl::setScrollLeft(int newLeft) +{ + if (m_innerText) + m_innerText->setScrollLeft(newLeft); +} + +void RenderTextControl::setScrollTop(int newTop) +{ + if (m_innerText) + m_innerText->setScrollTop(newTop); +} + +bool RenderTextControl::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier) +{ + RenderLayer* layer = m_innerText->renderer()->layer(); + if (layer && layer->scroll(direction, granularity, multiplier)) + return true; + return RenderObject::scroll(direction, granularity, multiplier); +} + +bool RenderTextControl::isScrollable() const +{ + if (m_innerText && m_innerText->renderer()->isScrollable()) + return true; + return RenderObject::isScrollable(); +} + +HTMLElement* RenderTextControl::innerTextElement() const +{ + return m_innerText.get(); +} + +FormControlElement* RenderTextControl::formControlElement() const +{ + if (node()->isHTMLElement()) + return static_cast<HTMLFormControlElement*>(node()); + + ASSERT_NOT_REACHED(); + return 0; +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderTextControl.h b/src/3rdparty/webkit/WebCore/rendering/RenderTextControl.h new file mode 100644 index 0000000..40f5116 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderTextControl.h @@ -0,0 +1,121 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * (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. + * + */ + +#ifndef RenderTextControl_h +#define RenderTextControl_h + +#include "RenderBlock.h" + +namespace WebCore { + +class FormControlElement; +class Selection; +class TextControlInnerElement; +class TextControlInnerTextElement; + +class RenderTextControl : public RenderBlock { +public: + virtual ~RenderTextControl(); + + virtual const char* renderName() const { return "RenderTextControl"; } + virtual bool hasControlClip() const { return false; } + virtual IntRect controlClipRect(int tx, int ty) const; + virtual void calcHeight(); + virtual void calcPrefWidths(); + virtual void removeLeftoverAnonymousBlock(RenderBlock*) { } + virtual void updateFromElement(); + virtual bool canHaveChildren() const { return false; } + virtual bool avoidsFloats() const { return true; } + + virtual bool isEdited() const { return m_edited; } + virtual void setEdited(bool isEdited) { m_edited = isEdited; } + + bool isUserEdited() const { return m_userEdited; } + void setUserEdited(bool isUserEdited); + + int selectionStart(); + int selectionEnd(); + void setSelectionStart(int); + void setSelectionEnd(int); + void select(); + void setSelectionRange(int start, int end); + Selection selection(int start, int end) const; + + virtual void subtreeHasChanged(); + String text(); + String textWithHardLineBreaks(); + void selectionChanged(bool userTriggered); + + virtual void addFocusRingRects(GraphicsContext*, int tx, int ty); + + virtual bool canBeProgramaticallyScrolled(bool) const { return true; } + virtual void autoscroll(); + + // Subclassed to forward to our inner div. + virtual int scrollLeft() const; + virtual int scrollTop() const; + virtual int scrollWidth() const; + virtual int scrollHeight() const; + virtual void setScrollLeft(int); + virtual void setScrollTop(int); + virtual bool scroll(ScrollDirection, ScrollGranularity, float multiplier = 1.0f); + virtual bool isScrollable() const; + + VisiblePosition visiblePositionForIndex(int index); + int indexForVisiblePosition(const VisiblePosition&); + +protected: + RenderTextControl(Node*); + + int scrollbarThickness() const; + void adjustInnerTextStyle(const RenderStyle* startStyle, RenderStyle* textBlockStyle) const; + void setInnerTextValue(const String&); + + virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + + void createSubtreeIfNeeded(TextControlInnerElement* innerBlock); + void hitInnerTextBlock(HitTestResult&, int x, int y, int tx, int ty); + void forwardEvent(Event*); + + int textBlockWidth() const; + int textBlockHeight() const; + + virtual int preferredContentWidth(float charWidth) const = 0; + virtual void adjustControlHeightBasedOnLineHeight(int lineHeight) = 0; + virtual void cacheSelection(int start, int end) = 0; + virtual PassRefPtr<RenderStyle> createInnerTextStyle(const RenderStyle* startStyle) const = 0; + + friend class TextIterator; + HTMLElement* innerTextElement() const; + + FormControlElement* formControlElement() const; + +private: + String finishText(Vector<UChar>&) const; + + bool m_edited; + bool m_userEdited; + RefPtr<TextControlInnerTextElement> m_innerText; +}; + +} // namespace WebCore + +#endif // RenderTextControl_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderTextControlMultiLine.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderTextControlMultiLine.cpp new file mode 100644 index 0000000..f860524 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderTextControlMultiLine.cpp @@ -0,0 +1,157 @@ +/** + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * (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" +#include "RenderTextControlMultiLine.h" + +#include "EventNames.h" +#include "Frame.h" +#include "HitTestResult.h" +#include "HTMLTextAreaElement.h" + +namespace WebCore { + +RenderTextControlMultiLine::RenderTextControlMultiLine(Node* node) + : RenderTextControl(node) +{ +} + +RenderTextControlMultiLine::~RenderTextControlMultiLine() +{ + if (node()) + static_cast<HTMLTextAreaElement*>(node())->rendererWillBeDestroyed(); +} + +void RenderTextControlMultiLine::subtreeHasChanged() +{ + RenderTextControl::subtreeHasChanged(); + formControlElement()->setValueMatchesRenderer(false); + + if (!node()->focused()) + return; + + if (Frame* frame = document()->frame()) + frame->textDidChangeInTextArea(static_cast<Element*>(node())); +} + +void RenderTextControlMultiLine::layout() +{ + int oldHeight = m_height; + calcHeight(); + + int oldWidth = m_width; + calcWidth(); + + bool relayoutChildren = oldHeight != m_height || oldWidth != m_width; + RenderObject* innerTextRenderer = innerTextElement()->renderer(); + + // Set the text block height + int desiredHeight = textBlockHeight(); + if (desiredHeight != innerTextRenderer->height()) + relayoutChildren = true; + innerTextRenderer->style()->setHeight(Length(desiredHeight, Fixed)); + + // Set the text block width + int desiredWidth = textBlockWidth(); + if (desiredWidth != innerTextRenderer->width()) + relayoutChildren = true; + innerTextRenderer->style()->setWidth(Length(desiredWidth, Fixed)); + + RenderBlock::layoutBlock(relayoutChildren); +} + +bool RenderTextControlMultiLine::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction) +{ + if (!RenderTextControl::nodeAtPoint(request, result, x, y, tx, ty, hitTestAction)) + return false; + + if (result.innerNode() == element()) { + hitInnerTextBlock(result, x, y, tx, ty); + return true; + } + + return false; +} + +void RenderTextControlMultiLine::forwardEvent(Event* event) +{ + RenderTextControl::forwardEvent(event); +} + +int RenderTextControlMultiLine::preferredContentWidth(float charWidth) const +{ + int factor = static_cast<HTMLTextAreaElement*>(node())->cols(); + return static_cast<int>(ceilf(charWidth * factor)) + scrollbarThickness(); +} + +void RenderTextControlMultiLine::adjustControlHeightBasedOnLineHeight(int lineHeight) +{ + m_height += lineHeight * static_cast<HTMLTextAreaElement*>(node())->rows(); +} + +int RenderTextControlMultiLine::baselinePosition(bool, bool) const +{ + return height() + marginTop() + marginBottom(); +} + +void RenderTextControlMultiLine::updateFromElement() +{ + createSubtreeIfNeeded(0); + RenderTextControl::updateFromElement(); + + setInnerTextValue(static_cast<HTMLTextAreaElement*>(node())->value()); +} + +void RenderTextControlMultiLine::cacheSelection(int start, int end) +{ + static_cast<HTMLTextAreaElement*>(node())->cacheSelection(start, end); +} + +PassRefPtr<RenderStyle> RenderTextControlMultiLine::createInnerTextStyle(const RenderStyle* startStyle) const +{ + RefPtr<RenderStyle> textBlockStyle = RenderStyle::create(); + textBlockStyle->inheritFrom(startStyle); + + adjustInnerTextStyle(startStyle, textBlockStyle.get()); + + // Forward overflow properties. + textBlockStyle->setOverflowX(startStyle->overflowX() == OVISIBLE ? OAUTO : startStyle->overflowX()); + textBlockStyle->setOverflowY(startStyle->overflowY() == OVISIBLE ? OAUTO : startStyle->overflowY()); + + // Set word wrap property based on wrap attribute. + if (static_cast<HTMLTextAreaElement*>(node())->shouldWrapText()) { + textBlockStyle->setWhiteSpace(PRE_WRAP); + textBlockStyle->setWordWrap(BreakWordWrap); + } else { + textBlockStyle->setWhiteSpace(PRE); + textBlockStyle->setWordWrap(NormalWordWrap); + } + + textBlockStyle->setDisplay(BLOCK); + + // We're adding three extra pixels of padding to line textareas up with text fields. + textBlockStyle->setPaddingLeft(Length(3, Fixed)); + textBlockStyle->setPaddingRight(Length(3, Fixed)); + + return textBlockStyle.release(); +} + +} diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderTextControlMultiLine.h b/src/3rdparty/webkit/WebCore/rendering/RenderTextControlMultiLine.h new file mode 100644 index 0000000..591a65d --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderTextControlMultiLine.h @@ -0,0 +1,54 @@ +/* + * 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. + * + */ + +#ifndef RenderTextControlMultiLine_h +#define RenderTextControlMultiLine_h + +#include "RenderTextControl.h" + +namespace WebCore { + +class RenderTextControlMultiLine : public RenderTextControl { +public: + RenderTextControlMultiLine(Node*); + virtual ~RenderTextControlMultiLine(); + + virtual bool isTextArea() const { return true; } + + virtual void subtreeHasChanged(); + virtual void layout(); + + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); + void forwardEvent(Event*); + +private: + virtual int preferredContentWidth(float charWidth) const; + virtual void adjustControlHeightBasedOnLineHeight(int lineHeight); + virtual int baselinePosition(bool firstLine, bool isRootLineBox) const; + + virtual void updateFromElement(); + virtual void cacheSelection(int start, int end); + + virtual PassRefPtr<RenderStyle> createInnerTextStyle(const RenderStyle* startStyle) const; +}; + +} + +#endif diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderTextControlSingleLine.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderTextControlSingleLine.cpp new file mode 100644 index 0000000..4f52527 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderTextControlSingleLine.cpp @@ -0,0 +1,759 @@ +/** + * Copyright (C) 2006, 2007 Apple Inc. All rights reserved. + * (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" +#include "RenderTextControlSingleLine.h" + +#include "CSSStyleSelector.h" +#include "Event.h" +#include "EventNames.h" +#include "Frame.h" +#include "FrameView.h" +#include "HitTestResult.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include "LocalizedStrings.h" +#include "MouseEvent.h" +#include "PlatformKeyboardEvent.h" +#include "RenderScrollbar.h" +#include "RenderTheme.h" +#include "SearchPopupMenu.h" +#include "SelectionController.h" +#include "Settings.h" +#include "TextControlInnerElements.h" + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +RenderTextControlSingleLine::RenderTextControlSingleLine(Node* node) + : RenderTextControl(node) + , m_placeholderVisible(false) + , m_searchPopupIsVisible(false) + , m_shouldDrawCapsLockIndicator(false) + , m_searchEventTimer(this, &RenderTextControlSingleLine::searchEventTimerFired) + , m_searchPopup(0) +{ +} + +RenderTextControlSingleLine::~RenderTextControlSingleLine() +{ + if (m_searchPopup) { + m_searchPopup->disconnectClient(); + m_searchPopup = 0; + } + + if (m_innerBlock) + m_innerBlock->detach(); +} + +bool RenderTextControlSingleLine::placeholderShouldBeVisible() const +{ + return static_cast<HTMLInputElement*>(node())->placeholderShouldBeVisible(); +} + +void RenderTextControlSingleLine::updatePlaceholderVisibility() +{ + RenderStyle* parentStyle = m_innerBlock ? m_innerBlock->renderer()->style() : style(); + + RefPtr<RenderStyle> textBlockStyle = createInnerTextStyle(parentStyle); + HTMLElement* innerText = innerTextElement(); + innerText->renderer()->setStyle(textBlockStyle); + + for (Node* n = innerText->firstChild(); n; n = n->traverseNextNode(innerText)) { + if (RenderObject* renderer = n->renderer()) + renderer->setStyle(textBlockStyle); + } + + updateFromElement(); +} + +void RenderTextControlSingleLine::addSearchResult() +{ + HTMLInputElement* input = static_cast<HTMLInputElement*>(node()); + if (input->maxResults() <= 0) + return; + + String value = input->value(); + if (value.isEmpty()) + return; + + Settings* settings = document()->settings(); + if (!settings || settings->privateBrowsingEnabled()) + return; + + int size = static_cast<int>(m_recentSearches.size()); + for (int i = size - 1; i >= 0; --i) { + if (m_recentSearches[i] == value) + m_recentSearches.remove(i); + } + + m_recentSearches.insert(0, value); + while (static_cast<int>(m_recentSearches.size()) > input->maxResults()) + m_recentSearches.removeLast(); + + const AtomicString& name = autosaveName(); + if (!m_searchPopup) + m_searchPopup = SearchPopupMenu::create(this); + + m_searchPopup->saveRecentSearches(name, m_recentSearches); +} + +void RenderTextControlSingleLine::stopSearchEventTimer() +{ + m_searchEventTimer.stop(); +} + +void RenderTextControlSingleLine::showPopup() +{ + if (m_searchPopupIsVisible) + return; + + if (!m_searchPopup) + m_searchPopup = SearchPopupMenu::create(this); + + if (!m_searchPopup->enabled()) + return; + + m_searchPopupIsVisible = true; + + const AtomicString& name = autosaveName(); + m_searchPopup->loadRecentSearches(name, m_recentSearches); + + // Trim the recent searches list if the maximum size has changed since we last saved. + HTMLInputElement* input = static_cast<HTMLInputElement*>(node()); + if (static_cast<int>(m_recentSearches.size()) > input->maxResults()) { + do { + m_recentSearches.removeLast(); + } while (static_cast<int>(m_recentSearches.size()) > input->maxResults()); + + m_searchPopup->saveRecentSearches(name, m_recentSearches); + } + + m_searchPopup->show(absoluteBoundingBoxRect(true), document()->view(), -1); +} + +void RenderTextControlSingleLine::hidePopup() +{ + if (m_searchPopup) + m_searchPopup->hide(); + + m_searchPopupIsVisible = false; +} + +void RenderTextControlSingleLine::subtreeHasChanged() +{ + bool wasEdited = isEdited(); + RenderTextControl::subtreeHasChanged(); + + HTMLInputElement* input = static_cast<HTMLInputElement*>(node()); + input->setValueFromRenderer(input->constrainValue(text())); + + if (RenderObject* cancelButtonRenderer = m_cancelButton ? m_cancelButton->renderer() : 0) + updateCancelButtonVisibility(cancelButtonRenderer->style()); + + // If the incremental attribute is set, then dispatch the search event + if (!input->getAttribute(incrementalAttr).isNull()) + startSearchEventTimer(); + + if (!wasEdited && input->focused()) { + if (Frame* frame = document()->frame()) + frame->textFieldDidBeginEditing(input); + } + + if (input->focused()) { + if (Frame* frame = document()->frame()) + frame->textDidChangeInTextField(input); + } +} + +void RenderTextControlSingleLine::paint(PaintInfo& paintInfo, int tx, int ty) +{ + RenderTextControl::paint(paintInfo, tx, ty); + + if (paintInfo.phase == PaintPhaseBlockBackground && m_shouldDrawCapsLockIndicator) { + IntRect contentsRect = contentBox(); + + // Convert the rect into the coords used for painting the content + contentsRect.move(tx + xPos(), ty + yPos()); + theme()->paintCapsLockIndicator(this, paintInfo, contentsRect); + } +} + +void RenderTextControlSingleLine::layout() +{ + int oldHeight = m_height; + calcHeight(); + + int oldWidth = m_width; + calcWidth(); + + bool relayoutChildren = oldHeight != m_height || oldWidth != m_width; + + RenderObject* innerTextRenderer = innerTextElement()->renderer(); + RenderObject* innerBlockRenderer = m_innerBlock ? m_innerBlock->renderer() : 0; + + // Set the text block height + int desiredHeight = textBlockHeight(); + int currentHeight = innerTextRenderer->height(); + + if (m_innerBlock || currentHeight > m_height) { + if (desiredHeight != currentHeight) + relayoutChildren = true; + innerTextRenderer->style()->setHeight(Length(desiredHeight, Fixed)); + } + + if (m_innerBlock) { + ASSERT(innerBlockRenderer); + if (desiredHeight != innerBlockRenderer->height()) + relayoutChildren = true; + innerBlockRenderer->style()->setHeight(Length(desiredHeight, Fixed)); + } + + // Set the text block width + int desiredWidth = textBlockWidth(); + if (desiredWidth != innerTextRenderer->width()) + relayoutChildren = true; + innerTextRenderer->style()->setWidth(Length(desiredWidth, Fixed)); + + if (m_innerBlock) { + int innerBlockWidth = m_width - paddingLeft() - paddingRight() - borderLeft() - borderRight(); + if (innerBlockWidth != innerBlockRenderer->width()) + relayoutChildren = true; + innerBlockRenderer->style()->setWidth(Length(innerBlockWidth, Fixed)); + } + + RenderBlock::layoutBlock(relayoutChildren); + + // For text fields, center the inner text vertically + // Don't do this for search fields, since we don't honor height for them + if (!m_innerBlock) { + currentHeight = innerTextRenderer->height(); + if (currentHeight < m_height) + innerTextRenderer->setPos(innerTextRenderer->xPos(), (m_height - currentHeight) / 2); + } +} + +bool RenderTextControlSingleLine::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction) +{ + // If we're within the text control, we want to act as if we've hit the inner text block element, in case the point + // was on the control but not on the inner element (see Radar 4617841). + + // In a search field, we want to act as if we've hit the results block if we're to the left of the inner text block, + // and act as if we've hit the close block if we're to the right of the inner text block. + + if (!RenderTextControl::nodeAtPoint(request, result, x, y, tx, ty, hitTestAction)) + return false; + + if (result.innerNode() != element() && result.innerNode() != m_innerBlock.get()) + return false; + + hitInnerTextBlock(result, x, y, tx, ty); + + if (!m_innerBlock) + return true; + + Node* innerNode = 0; + RenderObject* innerBlockRenderer = m_innerBlock->renderer(); + RenderObject* innerTextRenderer = innerTextElement()->renderer(); + + IntPoint localPoint = result.localPoint(); + localPoint.move(-innerBlockRenderer->xPos(), -innerBlockRenderer->yPos()); + + int textLeft = tx + m_x + innerBlockRenderer->xPos() + innerTextRenderer->xPos(); + if (m_resultsButton && m_resultsButton->renderer() && x < textLeft) + innerNode = m_resultsButton.get(); + + if (!innerNode) { + int textRight = textLeft + innerTextRenderer->width(); + if (m_cancelButton && m_cancelButton->renderer() && x > textRight) + innerNode = m_cancelButton.get(); + } + + if (innerNode) { + result.setInnerNode(innerNode); + localPoint.move(-innerNode->renderer()->xPos(), -innerNode->renderer()->yPos()); + } + + result.setLocalPoint(localPoint); + return true; +} + +void RenderTextControlSingleLine::forwardEvent(Event* event) +{ + RenderObject* innerTextRenderer = innerTextElement()->renderer(); + + if (event->type() == eventNames().blurEvent) { + if (innerTextRenderer) { + if (RenderLayer* innerLayer = innerTextRenderer->layer()) + innerLayer->scrollToOffset(style()->direction() == RTL ? innerLayer->scrollWidth() : 0, 0); + } + + capsLockStateMayHaveChanged(); + } else if (event->type() == eventNames().focusEvent) + capsLockStateMayHaveChanged(); + + if (!event->isMouseEvent()) { + RenderTextControl::forwardEvent(event); + return; + } + + FloatPoint localPoint = innerTextRenderer->absoluteToLocal(FloatPoint(static_cast<MouseEvent*>(event)->pageX(), static_cast<MouseEvent*>(event)->pageY()), false, true); + if (m_resultsButton && localPoint.x() < innerTextRenderer->borderBox().x()) + m_resultsButton->defaultEventHandler(event); + else if (m_cancelButton && localPoint.x() > innerTextRenderer->borderBox().right()) + m_cancelButton->defaultEventHandler(event); + else + RenderTextControl::forwardEvent(event); +} + +void RenderTextControlSingleLine::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +{ + RenderTextControl::styleDidChange(diff, oldStyle); + + if (RenderObject* innerBlockRenderer = m_innerBlock ? m_innerBlock->renderer() : 0) { + // We may have set the width and the height in the old style in layout(). + // Reset them now to avoid getting a spurious layout hint. + innerBlockRenderer->style()->setHeight(Length()); + innerBlockRenderer->style()->setWidth(Length()); + innerBlockRenderer->setStyle(createInnerBlockStyle(style())); + } + + if (RenderObject* resultsRenderer = m_resultsButton ? m_resultsButton->renderer() : 0) + resultsRenderer->setStyle(createResultsButtonStyle(style())); + + if (RenderObject* cancelRenderer = m_cancelButton ? m_cancelButton->renderer() : 0) + cancelRenderer->setStyle(createCancelButtonStyle(style())); +} + +void RenderTextControlSingleLine::capsLockStateMayHaveChanged() +{ + if (!node() || !document()) + return; + + // Only draw the caps lock indicator if these things are true: + // 1) The field is a password field + // 2) The frame is active + // 3) The element is focused + // 4) The caps lock is on + bool shouldDrawCapsLockIndicator = false; + + if (Frame* frame = document()->frame()) + shouldDrawCapsLockIndicator = static_cast<HTMLInputElement*>(node())->inputType() == HTMLInputElement::PASSWORD + && frame->selection()->isFocusedAndActive() + && document()->focusedNode() == node() + && PlatformKeyboardEvent::currentCapsLockState(); + + if (shouldDrawCapsLockIndicator != m_shouldDrawCapsLockIndicator) { + m_shouldDrawCapsLockIndicator = shouldDrawCapsLockIndicator; + repaint(); + } +} + +int RenderTextControlSingleLine::textBlockWidth() const +{ + int width = RenderTextControl::textBlockWidth(); + + if (RenderObject* resultsRenderer = m_resultsButton ? m_resultsButton->renderer() : 0) { + resultsRenderer->calcWidth(); + width -= resultsRenderer->width(); + } + + if (RenderObject* cancelRenderer = m_cancelButton ? m_cancelButton->renderer() : 0) { + cancelRenderer->calcWidth(); + width -= cancelRenderer->width(); + } + + return width; +} + +int RenderTextControlSingleLine::preferredContentWidth(float charWidth) const +{ + int factor = static_cast<HTMLInputElement*>(node())->size(); + if (factor <= 0) + factor = 20; + + int result = static_cast<int>(ceilf(charWidth * factor)); + + if (RenderObject* resultsRenderer = m_resultsButton ? m_resultsButton->renderer() : 0) + result += resultsRenderer->borderLeft() + resultsRenderer->borderRight() + + resultsRenderer->paddingLeft() + resultsRenderer->paddingRight(); + + if (RenderObject* cancelRenderer = m_cancelButton ? m_cancelButton->renderer() : 0) + result += cancelRenderer->borderLeft() + cancelRenderer->borderRight() + + cancelRenderer->paddingLeft() + cancelRenderer->paddingRight(); + + return result; +} + +void RenderTextControlSingleLine::adjustControlHeightBasedOnLineHeight(int lineHeight) +{ + if (RenderObject* resultsRenderer = m_resultsButton ? m_resultsButton->renderer() : 0) { + static_cast<RenderBlock*>(resultsRenderer)->calcHeight(); + m_height = max(m_height, + resultsRenderer->borderTop() + resultsRenderer->borderBottom() + + resultsRenderer->paddingTop() + resultsRenderer->paddingBottom() + + resultsRenderer->marginTop() + resultsRenderer->marginBottom()); + lineHeight = max(lineHeight, resultsRenderer->height()); + } + + if (RenderObject* cancelRenderer = m_cancelButton ? m_cancelButton->renderer() : 0) { + static_cast<RenderBlock*>(cancelRenderer)->calcHeight(); + m_height = max(m_height, + cancelRenderer->borderTop() + cancelRenderer->borderBottom() + + cancelRenderer->paddingTop() + cancelRenderer->paddingBottom() + + cancelRenderer->marginTop() + cancelRenderer->marginBottom()); + lineHeight = max(lineHeight, cancelRenderer->height()); + } + + m_height += lineHeight; +} + +void RenderTextControlSingleLine::createSubtreeIfNeeded() +{ + if (!static_cast<HTMLInputElement*>(node())->isSearchField()) { + RenderTextControl::createSubtreeIfNeeded(m_innerBlock.get()); + return; + } + + if (!m_innerBlock) { + // Create the inner block element + m_innerBlock = new TextControlInnerElement(document(), node()); + m_innerBlock->attachInnerElement(node(), createInnerBlockStyle(style()), renderArena()); + } + + if (!m_resultsButton) { + // Create the search results button element + m_resultsButton = new SearchFieldResultsButtonElement(document()); + m_resultsButton->attachInnerElement(m_innerBlock.get(), createResultsButtonStyle(m_innerBlock->renderer()->style()), renderArena()); + } + + // Create innerText element before adding the cancel button + RenderTextControl::createSubtreeIfNeeded(m_innerBlock.get()); + + if (!m_cancelButton) { + // Create the cancel button element + m_cancelButton = new SearchFieldCancelButtonElement(document()); + m_cancelButton->attachInnerElement(m_innerBlock.get(), createCancelButtonStyle(m_innerBlock->renderer()->style()), renderArena()); + } +} + +void RenderTextControlSingleLine::updateFromElement() +{ + createSubtreeIfNeeded(); + RenderTextControl::updateFromElement(); + + bool placeholderVisibilityShouldChange = m_placeholderVisible != placeholderShouldBeVisible(); + m_placeholderVisible = placeholderShouldBeVisible(); + + if (RenderObject* cancelButtonRenderer = m_cancelButton ? m_cancelButton->renderer() : 0) + updateCancelButtonVisibility(cancelButtonRenderer->style()); + + HTMLInputElement* element = static_cast<HTMLInputElement*>(node()); + + if (m_placeholderVisible) { + ExceptionCode ec = 0; + innerTextElement()->setInnerText(element->getAttribute(placeholderAttr), ec); + ASSERT(ec == 0); + } else if (!formControlElement()->valueMatchesRenderer() || placeholderVisibilityShouldChange) + setInnerTextValue(element->value()); + + if (m_searchPopupIsVisible) + m_searchPopup->updateFromElement(); +} + +void RenderTextControlSingleLine::cacheSelection(int start, int end) +{ + static_cast<HTMLInputElement*>(node())->cacheSelection(start, end); +} + +PassRefPtr<RenderStyle> RenderTextControlSingleLine::createInnerTextStyle(const RenderStyle* startStyle) const +{ + RefPtr<RenderStyle> textBlockStyle; + if (placeholderShouldBeVisible()) { + RenderStyle* pseudoStyle = getCachedPseudoStyle(RenderStyle::INPUT_PLACEHOLDER); + textBlockStyle = RenderStyle::clone(pseudoStyle); + } else { + textBlockStyle = RenderStyle::create(); + textBlockStyle->inheritFrom(startStyle); + } + + adjustInnerTextStyle(startStyle, textBlockStyle.get()); + + textBlockStyle->setWhiteSpace(PRE); + textBlockStyle->setWordWrap(NormalWordWrap); + textBlockStyle->setOverflowX(OHIDDEN); + textBlockStyle->setOverflowY(OHIDDEN); + + // Do not allow line-height to be smaller than our default. + if (textBlockStyle->font().lineSpacing() > lineHeight(true, true)) + textBlockStyle->setLineHeight(Length(-100.0f, Percent)); + + textBlockStyle->setDisplay(m_innerBlock ? INLINE_BLOCK : BLOCK); + + // We're adding one extra pixel of padding to match WinIE. + textBlockStyle->setPaddingLeft(Length(1, Fixed)); + textBlockStyle->setPaddingRight(Length(1, Fixed)); + + // When the placeholder is going to be displayed, temporarily override the text security to be "none". + // After this, updateFromElement will immediately update the text displayed. + // When the placeholder is no longer visible, updatePlaceholderVisiblity will reset the style, + // and the text security mode will be set back to the computed value correctly. + if (placeholderShouldBeVisible()) + textBlockStyle->setTextSecurity(TSNONE); + + return textBlockStyle.release(); +} + +PassRefPtr<RenderStyle> RenderTextControlSingleLine::createInnerBlockStyle(const RenderStyle* startStyle) const +{ + RefPtr<RenderStyle> innerBlockStyle = RenderStyle::create(); + innerBlockStyle->inheritFrom(startStyle); + + innerBlockStyle->setDisplay(BLOCK); + innerBlockStyle->setDirection(LTR); + + // We don't want the shadow dom to be editable, so we set this block to read-only in case the input itself is editable. + innerBlockStyle->setUserModify(READ_ONLY); + + return innerBlockStyle.release(); +} + +PassRefPtr<RenderStyle> RenderTextControlSingleLine::createResultsButtonStyle(const RenderStyle* startStyle) const +{ + HTMLInputElement* input = static_cast<HTMLInputElement*>(node()); + + RefPtr<RenderStyle> resultsBlockStyle; + if (input->maxResults() < 0) + resultsBlockStyle = getCachedPseudoStyle(RenderStyle::SEARCH_DECORATION); + else if (!input->maxResults()) + resultsBlockStyle = getCachedPseudoStyle(RenderStyle::SEARCH_RESULTS_DECORATION); + else + resultsBlockStyle = getCachedPseudoStyle(RenderStyle::SEARCH_RESULTS_BUTTON); + + if (!resultsBlockStyle) + resultsBlockStyle = RenderStyle::create(); + + if (startStyle) + resultsBlockStyle->inheritFrom(startStyle); + + return resultsBlockStyle.release(); +} + +PassRefPtr<RenderStyle> RenderTextControlSingleLine::createCancelButtonStyle(const RenderStyle* startStyle) const +{ + RefPtr<RenderStyle> cancelBlockStyle; + + if (RefPtr<RenderStyle> pseudoStyle = getCachedPseudoStyle(RenderStyle::SEARCH_CANCEL_BUTTON)) + // We may be sharing style with another search field, but we must not share the cancel button style. + cancelBlockStyle = RenderStyle::clone(pseudoStyle.get()); + else + cancelBlockStyle = RenderStyle::create(); + + if (startStyle) + cancelBlockStyle->inheritFrom(startStyle); + + updateCancelButtonVisibility(cancelBlockStyle.get()); + return cancelBlockStyle.release(); +} + +void RenderTextControlSingleLine::updateCancelButtonVisibility(RenderStyle* style) const +{ + HTMLInputElement* input = static_cast<HTMLInputElement*>(node()); + style->setVisibility(input->value().isEmpty() ? HIDDEN : VISIBLE); +} + +const AtomicString& RenderTextControlSingleLine::autosaveName() const +{ + return static_cast<Element*>(node())->getAttribute(autosaveAttr); +} + +void RenderTextControlSingleLine::startSearchEventTimer() +{ + unsigned length = text().length(); + + // If there's no text, fire the event right away. + if (!length) { + stopSearchEventTimer(); + static_cast<HTMLInputElement*>(node())->onSearch(); + return; + } + + // After typing the first key, we wait 0.5 seconds. + // After the second key, 0.4 seconds, then 0.3, then 0.2 from then on. + m_searchEventTimer.startOneShot(max(0.2, 0.6 - 0.1 * length)); +} + +void RenderTextControlSingleLine::searchEventTimerFired(Timer<RenderTextControlSingleLine>*) +{ + static_cast<HTMLInputElement*>(node())->onSearch(); +} + +// PopupMenuClient methods +void RenderTextControlSingleLine::valueChanged(unsigned listIndex, bool fireEvents) +{ + ASSERT(static_cast<int>(listIndex) < listSize()); + HTMLInputElement* input = static_cast<HTMLInputElement*>(node()); + if (static_cast<int>(listIndex) == (listSize() - 1)) { + if (fireEvents) { + m_recentSearches.clear(); + const AtomicString& name = autosaveName(); + if (!name.isEmpty()) { + if (!m_searchPopup) + m_searchPopup = SearchPopupMenu::create(this); + m_searchPopup->saveRecentSearches(name, m_recentSearches); + } + } + } else { + input->setValue(itemText(listIndex)); + if (fireEvents) + input->onSearch(); + input->select(); + } +} + +String RenderTextControlSingleLine::itemText(unsigned listIndex) const +{ + int size = listSize(); + if (size == 1) { + ASSERT(!listIndex); + return searchMenuNoRecentSearchesText(); + } + if (!listIndex) + return searchMenuRecentSearchesText(); + if (itemIsSeparator(listIndex)) + return String(); + if (static_cast<int>(listIndex) == (size - 1)) + return searchMenuClearRecentSearchesText(); + return m_recentSearches[listIndex - 1]; +} + +bool RenderTextControlSingleLine::itemIsEnabled(unsigned listIndex) const +{ + if (!listIndex || itemIsSeparator(listIndex)) + return false; + return true; +} + +PopupMenuStyle RenderTextControlSingleLine::itemStyle(unsigned) const +{ + return menuStyle(); +} + +PopupMenuStyle RenderTextControlSingleLine::menuStyle() const +{ + return PopupMenuStyle(style()->color(), style()->backgroundColor(), style()->font(), style()->visibility() == VISIBLE); +} + +int RenderTextControlSingleLine::clientInsetLeft() const +{ + // Inset the menu by the radius of the cap on the left so that + // it only runs along the straight part of the bezel. + return height() / 2; +} + +int RenderTextControlSingleLine::clientInsetRight() const +{ + // Inset the menu by the radius of the cap on the right so that + // it only runs along the straight part of the bezel (unless it needs + // to be wider). + return height() / 2; +} + +int RenderTextControlSingleLine::clientPaddingLeft() const +{ + int padding = clientPaddingLeft(); + + if (RenderObject* resultsRenderer = m_resultsButton ? m_resultsButton->renderer() : 0) + padding += resultsRenderer->width(); + + return padding; +} + +int RenderTextControlSingleLine::clientPaddingRight() const +{ + int padding = clientPaddingRight(); + + if (RenderObject* cancelRenderer = m_cancelButton ? m_cancelButton->renderer() : 0) + padding += cancelRenderer->width(); + + return padding; +} + +int RenderTextControlSingleLine::listSize() const +{ + // If there are no recent searches, then our menu will have 1 "No recent searches" item. + if (!m_recentSearches.size()) + return 1; + // Otherwise, leave room in the menu for a header, a separator, and the "Clear recent searches" item. + return m_recentSearches.size() + 3; +} + +int RenderTextControlSingleLine::selectedIndex() const +{ + return -1; +} + +bool RenderTextControlSingleLine::itemIsSeparator(unsigned listIndex) const +{ + // The separator will be the second to last item in our list. + return static_cast<int>(listIndex) == (listSize() - 2); +} + +bool RenderTextControlSingleLine::itemIsLabel(unsigned listIndex) const +{ + return listIndex == 0; +} + +bool RenderTextControlSingleLine::itemIsSelected(unsigned) const +{ + return false; +} + +void RenderTextControlSingleLine::setTextFromItem(unsigned listIndex) +{ + static_cast<HTMLInputElement*>(node())->setValue(itemText(listIndex)); +} + +FontSelector* RenderTextControlSingleLine::fontSelector() const +{ + return document()->styleSelector()->fontSelector(); +} + +HostWindow* RenderTextControlSingleLine::hostWindow() const +{ + return document()->view()->hostWindow(); +} + +PassRefPtr<Scrollbar> RenderTextControlSingleLine::createScrollbar(ScrollbarClient* client, ScrollbarOrientation orientation, ScrollbarControlSize controlSize) +{ + RefPtr<Scrollbar> widget; + bool hasCustomScrollbarStyle = style()->hasPseudoStyle(RenderStyle::SCROLLBAR); + if (hasCustomScrollbarStyle) + widget = RenderScrollbar::createCustomScrollbar(client, orientation, this); + else + widget = Scrollbar::createNativeScrollbar(client, orientation, controlSize); + return widget.release(); +} + +} diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderTextControlSingleLine.h b/src/3rdparty/webkit/WebCore/rendering/RenderTextControlSingleLine.h new file mode 100644 index 0000000..a0d8d68 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderTextControlSingleLine.h @@ -0,0 +1,123 @@ +/* + * 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. + * + */ + +#ifndef RenderTextControlSingleLine_h +#define RenderTextControlSingleLine_h + +#include "PopupMenuClient.h" +#include "RenderTextControl.h" +#include "Timer.h" + +namespace WebCore { + +class SearchFieldCancelButtonElement; +class SearchFieldResultsButtonElement; +class SearchPopupMenu; +class TextControlInnerElement; + +class RenderTextControlSingleLine : public RenderTextControl, private PopupMenuClient { +public: + RenderTextControlSingleLine(Node*); + virtual ~RenderTextControlSingleLine(); + + virtual bool hasControlClip() const { return m_cancelButton; } + virtual bool isTextField() const { return true; } + + bool placeholderIsVisible() const { return m_placeholderVisible; } + bool placeholderShouldBeVisible() const; + void updatePlaceholderVisibility(); + + void addSearchResult(); + void stopSearchEventTimer(); + + bool popupIsVisible() const { return m_searchPopupIsVisible; } + void showPopup(); + virtual void hidePopup(); // PopupMenuClient method + + virtual void subtreeHasChanged(); + virtual void paint(PaintInfo&, int tx, int ty); + virtual void layout(); + + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); + void forwardEvent(Event*); + +private: + virtual void capsLockStateMayHaveChanged(); + + int textBlockWidth() const; + virtual int preferredContentWidth(float charWidth) const; + virtual void adjustControlHeightBasedOnLineHeight(int lineHeight); + + void createSubtreeIfNeeded(); + virtual void updateFromElement(); + virtual void cacheSelection(int start, int end); + virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + + virtual PassRefPtr<RenderStyle> createInnerTextStyle(const RenderStyle* startStyle) const; + PassRefPtr<RenderStyle> createInnerBlockStyle(const RenderStyle* startStyle) const; + PassRefPtr<RenderStyle> createResultsButtonStyle(const RenderStyle* startStyle) const; + PassRefPtr<RenderStyle> createCancelButtonStyle(const RenderStyle* startStyle) const; + + void updateCancelButtonVisibility(RenderStyle*) const; + const AtomicString& autosaveName() const; + + void startSearchEventTimer(); + void searchEventTimerFired(Timer<RenderTextControlSingleLine>*); + +private: + // PopupMenuClient methods + virtual void valueChanged(unsigned listIndex, bool fireEvents = true); + virtual String itemText(unsigned listIndex) const; + virtual bool itemIsEnabled(unsigned listIndex) const; + virtual PopupMenuStyle itemStyle(unsigned listIndex) const; + virtual PopupMenuStyle menuStyle() const; + virtual int clientInsetLeft() const; + virtual int clientInsetRight() const; + virtual int clientPaddingLeft() const; + virtual int clientPaddingRight() const; + virtual int listSize() const; + virtual int selectedIndex() const; + virtual bool itemIsSeparator(unsigned listIndex) const; + virtual bool itemIsLabel(unsigned listIndex) const; + virtual bool itemIsSelected(unsigned listIndex) const; + virtual bool shouldPopOver() const { return false; } + virtual bool valueShouldChangeOnHotTrack() const { return false; } + virtual void setTextFromItem(unsigned listIndex); + virtual FontSelector* fontSelector() const; + virtual HostWindow* hostWindow() const; + virtual PassRefPtr<Scrollbar> createScrollbar(ScrollbarClient*, ScrollbarOrientation, ScrollbarControlSize); + +private: + bool m_placeholderVisible; + bool m_searchPopupIsVisible; + bool m_shouldDrawCapsLockIndicator; + + RefPtr<TextControlInnerElement> m_innerBlock; + RefPtr<SearchFieldResultsButtonElement> m_resultsButton; + RefPtr<SearchFieldCancelButtonElement> m_cancelButton; + + Timer<RenderTextControlSingleLine> m_searchEventTimer; + RefPtr<SearchPopupMenu> m_searchPopup; + Vector<String> m_recentSearches; +}; + +} + +#endif diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderTextFragment.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderTextFragment.cpp new file mode 100644 index 0000000..c8beba0 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderTextFragment.cpp @@ -0,0 +1,87 @@ +/* + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * 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" +#include "RenderTextFragment.h" + +#include "Text.h" + +namespace WebCore { + +RenderTextFragment::RenderTextFragment(Node* node, StringImpl* str, int startOffset, int length) + : RenderText(node, str ? str->substring(startOffset, length) : 0) + , m_start(startOffset) + , m_end(length) + , m_firstLetter(0) +{ +} + +RenderTextFragment::RenderTextFragment(Node* node, StringImpl* str) + : RenderText(node, str) + , m_start(0) + , m_end(str ? str->length() : 0) + , m_contentString(str) + , m_firstLetter(0) +{ +} + +PassRefPtr<StringImpl> RenderTextFragment::originalText() const +{ + Node* e = element(); + RefPtr<StringImpl> result = (e ? static_cast<Text*>(e)->string() : contentString()); + if (result && (start() > 0 || start() < result->length())) + result = result->substring(start(), end()); + return result.release(); +} + +void RenderTextFragment::destroy() +{ + if (m_firstLetter) + m_firstLetter->destroy(); + RenderText::destroy(); +} + +void RenderTextFragment::setTextInternal(PassRefPtr<StringImpl> text) +{ + RenderText::setTextInternal(text); + if (m_firstLetter) { + ASSERT(!m_contentString); + m_firstLetter->destroy(); + m_firstLetter = 0; + m_start = 0; + m_end = textLength(); + } +} + +UChar RenderTextFragment::previousCharacter() +{ + if (start()) { + Node* e = element(); + StringImpl* original = (e ? static_cast<Text*>(e)->string() : contentString()); + if (original) + return (*original)[start() - 1]; + } + + return RenderText::previousCharacter(); +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderTextFragment.h b/src/3rdparty/webkit/WebCore/rendering/RenderTextFragment.h new file mode 100644 index 0000000..1fa509a --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderTextFragment.h @@ -0,0 +1,64 @@ +/* + * (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef RenderTextFragment_h +#define RenderTextFragment_h + +#include "RenderText.h" + +namespace WebCore { + +// Used to represent a text substring of an element, e.g., for text runs that are split because of +// first letter and that must therefore have different styles (and positions in the render tree). +// We cache offsets so that text transformations can be applied in such a way that we can recover +// the original unaltered string from our corresponding DOM node. +class RenderTextFragment : public RenderText { +public: + RenderTextFragment(Node*, StringImpl*, int startOffset, int length); + RenderTextFragment(Node*, StringImpl*); + + virtual bool isTextFragment() const { return true; } + + virtual void destroy(); + + unsigned start() const { return m_start; } + unsigned end() const { return m_end; } + + RenderObject* firstLetter() const { return m_firstLetter; } + void setFirstLetter(RenderObject* firstLetter) { m_firstLetter = firstLetter; } + + StringImpl* contentString() const { return m_contentString.get(); } + virtual PassRefPtr<StringImpl> originalText() const; + +private: + virtual void setTextInternal(PassRefPtr<StringImpl>); + virtual UChar previousCharacter(); + + unsigned m_start; + unsigned m_end; + RefPtr<StringImpl> m_contentString; + RenderObject* m_firstLetter; +}; + +} // namespace WebCore + +#endif // RenderTextFragment_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderTheme.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderTheme.cpp new file mode 100644 index 0000000..aa18407 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderTheme.cpp @@ -0,0 +1,776 @@ +/** + * This file is part of the theme implementation for form controls in WebCore. + * + * Copyright (C) 2005, 2006, 2007, 2008 Apple Computer, Inc. + * + * 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" +#include "RenderTheme.h" + +#include "CSSValueKeywords.h" +#include "Document.h" +#include "FocusController.h" +#include "FontSelector.h" +#include "Frame.h" +#include "GraphicsContext.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include "Page.h" +#include "RenderStyle.h" +#include "RenderView.h" +#include "SelectionController.h" +#include "Settings.h" + +// The methods in this file are shared by all themes on every platform. + +namespace WebCore { + +using namespace HTMLNames; + +RenderTheme::RenderTheme() +#if USE(NEW_THEME) + : m_theme(platformTheme()) +#endif +{ +} + +void RenderTheme::adjustStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e, + bool UAHasAppearance, const BorderData& border, const FillLayer& background, const Color& backgroundColor) +{ + // Force inline and table display styles to be inline-block (except for table- which is block) + ControlPart part = style->appearance(); + if (style->display() == INLINE || style->display() == INLINE_TABLE || style->display() == TABLE_ROW_GROUP || + style->display() == TABLE_HEADER_GROUP || style->display() == TABLE_FOOTER_GROUP || + style->display() == TABLE_ROW || style->display() == TABLE_COLUMN_GROUP || style->display() == TABLE_COLUMN || + style->display() == TABLE_CELL || style->display() == TABLE_CAPTION) + style->setDisplay(INLINE_BLOCK); + else if (style->display() == COMPACT || style->display() == RUN_IN || style->display() == LIST_ITEM || style->display() == TABLE) + style->setDisplay(BLOCK); + + if (UAHasAppearance && theme()->isControlStyled(style, border, background, backgroundColor)) { + if (part == MenulistPart) { + style->setAppearance(MenulistButtonPart); + part = MenulistButtonPart; + } else + style->setAppearance(NoControlPart); + } + + if (!style->hasAppearance()) + return; + + // Never support box-shadow on native controls. + style->setBoxShadow(0); + +#if USE(NEW_THEME) + switch (part) { + case CheckboxPart: + case RadioPart: + case PushButtonPart: + case SquareButtonPart: + case DefaultButtonPart: + case ButtonPart: { + // Border + LengthBox borderBox(style->borderTopWidth(), style->borderRightWidth(), style->borderBottomWidth(), style->borderLeftWidth()); + borderBox = m_theme->controlBorder(part, style->font(), borderBox, style->effectiveZoom()); + if (borderBox.top().value() != style->borderTopWidth()) { + if (borderBox.top().value()) + style->setBorderTopWidth(borderBox.top().value()); + else + style->resetBorderTop(); + } + if (borderBox.right().value() != style->borderRightWidth()) { + if (borderBox.right().value()) + style->setBorderRightWidth(borderBox.right().value()); + else + style->resetBorderRight(); + } + if (borderBox.bottom().value() != style->borderBottomWidth()) { + style->setBorderBottomWidth(borderBox.bottom().value()); + if (borderBox.bottom().value()) + style->setBorderBottomWidth(borderBox.bottom().value()); + else + style->resetBorderBottom(); + } + if (borderBox.left().value() != style->borderLeftWidth()) { + style->setBorderLeftWidth(borderBox.left().value()); + if (borderBox.left().value()) + style->setBorderLeftWidth(borderBox.left().value()); + else + style->resetBorderLeft(); + } + + // Padding + LengthBox paddingBox = m_theme->controlPadding(part, style->font(), style->paddingBox(), style->effectiveZoom()); + if (paddingBox != style->paddingBox()) + style->setPaddingBox(paddingBox); + + // Whitespace + if (m_theme->controlRequiresPreWhiteSpace(part)) + style->setWhiteSpace(PRE); + + // Width / Height + // The width and height here are affected by the zoom. + // FIXME: Check is flawed, since it doesn't take min-width/max-width into account. + LengthSize controlSize = m_theme->controlSize(part, style->font(), LengthSize(style->width(), style->height()), style->effectiveZoom()); + if (controlSize.width() != style->width()) + style->setWidth(controlSize.width()); + if (controlSize.height() != style->height()) + style->setHeight(controlSize.height()); + + // Min-Width / Min-Height + LengthSize minControlSize = m_theme->minimumControlSize(part, style->font(), style->effectiveZoom()); + if (minControlSize.width() != style->minWidth()) + style->setMinWidth(minControlSize.width()); + if (minControlSize.height() != style->minHeight()) + style->setMinHeight(minControlSize.height()); + + // Font + FontDescription controlFont = m_theme->controlFont(part, style->font(), style->effectiveZoom()); + if (controlFont != style->font().fontDescription()) { + // Reset our line-height + style->setLineHeight(RenderStyle::initialLineHeight()); + + // Now update our font. + if (style->setFontDescription(controlFont)) + style->font().update(0); + } + } + default: + break; + } +#endif + + // Call the appropriate style adjustment method based off the appearance value. + switch (style->appearance()) { +#if !USE(NEW_THEME) + case CheckboxPart: + return adjustCheckboxStyle(selector, style, e); + case RadioPart: + return adjustRadioStyle(selector, style, e); + case PushButtonPart: + case SquareButtonPart: + case DefaultButtonPart: + case ButtonPart: + return adjustButtonStyle(selector, style, e); +#endif + case TextFieldPart: + return adjustTextFieldStyle(selector, style, e); + case TextAreaPart: + return adjustTextAreaStyle(selector, style, e); + case MenulistPart: + return adjustMenuListStyle(selector, style, e); + case MenulistButtonPart: + return adjustMenuListButtonStyle(selector, style, e); + case MediaSliderPart: + case SliderHorizontalPart: + case SliderVerticalPart: + return adjustSliderTrackStyle(selector, style, e); + case SliderThumbHorizontalPart: + case SliderThumbVerticalPart: + return adjustSliderThumbStyle(selector, style, e); + case SearchFieldPart: + return adjustSearchFieldStyle(selector, style, e); + case SearchFieldCancelButtonPart: + return adjustSearchFieldCancelButtonStyle(selector, style, e); + case SearchFieldDecorationPart: + return adjustSearchFieldDecorationStyle(selector, style, e); + case SearchFieldResultsDecorationPart: + return adjustSearchFieldResultsDecorationStyle(selector, style, e); + case SearchFieldResultsButtonPart: + return adjustSearchFieldResultsButtonStyle(selector, style, e); + default: + break; + } +} + +bool RenderTheme::paint(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) +{ + // If painting is disabled, but we aren't updating control tints, then just bail. + // If we are updating control tints, just schedule a repaint if the theme supports tinting + // for that control. + if (paintInfo.context->updatingControlTints()) { + if (controlSupportsTints(o)) + o->repaint(); + return false; + } + if (paintInfo.context->paintingDisabled()) + return false; + + ControlPart part = o->style()->appearance(); + +#if USE(NEW_THEME) + switch (part) { + case CheckboxPart: + case RadioPart: + case PushButtonPart: + case SquareButtonPart: + case DefaultButtonPart: + case ButtonPart: + m_theme->paint(part, controlStatesForRenderer(o), const_cast<GraphicsContext*>(paintInfo.context), r, o->style()->effectiveZoom(), o->view()->frameView()); + return false; + default: + break; + } +#endif + + // Call the appropriate paint method based off the appearance value. + switch (part) { +#if !USE(NEW_THEME) + case CheckboxPart: + return paintCheckbox(o, paintInfo, r); + case RadioPart: + return paintRadio(o, paintInfo, r); + case PushButtonPart: + case SquareButtonPart: + case DefaultButtonPart: + case ButtonPart: + return paintButton(o, paintInfo, r); +#endif + case MenulistPart: + return paintMenuList(o, paintInfo, r); + case SliderHorizontalPart: + case SliderVerticalPart: + return paintSliderTrack(o, paintInfo, r); + case SliderThumbHorizontalPart: + case SliderThumbVerticalPart: + if (o->parent()->isSlider()) + return paintSliderThumb(o, paintInfo, r); + // We don't support drawing a slider thumb without a parent slider + break; + case MediaFullscreenButtonPart: + return paintMediaFullscreenButton(o, paintInfo, r); + case MediaPlayButtonPart: + return paintMediaPlayButton(o, paintInfo, r); + case MediaMuteButtonPart: + return paintMediaMuteButton(o, paintInfo, r); + case MediaSeekBackButtonPart: + return paintMediaSeekBackButton(o, paintInfo, r); + case MediaSeekForwardButtonPart: + return paintMediaSeekForwardButton(o, paintInfo, r); + case MediaSliderPart: + return paintMediaSliderTrack(o, paintInfo, r); + case MediaSliderThumbPart: + if (o->parent()->isSlider()) + return paintMediaSliderThumb(o, paintInfo, r); + break; + case MenulistButtonPart: + case TextFieldPart: + case TextAreaPart: + case ListboxPart: + return true; + case SearchFieldPart: + return paintSearchField(o, paintInfo, r); + case SearchFieldCancelButtonPart: + return paintSearchFieldCancelButton(o, paintInfo, r); + case SearchFieldDecorationPart: + return paintSearchFieldDecoration(o, paintInfo, r); + case SearchFieldResultsDecorationPart: + return paintSearchFieldResultsDecoration(o, paintInfo, r); + case SearchFieldResultsButtonPart: + return paintSearchFieldResultsButton(o, paintInfo, r); + default: + break; + } + + return true; // We don't support the appearance, so let the normal background/border paint. +} + +bool RenderTheme::paintBorderOnly(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) +{ + if (paintInfo.context->paintingDisabled()) + return false; + + // Call the appropriate paint method based off the appearance value. + switch (o->style()->appearance()) { + case TextFieldPart: + return paintTextField(o, paintInfo, r); + case ListboxPart: + case TextAreaPart: + return paintTextArea(o, paintInfo, r); + case MenulistButtonPart: + return true; + case CheckboxPart: + case RadioPart: + case PushButtonPart: + case SquareButtonPart: + case DefaultButtonPart: + case ButtonPart: + case MenulistPart: + case SliderHorizontalPart: + case SliderVerticalPart: + case SliderThumbHorizontalPart: + case SliderThumbVerticalPart: + case SearchFieldPart: + case SearchFieldCancelButtonPart: + case SearchFieldDecorationPart: + case SearchFieldResultsDecorationPart: + case SearchFieldResultsButtonPart: + default: + break; + } + + return false; +} + +bool RenderTheme::paintDecorations(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) +{ + if (paintInfo.context->paintingDisabled()) + return false; + + // Call the appropriate paint method based off the appearance value. + switch (o->style()->appearance()) { + case MenulistButtonPart: + return paintMenuListButton(o, paintInfo, r); + case TextFieldPart: + case TextAreaPart: + case ListboxPart: + case CheckboxPart: + case RadioPart: + case PushButtonPart: + case SquareButtonPart: + case DefaultButtonPart: + case ButtonPart: + case MenulistPart: + case SliderHorizontalPart: + case SliderVerticalPart: + case SliderThumbHorizontalPart: + case SliderThumbVerticalPart: + case SearchFieldPart: + case SearchFieldCancelButtonPart: + case SearchFieldDecorationPart: + case SearchFieldResultsDecorationPart: + case SearchFieldResultsButtonPart: + default: + break; + } + + return false; +} + +#if ENABLE(VIDEO) +bool RenderTheme::hitTestMediaControlPart(RenderObject* o, const IntPoint& absPoint) +{ + FloatPoint localPoint = o->absoluteToLocal(absPoint, false, true); // respect transforms + + return o->borderBox().contains(roundedIntPoint(localPoint)); +} +#endif + +Color RenderTheme::activeSelectionBackgroundColor() const +{ + if (!m_activeSelectionColor.isValid()) + m_activeSelectionColor = platformActiveSelectionBackgroundColor().blendWithWhite(); + return m_activeSelectionColor; +} + +Color RenderTheme::inactiveSelectionBackgroundColor() const +{ + if (!m_inactiveSelectionColor.isValid()) + m_inactiveSelectionColor = platformInactiveSelectionBackgroundColor().blendWithWhite(); + return m_inactiveSelectionColor; +} + +Color RenderTheme::platformActiveSelectionBackgroundColor() const +{ + // Use a blue color by default if the platform theme doesn't define anything. + return Color(0, 0, 255); +} + +Color RenderTheme::platformInactiveSelectionBackgroundColor() const +{ + // Use a grey color by default if the platform theme doesn't define anything. + return Color(128, 128, 128); +} + +Color RenderTheme::platformActiveSelectionForegroundColor() const +{ + return Color(); +} + +Color RenderTheme::platformInactiveSelectionForegroundColor() const +{ + return Color(); +} + +Color RenderTheme::activeListBoxSelectionBackgroundColor() const +{ + return activeSelectionBackgroundColor(); +} + +Color RenderTheme::activeListBoxSelectionForegroundColor() const +{ + // Use a white color by default if the platform theme doesn't define anything. + return Color(255, 255, 255); +} + +Color RenderTheme::inactiveListBoxSelectionBackgroundColor() const +{ + return inactiveSelectionBackgroundColor(); +} + +Color RenderTheme::inactiveListBoxSelectionForegroundColor() const +{ + // Use a black color by default if the platform theme doesn't define anything. + return Color(0, 0, 0); +} + +int RenderTheme::baselinePosition(const RenderObject* o) const +{ +#if USE(NEW_THEME) + return o->height() + o->marginTop() + m_theme->baselinePositionAdjustment(o->style()->appearance()) * o->style()->effectiveZoom(); +#else + return o->height() + o->marginTop(); +#endif +} + +bool RenderTheme::isControlContainer(ControlPart appearance) const +{ + // There are more leaves than this, but we'll patch this function as we add support for + // more controls. + return appearance != CheckboxPart && appearance != RadioPart; +} + +bool RenderTheme::isControlStyled(const RenderStyle* style, const BorderData& border, const FillLayer& background, + const Color& backgroundColor) const +{ + switch (style->appearance()) { + case PushButtonPart: + case SquareButtonPart: + case DefaultButtonPart: + case ButtonPart: + case ListboxPart: + case MenulistPart: + // FIXME: Uncomment this when making search fields style-able. + // case SearchFieldPart: + case TextFieldPart: + case TextAreaPart: + // Test the style to see if the UA border and background match. + return (style->border() != border || + *style->backgroundLayers() != background || + style->backgroundColor() != backgroundColor); + default: + return false; + } +} + +void RenderTheme::adjustRepaintRect(const RenderObject* o, IntRect& r) +{ +#if USE(NEW_THEME) + m_theme->inflateControlPaintRect(o->style()->appearance(), controlStatesForRenderer(o), r, o->style()->effectiveZoom()); +#endif +} + +bool RenderTheme::supportsFocusRing(const RenderStyle* style) const +{ + return (style->hasAppearance() && style->appearance() != TextFieldPart && style->appearance() != TextAreaPart && style->appearance() != MenulistButtonPart && style->appearance() != ListboxPart); +} + +bool RenderTheme::stateChanged(RenderObject* o, ControlState state) const +{ + // Default implementation assumes the controls dont respond to changes in :hover state + if (state == HoverState && !supportsHover(o->style())) + return false; + + // Assume pressed state is only responded to if the control is enabled. + if (state == PressedState && !isEnabled(o)) + return false; + + // Repaint the control. + o->repaint(); + return true; +} + +ControlStates RenderTheme::controlStatesForRenderer(const RenderObject* o) const +{ + ControlStates result = 0; + if (isHovered(o)) + result |= HoverState; + if (isPressed(o)) + result |= PressedState; + if (isFocused(o) && o->style()->outlineStyleIsAuto()) + result |= FocusState; + if (isEnabled(o)) + result |= EnabledState; + if (isChecked(o)) + result |= CheckedState; + if (isReadOnlyControl(o)) + result |= ReadOnlyState; + if (isDefault(o)) + result |= DefaultState; + if (!isActive(o)) + result |= WindowInactiveState; + if (isIndeterminate(o)) + result |= IndeterminateState; + return result; +} + +bool RenderTheme::isActive(const RenderObject* o) const +{ + Node* node = o->element(); + if (!node) + return false; + + Frame* frame = node->document()->frame(); + if (!frame) + return false; + + Page* page = frame->page(); + if (!page) + return false; + + return page->focusController()->isActive(); +} + +bool RenderTheme::isChecked(const RenderObject* o) const +{ + if (!o->element()) + return false; + return o->element()->isChecked(); +} + +bool RenderTheme::isIndeterminate(const RenderObject* o) const +{ + if (!o->element()) + return false; + return o->element()->isIndeterminate(); +} + +bool RenderTheme::isEnabled(const RenderObject* o) const +{ + if (!o->element()) + return true; + return o->element()->isEnabled(); +} + +bool RenderTheme::isFocused(const RenderObject* o) const +{ + Node* node = o->element(); + if (!node) + return false; + Document* document = node->document(); + Frame* frame = document->frame(); + return node == document->focusedNode() && frame && frame->selection()->isFocusedAndActive(); +} + +bool RenderTheme::isPressed(const RenderObject* o) const +{ + if (!o->element()) + return false; + return o->element()->active(); +} + +bool RenderTheme::isReadOnlyControl(const RenderObject* o) const +{ + if (!o->element()) + return false; + return o->element()->isReadOnlyControl(); +} + +bool RenderTheme::isHovered(const RenderObject* o) const +{ + if (!o->element()) + return false; + return o->element()->hovered(); +} + +bool RenderTheme::isDefault(const RenderObject* o) const +{ + if (!o->document()) + return false; + + Settings* settings = o->document()->settings(); + if (!settings || !settings->inApplicationChromeMode()) + return false; + + return o->style()->appearance() == DefaultButtonPart; +} + +#if !USE(NEW_THEME) + +void RenderTheme::adjustCheckboxStyle(CSSStyleSelector*, RenderStyle* style, Element*) const +{ + // A summary of the rules for checkbox designed to match WinIE: + // width/height - honored (WinIE actually scales its control for small widths, but lets it overflow for small heights.) + // font-size - not honored (control has no text), but we use it to decide which control size to use. + setCheckboxSize(style); + + // padding - not honored by WinIE, needs to be removed. + style->resetPadding(); + + // border - honored by WinIE, but looks terrible (just paints in the control box and turns off the Windows XP theme) + // for now, we will not honor it. + style->resetBorder(); + + style->setBoxShadow(0); +} + +void RenderTheme::adjustRadioStyle(CSSStyleSelector*, RenderStyle* style, Element*) const +{ + // A summary of the rules for checkbox designed to match WinIE: + // width/height - honored (WinIE actually scales its control for small widths, but lets it overflow for small heights.) + // font-size - not honored (control has no text), but we use it to decide which control size to use. + setRadioSize(style); + + // padding - not honored by WinIE, needs to be removed. + style->resetPadding(); + + // border - honored by WinIE, but looks terrible (just paints in the control box and turns off the Windows XP theme) + // for now, we will not honor it. + style->resetBorder(); + + style->setBoxShadow(0); +} + +void RenderTheme::adjustButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const +{ + // Most platforms will completely honor all CSS, and so we have no need to adjust the style + // at all by default. We will still allow the theme a crack at setting up a desired vertical size. + setButtonSize(style); +} + +#endif + +void RenderTheme::adjustTextFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const +{ +} + +void RenderTheme::adjustTextAreaStyle(CSSStyleSelector*, RenderStyle*, Element*) const +{ +} + +void RenderTheme::adjustMenuListStyle(CSSStyleSelector*, RenderStyle*, Element*) const +{ +} + +void RenderTheme::adjustMenuListButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const +{ +} + +void RenderTheme::adjustButtonInnerStyle(RenderStyle*) const +{ +} + +void RenderTheme::adjustSliderTrackStyle(CSSStyleSelector*, RenderStyle*, Element*) const +{ +} + +void RenderTheme::adjustSliderThumbStyle(CSSStyleSelector*, RenderStyle*, Element*) const +{ +} + +void RenderTheme::adjustSliderThumbSize(RenderObject*) const +{ +} + +void RenderTheme::adjustSearchFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const +{ +} + +void RenderTheme::adjustSearchFieldCancelButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const +{ +} + +void RenderTheme::adjustSearchFieldDecorationStyle(CSSStyleSelector*, RenderStyle*, Element*) const +{ +} + +void RenderTheme::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector*, RenderStyle*, Element*) const +{ +} + +void RenderTheme::adjustSearchFieldResultsButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const +{ +} + +void RenderTheme::platformColorsDidChange() +{ + m_activeSelectionColor = Color(); + m_inactiveSelectionColor = Color(); +} + +Color RenderTheme::systemColor(int cssValueId) const +{ + switch (cssValueId) { + case CSSValueActiveborder: + return 0xFFFFFFFF; + case CSSValueActivecaption: + return 0xFFCCCCCC; + case CSSValueAppworkspace: + return 0xFFFFFFFF; + case CSSValueBackground: + return 0xFF6363CE; + case CSSValueButtonface: + return 0xFFC0C0C0; + case CSSValueButtonhighlight: + return 0xFFDDDDDD; + case CSSValueButtonshadow: + return 0xFF888888; + case CSSValueButtontext: + return 0xFF000000; + case CSSValueCaptiontext: + return 0xFF000000; + case CSSValueGraytext: + return 0xFF808080; + case CSSValueHighlight: + return 0xFFB5D5FF; + case CSSValueHighlighttext: + return 0xFF000000; + case CSSValueInactiveborder: + return 0xFFFFFFFF; + case CSSValueInactivecaption: + return 0xFFFFFFFF; + case CSSValueInactivecaptiontext: + return 0xFF7F7F7F; + case CSSValueInfobackground: + return 0xFFFBFCC5; + case CSSValueInfotext: + return 0xFF000000; + case CSSValueMenu: + return 0xFFC0C0C0; + case CSSValueMenutext: + return 0xFF000000; + case CSSValueScrollbar: + return 0xFFFFFFFF; + case CSSValueText: + return 0xFF000000; + case CSSValueThreeddarkshadow: + return 0xFF666666; + case CSSValueThreedface: + return 0xFFC0C0C0; + case CSSValueThreedhighlight: + return 0xFFDDDDDD; + case CSSValueThreedlightshadow: + return 0xFFC0C0C0; + case CSSValueThreedshadow: + return 0xFF888888; + case CSSValueWindow: + return 0xFFFFFFFF; + case CSSValueWindowframe: + return 0xFFCCCCCC; + case CSSValueWindowtext: + return 0xFF000000; + } + return Color(); +} + +Color RenderTheme::platformTextSearchHighlightColor() const +{ + return Color(255, 255, 0); +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderTheme.h b/src/3rdparty/webkit/WebCore/rendering/RenderTheme.h new file mode 100644 index 0000000..f948936 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderTheme.h @@ -0,0 +1,234 @@ +/* + * This file is part of the theme implementation for form controls in WebCore. + * + * Copyright (C) 2005, 2006, 2007, 2008 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef RenderTheme_h +#define RenderTheme_h + +#include "RenderObject.h" +#if USE(NEW_THEME) +#include "Theme.h" +#else +#include "ThemeTypes.h" +#endif + +namespace WebCore { + +class Element; +class PopupMenu; +class RenderMenuList; +class CSSStyleSheet; + +class RenderTheme { +public: + RenderTheme(); + virtual ~RenderTheme() { } + + // This method is called whenever style has been computed for an element and the appearance + // property has been set to a value other than "none". The theme should map in all of the appropriate + // metrics and defaults given the contents of the style. This includes sophisticated operations like + // selection of control size based off the font, the disabling of appearance when certain other properties like + // "border" are set, or if the appearance is not supported by the theme. + void adjustStyle(CSSStyleSelector*, RenderStyle*, Element*, bool UAHasAppearance, + const BorderData&, const FillLayer&, const Color& backgroundColor); + + // This method is called to paint the widget as a background of the RenderObject. A widget's foreground, e.g., the + // text of a button, is always rendered by the engine itself. The boolean return value indicates + // whether the CSS border/background should also be painted. + bool paint(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + bool paintBorderOnly(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + bool paintDecorations(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + + // The remaining methods should be implemented by the platform-specific portion of the theme, e.g., + // RenderThemeMac.cpp for Mac OS X. + + // These methods return the theme's extra style sheets rules, to let each platform + // adjust the default CSS rules in html4.css, quirks.css, or mediaControls.css + virtual String extraDefaultStyleSheet() { return String(); } + virtual String extraQuirksStyleSheet() { return String(); } +#if ENABLE(VIDEO) + virtual String extraMediaControlsStyleSheet() { return String(); }; +#endif + + // A method to obtain the baseline position for a "leaf" control. This will only be used if a baseline + // position cannot be determined by examining child content. Checkboxes and radio buttons are examples of + // controls that need to do this. + virtual int baselinePosition(const RenderObject*) const; + + // A method for asking if a control is a container or not. Leaf controls have to have some special behavior (like + // the baseline position API above). + bool isControlContainer(ControlPart) const; + + // A method asking if the control changes its tint when the window has focus or not. + virtual bool controlSupportsTints(const RenderObject*) const { return false; } + + // Whether or not the control has been styled enough by the author to disable the native appearance. + virtual bool isControlStyled(const RenderStyle*, const BorderData&, const FillLayer&, const Color& backgroundColor) const; + + // A general method asking if any control tinting is supported at all. + virtual bool supportsControlTints() const { return false; } + + // Some controls may spill out of their containers (e.g., the check on an OS X checkbox). When these controls repaint, + // the theme needs to communicate this inflated rect to the engine so that it can invalidate the whole control. + virtual void adjustRepaintRect(const RenderObject*, IntRect&); + + // This method is called whenever a relevant state changes on a particular themed object, e.g., the mouse becomes pressed + // or a control becomes disabled. + virtual bool stateChanged(RenderObject*, ControlState) const; + + // This method is called whenever the theme changes on the system in order to flush cached resources from the + // old theme. + virtual void themeChanged() { } + + // A method asking if the theme is able to draw the focus ring. + virtual bool supportsFocusRing(const RenderStyle*) const; + + // A method asking if the theme's controls actually care about redrawing when hovered. + virtual bool supportsHover(const RenderStyle*) const { return false; } + + // The selection color. + Color activeSelectionBackgroundColor() const; + Color inactiveSelectionBackgroundColor() const; + + virtual Color platformTextSearchHighlightColor() const; + + // The platform selection color. + virtual Color platformActiveSelectionBackgroundColor() const; + virtual Color platformInactiveSelectionBackgroundColor() const; + virtual Color platformActiveSelectionForegroundColor() const; + virtual Color platformInactiveSelectionForegroundColor() const; + + // List Box selection color + virtual Color activeListBoxSelectionBackgroundColor() const; + virtual Color activeListBoxSelectionForegroundColor() const; + virtual Color inactiveListBoxSelectionBackgroundColor() const; + virtual Color inactiveListBoxSelectionForegroundColor() const; + + virtual void platformColorsDidChange(); + + virtual double caretBlinkInterval() const { return 0.5; } + + // System fonts and colors for CSS. + virtual void systemFont(int cssValueId, FontDescription&) const = 0; + virtual Color systemColor(int cssValueId) const; + + virtual int minimumMenuListSize(RenderStyle*) const { return 0; } + + virtual void adjustButtonInnerStyle(RenderStyle*) const; + virtual void adjustSliderThumbSize(RenderObject*) const; + + virtual int popupInternalPaddingLeft(RenderStyle*) const { return 0; } + virtual int popupInternalPaddingRight(RenderStyle*) const { return 0; } + virtual int popupInternalPaddingTop(RenderStyle*) const { return 0; } + virtual int popupInternalPaddingBottom(RenderStyle*) const { return 0; } + + // Method for painting the caps lock indicator + virtual bool paintCapsLockIndicator(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return 0; }; + +#if ENABLE(VIDEO) + virtual bool hitTestMediaControlPart(RenderObject*, const IntPoint& absPoint); +#endif + +protected: +#if !USE(NEW_THEME) + // Methods for each appearance value. + virtual void adjustCheckboxStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintCheckbox(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } + virtual void setCheckboxSize(RenderStyle*) const { } + + virtual void adjustRadioStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintRadio(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } + virtual void setRadioSize(RenderStyle*) const { } + + virtual void adjustButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } + virtual void setButtonSize(RenderStyle*) const { } +#endif + + virtual void adjustTextFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintTextField(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } + + virtual void adjustTextAreaStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintTextArea(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } + + virtual void adjustMenuListStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintMenuList(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } + + virtual void adjustMenuListButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintMenuListButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } + + virtual void adjustSliderTrackStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintSliderTrack(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } + + virtual void adjustSliderThumbStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintSliderThumb(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } + + virtual void adjustSearchFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintSearchField(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } + + virtual void adjustSearchFieldCancelButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintSearchFieldCancelButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } + + virtual void adjustSearchFieldDecorationStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintSearchFieldDecoration(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } + + virtual void adjustSearchFieldResultsDecorationStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintSearchFieldResultsDecoration(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } + + virtual void adjustSearchFieldResultsButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintSearchFieldResultsButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } + + virtual bool paintMediaFullscreenButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } + virtual bool paintMediaPlayButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } + virtual bool paintMediaMuteButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } + virtual bool paintMediaSeekBackButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } + virtual bool paintMediaSeekForwardButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } + virtual bool paintMediaSliderTrack(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } + virtual bool paintMediaSliderThumb(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return true; } + +public: + // Methods for state querying + ControlStates controlStatesForRenderer(const RenderObject* o) const; + bool isActive(const RenderObject*) const; + bool isChecked(const RenderObject*) const; + bool isIndeterminate(const RenderObject*) const; + bool isEnabled(const RenderObject*) const; + bool isFocused(const RenderObject*) const; + bool isPressed(const RenderObject*) const; + bool isHovered(const RenderObject*) const; + bool isReadOnlyControl(const RenderObject*) const; + bool isDefault(const RenderObject*) const; + +private: + mutable Color m_activeSelectionColor; + mutable Color m_inactiveSelectionColor; +#if USE(NEW_THEME) + Theme* m_theme; // The platform-specific theme. +#endif +}; + +// Function to obtain the theme. This is implemented in your platform-specific theme implementation to hand +// back the appropriate platform theme. +RenderTheme* theme(); + +} // namespace WebCore + +#endif // RenderTheme_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderThemeMac.h b/src/3rdparty/webkit/WebCore/rendering/RenderThemeMac.h new file mode 100644 index 0000000..a1da7ff --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderThemeMac.h @@ -0,0 +1,177 @@ +/* + * This file is part of the theme implementation for form controls in WebCore. + * + * Copyright (C) 2005 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef RenderThemeMac_h +#define RenderThemeMac_h + +#import "RenderTheme.h" +#import <wtf/HashMap.h> +#import <wtf/RetainPtr.h> + +#ifdef __OBJC__ +@class WebCoreRenderThemeNotificationObserver; +#else +class WebCoreRenderThemeNotificationObserver; +#endif + +namespace WebCore { + +class RenderStyle; + +class RenderThemeMac : public RenderTheme { +public: + RenderThemeMac(); + virtual ~RenderThemeMac(); + + // A method asking if the control changes its tint when the window has focus or not. + virtual bool controlSupportsTints(const RenderObject*) const; + + // A general method asking if any control tinting is supported at all. + virtual bool supportsControlTints() const { return true; } + + virtual void adjustRepaintRect(const RenderObject*, IntRect&); + + virtual bool isControlStyled(const RenderStyle*, const BorderData&, + const FillLayer&, const Color& backgroundColor) const; + + virtual Color platformActiveSelectionBackgroundColor() const; + virtual Color platformInactiveSelectionBackgroundColor() const; + virtual Color activeListBoxSelectionBackgroundColor() const; + + virtual void platformColorsDidChange(); + + // System fonts. + virtual void systemFont(int cssValueId, FontDescription&) const; + + virtual int minimumMenuListSize(RenderStyle*) const; + + virtual void adjustSliderThumbSize(RenderObject*) const; + + virtual int popupInternalPaddingLeft(RenderStyle*) const; + virtual int popupInternalPaddingRight(RenderStyle*) const; + virtual int popupInternalPaddingTop(RenderStyle*) const; + virtual int popupInternalPaddingBottom(RenderStyle*) const; + + virtual bool paintCapsLockIndicator(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + + virtual Color systemColor(int cssValueId) const; + +protected: + virtual bool paintTextField(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual void adjustTextFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + + virtual bool paintTextArea(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual void adjustTextAreaStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + + virtual bool paintMenuList(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual void adjustMenuListStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + + virtual bool paintMenuListButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual void adjustMenuListButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + + virtual bool paintSliderTrack(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual void adjustSliderTrackStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + + virtual bool paintSliderThumb(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual void adjustSliderThumbStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + + virtual bool paintSearchField(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual void adjustSearchFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + + virtual void adjustSearchFieldCancelButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintSearchFieldCancelButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + + virtual void adjustSearchFieldDecorationStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintSearchFieldDecoration(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + + virtual void adjustSearchFieldResultsDecorationStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintSearchFieldResultsDecoration(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + + virtual void adjustSearchFieldResultsButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintSearchFieldResultsButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + + virtual bool paintMediaFullscreenButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual bool paintMediaPlayButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual bool paintMediaMuteButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual bool paintMediaSeekBackButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual bool paintMediaSeekForwardButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual bool paintMediaSliderTrack(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual bool paintMediaSliderThumb(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + +private: + IntRect inflateRect(const IntRect&, const IntSize&, const int* margins, float zoomLevel = 1.0f) const; + + FloatRect convertToPaintingRect(const RenderObject* inputRenderer, const RenderObject* partRenderer, const FloatRect& inputRect, const IntRect& r) const; + + // Get the control size based off the font. Used by some of the controls (like buttons). + NSControlSize controlSizeForFont(RenderStyle*) const; + NSControlSize controlSizeForSystemFont(RenderStyle*) const; + void setControlSize(NSCell*, const IntSize* sizes, const IntSize& minSize, float zoomLevel = 1.0f); + void setSizeFromFont(RenderStyle*, const IntSize* sizes) const; + IntSize sizeForFont(RenderStyle*, const IntSize* sizes) const; + IntSize sizeForSystemFont(RenderStyle*, const IntSize* sizes) const; + void setFontFromControlSize(CSSStyleSelector*, RenderStyle*, NSControlSize) const; + + void updateCheckedState(NSCell*, const RenderObject*); + void updateEnabledState(NSCell*, const RenderObject*); + void updateFocusedState(NSCell*, const RenderObject*); + void updatePressedState(NSCell*, const RenderObject*); + + // Helpers for adjusting appearance and for painting + + void setPopupButtonCellState(const RenderObject*, const IntRect&); + const IntSize* popupButtonSizes() const; + const int* popupButtonMargins() const; + const int* popupButtonPadding(NSControlSize) const; + void paintMenuListButtonGradients(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + const IntSize* menuListSizes() const; + + const IntSize* searchFieldSizes() const; + const IntSize* cancelButtonSizes() const; + const IntSize* resultsButtonSizes() const; + void setSearchCellState(RenderObject*, const IntRect&); + void setSearchFieldSize(RenderStyle*) const; + + NSPopUpButtonCell* popupButton() const; + NSSearchFieldCell* search() const; + NSMenu* searchMenuTemplate() const; + NSSliderCell* sliderThumbHorizontal() const; + NSSliderCell* sliderThumbVertical() const; + +private: + mutable RetainPtr<NSPopUpButtonCell> m_popupButton; + mutable RetainPtr<NSSearchFieldCell> m_search; + mutable RetainPtr<NSMenu> m_searchMenuTemplate; + mutable RetainPtr<NSSliderCell> m_sliderThumbHorizontal; + mutable RetainPtr<NSSliderCell> m_sliderThumbVertical; + + bool m_isSliderThumbHorizontalPressed; + bool m_isSliderThumbVerticalPressed; + + mutable HashMap<int, RGBA32> m_systemColorCache; + + RetainPtr<WebCoreRenderThemeNotificationObserver> m_notificationObserver; +}; + +} // namespace WebCore + +#endif // RenderThemeMac_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderThemeSafari.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderThemeSafari.cpp new file mode 100644 index 0000000..31315bc --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderThemeSafari.cpp @@ -0,0 +1,1235 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. + * + * 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" +#include "RenderThemeSafari.h" +#include "RenderThemeWin.h" +#include "Settings.h" + +#if USE(SAFARI_THEME) + +#include "CSSValueKeywords.h" +#include "Document.h" +#include "Element.h" +#include "Frame.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "HTMLInputElement.h" +#include "HTMLMediaElement.h" +#include "HTMLNames.h" +#include "RenderSlider.h" +#include "RenderView.h" +#include "RetainPtr.h" +#include "SoftLinking.h" +#include "cssstyleselector.h" +#include <CoreGraphics/CoreGraphics.h> + +using std::min; + +// FIXME: The platform-independent code in this class should be factored out and merged with RenderThemeMac. + +namespace WebCore { + +using namespace HTMLNames; +using namespace SafariTheme; + +enum { + topMargin, + rightMargin, + bottomMargin, + leftMargin +}; + +enum { + topPadding, + rightPadding, + bottomPadding, + leftPadding +}; + +RenderTheme* theme() +{ + static RenderThemeSafari safariTheme; + static RenderThemeWin windowsTheme; + if (Settings::shouldPaintNativeControls()) + return &windowsTheme; + return &safariTheme; +} + +#if !defined(NDEBUG) && defined(USE_DEBUG_SAFARI_THEME) +SOFT_LINK_DEBUG_LIBRARY(SafariTheme) +#else +SOFT_LINK_LIBRARY(SafariTheme) +#endif + +SOFT_LINK(SafariTheme, paintThemePart, void, __stdcall, (ThemePart part, CGContextRef context, const CGRect& rect, NSControlSize size, ThemeControlState state), (part, context, rect, size, state)) +#if defined(SAFARI_THEME_VERSION) && SAFARI_THEME_VERSION >= 2 +SOFT_LINK(SafariTheme, STPaintProgressIndicator, void, APIENTRY, (ProgressIndicatorType type, CGContextRef context, const CGRect& rect, NSControlSize size, ThemeControlState state, float value), (type, context, rect, size, state, value)) +#endif + +ThemeControlState RenderThemeSafari::determineState(RenderObject* o) const +{ + ThemeControlState result = 0; + if (isActive(o)) + result |= SafariTheme::ActiveState; + if (isEnabled(o) && !isReadOnlyControl(o)) + result |= SafariTheme::EnabledState; + if (isPressed(o)) + result |= SafariTheme::PressedState; + if (isChecked(o)) + result |= SafariTheme::CheckedState; + if (isIndeterminate(o)) + result |= SafariTheme::IndeterminateCheckedState; + if (isFocused(o)) + result |= SafariTheme::FocusedState; + if (isDefault(o)) + result |= SafariTheme::DefaultState; + return result; +} + +static NSControlSize controlSizeFromRect(const IntRect& rect, const IntSize sizes[]) +{ + if (sizes[NSRegularControlSize].height() == rect.height()) + return NSRegularControlSize; + else if (sizes[NSMiniControlSize].height() == rect.height()) + return NSMiniControlSize; + + return NSSmallControlSize; +} + +RenderThemeSafari::RenderThemeSafari() +{ +} + +RenderThemeSafari::~RenderThemeSafari() +{ +} + +Color RenderThemeSafari::platformActiveSelectionBackgroundColor() const +{ + return Color(181, 213, 255); +} + +Color RenderThemeSafari::platformInactiveSelectionBackgroundColor() const +{ + return Color(212, 212, 212); +} + +Color RenderThemeSafari::activeListBoxSelectionBackgroundColor() const +{ + // FIXME: This should probably just be a darker version of the platformActiveSelectionBackgroundColor + return Color(56, 117, 215); +} + +static float systemFontSizeForControlSize(NSControlSize controlSize) +{ + static float sizes[] = { 13.0f, 11.0f, 9.0f }; + + return sizes[controlSize]; +} + +void RenderThemeSafari::systemFont(int propId, FontDescription& fontDescription) const +{ + static FontDescription systemFont; + static FontDescription smallSystemFont; + static FontDescription menuFont; + static FontDescription labelFont; + static FontDescription miniControlFont; + static FontDescription smallControlFont; + static FontDescription controlFont; + + FontDescription* cachedDesc; + float fontSize = 0; + switch (propId) { + case CSSValueSmallCaption: + cachedDesc = &smallSystemFont; + if (!smallSystemFont.isAbsoluteSize()) + fontSize = systemFontSizeForControlSize(NSSmallControlSize); + break; + case CSSValueMenu: + cachedDesc = &menuFont; + if (!menuFont.isAbsoluteSize()) + fontSize = systemFontSizeForControlSize(NSRegularControlSize); + break; + case CSSValueStatusBar: + cachedDesc = &labelFont; + if (!labelFont.isAbsoluteSize()) + fontSize = 10.0f; + break; + case CSSValueWebkitMiniControl: + cachedDesc = &miniControlFont; + if (!miniControlFont.isAbsoluteSize()) + fontSize = systemFontSizeForControlSize(NSMiniControlSize); + break; + case CSSValueWebkitSmallControl: + cachedDesc = &smallControlFont; + if (!smallControlFont.isAbsoluteSize()) + fontSize = systemFontSizeForControlSize(NSSmallControlSize); + break; + case CSSValueWebkitControl: + cachedDesc = &controlFont; + if (!controlFont.isAbsoluteSize()) + fontSize = systemFontSizeForControlSize(NSRegularControlSize); + break; + default: + cachedDesc = &systemFont; + if (!systemFont.isAbsoluteSize()) + fontSize = 13.0f; + } + + if (fontSize) { + cachedDesc->setIsAbsoluteSize(true); + cachedDesc->setGenericFamily(FontDescription::NoFamily); + cachedDesc->firstFamily().setFamily("Lucida Grande"); + cachedDesc->setSpecifiedSize(fontSize); + cachedDesc->setWeight(FontWeightNormal); + cachedDesc->setItalic(false); + } + fontDescription = *cachedDesc; +} + +bool RenderThemeSafari::isControlStyled(const RenderStyle* style, const BorderData& border, + const FillLayer& background, const Color& backgroundColor) const +{ + // If we didn't find SafariTheme.dll we won't be able to paint any themed controls. + if (!SafariThemeLibrary()) + return true; + + if (style->appearance() == TextFieldPart || style->appearance() == TextAreaPart || style->appearance() == ListboxPart) + return style->border() != border; + return RenderTheme::isControlStyled(style, border, background, backgroundColor); +} + +void RenderThemeSafari::adjustRepaintRect(const RenderObject* o, IntRect& r) +{ + NSControlSize controlSize = controlSizeForFont(o->style()); + + switch (o->style()->appearance()) { + case CheckboxPart: { + // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox + // shadow" and the check. We don't consider this part of the bounds of the control in WebKit. + r = inflateRect(r, checkboxSizes()[controlSize], checkboxMargins(controlSize)); + break; + } + case RadioPart: { + // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox + // shadow" and the check. We don't consider this part of the bounds of the control in WebKit. + r = inflateRect(r, radioSizes()[controlSize], radioMargins(controlSize)); + break; + } + case PushButtonPart: + case DefaultButtonPart: + case ButtonPart: { + // We inflate the rect as needed to account for padding included in the cell to accommodate the checkbox + // shadow" and the check. We don't consider this part of the bounds of the control in WebKit. + if (r.height() <= buttonSizes()[NSRegularControlSize].height()) + r = inflateRect(r, buttonSizes()[controlSize], buttonMargins(controlSize)); + break; + } + case MenulistPart: { + r = inflateRect(r, popupButtonSizes()[controlSize], popupButtonMargins(controlSize)); + break; + } + default: + break; + } +} + +IntRect RenderThemeSafari::inflateRect(const IntRect& r, const IntSize& size, const int* margins) const +{ + // Only do the inflation if the available width/height are too small. Otherwise try to + // fit the glow/check space into the available box's width/height. + int widthDelta = r.width() - (size.width() + margins[leftMargin] + margins[rightMargin]); + int heightDelta = r.height() - (size.height() + margins[topMargin] + margins[bottomMargin]); + IntRect result(r); + if (widthDelta < 0) { + result.setX(result.x() - margins[leftMargin]); + result.setWidth(result.width() - widthDelta); + } + if (heightDelta < 0) { + result.setY(result.y() - margins[topMargin]); + result.setHeight(result.height() - heightDelta); + } + return result; +} + +int RenderThemeSafari::baselinePosition(const RenderObject* o) const +{ + if (o->style()->appearance() == CheckboxPart || o->style()->appearance() == RadioPart) + return o->marginTop() + o->height() - 2; // The baseline is 2px up from the bottom of the checkbox/radio in AppKit. + return RenderTheme::baselinePosition(o); +} + +bool RenderThemeSafari::controlSupportsTints(const RenderObject* o) const +{ + if (!isEnabled(o)) + return false; + + // Checkboxes only have tint when checked. + if (o->style()->appearance() == CheckboxPart) + return isChecked(o); + + // For now assume other controls have tint if enabled. + return true; +} + +NSControlSize RenderThemeSafari::controlSizeForFont(RenderStyle* style) const +{ + int fontSize = style->fontSize(); + if (fontSize >= 16) + return NSRegularControlSize; + if (fontSize >= 11) + return NSSmallControlSize; + return NSMiniControlSize; +} +/* +void RenderThemeSafari::setControlSize(NSCell* cell, const IntSize* sizes, const IntSize& minSize) +{ + NSControlSize size; + if (minSize.width() >= sizes[NSRegularControlSize].width() && + minSize.height() >= sizes[NSRegularControlSize].height()) + size = NSRegularControlSize; + else if (minSize.width() >= sizes[NSSmallControlSize].width() && + minSize.height() >= sizes[NSSmallControlSize].height()) + size = NSSmallControlSize; + else + size = NSMiniControlSize; + if (size != [cell controlSize]) // Only update if we have to, since AppKit does work even if the size is the same. + [cell setControlSize:size]; +} +*/ +IntSize RenderThemeSafari::sizeForFont(RenderStyle* style, const IntSize* sizes) const +{ + return sizes[controlSizeForFont(style)]; +} + +IntSize RenderThemeSafari::sizeForSystemFont(RenderStyle* style, const IntSize* sizes) const +{ + return sizes[controlSizeForSystemFont(style)]; +} + +void RenderThemeSafari::setSizeFromFont(RenderStyle* style, const IntSize* sizes) const +{ + // FIXME: Check is flawed, since it doesn't take min-width/max-width into account. + IntSize size = sizeForFont(style, sizes); + if (style->width().isIntrinsicOrAuto() && size.width() > 0) + style->setWidth(Length(size.width(), Fixed)); + if (style->height().isAuto() && size.height() > 0) + style->setHeight(Length(size.height(), Fixed)); +} + +void RenderThemeSafari::setFontFromControlSize(CSSStyleSelector* selector, RenderStyle* style, NSControlSize controlSize) const +{ + FontDescription fontDescription; + fontDescription.setIsAbsoluteSize(true); + fontDescription.setGenericFamily(FontDescription::SerifFamily); + + float fontSize = systemFontSizeForControlSize(controlSize); + fontDescription.firstFamily().setFamily("Lucida Grande"); + fontDescription.setComputedSize(fontSize); + fontDescription.setSpecifiedSize(fontSize); + + // Reset line height + style->setLineHeight(RenderStyle::initialLineHeight()); + + if (style->setFontDescription(fontDescription)) + style->font().update(selector->fontSelector()); +} + +NSControlSize RenderThemeSafari::controlSizeForSystemFont(RenderStyle* style) const +{ + int fontSize = style->fontSize(); + if (fontSize >= 13) + return NSRegularControlSize; + if (fontSize >= 11) + return NSSmallControlSize; + return NSMiniControlSize; +} + +bool RenderThemeSafari::paintCheckbox(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) +{ + ASSERT(SafariThemeLibrary()); + + NSControlSize controlSize = controlSizeForFont(o->style()); + + IntRect inflatedRect = inflateRect(r, checkboxSizes()[controlSize], checkboxMargins(controlSize)); + paintThemePart(SafariTheme::CheckboxPart, paintInfo.context->platformContext(), inflatedRect, controlSize, determineState(o)); + + return false; +} + +const IntSize* RenderThemeSafari::checkboxSizes() const +{ + static const IntSize sizes[3] = { IntSize(14, 14), IntSize(12, 12), IntSize(10, 10) }; + return sizes; +} + +const int* RenderThemeSafari::checkboxMargins(NSControlSize controlSize) const +{ + static const int margins[3][4] = + { + { 2, 2, 2, 2 }, + { 2, 2, 2, 1 }, + { 1, 0, 0, 0 }, + }; + return margins[controlSize]; +} + +void RenderThemeSafari::setCheckboxSize(RenderStyle* style) const +{ + // If the width and height are both specified, then we have nothing to do. + if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) + return; + + // Use the font size to determine the intrinsic width of the control. + setSizeFromFont(style, checkboxSizes()); +} + +bool RenderThemeSafari::paintRadio(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) +{ + ASSERT(SafariThemeLibrary()); + + NSControlSize controlSize = controlSizeForFont(o->style()); + + IntRect inflatedRect = inflateRect(r, radioSizes()[controlSize], radioMargins(controlSize)); + paintThemePart(RadioButtonPart, paintInfo.context->platformContext(), inflatedRect, controlSize, determineState(o)); + + return false; +} + +const IntSize* RenderThemeSafari::radioSizes() const +{ + static const IntSize sizes[3] = { IntSize(14, 15), IntSize(12, 13), IntSize(10, 10) }; + return sizes; +} + +const int* RenderThemeSafari::radioMargins(NSControlSize controlSize) const +{ + static const int margins[3][4] = + { + { 1, 2, 2, 2 }, + { 0, 1, 2, 1 }, + { 0, 0, 1, 0 }, + }; + return margins[controlSize]; +} + +void RenderThemeSafari::setRadioSize(RenderStyle* style) const +{ + // If the width and height are both specified, then we have nothing to do. + if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) + return; + + // Use the font size to determine the intrinsic width of the control. + setSizeFromFont(style, radioSizes()); +} + +void RenderThemeSafari::setButtonPaddingFromControlSize(RenderStyle* style, NSControlSize size) const +{ + // Just use 8px. AppKit wants to use 11px for mini buttons, but that padding is just too large + // for real-world Web sites (creating a huge necessary minimum width for buttons whose space is + // by definition constrained, since we select mini only for small cramped environments. + // This also guarantees the HTML4 <button> will match our rendering by default, since we're using a consistent + // padding. + const int padding = 8; + style->setPaddingLeft(Length(padding, Fixed)); + style->setPaddingRight(Length(padding, Fixed)); + style->setPaddingTop(Length(0, Fixed)); + style->setPaddingBottom(Length(0, Fixed)); +} + +void RenderThemeSafari::adjustButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + // There are three appearance constants for buttons. + // (1) Push-button is the constant for the default Aqua system button. Push buttons will not scale vertically and will not allow + // custom fonts or colors. <input>s use this constant. This button will allow custom colors and font weights/variants but won't + // scale vertically. + // (2) square-button is the constant for the square button. This button will allow custom fonts and colors and will scale vertically. + // (3) Button is the constant that means "pick the best button as appropriate." <button>s use this constant. This button will + // also scale vertically and allow custom fonts and colors. It will attempt to use Aqua if possible and will make this determination + // solely on the rectangle of the control. + + // Determine our control size based off our font. + NSControlSize controlSize = controlSizeForFont(style); + + if (style->appearance() == PushButtonPart) { + // Ditch the border. + style->resetBorder(); + + // Height is locked to auto. + style->setHeight(Length(Auto)); + + // White-space is locked to pre + style->setWhiteSpace(PRE); + + // Set the button's vertical size. + setButtonSize(style); + + // Add in the padding that we'd like to use. + setButtonPaddingFromControlSize(style, controlSize); + + // Our font is locked to the appropriate system font size for the control. To clarify, we first use the CSS-specified font to figure out + // a reasonable control size, but once that control size is determined, we throw that font away and use the appropriate + // system font for the control size instead. + setFontFromControlSize(selector, style, controlSize); + } else { + // Set a min-height so that we can't get smaller than the mini button. + style->setMinHeight(Length(15, Fixed)); + + // Reset the top and bottom borders. + style->resetBorderTop(); + style->resetBorderBottom(); + } +} + +const IntSize* RenderThemeSafari::buttonSizes() const +{ + static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) }; + return sizes; +} + +const int* RenderThemeSafari::buttonMargins(NSControlSize controlSize) const +{ + static const int margins[3][4] = + { + { 4, 6, 7, 6 }, + { 4, 5, 6, 5 }, + { 0, 1, 1, 1 }, + }; + return margins[controlSize]; +} + +void RenderThemeSafari::setButtonSize(RenderStyle* style) const +{ + // If the width and height are both specified, then we have nothing to do. + if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) + return; + + // Use the font size to determine the intrinsic width of the control. + setSizeFromFont(style, buttonSizes()); +} + +bool RenderThemeSafari::paintButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) +{ + ASSERT(SafariThemeLibrary()); + + // We inflate the rect as needed to account for padding included in the cell to accommodate the button + // shadow. We don't consider this part of the bounds of the control in WebKit. + + NSControlSize controlSize = controlSizeFromRect(r, buttonSizes()); + IntRect inflatedRect = r; + + ThemePart part; + if (r.height() <= buttonSizes()[NSRegularControlSize].height()) { + // Push button + part = SafariTheme::PushButtonPart; + + IntSize size = buttonSizes()[controlSize]; + size.setWidth(r.width()); + + // Center the button within the available space. + if (inflatedRect.height() > size.height()) { + inflatedRect.setY(inflatedRect.y() + (inflatedRect.height() - size.height()) / 2); + inflatedRect.setHeight(size.height()); + } + + // Now inflate it to account for the shadow. + inflatedRect = inflateRect(inflatedRect, size, buttonMargins(controlSize)); + } else + part = SafariTheme::SquareButtonPart; + + paintThemePart(part, paintInfo.context->platformContext(), inflatedRect, controlSize, determineState(o)); + return false; +} + +bool RenderThemeSafari::paintTextField(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) +{ + ASSERT(SafariThemeLibrary()); + + paintThemePart(SafariTheme::TextFieldPart, paintInfo.context->platformContext(), r, (NSControlSize)0, determineState(o) & ~FocusedState); + return false; +} + +void RenderThemeSafari::adjustTextFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const +{ +} + +bool RenderThemeSafari::paintCapsLockIndicator(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) +{ +#if defined(SAFARI_THEME_VERSION) && SAFARI_THEME_VERSION >= 1 + ASSERT(SafariThemeLibrary()); + + if (paintInfo.context->paintingDisabled()) + return true; + + paintThemePart(CapsLockPart, paintInfo.context->platformContext(), r, (NSControlSize)0, (ThemeControlState)0); + + return false; +#else + return true; +#endif +} + +bool RenderThemeSafari::paintTextArea(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) +{ + ASSERT(SafariThemeLibrary()); + + paintThemePart(SafariTheme::TextAreaPart, paintInfo.context->platformContext(), r, (NSControlSize)0, determineState(o) & ~FocusedState); + return false; +} + +void RenderThemeSafari::adjustTextAreaStyle(CSSStyleSelector*, RenderStyle*, Element*) const +{ +} + +const int* RenderThemeSafari::popupButtonMargins(NSControlSize size) const +{ + static const int margins[3][4] = + { + { 2, 3, 3, 3 }, + { 1, 3, 3, 3 }, + { 0, 1, 0, 1 } + }; + return margins[size]; +} + +const IntSize* RenderThemeSafari::popupButtonSizes() const +{ + static const IntSize sizes[3] = { IntSize(0, 21), IntSize(0, 18), IntSize(0, 15) }; + return sizes; +} + +const int* RenderThemeSafari::popupButtonPadding(NSControlSize size) const +{ + static const int padding[3][4] = + { + { 2, 26, 3, 8 }, + { 2, 23, 3, 8 }, + { 2, 22, 3, 10 } + }; + return padding[size]; +} + +bool RenderThemeSafari::paintMenuList(RenderObject* o, const RenderObject::PaintInfo& info, const IntRect& r) +{ + ASSERT(SafariThemeLibrary()); + + NSControlSize controlSize = controlSizeFromRect(r, popupButtonSizes()); + IntRect inflatedRect = r; + IntSize size = popupButtonSizes()[controlSize]; + size.setWidth(r.width()); + + // Now inflate it to account for the shadow. + if (r.width() >= minimumMenuListSize(o->style())) + inflatedRect = inflateRect(inflatedRect, size, popupButtonMargins(controlSize)); + + paintThemePart(DropDownButtonPart, info.context->platformContext(), inflatedRect, controlSize, determineState(o)); + + return false; +} + +const float baseFontSize = 11.0f; +const float baseArrowHeight = 5.0f; +const float baseArrowWidth = 7.0f; +const int arrowPaddingLeft = 5; +const int arrowPaddingRight = 5; +const int paddingBeforeSeparator = 4; +const int baseBorderRadius = 5; +const int styledPopupPaddingLeft = 8; +const int styledPopupPaddingTop = 1; +const int styledPopupPaddingBottom = 2; + +static void TopGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData) +{ + static float dark[4] = { 1.0f, 1.0f, 1.0f, 0.4f }; + static float light[4] = { 1.0f, 1.0f, 1.0f, 0.15f }; + float a = inData[0]; + int i = 0; + for (i = 0; i < 4; i++) + outData[i] = (1.0f - a) * dark[i] + a * light[i]; +} + +static void BottomGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData) +{ + static float dark[4] = { 1.0f, 1.0f, 1.0f, 0.0f }; + static float light[4] = { 1.0f, 1.0f, 1.0f, 0.3f }; + float a = inData[0]; + int i = 0; + for (i = 0; i < 4; i++) + outData[i] = (1.0f - a) * dark[i] + a * light[i]; +} + +static void MainGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData) +{ + static float dark[4] = { 0.0f, 0.0f, 0.0f, 0.15f }; + static float light[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; + float a = inData[0]; + int i = 0; + for (i = 0; i < 4; i++) + outData[i] = (1.0f - a) * dark[i] + a * light[i]; +} + +static void TrackGradientInterpolate(void* info, const CGFloat* inData, CGFloat* outData) +{ + static float dark[4] = { 0.0f, 0.0f, 0.0f, 0.678f }; + static float light[4] = { 0.0f, 0.0f, 0.0f, 0.13f }; + float a = inData[0]; + int i = 0; + for (i = 0; i < 4; i++) + outData[i] = (1.0f - a) * dark[i] + a * light[i]; +} + +void RenderThemeSafari::paintMenuListButtonGradients(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) +{ + CGContextRef context = paintInfo.context->platformContext(); + + paintInfo.context->save(); + + int radius = o->style()->borderTopLeftRadius().width(); + + RetainPtr<CGColorSpaceRef> cspace(AdoptCF, CGColorSpaceCreateDeviceRGB()); + + FloatRect topGradient(r.x(), r.y(), r.width(), r.height() / 2.0f); + struct CGFunctionCallbacks topCallbacks = { 0, TopGradientInterpolate, NULL }; + RetainPtr<CGFunctionRef> topFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &topCallbacks)); + RetainPtr<CGShadingRef> topShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(topGradient.x(), topGradient.y()), CGPointMake(topGradient.x(), topGradient.bottom()), topFunction.get(), false, false)); + + FloatRect bottomGradient(r.x() + radius, r.y() + r.height() / 2.0f, r.width() - 2.0f * radius, r.height() / 2.0f); + struct CGFunctionCallbacks bottomCallbacks = { 0, BottomGradientInterpolate, NULL }; + RetainPtr<CGFunctionRef> bottomFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &bottomCallbacks)); + RetainPtr<CGShadingRef> bottomShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(bottomGradient.x(), bottomGradient.y()), CGPointMake(bottomGradient.x(), bottomGradient.bottom()), bottomFunction.get(), false, false)); + + struct CGFunctionCallbacks mainCallbacks = { 0, MainGradientInterpolate, NULL }; + RetainPtr<CGFunctionRef> mainFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks)); + RetainPtr<CGShadingRef> mainShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(r.x(), r.y()), CGPointMake(r.x(), r.bottom()), mainFunction.get(), false, false)); + + RetainPtr<CGShadingRef> leftShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(r.x(), r.y()), CGPointMake(r.x() + radius, r.y()), mainFunction.get(), false, false)); + + RetainPtr<CGShadingRef> rightShading(AdoptCF, CGShadingCreateAxial(cspace.get(), CGPointMake(r.right(), r.y()), CGPointMake(r.right() - radius, r.y()), mainFunction.get(), false, false)); + paintInfo.context->save(); + CGContextClipToRect(context, r); + paintInfo.context->addRoundedRectClip(r, + o->style()->borderTopLeftRadius(), o->style()->borderTopRightRadius(), + o->style()->borderBottomLeftRadius(), o->style()->borderBottomRightRadius()); + CGContextDrawShading(context, mainShading.get()); + paintInfo.context->restore(); + + paintInfo.context->save(); + CGContextClipToRect(context, topGradient); + paintInfo.context->addRoundedRectClip(enclosingIntRect(topGradient), + o->style()->borderTopLeftRadius(), o->style()->borderTopRightRadius(), + IntSize(), IntSize()); + CGContextDrawShading(context, topShading.get()); + paintInfo.context->restore(); + + paintInfo.context->save(); + CGContextClipToRect(context, bottomGradient); + paintInfo.context->addRoundedRectClip(enclosingIntRect(bottomGradient), + IntSize(), IntSize(), + o->style()->borderBottomLeftRadius(), o->style()->borderBottomRightRadius()); + CGContextDrawShading(context, bottomShading.get()); + paintInfo.context->restore(); + + paintInfo.context->save(); + CGContextClipToRect(context, r); + paintInfo.context->addRoundedRectClip(r, + o->style()->borderTopLeftRadius(), o->style()->borderTopRightRadius(), + o->style()->borderBottomLeftRadius(), o->style()->borderBottomRightRadius()); + CGContextDrawShading(context, leftShading.get()); + CGContextDrawShading(context, rightShading.get()); + paintInfo.context->restore(); + + paintInfo.context->restore(); +} + +bool RenderThemeSafari::paintMenuListButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) +{ + paintInfo.context->save(); + + IntRect bounds = IntRect(r.x() + o->style()->borderLeftWidth(), + r.y() + o->style()->borderTopWidth(), + r.width() - o->style()->borderLeftWidth() - o->style()->borderRightWidth(), + r.height() - o->style()->borderTopWidth() - o->style()->borderBottomWidth()); + // Draw the gradients to give the styled popup menu a button appearance + paintMenuListButtonGradients(o, paintInfo, bounds); + + // Since we actually know the size of the control here, we restrict the font scale to make sure the arrow will fit vertically in the bounds + float fontScale = min(o->style()->fontSize() / baseFontSize, bounds.height() / baseArrowHeight); + float centerY = bounds.y() + bounds.height() / 2.0f; + float arrowHeight = baseArrowHeight * fontScale; + float arrowWidth = baseArrowWidth * fontScale; + float leftEdge = bounds.right() - arrowPaddingRight - arrowWidth; + + if (bounds.width() < arrowWidth + arrowPaddingLeft) + return false; + + paintInfo.context->setFillColor(o->style()->color()); + paintInfo.context->setStrokeColor(NoStroke); + + FloatPoint arrow[3]; + arrow[0] = FloatPoint(leftEdge, centerY - arrowHeight / 2.0f); + arrow[1] = FloatPoint(leftEdge + arrowWidth, centerY - arrowHeight / 2.0f); + arrow[2] = FloatPoint(leftEdge + arrowWidth / 2.0f, centerY + arrowHeight / 2.0f); + + // Draw the arrow + paintInfo.context->drawConvexPolygon(3, arrow, true); + + Color leftSeparatorColor(0, 0, 0, 40); + Color rightSeparatorColor(255, 255, 255, 40); + + // FIXME: Should the separator thickness and space be scaled up by fontScale? + int separatorSpace = 2; + int leftEdgeOfSeparator = static_cast<int>(leftEdge - arrowPaddingLeft); // FIXME: Round? + + // Draw the separator to the left of the arrows + paintInfo.context->setStrokeThickness(1.0f); + paintInfo.context->setStrokeStyle(SolidStroke); + paintInfo.context->setStrokeColor(leftSeparatorColor); + paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator, bounds.y()), + IntPoint(leftEdgeOfSeparator, bounds.bottom())); + + paintInfo.context->setStrokeColor(rightSeparatorColor); + paintInfo.context->drawLine(IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.y()), + IntPoint(leftEdgeOfSeparator + separatorSpace, bounds.bottom())); + + paintInfo.context->restore(); + return false; +} + +void RenderThemeSafari::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + NSControlSize controlSize = controlSizeForFont(style); + + style->resetBorder(); + style->resetPadding(); + + // Height is locked to auto. + style->setHeight(Length(Auto)); + + // White-space is locked to pre + style->setWhiteSpace(PRE); + + // Set the foreground color to black or gray when we have the aqua look. + // Cast to RGB32 is to work around a compiler bug. + style->setColor(e->isEnabled() ? static_cast<RGBA32>(Color::black) : Color::darkGray); + + // Set the button's vertical size. + setButtonSize(style); + + // Our font is locked to the appropriate system font size for the control. To clarify, we first use the CSS-specified font to figure out + // a reasonable control size, but once that control size is determined, we throw that font away and use the appropriate + // system font for the control size instead. + setFontFromControlSize(selector, style, controlSize); +} + +int RenderThemeSafari::popupInternalPaddingLeft(RenderStyle* style) const +{ + if (style->appearance() == MenulistPart) + return popupButtonPadding(controlSizeForFont(style))[leftPadding]; + if (style->appearance() == MenulistButtonPart) + return styledPopupPaddingLeft; + return 0; +} + +int RenderThemeSafari::popupInternalPaddingRight(RenderStyle* style) const +{ + if (style->appearance() == MenulistPart) + return popupButtonPadding(controlSizeForFont(style))[rightPadding]; + if (style->appearance() == MenulistButtonPart) { + float fontScale = style->fontSize() / baseFontSize; + float arrowWidth = baseArrowWidth * fontScale; + return static_cast<int>(ceilf(arrowWidth + arrowPaddingLeft + arrowPaddingRight + paddingBeforeSeparator)); + } + return 0; +} + +int RenderThemeSafari::popupInternalPaddingTop(RenderStyle* style) const +{ + if (style->appearance() == MenulistPart) + return popupButtonPadding(controlSizeForFont(style))[topPadding]; + if (style->appearance() == MenulistButtonPart) + return styledPopupPaddingTop; + return 0; +} + +int RenderThemeSafari::popupInternalPaddingBottom(RenderStyle* style) const +{ + if (style->appearance() == MenulistPart) + return popupButtonPadding(controlSizeForFont(style))[bottomPadding]; + if (style->appearance() == MenulistButtonPart) + return styledPopupPaddingBottom; + return 0; +} + +void RenderThemeSafari::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + float fontScale = style->fontSize() / baseFontSize; + + style->resetPadding(); + style->setBorderRadius(IntSize(int(baseBorderRadius + fontScale - 1), int(baseBorderRadius + fontScale - 1))); // FIXME: Round up? + + const int minHeight = 15; + style->setMinHeight(Length(minHeight, Fixed)); + + style->setLineHeight(RenderStyle::initialLineHeight()); +} + +const IntSize* RenderThemeSafari::menuListSizes() const +{ + static const IntSize sizes[3] = { IntSize(9, 0), IntSize(5, 0), IntSize(0, 0) }; + return sizes; +} + +int RenderThemeSafari::minimumMenuListSize(RenderStyle* style) const +{ + return sizeForSystemFont(style, menuListSizes()).width(); +} + +const int trackWidth = 5; +const int trackRadius = 2; + +bool RenderThemeSafari::paintSliderTrack(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) +{ + IntRect bounds = r; + + if (o->style()->appearance() == SliderHorizontalPart || + o->style()->appearance() == MediaSliderPart) { + bounds.setHeight(trackWidth); + bounds.setY(r.y() + r.height() / 2 - trackWidth / 2); + } else if (o->style()->appearance() == SliderVerticalPart) { + bounds.setWidth(trackWidth); + bounds.setX(r.x() + r.width() / 2 - trackWidth / 2); + } + + CGContextRef context = paintInfo.context->platformContext(); + RetainPtr<CGColorSpaceRef> cspace(AdoptCF, CGColorSpaceCreateDeviceRGB()); + + paintInfo.context->save(); + CGContextClipToRect(context, bounds); + + struct CGFunctionCallbacks mainCallbacks = { 0, TrackGradientInterpolate, NULL }; + RetainPtr<CGFunctionRef> mainFunction(AdoptCF, CGFunctionCreate(NULL, 1, NULL, 4, NULL, &mainCallbacks)); + RetainPtr<CGShadingRef> mainShading; + if (o->style()->appearance() == SliderVerticalPart) + mainShading.adoptCF(CGShadingCreateAxial(cspace.get(), CGPointMake(bounds.x(), bounds.bottom()), CGPointMake(bounds.right(), bounds.bottom()), mainFunction.get(), false, false)); + else + mainShading.adoptCF(CGShadingCreateAxial(cspace.get(), CGPointMake(bounds.x(), bounds.y()), CGPointMake(bounds.x(), bounds.bottom()), mainFunction.get(), false, false)); + + IntSize radius(trackRadius, trackRadius); + paintInfo.context->addRoundedRectClip(bounds, + radius, radius, + radius, radius); + CGContextDrawShading(context, mainShading.get()); + paintInfo.context->restore(); + + return false; +} + +void RenderThemeSafari::adjustSliderThumbStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + style->setBoxShadow(0); +} + +const float verticalSliderHeightPadding = 0.1f; + +bool RenderThemeSafari::paintSliderThumb(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) +{ + ASSERT(SafariThemeLibrary()); + + ASSERT(o->parent()->isSlider()); + + bool pressed = static_cast<RenderSlider*>(o->parent())->inDragMode(); + ThemeControlState state = determineState(o->parent()); + state &= ~SafariTheme::PressedState; + if (pressed) + state |= SafariTheme::PressedState; + + paintThemePart(SliderThumbPart, paintInfo.context->platformContext(), r, NSSmallControlSize, state); + return false; +} + +const int sliderThumbWidth = 15; +const int sliderThumbHeight = 15; +const int mediaSliderThumbWidth = 13; +const int mediaSliderThumbHeight = 14; + +void RenderThemeSafari::adjustSliderThumbSize(RenderObject* o) const +{ + if (o->style()->appearance() == SliderThumbHorizontalPart || o->style()->appearance() == SliderThumbVerticalPart) { + o->style()->setWidth(Length(sliderThumbWidth, Fixed)); + o->style()->setHeight(Length(sliderThumbHeight, Fixed)); + } else if (o->style()->appearance() == MediaSliderThumbPart) { + o->style()->setWidth(Length(mediaSliderThumbWidth, Fixed)); + o->style()->setHeight(Length(mediaSliderThumbHeight, Fixed)); + } + +} + +bool RenderThemeSafari::paintSearchField(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) +{ + ASSERT(SafariThemeLibrary()); + + paintThemePart(SafariTheme::SearchFieldPart, paintInfo.context->platformContext(), r, controlSizeFromRect(r, searchFieldSizes()), determineState(o)); + return false; +} + +const IntSize* RenderThemeSafari::searchFieldSizes() const +{ + static const IntSize sizes[3] = { IntSize(0, 22), IntSize(0, 19), IntSize(0, 15) }; + return sizes; +} + +void RenderThemeSafari::setSearchFieldSize(RenderStyle* style) const +{ + // If the width and height are both specified, then we have nothing to do. + if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) + return; + + // Use the font size to determine the intrinsic width of the control. + setSizeFromFont(style, searchFieldSizes()); +} + +void RenderThemeSafari::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + // Override border. + style->resetBorder(); + const short borderWidth = 2; + style->setBorderLeftWidth(borderWidth); + style->setBorderLeftStyle(INSET); + style->setBorderRightWidth(borderWidth); + style->setBorderRightStyle(INSET); + style->setBorderBottomWidth(borderWidth); + style->setBorderBottomStyle(INSET); + style->setBorderTopWidth(borderWidth); + style->setBorderTopStyle(INSET); + + // Override height. + style->setHeight(Length(Auto)); + setSearchFieldSize(style); + + // Override padding size to match AppKit text positioning. + const int padding = 1; + style->setPaddingLeft(Length(padding, Fixed)); + style->setPaddingRight(Length(padding, Fixed)); + style->setPaddingTop(Length(padding, Fixed)); + style->setPaddingBottom(Length(padding, Fixed)); + + NSControlSize controlSize = controlSizeForFont(style); + setFontFromControlSize(selector, style, controlSize); +} + +bool RenderThemeSafari::paintSearchFieldCancelButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect&) +{ + ASSERT(SafariThemeLibrary()); + + Node* input = o->node()->shadowAncestorNode(); + ASSERT(input); + RenderObject* renderer = input->renderer(); + ASSERT(renderer); + + IntRect searchRect = renderer->absoluteBoundingBoxRect(); + + paintThemePart(SafariTheme::SearchFieldCancelButtonPart, paintInfo.context->platformContext(), searchRect, controlSizeFromRect(searchRect, searchFieldSizes()), determineState(o)); + return false; +} + +const IntSize* RenderThemeSafari::cancelButtonSizes() const +{ + static const IntSize sizes[3] = { IntSize(16, 13), IntSize(13, 11), IntSize(13, 9) }; + return sizes; +} + +void RenderThemeSafari::adjustSearchFieldCancelButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + IntSize size = sizeForSystemFont(style, cancelButtonSizes()); + style->setWidth(Length(size.width(), Fixed)); + style->setHeight(Length(size.height(), Fixed)); +} + +const IntSize* RenderThemeSafari::resultsButtonSizes() const +{ + static const IntSize sizes[3] = { IntSize(19, 13), IntSize(17, 11), IntSize(17, 9) }; + return sizes; +} + +const int emptyResultsOffset = 9; +void RenderThemeSafari::adjustSearchFieldDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + IntSize size = sizeForSystemFont(style, resultsButtonSizes()); + style->setWidth(Length(size.width() - emptyResultsOffset, Fixed)); + style->setHeight(Length(size.height(), Fixed)); +} + +bool RenderThemeSafari::paintSearchFieldDecoration(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) +{ + return false; +} + +void RenderThemeSafari::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + IntSize size = sizeForSystemFont(style, resultsButtonSizes()); + style->setWidth(Length(size.width(), Fixed)); + style->setHeight(Length(size.height(), Fixed)); +} + +bool RenderThemeSafari::paintSearchFieldResultsDecoration(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect&) +{ + ASSERT(SafariThemeLibrary()); + + Node* input = o->node()->shadowAncestorNode(); + ASSERT(input); + RenderObject* renderer = input->renderer(); + ASSERT(renderer); + + IntRect searchRect = renderer->absoluteBoundingBoxRect(); + + paintThemePart(SafariTheme::SearchFieldResultsDecorationPart, paintInfo.context->platformContext(), searchRect, controlSizeFromRect(searchRect, searchFieldSizes()), determineState(o)); + return false; +} + +const int resultsArrowWidth = 5; +void RenderThemeSafari::adjustSearchFieldResultsButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + IntSize size = sizeForSystemFont(style, resultsButtonSizes()); + style->setWidth(Length(size.width() + resultsArrowWidth, Fixed)); + style->setHeight(Length(size.height(), Fixed)); +} + +bool RenderThemeSafari::paintSearchFieldResultsButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect&) +{ + ASSERT(SafariThemeLibrary()); + + Node* input = o->node()->shadowAncestorNode(); + ASSERT(input); + RenderObject* renderer = input->renderer(); + ASSERT(renderer); + + IntRect searchRect = renderer->absoluteBoundingBoxRect(); + + paintThemePart(SafariTheme::SearchFieldResultsButtonPart, paintInfo.context->platformContext(), searchRect, controlSizeFromRect(searchRect, searchFieldSizes()), determineState(o)); + return false; +} +#if ENABLE(VIDEO) +bool RenderThemeSafari::paintMediaFullscreenButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) +{ +#if defined(SAFARI_THEME_VERSION) && SAFARI_THEME_VERSION >= 2 + ASSERT(SafariThemeLibrary()); + paintThemePart(SafariTheme::MediaFullscreenButtonPart, paintInfo.context->platformContext(), r, NSRegularControlSize, determineState(o)); +#endif + + return false; +} + +bool RenderThemeSafari::paintMediaMuteButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) +{ + Node* node = o->element(); + Node* mediaNode = node ? node->shadowAncestorNode() : 0; + if (!mediaNode || (!mediaNode->hasTagName(videoTag) && !mediaNode->hasTagName(audioTag))) + return false; + + HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(mediaNode); + if (!mediaElement) + return false; + +#if defined(SAFARI_THEME_VERSION) && SAFARI_THEME_VERSION >= 2 + ASSERT(SafariThemeLibrary()); + paintThemePart(mediaElement->muted() ? SafariTheme::MediaUnMuteButtonPart : SafariTheme::MediaMuteButtonPart, paintInfo.context->platformContext(), r, NSRegularControlSize, determineState(o)); +#endif + + return false; +} + +bool RenderThemeSafari::paintMediaPlayButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) +{ + Node* node = o->element(); + Node* mediaNode = node ? node->shadowAncestorNode() : 0; + if (!mediaNode || (!mediaNode->hasTagName(videoTag) && !mediaNode->hasTagName(audioTag))) + return false; + + HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(mediaNode); + if (!mediaElement) + return false; + +#if defined(SAFARI_THEME_VERSION) && SAFARI_THEME_VERSION >= 2 + ASSERT(SafariThemeLibrary()); + paintThemePart(mediaElement->canPlay() ? SafariTheme::MediaPlayButtonPart : SafariTheme::MediaPauseButtonPart, paintInfo.context->platformContext(), r, NSRegularControlSize, determineState(o)); +#endif + + return false; +} + +bool RenderThemeSafari::paintMediaSeekBackButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) +{ +#if defined(SAFARI_THEME_VERSION) && SAFARI_THEME_VERSION >= 2 + ASSERT(SafariThemeLibrary()); + paintThemePart(SafariTheme::MediaSeekBackButtonPart, paintInfo.context->platformContext(), r, NSRegularControlSize, determineState(o)); +#endif + + return false; +} + +bool RenderThemeSafari::paintMediaSeekForwardButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) +{ +#if defined(SAFARI_THEME_VERSION) && SAFARI_THEME_VERSION >= 2 + ASSERT(SafariThemeLibrary()); + paintThemePart(SafariTheme::MediaSeekForwardButtonPart, paintInfo.context->platformContext(), r, NSRegularControlSize, determineState(o)); +#endif + + return false; +} + +bool RenderThemeSafari::paintMediaSliderTrack(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) +{ + Node* node = o->element(); + Node* mediaNode = node ? node->shadowAncestorNode() : 0; + if (!mediaNode || (!mediaNode->hasTagName(videoTag) && !mediaNode->hasTagName(audioTag))) + return false; + + HTMLMediaElement* mediaElement = static_cast<HTMLMediaElement*>(mediaNode); + if (!mediaElement) + return false; + + float percentLoaded = 0; + if (MediaPlayer* player = mediaElement->player()) + if (player->duration()) + percentLoaded = player->maxTimeBuffered() / player->duration(); + +#if defined(SAFARI_THEME_VERSION) && SAFARI_THEME_VERSION >= 2 + ASSERT(SafariThemeLibrary()); + STPaintProgressIndicator(SafariTheme::MediaType, paintInfo.context->platformContext(), r, NSRegularControlSize, 0, percentLoaded); +#endif + return false; +} + +bool RenderThemeSafari::paintMediaSliderThumb(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) +{ + ASSERT(SafariThemeLibrary()); + +#if defined(SAFARI_THEME_VERSION) && SAFARI_THEME_VERSION >= 2 + paintThemePart(SafariTheme::MediaSliderThumbPart, paintInfo.context->platformContext(), r, NSRegularControlSize, determineState(o)); +#endif + + return false; +} +#endif + +} // namespace WebCore + +#endif // #if USE(SAFARI_THEME) diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderThemeSafari.h b/src/3rdparty/webkit/WebCore/rendering/RenderThemeSafari.h new file mode 100644 index 0000000..8ac5acf --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderThemeSafari.h @@ -0,0 +1,181 @@ +/* + * Copyright (C) 2007, 2008 Apple Inc. + * + * 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. + * + */ + +#ifndef RenderThemeSafari_h +#define RenderThemeSafari_h + +#if USE(SAFARI_THEME) + +#include "RenderTheme.h" + +// If you have an empty placeholder SafariThemeConstants.h, then include SafariTheme.h +// This is a workaround until a version of WebKitSupportLibrary is released with an updated SafariThemeConstants.h +#include <SafariTheme/SafariThemeConstants.h> +#ifndef SafariThemeConstants_h +#include <SafariTheme/SafariTheme.h> +#endif + +#if PLATFORM(WIN) +typedef void* HANDLE; +typedef struct HINSTANCE__* HINSTANCE; +typedef HINSTANCE HMODULE; +#endif + +namespace WebCore { + +using namespace SafariTheme; + +class RenderStyle; + +class RenderThemeSafari : public RenderTheme { +public: + RenderThemeSafari(); + virtual ~RenderThemeSafari(); + + // A method to obtain the baseline position for a "leaf" control. This will only be used if a baseline + // position cannot be determined by examining child content. Checkboxes and radio buttons are examples of + // controls that need to do this. + virtual int baselinePosition(const RenderObject*) const; + + // A method asking if the control changes its tint when the window has focus or not. + virtual bool controlSupportsTints(const RenderObject*) const; + + // A general method asking if any control tinting is supported at all. + virtual bool supportsControlTints() const { return true; } + + virtual void adjustRepaintRect(const RenderObject*, IntRect&); + + virtual bool isControlStyled(const RenderStyle*, const BorderData&, + const FillLayer&, const Color& backgroundColor) const; + + virtual Color platformActiveSelectionBackgroundColor() const; + virtual Color platformInactiveSelectionBackgroundColor() const; + virtual Color activeListBoxSelectionBackgroundColor() const; + + // System fonts. + virtual void systemFont(int propId, FontDescription&) const; + + virtual int minimumMenuListSize(RenderStyle*) const; + + virtual void adjustSliderThumbSize(RenderObject*) const; + virtual void adjustSliderThumbStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + + virtual int popupInternalPaddingLeft(RenderStyle*) const; + virtual int popupInternalPaddingRight(RenderStyle*) const; + virtual int popupInternalPaddingTop(RenderStyle*) const; + virtual int popupInternalPaddingBottom(RenderStyle*) const; + +protected: + // Methods for each appearance value. + virtual bool paintCheckbox(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual void setCheckboxSize(RenderStyle*) const; + + virtual bool paintRadio(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual void setRadioSize(RenderStyle*) const; + + virtual void adjustButtonStyle(CSSStyleSelector*, RenderStyle*, WebCore::Element*) const; + virtual bool paintButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual void setButtonSize(RenderStyle*) const; + + virtual bool paintTextField(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual void adjustTextFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + + virtual bool paintTextArea(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual void adjustTextAreaStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + + virtual bool paintMenuList(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual void adjustMenuListStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + + virtual bool paintMenuListButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual void adjustMenuListButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + + virtual bool paintSliderTrack(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual bool paintSliderThumb(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + + virtual bool paintSearchField(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual void adjustSearchFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + + virtual void adjustSearchFieldCancelButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintSearchFieldCancelButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + + virtual void adjustSearchFieldDecorationStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintSearchFieldDecoration(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + + virtual void adjustSearchFieldResultsDecorationStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintSearchFieldResultsDecoration(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + + virtual void adjustSearchFieldResultsButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintSearchFieldResultsButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + + virtual bool paintCapsLockIndicator(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + +#if ENABLE(VIDEO) + virtual bool paintMediaFullscreenButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual bool paintMediaPlayButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual bool paintMediaMuteButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual bool paintMediaSeekBackButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual bool paintMediaSeekForwardButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual bool paintMediaSliderTrack(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual bool paintMediaSliderThumb(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); +#endif + +private: + IntRect inflateRect(const IntRect&, const IntSize&, const int* margins) const; + + // Get the control size based off the font. Used by some of the controls (like buttons). + + NSControlSize controlSizeForFont(RenderStyle*) const; + NSControlSize controlSizeForSystemFont(RenderStyle*) const; + //void setControlSize(NSCell*, const IntSize* sizes, const IntSize& minSize); + void setSizeFromFont(RenderStyle*, const IntSize* sizes) const; + IntSize sizeForFont(RenderStyle*, const IntSize* sizes) const; + IntSize sizeForSystemFont(RenderStyle*, const IntSize* sizes) const; + void setFontFromControlSize(CSSStyleSelector*, RenderStyle*, NSControlSize) const; + + // Helpers for adjusting appearance and for painting + const IntSize* checkboxSizes() const; + const int* checkboxMargins(NSControlSize) const; + + const IntSize* radioSizes() const; + const int* radioMargins(NSControlSize) const; + + void setButtonPaddingFromControlSize(RenderStyle*, NSControlSize) const; + const IntSize* buttonSizes() const; + const int* buttonMargins(NSControlSize) const; + + const IntSize* popupButtonSizes() const; + const int* popupButtonMargins(NSControlSize) const; + const int* popupButtonPadding(NSControlSize) const; + void paintMenuListButtonGradients(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + const IntSize* menuListSizes() const; + + const IntSize* searchFieldSizes() const; + const IntSize* cancelButtonSizes() const; + const IntSize* resultsButtonSizes() const; + void setSearchFieldSize(RenderStyle*) const; + + ThemeControlState determineState(RenderObject*) const; +}; + +} // namespace WebCore + +#endif // #if USE(SAFARI_THEME) + +#endif // RenderThemeSafari_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderThemeWin.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderThemeWin.cpp new file mode 100644 index 0000000..fd07b54 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderThemeWin.cpp @@ -0,0 +1,852 @@ +/* + * Copyright (C) 2006, 2007 Apple Inc. + * + * 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., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + */ + +#include "config.h" +#include "RenderThemeWin.h" + +#include "CSSStyleSheet.h" +#include "CSSValueKeywords.h" +#include "Document.h" +#include "GraphicsContext.h" +#include "HTMLElement.h" +#include "HTMLSelectElement.h" +#include "Icon.h" +#include "RenderSlider.h" +#include "SoftLinking.h" +#include "UserAgentStyleSheets.h" + +#include <tchar.h> + +/* + * The following constants are used to determine how a widget is drawn using + * Windows' Theme API. For more information on theme parts and states see + * http://msdn.microsoft.com/library/default.asp?url=/library/en-us/shellcc/platform/commctls/userex/topics/partsandstates.asp + */ + +// Generic state constants +#define TS_NORMAL 1 +#define TS_HOVER 2 +#define TS_ACTIVE 3 +#define TS_DISABLED 4 +#define TS_FOCUSED 5 + +// Button constants +#define BP_BUTTON 1 +#define BP_RADIO 2 +#define BP_CHECKBOX 3 + +// Textfield constants +#define TFP_TEXTFIELD 1 +#define TFS_READONLY 6 + +// ComboBox constants (from tmschema.h) +#define CP_DROPDOWNBUTTON 1 + +// TrackBar (slider) parts +#define TKP_TRACK 1 +#define TKP_TRACKVERT 2 + +// TrackBar (slider) thumb parts +#define TKP_THUMBBOTTOM 4 +#define TKP_THUMBTOP 5 +#define TKP_THUMBLEFT 7 +#define TKP_THUMBRIGHT 8 + +// Trackbar (slider) thumb states +#define TUS_NORMAL 1 +#define TUS_HOT 2 +#define TUS_PRESSED 3 +#define TUS_FOCUSED 4 +#define TUS_DISABLED 5 + +// button states +#define PBS_NORMAL 1 +#define PBS_HOT 2 +#define PBS_PRESSED 3 +#define PBS_DISABLED 4 +#define PBS_DEFAULTED 5 + +// This is the fixed width IE and Firefox use for buttons on dropdown menus +static const int dropDownButtonWidth = 17; + +static const int shell32MagnifierIconIndex = 22; + +// Default font size to match Firefox. +static const float defaultControlFontPixelSize = 13; + +SOFT_LINK_LIBRARY(uxtheme) +SOFT_LINK(uxtheme, OpenThemeData, HANDLE, WINAPI, (HWND hwnd, LPCWSTR pszClassList), (hwnd, pszClassList)) +SOFT_LINK(uxtheme, CloseThemeData, HRESULT, WINAPI, (HANDLE hTheme), (hTheme)) +SOFT_LINK(uxtheme, DrawThemeBackground, HRESULT, WINAPI, (HANDLE hTheme, HDC hdc, int iPartId, int iStateId, const RECT* pRect, const RECT* pClipRect), (hTheme, hdc, iPartId, iStateId, pRect, pClipRect)) +SOFT_LINK(uxtheme, IsThemeActive, BOOL, WINAPI, (), ()) +SOFT_LINK(uxtheme, IsThemeBackgroundPartiallyTransparent, BOOL, WINAPI, (HANDLE hTheme, int iPartId, int iStateId), (hTheme, iPartId, iStateId)) + +static bool haveTheme; + +namespace WebCore { + +static bool gWebKitIsBeingUnloaded; + +void RenderThemeWin::setWebKitIsBeingUnloaded() +{ + gWebKitIsBeingUnloaded = true; +} + +#if !USE(SAFARI_THEME) +RenderTheme* theme() +{ + static RenderThemeWin winTheme; + return &winTheme; +} +#endif + +RenderThemeWin::RenderThemeWin() + : m_buttonTheme(0) + , m_textFieldTheme(0) + , m_menuListTheme(0) + , m_sliderTheme(0) +{ + haveTheme = uxthemeLibrary() && IsThemeActive(); +} + +RenderThemeWin::~RenderThemeWin() +{ + // If WebKit is being unloaded, then uxtheme.dll is no longer available. + if (gWebKitIsBeingUnloaded || !uxthemeLibrary()) + return; + close(); +} + +HANDLE RenderThemeWin::buttonTheme() const +{ + if (haveTheme && !m_buttonTheme) + m_buttonTheme = OpenThemeData(0, L"Button"); + return m_buttonTheme; +} + +HANDLE RenderThemeWin::textFieldTheme() const +{ + if (haveTheme && !m_textFieldTheme) + m_textFieldTheme = OpenThemeData(0, L"Edit"); + return m_textFieldTheme; +} + +HANDLE RenderThemeWin::menuListTheme() const +{ + if (haveTheme && !m_menuListTheme) + m_menuListTheme = OpenThemeData(0, L"ComboBox"); + return m_menuListTheme; +} + +HANDLE RenderThemeWin::sliderTheme() const +{ + if (haveTheme && !m_sliderTheme) + m_sliderTheme = OpenThemeData(0, L"TrackBar"); + return m_sliderTheme; +} + +void RenderThemeWin::close() +{ + // This method will need to be called when the OS theme changes to flush our cached themes. + if (m_buttonTheme) + CloseThemeData(m_buttonTheme); + if (m_textFieldTheme) + CloseThemeData(m_textFieldTheme); + if (m_menuListTheme) + CloseThemeData(m_menuListTheme); + if (m_sliderTheme) + CloseThemeData(m_sliderTheme); + m_buttonTheme = m_textFieldTheme = m_menuListTheme = m_sliderTheme = 0; + + haveTheme = uxthemeLibrary() && IsThemeActive(); +} + +void RenderThemeWin::themeChanged() +{ + close(); +} + +String RenderThemeWin::extraDefaultStyleSheet() +{ + return String(themeWinUserAgentStyleSheet, sizeof(themeWinUserAgentStyleSheet)); +} + +String RenderThemeWin::extraQuirksStyleSheet() +{ + return String(themeWinQuirksUserAgentStyleSheet, sizeof(themeWinQuirksUserAgentStyleSheet)); +} + +bool RenderThemeWin::supportsHover(const RenderStyle*) const +{ + // The Classic/2k look has no hover effects. + return haveTheme; +} + +Color RenderThemeWin::platformActiveSelectionBackgroundColor() const +{ + COLORREF color = GetSysColor(COLOR_HIGHLIGHT); + return Color(GetRValue(color), GetGValue(color), GetBValue(color), 255); +} + +Color RenderThemeWin::platformInactiveSelectionBackgroundColor() const +{ + COLORREF color = GetSysColor(COLOR_GRAYTEXT); + return Color(GetRValue(color), GetGValue(color), GetBValue(color), 255); +} + +Color RenderThemeWin::platformActiveSelectionForegroundColor() const +{ + COLORREF color = GetSysColor(COLOR_HIGHLIGHTTEXT); + return Color(GetRValue(color), GetGValue(color), GetBValue(color), 255); +} + +Color RenderThemeWin::platformInactiveSelectionForegroundColor() const +{ + return Color::white; +} + +static void fillFontDescription(FontDescription& fontDescription, LOGFONT& logFont, float fontSize) +{ + fontDescription.setIsAbsoluteSize(true); + fontDescription.setGenericFamily(FontDescription::NoFamily); + fontDescription.firstFamily().setFamily(String(logFont.lfFaceName)); + fontDescription.setSpecifiedSize(fontSize); + fontDescription.setWeight(logFont.lfWeight >= 700 ? FontWeightBold : FontWeightNormal); // FIXME: Use real weight. + fontDescription.setItalic(logFont.lfItalic); +} + +static void fillFontDescription(FontDescription& fontDescription, LOGFONT& logFont) +{ + fillFontDescription(fontDescription, logFont, abs(logFont.lfHeight)); +} + +void RenderThemeWin::systemFont(int propId, FontDescription& fontDescription) const +{ + static FontDescription captionFont; + static FontDescription controlFont; + static FontDescription smallCaptionFont; + static FontDescription menuFont; + static FontDescription iconFont; + static FontDescription messageBoxFont; + static FontDescription statusBarFont; + static FontDescription systemFont; + + static bool initialized; + static NONCLIENTMETRICS ncm; + + if (!initialized) { + initialized = true; + ncm.cbSize = sizeof(NONCLIENTMETRICS); + ::SystemParametersInfo(SPI_GETNONCLIENTMETRICS, sizeof(ncm), &ncm, 0); + } + + switch (propId) { + case CSSValueIcon: { + if (!iconFont.isAbsoluteSize()) { + LOGFONT logFont; + ::SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(logFont), &logFont, 0); + fillFontDescription(iconFont, logFont); + } + fontDescription = iconFont; + break; + } + case CSSValueMenu: + if (!menuFont.isAbsoluteSize()) + fillFontDescription(menuFont, ncm.lfMenuFont); + fontDescription = menuFont; + break; + case CSSValueMessageBox: + if (!messageBoxFont.isAbsoluteSize()) + fillFontDescription(messageBoxFont, ncm.lfMessageFont); + fontDescription = messageBoxFont; + break; + case CSSValueStatusBar: + if (!statusBarFont.isAbsoluteSize()) + fillFontDescription(statusBarFont, ncm.lfStatusFont); + fontDescription = statusBarFont; + break; + case CSSValueCaption: + if (!captionFont.isAbsoluteSize()) + fillFontDescription(captionFont, ncm.lfCaptionFont); + fontDescription = captionFont; + break; + case CSSValueSmallCaption: + if (!smallCaptionFont.isAbsoluteSize()) + fillFontDescription(smallCaptionFont, ncm.lfSmCaptionFont); + fontDescription = smallCaptionFont; + break; + case CSSValueWebkitSmallControl: + case CSSValueWebkitMiniControl: // Just map to small. + case CSSValueWebkitControl: // Just map to small. + if (!controlFont.isAbsoluteSize()) { + HGDIOBJ hGDI = ::GetStockObject(DEFAULT_GUI_FONT); + if (hGDI) { + LOGFONT logFont; + if (::GetObject(hGDI, sizeof(logFont), &logFont) > 0) + fillFontDescription(controlFont, logFont, defaultControlFontPixelSize); + } + } + fontDescription = controlFont; + break; + default: { // Everything else uses the stock GUI font. + if (!systemFont.isAbsoluteSize()) { + HGDIOBJ hGDI = ::GetStockObject(DEFAULT_GUI_FONT); + if (hGDI) { + LOGFONT logFont; + if (::GetObject(hGDI, sizeof(logFont), &logFont) > 0) + fillFontDescription(systemFont, logFont); + } + } + fontDescription = systemFont; + } + } +} + +bool RenderThemeWin::supportsFocus(ControlPart appearance) +{ + switch (appearance) { + case PushButtonPart: + case ButtonPart: + case DefaultButtonPart: + case TextFieldPart: + case TextAreaPart: + return true; + case MenulistPart: + return false; + default: + return false; + } +} + +unsigned RenderThemeWin::determineClassicState(RenderObject* o) +{ + unsigned state = 0; + switch (o->style()->appearance()) { + case PushButtonPart: + case ButtonPart: + case DefaultButtonPart: + state = DFCS_BUTTONPUSH; + if (!isEnabled(o)) + state |= DFCS_INACTIVE; + else if (isPressed(o)) + state |= DFCS_PUSHED; + break; + case RadioPart: + case CheckboxPart: + state = (o->style()->appearance() == RadioPart) ? DFCS_BUTTONRADIO : DFCS_BUTTONCHECK; + if (isChecked(o)) + state |= DFCS_CHECKED; + if (!isEnabled(o)) + state |= DFCS_INACTIVE; + else if (isPressed(o)) + state |= DFCS_PUSHED; + break; + case MenulistPart: + state = DFCS_SCROLLCOMBOBOX; + if (!isEnabled(o)) + state |= DFCS_INACTIVE; + else if (isPressed(o)) + state |= DFCS_PUSHED; + default: + break; + } + return state; +} + +unsigned RenderThemeWin::determineState(RenderObject* o) +{ + unsigned result = TS_NORMAL; + ControlPart appearance = o->style()->appearance(); + if (!isEnabled(o)) + result = TS_DISABLED; + else if (isReadOnlyControl(o) && (TextFieldPart == appearance || TextAreaPart == appearance)) + result = TFS_READONLY; // Readonly is supported on textfields. + else if (isPressed(o)) // Active overrides hover and focused. + result = TS_ACTIVE; + else if (supportsFocus(appearance) && isFocused(o)) + result = TS_FOCUSED; + else if (isHovered(o)) + result = TS_HOVER; + if (isChecked(o)) + result += 4; // 4 unchecked states, 4 checked states. + return result; +} + +unsigned RenderThemeWin::determineSliderThumbState(RenderObject* o) +{ + unsigned result = TUS_NORMAL; + if (!isEnabled(o->parent())) + result = TUS_DISABLED; + else if (supportsFocus(o->style()->appearance()) && isFocused(o->parent())) + result = TUS_FOCUSED; + else if (static_cast<RenderSlider*>(o->parent())->inDragMode()) + result = TUS_PRESSED; + else if (isHovered(o)) + result = TUS_HOT; + return result; +} + +unsigned RenderThemeWin::determineButtonState(RenderObject* o) +{ + unsigned result = PBS_NORMAL; + if (!isEnabled(o)) + result = PBS_DISABLED; + else if (isPressed(o)) + result = PBS_PRESSED; + else if (supportsFocus(o->style()->appearance()) && isFocused(o)) + result = PBS_DEFAULTED; + else if (isHovered(o)) + result = PBS_HOT; + else if (isDefault(o)) + result = PBS_DEFAULTED; + return result; +} + +ThemeData RenderThemeWin::getClassicThemeData(RenderObject* o) +{ + ThemeData result; + switch (o->style()->appearance()) { + case PushButtonPart: + case ButtonPart: + case DefaultButtonPart: + case CheckboxPart: + case RadioPart: + result.m_part = DFC_BUTTON; + result.m_state = determineClassicState(o); + break; + case MenulistPart: + result.m_part = DFC_SCROLL; + result.m_state = determineClassicState(o); + break; + case TextFieldPart: + case TextAreaPart: + result.m_part = TFP_TEXTFIELD; + result.m_state = determineState(o); + break; + case SliderHorizontalPart: + result.m_part = TKP_TRACK; + result.m_state = TS_NORMAL; + break; + case SliderVerticalPart: + result.m_part = TKP_TRACKVERT; + result.m_state = TS_NORMAL; + break; + case SliderThumbHorizontalPart: + result.m_part = TKP_THUMBBOTTOM; + result.m_state = determineSliderThumbState(o); + break; + case SliderThumbVerticalPart: + result.m_part = TKP_THUMBRIGHT; + result.m_state = determineSliderThumbState(o); + break; + default: + break; + } + return result; +} + +ThemeData RenderThemeWin::getThemeData(RenderObject* o) +{ + if (!haveTheme) + return getClassicThemeData(o); + + ThemeData result; + switch (o->style()->appearance()) { + case PushButtonPart: + case ButtonPart: + case DefaultButtonPart: + result.m_part = BP_BUTTON; + result.m_state = determineButtonState(o); + break; + case CheckboxPart: + result.m_part = BP_CHECKBOX; + result.m_state = determineState(o); + break; + case MenulistPart: + case MenulistButtonPart: + result.m_part = CP_DROPDOWNBUTTON; + result.m_state = determineState(o); + break; + case RadioPart: + result.m_part = BP_RADIO; + result.m_state = determineState(o); + break; + case TextFieldPart: + case TextAreaPart: + result.m_part = TFP_TEXTFIELD; + result.m_state = determineState(o); + break; + case SliderHorizontalPart: + result.m_part = TKP_TRACK; + result.m_state = TS_NORMAL; + break; + case SliderVerticalPart: + result.m_part = TKP_TRACKVERT; + result.m_state = TS_NORMAL; + break; + case SliderThumbHorizontalPart: + result.m_part = TKP_THUMBBOTTOM; + result.m_state = determineSliderThumbState(o); + break; + case SliderThumbVerticalPart: + result.m_part = TKP_THUMBRIGHT; + result.m_state = determineSliderThumbState(o); + break; + } + + return result; +} + +static void drawControl(GraphicsContext* context, RenderObject* o, HANDLE theme, const ThemeData& themeData, const IntRect& r) +{ + bool alphaBlend = false; + if (theme) + alphaBlend = IsThemeBackgroundPartiallyTransparent(theme, themeData.m_part, themeData.m_state); + HDC hdc = context->getWindowsContext(r, alphaBlend); + RECT widgetRect = r; + if (theme) + DrawThemeBackground(theme, hdc, themeData.m_part, themeData.m_state, &widgetRect, NULL); + else { + if (themeData.m_part == TFP_TEXTFIELD) { + ::DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST); + if (themeData.m_state == TS_DISABLED || themeData.m_state == TFS_READONLY) + ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_BTNFACE+1)); + else + ::FillRect(hdc, &widgetRect, (HBRUSH)(COLOR_WINDOW+1)); + } else if (themeData.m_part == TKP_TRACK || themeData.m_part == TKP_TRACKVERT) { + ::DrawEdge(hdc, &widgetRect, EDGE_SUNKEN, BF_RECT | BF_ADJUST); + ::FillRect(hdc, &widgetRect, (HBRUSH)GetStockObject(GRAY_BRUSH)); + } else if ((o->style()->appearance() == SliderThumbHorizontalPart || + o->style()->appearance() == SliderThumbVerticalPart) && + (themeData.m_part == TKP_THUMBBOTTOM || themeData.m_part == TKP_THUMBTOP || + themeData.m_part == TKP_THUMBLEFT || themeData.m_part == TKP_THUMBRIGHT)) { + ::DrawEdge(hdc, &widgetRect, EDGE_RAISED, BF_RECT | BF_SOFT | BF_MIDDLE | BF_ADJUST); + if (themeData.m_state == TUS_DISABLED) { + static WORD patternBits[8] = {0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55}; + HBITMAP patternBmp = ::CreateBitmap(8, 8, 1, 1, patternBits); + if (patternBmp) { + HBRUSH brush = (HBRUSH) ::CreatePatternBrush(patternBmp); + COLORREF oldForeColor = ::SetTextColor(hdc, ::GetSysColor(COLOR_3DFACE)); + COLORREF oldBackColor = ::SetBkColor(hdc, ::GetSysColor(COLOR_3DHILIGHT)); + POINT p; + ::GetViewportOrgEx(hdc, &p); + ::SetBrushOrgEx(hdc, p.x + widgetRect.left, p.y + widgetRect.top, NULL); + HBRUSH oldBrush = (HBRUSH) ::SelectObject(hdc, brush); + ::FillRect(hdc, &widgetRect, brush); + ::SetTextColor(hdc, oldForeColor); + ::SetBkColor(hdc, oldBackColor); + ::SelectObject(hdc, oldBrush); + ::DeleteObject(brush); + } else + ::FillRect(hdc, &widgetRect, (HBRUSH)COLOR_3DHILIGHT); + ::DeleteObject(patternBmp); + } + } else { + // Push buttons, buttons, checkboxes and radios, and the dropdown arrow in menulists. + if (o->style()->appearance() == DefaultButtonPart) { + HBRUSH brush = ::GetSysColorBrush(COLOR_3DDKSHADOW); + ::FrameRect(hdc, &widgetRect, brush); + ::InflateRect(&widgetRect, -1, -1); + ::DrawEdge(hdc, &widgetRect, BDR_RAISEDOUTER, BF_RECT | BF_MIDDLE); + } + ::DrawFrameControl(hdc, &widgetRect, themeData.m_part, themeData.m_state); + } + } + context->releaseWindowsContext(hdc, r, alphaBlend); +} + +bool RenderThemeWin::paintButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) +{ + drawControl(i.context, o, buttonTheme(), getThemeData(o), r); + return false; +} + +void RenderThemeWin::setCheckboxSize(RenderStyle* style) const +{ + // If the width and height are both specified, then we have nothing to do. + if (!style->width().isIntrinsicOrAuto() && !style->height().isAuto()) + return; + + // FIXME: A hard-coded size of 13 is used. This is wrong but necessary for now. It matches Firefox. + // At different DPI settings on Windows, querying the theme gives you a larger size that accounts for + // the higher DPI. Until our entire engine honors a DPI setting other than 96, we can't rely on the theme's + // metrics. + if (style->width().isIntrinsicOrAuto()) + style->setWidth(Length(13, Fixed)); + if (style->height().isAuto()) + style->setHeight(Length(13, Fixed)); +} + +bool RenderThemeWin::paintTextField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) +{ + drawControl(i.context, o, textFieldTheme(), getThemeData(o), r); + return false; +} + +bool RenderThemeWin::paintMenuList(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) +{ + // The outer box of a menu list is just a text field. Paint it first. + drawControl(i.context, o, textFieldTheme(), ThemeData(TFP_TEXTFIELD, determineState(o)), r); + + return paintMenuListButton(o, i, r); +} + +void RenderThemeWin::adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + style->resetBorder(); + adjustMenuListButtonStyle(selector, style, e); +} + +void RenderThemeWin::adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + // These are the paddings needed to place the text correctly in the <select> box + const int dropDownBoxPaddingTop = 2; + const int dropDownBoxPaddingRight = style->direction() == LTR ? 4 + dropDownButtonWidth : 4; + const int dropDownBoxPaddingBottom = 2; + const int dropDownBoxPaddingLeft = style->direction() == LTR ? 4 : 4 + dropDownButtonWidth; + // The <select> box must be at least 12px high for the button to render nicely on Windows + const int dropDownBoxMinHeight = 12; + + // Position the text correctly within the select box and make the box wide enough to fit the dropdown button + style->setPaddingTop(Length(dropDownBoxPaddingTop, Fixed)); + style->setPaddingRight(Length(dropDownBoxPaddingRight, Fixed)); + style->setPaddingBottom(Length(dropDownBoxPaddingBottom, Fixed)); + style->setPaddingLeft(Length(dropDownBoxPaddingLeft, Fixed)); + + // Height is locked to auto + style->setHeight(Length(Auto)); + + // Calculate our min-height + int minHeight = style->font().height(); + minHeight = max(minHeight, dropDownBoxMinHeight); + + style->setMinHeight(Length(minHeight, Fixed)); + + // White-space is locked to pre + style->setWhiteSpace(PRE); +} + +bool RenderThemeWin::paintMenuListButton(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) +{ + // FIXME: Don't make hardcoded assumptions about the thickness of the textfield border. + int borderThickness = haveTheme ? 1 : 2; + + // Paint the dropdown button on the inner edge of the text field, + // leaving space for the text field's 1px border + IntRect buttonRect(r); + buttonRect.inflate(-borderThickness); + if (o->style()->direction() == LTR) + buttonRect.setX(buttonRect.right() - dropDownButtonWidth); + buttonRect.setWidth(dropDownButtonWidth); + + drawControl(i.context, o, menuListTheme(), getThemeData(o), buttonRect); + + return false; +} + +const int trackWidth = 4; + +bool RenderThemeWin::paintSliderTrack(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) +{ + IntRect bounds = r; + + if (o->style()->appearance() == SliderHorizontalPart) { + bounds.setHeight(trackWidth); + bounds.setY(r.y() + r.height() / 2 - trackWidth / 2); + } else if (o->style()->appearance() == SliderVerticalPart) { + bounds.setWidth(trackWidth); + bounds.setX(r.x() + r.width() / 2 - trackWidth / 2); + } + + drawControl(i.context, o, sliderTheme(), getThemeData(o), bounds); + return false; +} + +bool RenderThemeWin::paintSliderThumb(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) +{ + drawControl(i.context, o, sliderTheme(), getThemeData(o), r); + return false; +} + +const int sliderThumbWidth = 7; +const int sliderThumbHeight = 15; + +void RenderThemeWin::adjustSliderThumbSize(RenderObject* o) const +{ + if (o->style()->appearance() == SliderThumbVerticalPart) { + o->style()->setWidth(Length(sliderThumbHeight, Fixed)); + o->style()->setHeight(Length(sliderThumbWidth, Fixed)); + } else if (o->style()->appearance() == SliderThumbHorizontalPart) { + o->style()->setWidth(Length(sliderThumbWidth, Fixed)); + o->style()->setHeight(Length(sliderThumbHeight, Fixed)); + } +} + +void RenderThemeWin::adjustButtonInnerStyle(RenderStyle* style) const +{ + // This inner padding matches Firefox. + style->setPaddingTop(Length(1, Fixed)); + style->setPaddingRight(Length(3, Fixed)); + style->setPaddingBottom(Length(1, Fixed)); + style->setPaddingLeft(Length(3, Fixed)); +} + +bool RenderThemeWin::paintSearchField(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) +{ + return paintTextField(o, i, r); +} + +void RenderThemeWin::adjustSearchFieldStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + // Override padding size to match AppKit text positioning. + const int padding = 1; + style->setPaddingLeft(Length(padding, Fixed)); + style->setPaddingRight(Length(padding, Fixed)); + style->setPaddingTop(Length(padding, Fixed)); + style->setPaddingBottom(Length(padding, Fixed)); +} + +bool RenderThemeWin::paintSearchFieldCancelButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) +{ + Color buttonColor = (o->element() && o->element()->active()) ? Color(138, 138, 138) : Color(186, 186, 186); + + IntSize cancelSize(10, 10); + IntSize cancelRadius(cancelSize.width() / 2, cancelSize.height() / 2); + int x = r.x() + (r.width() - cancelSize.width()) / 2; + int y = r.y() + (r.height() - cancelSize.height()) / 2 + 1; + IntRect cancelBounds(IntPoint(x, y), cancelSize); + paintInfo.context->save(); + paintInfo.context->addRoundedRectClip(cancelBounds, cancelRadius, cancelRadius, cancelRadius, cancelRadius); + paintInfo.context->fillRect(cancelBounds, buttonColor); + + // Draw the 'x' + IntSize xSize(3, 3); + IntRect xBounds(cancelBounds.location() + IntSize(3, 3), xSize); + paintInfo.context->setStrokeColor(Color::white); + paintInfo.context->drawLine(xBounds.location(), xBounds.location() + xBounds.size()); + paintInfo.context->drawLine(IntPoint(xBounds.right(), xBounds.y()), IntPoint(xBounds.x(), xBounds.bottom())); + + paintInfo.context->restore(); + return false; +} + +void RenderThemeWin::adjustSearchFieldCancelButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + IntSize cancelSize(13, 11); + style->setWidth(Length(cancelSize.width(), Fixed)); + style->setHeight(Length(cancelSize.height(), Fixed)); +} + +void RenderThemeWin::adjustSearchFieldDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + IntSize emptySize(1, 11); + style->setWidth(Length(emptySize.width(), Fixed)); + style->setHeight(Length(emptySize.height(), Fixed)); +} + +void RenderThemeWin::adjustSearchFieldResultsDecorationStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + IntSize magnifierSize(15, 11); + style->setWidth(Length(magnifierSize.width(), Fixed)); + style->setHeight(Length(magnifierSize.height(), Fixed)); +} + +bool RenderThemeWin::paintSearchFieldResultsDecoration(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) +{ + IntRect bounds = r; + bounds.setWidth(bounds.height()); + + TCHAR buffer[MAX_PATH]; + UINT length = ::GetSystemDirectory(buffer, ARRAYSIZE(buffer)); + if (!length) + return 0; + + if (_tcscat_s(buffer, TEXT("\\shell32.dll"))) + return 0; + + HICON hIcon; + if (!::ExtractIconEx(buffer, shell32MagnifierIconIndex, 0, &hIcon, 1)) + return 0; + + RefPtr<Icon> icon = Icon::create(hIcon); + icon->paint(paintInfo.context, bounds); + return false; +} + +void RenderThemeWin::adjustSearchFieldResultsButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const +{ + IntSize magnifierSize(15, 11); + style->setWidth(Length(magnifierSize.width(), Fixed)); + style->setHeight(Length(magnifierSize.height(), Fixed)); +} + +bool RenderThemeWin::paintSearchFieldResultsButton(RenderObject* o, const RenderObject::PaintInfo& paintInfo, const IntRect& r) +{ + paintSearchFieldResultsDecoration(o, paintInfo, r); + return false; +} + +// Map a CSSValue* system color to an index understood by GetSysColor +static int cssValueIdToSysColorIndex(int cssValueId) +{ + switch (cssValueId) { + case CSSValueActiveborder: return COLOR_ACTIVEBORDER; + case CSSValueActivecaption: return COLOR_ACTIVECAPTION; + case CSSValueAppworkspace: return COLOR_APPWORKSPACE; + case CSSValueBackground: return COLOR_BACKGROUND; + case CSSValueButtonface: return COLOR_BTNFACE; + case CSSValueButtonhighlight: return COLOR_BTNHIGHLIGHT; + case CSSValueButtonshadow: return COLOR_BTNSHADOW; + case CSSValueButtontext: return COLOR_BTNTEXT; + case CSSValueCaptiontext: return COLOR_CAPTIONTEXT; + case CSSValueGraytext: return COLOR_GRAYTEXT; + case CSSValueHighlight: return COLOR_HIGHLIGHT; + case CSSValueHighlighttext: return COLOR_HIGHLIGHTTEXT; + case CSSValueInactiveborder: return COLOR_INACTIVEBORDER; + case CSSValueInactivecaption: return COLOR_INACTIVECAPTION; + case CSSValueInactivecaptiontext: return COLOR_INACTIVECAPTIONTEXT; + case CSSValueInfobackground: return COLOR_INFOBK; + case CSSValueInfotext: return COLOR_INFOTEXT; + case CSSValueMenu: return COLOR_MENU; + case CSSValueMenutext: return COLOR_MENUTEXT; + case CSSValueScrollbar: return COLOR_SCROLLBAR; + case CSSValueThreeddarkshadow: return COLOR_3DDKSHADOW; + case CSSValueThreedface: return COLOR_3DFACE; + case CSSValueThreedhighlight: return COLOR_3DHIGHLIGHT; + case CSSValueThreedlightshadow: return COLOR_3DLIGHT; + case CSSValueThreedshadow: return COLOR_3DSHADOW; + case CSSValueWindow: return COLOR_WINDOW; + case CSSValueWindowframe: return COLOR_WINDOWFRAME; + case CSSValueWindowtext: return COLOR_WINDOWTEXT; + default: return -1; // Unsupported CSSValue + } +} + +Color RenderThemeWin::systemColor(int cssValueId) const +{ + int sysColorIndex = cssValueIdToSysColorIndex(cssValueId); + if (sysColorIndex == -1) + return RenderTheme::systemColor(cssValueId); + + COLORREF color = GetSysColor(sysColorIndex); + return Color(GetRValue(color), GetGValue(color), GetBValue(color)); +} + +} diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderThemeWin.h b/src/3rdparty/webkit/WebCore/rendering/RenderThemeWin.h new file mode 100644 index 0000000..8562b22 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderThemeWin.h @@ -0,0 +1,147 @@ +/* + * This file is part of the WebKit project. + * + * Copyright (C) 2006, 2008 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef RenderThemeWin_h +#define RenderThemeWin_h + +#include "RenderTheme.h" + +#if WIN32 +typedef void* HANDLE; +typedef struct HINSTANCE__* HINSTANCE; +typedef HINSTANCE HMODULE; +#endif + +namespace WebCore { + +struct ThemeData { + ThemeData() :m_part(0), m_state(0), m_classicState(0) {} + ThemeData(int part, int state) + : m_part(part) + , m_state(state) + , m_classicState(0) + { } + + unsigned m_part; + unsigned m_state; + unsigned m_classicState; +}; + +class RenderThemeWin : public RenderTheme { +public: + RenderThemeWin(); + ~RenderThemeWin(); + + virtual String extraDefaultStyleSheet(); + virtual String extraQuirksStyleSheet(); + + // A method asking if the theme's controls actually care about redrawing when hovered. + virtual bool supportsHover(const RenderStyle*) const; + + virtual Color platformActiveSelectionBackgroundColor() const; + virtual Color platformInactiveSelectionBackgroundColor() const; + virtual Color platformActiveSelectionForegroundColor() const; + virtual Color platformInactiveSelectionForegroundColor() const; + + // System fonts. + virtual void systemFont(int propId, FontDescription&) const; + virtual Color systemColor(int cssValueId) const; + + virtual bool paintCheckbox(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) + { return paintButton(o, i, r); } + virtual void setCheckboxSize(RenderStyle*) const; + + virtual bool paintRadio(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) + { return paintButton(o, i, r); } + virtual void setRadioSize(RenderStyle* style) const + { return setCheckboxSize(style); } + + virtual bool paintButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + + virtual bool paintTextField(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + + virtual bool paintTextArea(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r) + { return paintTextField(o, i, r); } + + virtual void adjustMenuListStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const; + virtual bool paintMenuList(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + virtual void adjustMenuListButtonStyle(CSSStyleSelector* selector, RenderStyle* style, Element* e) const; + + virtual bool paintMenuListButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + + virtual bool paintSliderTrack(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r); + virtual bool paintSliderThumb(RenderObject* o, const RenderObject::PaintInfo& i, const IntRect& r); + virtual void adjustSliderThumbSize(RenderObject*) const; + + virtual void adjustButtonInnerStyle(RenderStyle*) const; + + virtual void adjustSearchFieldStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintSearchField(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + + virtual void adjustSearchFieldCancelButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintSearchFieldCancelButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + + virtual void adjustSearchFieldDecorationStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintSearchFieldDecoration(RenderObject*, const RenderObject::PaintInfo&, const IntRect&) { return false; } + + virtual void adjustSearchFieldResultsDecorationStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintSearchFieldResultsDecoration(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + + virtual void adjustSearchFieldResultsButtonStyle(CSSStyleSelector*, RenderStyle*, Element*) const; + virtual bool paintSearchFieldResultsButton(RenderObject*, const RenderObject::PaintInfo&, const IntRect&); + + virtual void themeChanged(); + + virtual void adjustButtonStyle(CSSStyleSelector*, RenderStyle* style, Element*) const {} + virtual void adjustTextFieldStyle(CSSStyleSelector*, RenderStyle* style, Element*) const {} + virtual void adjustTextAreaStyle(CSSStyleSelector*, RenderStyle* style, Element*) const {} + + static void setWebKitIsBeingUnloaded(); + +private: + void addIntrinsicMargins(RenderStyle*) const; + void close(); + + unsigned determineState(RenderObject*); + unsigned determineClassicState(RenderObject*); + unsigned determineSliderThumbState(RenderObject*); + unsigned determineButtonState(RenderObject*); + + bool supportsFocus(ControlPart); + + ThemeData getThemeData(RenderObject*); + ThemeData getClassicThemeData(RenderObject* o); + + HANDLE buttonTheme() const; + HANDLE textFieldTheme() const; + HANDLE menuListTheme() const; + HANDLE sliderTheme() const; + + mutable HANDLE m_buttonTheme; + mutable HANDLE m_textFieldTheme; + mutable HANDLE m_menuListTheme; + mutable HANDLE m_sliderTheme; +}; + +}; + +#endif diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderTreeAsText.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderTreeAsText.cpp new file mode 100644 index 0000000..f46ff20 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderTreeAsText.cpp @@ -0,0 +1,512 @@ +/* + * Copyright (C) 2004, 2006, 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "RenderTreeAsText.h" + +#include "CharacterNames.h" +#include "Document.h" +#include "Frame.h" +#include "FrameView.h" +#include "HTMLElement.h" +#include "HTMLNames.h" +#include "InlineTextBox.h" +#include "RenderBR.h" +#include "RenderListMarker.h" +#include "RenderTableCell.h" +#include "RenderView.h" +#include "RenderWidget.h" +#include "SelectionController.h" +#include "TextStream.h" +#include <wtf/Vector.h> + +#if ENABLE(SVG) +#include "RenderSVGRoot.h" +#include "RenderSVGContainer.h" +#include "RenderSVGInlineText.h" +#include "RenderSVGText.h" +#include "SVGRenderTreeAsText.h" +#endif + +namespace WebCore { + +using namespace HTMLNames; + +static void writeLayers(TextStream&, const RenderLayer* rootLayer, RenderLayer*, const IntRect& paintDirtyRect, int indent = 0); + +#if !ENABLE(SVG) +static TextStream &operator<<(TextStream& ts, const IntRect& r) +{ + return ts << "at (" << r.x() << "," << r.y() << ") size " << r.width() << "x" << r.height(); +} +#endif + +static void writeIndent(TextStream& ts, int indent) +{ + for (int i = 0; i != indent; ++i) + ts << " "; +} + +static void printBorderStyle(TextStream& ts, const RenderObject& o, const EBorderStyle borderStyle) +{ + switch (borderStyle) { + case BNONE: + ts << "none"; + break; + case BHIDDEN: + ts << "hidden"; + break; + case INSET: + ts << "inset"; + break; + case GROOVE: + ts << "groove"; + break; + case RIDGE: + ts << "ridge"; + break; + case OUTSET: + ts << "outset"; + break; + case DOTTED: + ts << "dotted"; + break; + case DASHED: + ts << "dashed"; + break; + case SOLID: + ts << "solid"; + break; + case DOUBLE: + ts << "double"; + break; + } + + ts << " "; +} + +static String getTagName(Node* n) +{ + if (n->isDocumentNode()) + return ""; + if (n->isCommentNode()) + return "COMMENT"; + return n->nodeName(); +} + +static bool isEmptyOrUnstyledAppleStyleSpan(const Node* node) +{ + if (!node || !node->isHTMLElement() || !node->hasTagName(spanTag)) + return false; + + const HTMLElement* elem = static_cast<const HTMLElement*>(node); + if (elem->getAttribute(classAttr) != "Apple-style-span") + return false; + + if (!node->hasChildNodes()) + return true; + + CSSMutableStyleDeclaration* inlineStyleDecl = elem->inlineStyleDecl(); + return (!inlineStyleDecl || inlineStyleDecl->length() == 0); +} + +String quoteAndEscapeNonPrintables(const String& s) +{ + Vector<UChar> result; + result.append('"'); + for (unsigned i = 0; i != s.length(); ++i) { + UChar c = s[i]; + if (c == '\\') { + result.append('\\'); + result.append('\\'); + } else if (c == '"') { + result.append('\\'); + result.append('"'); + } else if (c == '\n' || c == noBreakSpace) + result.append(' '); + else { + if (c >= 0x20 && c < 0x7F) + result.append(c); + else { + unsigned u = c; + String hex = String::format("\\x{%X}", u); + unsigned len = hex.length(); + for (unsigned i = 0; i < len; ++i) + result.append(hex[i]); + } + } + } + result.append('"'); + return String::adopt(result); +} + +static TextStream &operator<<(TextStream& ts, const RenderObject& o) +{ + ts << o.renderName(); + + if (o.style() && o.style()->zIndex()) + ts << " zI: " << o.style()->zIndex(); + + if (o.element()) { + String tagName = getTagName(o.element()); + if (!tagName.isEmpty()) { + ts << " {" << tagName << "}"; + // flag empty or unstyled AppleStyleSpan because we never + // want to leave them in the DOM + if (isEmptyOrUnstyledAppleStyleSpan(o.element())) + ts << " *empty or unstyled AppleStyleSpan*"; + } + } + + IntRect r(o.xPos(), o.yPos(), o.width(), o.height()); + ts << " " << r; + + if (!(o.isText() && !o.isBR())) { + if (o.parent() && (o.parent()->style()->color() != o.style()->color())) + ts << " [color=" << o.style()->color().name() << "]"; + + if (o.parent() && (o.parent()->style()->backgroundColor() != o.style()->backgroundColor()) && + o.style()->backgroundColor().isValid() && o.style()->backgroundColor().rgb()) + // Do not dump invalid or transparent backgrounds, since that is the default. + ts << " [bgcolor=" << o.style()->backgroundColor().name() << "]"; + + if (o.parent() && (o.parent()->style()->textFillColor() != o.style()->textFillColor()) && + o.style()->textFillColor().isValid() && o.style()->textFillColor() != o.style()->color() && + o.style()->textFillColor().rgb()) + ts << " [textFillColor=" << o.style()->textFillColor().name() << "]"; + + if (o.parent() && (o.parent()->style()->textStrokeColor() != o.style()->textStrokeColor()) && + o.style()->textStrokeColor().isValid() && o.style()->textStrokeColor() != o.style()->color() && + o.style()->textStrokeColor().rgb()) + ts << " [textStrokeColor=" << o.style()->textStrokeColor().name() << "]"; + + if (o.parent() && (o.parent()->style()->textStrokeWidth() != o.style()->textStrokeWidth()) && + o.style()->textStrokeWidth() > 0) + ts << " [textStrokeWidth=" << o.style()->textStrokeWidth() << "]"; + + if (o.borderTop() || o.borderRight() || o.borderBottom() || o.borderLeft()) { + ts << " [border:"; + + BorderValue prevBorder; + if (o.style()->borderTop() != prevBorder) { + prevBorder = o.style()->borderTop(); + if (!o.borderTop()) + ts << " none"; + else { + ts << " (" << o.borderTop() << "px "; + printBorderStyle(ts, o, o.style()->borderTopStyle()); + Color col = o.style()->borderTopColor(); + if (!col.isValid()) + col = o.style()->color(); + ts << col.name() << ")"; + } + } + + if (o.style()->borderRight() != prevBorder) { + prevBorder = o.style()->borderRight(); + if (!o.borderRight()) + ts << " none"; + else { + ts << " (" << o.borderRight() << "px "; + printBorderStyle(ts, o, o.style()->borderRightStyle()); + Color col = o.style()->borderRightColor(); + if (!col.isValid()) + col = o.style()->color(); + ts << col.name() << ")"; + } + } + + if (o.style()->borderBottom() != prevBorder) { + prevBorder = o.style()->borderBottom(); + if (!o.borderBottom()) + ts << " none"; + else { + ts << " (" << o.borderBottom() << "px "; + printBorderStyle(ts, o, o.style()->borderBottomStyle()); + Color col = o.style()->borderBottomColor(); + if (!col.isValid()) + col = o.style()->color(); + ts << col.name() << ")"; + } + } + + if (o.style()->borderLeft() != prevBorder) { + prevBorder = o.style()->borderLeft(); + if (!o.borderLeft()) + ts << " none"; + else { + ts << " (" << o.borderLeft() << "px "; + printBorderStyle(ts, o, o.style()->borderLeftStyle()); + Color col = o.style()->borderLeftColor(); + if (!col.isValid()) + col = o.style()->color(); + ts << col.name() << ")"; + } + } + + ts << "]"; + } + } + + if (o.isTableCell()) { + const RenderTableCell& c = static_cast<const RenderTableCell&>(o); + ts << " [r=" << c.row() << " c=" << c.col() << " rs=" << c.rowSpan() << " cs=" << c.colSpan() << "]"; + } + + if (o.isListMarker()) { + String text = static_cast<const RenderListMarker&>(o).text(); + if (!text.isEmpty()) { + if (text.length() != 1) + text = quoteAndEscapeNonPrintables(text); + else { + switch (text[0]) { + case bullet: + text = "bullet"; + break; + case blackSquare: + text = "black square"; + break; + case whiteBullet: + text = "white bullet"; + break; + default: + text = quoteAndEscapeNonPrintables(text); + } + } + ts << ": " << text; + } + } + + return ts; +} + +static void writeTextRun(TextStream& ts, const RenderText& o, const InlineTextBox& run) +{ + ts << "text run at (" << run.m_x << "," << run.m_y << ") width " << run.m_width; + if (run.direction() == RTL || run.m_dirOverride) { + ts << (run.direction() == RTL ? " RTL" : " LTR"); + if (run.m_dirOverride) + ts << " override"; + } + ts << ": " + << quoteAndEscapeNonPrintables(String(o.text()).substring(run.m_start, run.m_len)) + << "\n"; +} + +void write(TextStream& ts, const RenderObject& o, int indent) +{ +#if ENABLE(SVG) + if (o.isRenderPath()) { + write(ts, static_cast<const RenderPath&>(o), indent); + return; + } + if (o.isSVGContainer()) { + write(ts, static_cast<const RenderSVGContainer&>(o), indent); + return; + } + if (o.isSVGRoot()) { + write(ts, static_cast<const RenderSVGRoot&>(o), indent); + return; + } + if (o.isSVGText()) { + if (!o.isText()) + write(ts, static_cast<const RenderSVGText&>(o), indent); + else + write(ts, static_cast<const RenderSVGInlineText&>(o), indent); + return; + } +#endif + + writeIndent(ts, indent); + + ts << o << "\n"; + + if (o.isText() && !o.isBR()) { + const RenderText& text = static_cast<const RenderText&>(o); + for (InlineTextBox* box = text.firstTextBox(); box; box = box->nextTextBox()) { + writeIndent(ts, indent + 1); + writeTextRun(ts, text, *box); + } + } + + for (RenderObject* child = o.firstChild(); child; child = child->nextSibling()) { + if (child->hasLayer()) + continue; + write(ts, *child, indent + 1); + } + + if (o.isWidget()) { + Widget* widget = static_cast<const RenderWidget&>(o).widget(); + if (widget && widget->isFrameView()) { + FrameView* view = static_cast<FrameView*>(widget); + RenderObject* root = view->frame()->contentRenderer(); + if (root) { + view->layout(); + RenderLayer* l = root->layer(); + if (l) + writeLayers(ts, l, l, IntRect(l->xPos(), l->yPos(), l->width(), l->height()), indent + 1); + } + } + } +} + +static void write(TextStream& ts, RenderLayer& l, + const IntRect& layerBounds, const IntRect& backgroundClipRect, const IntRect& clipRect, const IntRect& outlineClipRect, + int layerType = 0, int indent = 0) +{ + writeIndent(ts, indent); + + ts << "layer " << layerBounds; + + if (!layerBounds.isEmpty()) { + if (!backgroundClipRect.contains(layerBounds)) + ts << " backgroundClip " << backgroundClipRect; + if (!clipRect.contains(layerBounds)) + ts << " clip " << clipRect; + if (!outlineClipRect.contains(layerBounds)) + ts << " outlineClip " << outlineClipRect; + } + + if (l.renderer()->hasOverflowClip()) { + if (l.scrollXOffset()) + ts << " scrollX " << l.scrollXOffset(); + if (l.scrollYOffset()) + ts << " scrollY " << l.scrollYOffset(); + if (l.renderer()->clientWidth() != l.scrollWidth()) + ts << " scrollWidth " << l.scrollWidth(); + if (l.renderer()->clientHeight() != l.scrollHeight()) + ts << " scrollHeight " << l.scrollHeight(); + } + + if (layerType == -1) + ts << " layerType: background only"; + else if (layerType == 1) + ts << " layerType: foreground only"; + + ts << "\n"; + + if (layerType != -1) + write(ts, *l.renderer(), indent + 1); +} + +static void writeLayers(TextStream& ts, const RenderLayer* rootLayer, RenderLayer* l, + const IntRect& paintDirtyRect, int indent) +{ + // Calculate the clip rects we should use. + IntRect layerBounds, damageRect, clipRectToApply, outlineRect; + l->calculateRects(rootLayer, paintDirtyRect, layerBounds, damageRect, clipRectToApply, outlineRect, true); + + // Ensure our lists are up-to-date. + l->updateZOrderLists(); + l->updateOverflowList(); + + bool shouldPaint = l->intersectsDamageRect(layerBounds, damageRect, rootLayer); + Vector<RenderLayer*>* negList = l->negZOrderList(); + if (shouldPaint && negList && negList->size() > 0) + write(ts, *l, layerBounds, damageRect, clipRectToApply, outlineRect, -1, indent); + + if (negList) { + for (unsigned i = 0; i != negList->size(); ++i) + writeLayers(ts, rootLayer, negList->at(i), paintDirtyRect, indent); + } + + if (shouldPaint) + write(ts, *l, layerBounds, damageRect, clipRectToApply, outlineRect, negList && negList->size() > 0, indent); + + Vector<RenderLayer*>* overflowList = l->overflowList(); + if (overflowList) { + for (unsigned i = 0; i != overflowList->size(); ++i) + writeLayers(ts, rootLayer, overflowList->at(i), paintDirtyRect, indent); + } + + Vector<RenderLayer*>* posList = l->posZOrderList(); + if (posList) { + for (unsigned i = 0; i != posList->size(); ++i) + writeLayers(ts, rootLayer, posList->at(i), paintDirtyRect, indent); + } +} + +static String nodePosition(Node* node) +{ + String result; + + Node* parent; + for (Node* n = node; n; n = parent) { + parent = n->parentNode(); + if (!parent) + parent = n->shadowParentNode(); + if (n != node) + result += " of "; + if (parent) + result += "child " + String::number(n->nodeIndex()) + " {" + getTagName(n) + "}"; + else + result += "document"; + } + + return result; +} + +static void writeSelection(TextStream& ts, const RenderObject* o) +{ + Node* n = o->element(); + if (!n || !n->isDocumentNode()) + return; + + Document* doc = static_cast<Document*>(n); + Frame* frame = doc->frame(); + if (!frame) + return; + + Selection selection = frame->selection()->selection(); + if (selection.isCaret()) { + ts << "caret: position " << selection.start().offset() << " of " << nodePosition(selection.start().node()); + if (selection.affinity() == UPSTREAM) + ts << " (upstream affinity)"; + ts << "\n"; + } else if (selection.isRange()) + ts << "selection start: position " << selection.start().offset() << " of " << nodePosition(selection.start().node()) << "\n" + << "selection end: position " << selection.end().offset() << " of " << nodePosition(selection.end().node()) << "\n"; +} + +String externalRepresentation(RenderObject* o) +{ + if (!o) + return String(); + + TextStream ts; +#if ENABLE(SVG) + writeRenderResources(ts, o->document()); +#endif + if (o->view()->frameView()) + o->view()->frameView()->layout(); + RenderLayer* l = o->layer(); + if (l) { + writeLayers(ts, l, l, IntRect(l->xPos(), l->yPos(), l->width(), l->height())); + writeSelection(ts, o); + } + return ts.release(); +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderTreeAsText.h b/src/3rdparty/webkit/WebCore/rendering/RenderTreeAsText.h new file mode 100644 index 0000000..daca253 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderTreeAsText.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2003, 2006, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RenderTreeAsText_h +#define RenderTreeAsText_h + +namespace WebCore { + + class RenderObject; + class String; + class TextStream; + + String externalRepresentation(RenderObject*); + void write(TextStream&, const RenderObject&, int indent = 0); + + // Helper function shared with SVGRenderTreeAsText + String quoteAndEscapeNonPrintables(const String&); + +} // namespace WebCore + +#endif // RenderTreeAsText_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderVideo.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderVideo.cpp new file mode 100644 index 0000000..3d9cb3d --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderVideo.cpp @@ -0,0 +1,239 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(VIDEO) +#include "RenderVideo.h" + +#include "Document.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "HTMLNames.h" +#include "HTMLVideoElement.h" +#include "MediaPlayer.h" + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +RenderVideo::RenderVideo(HTMLMediaElement* video) + : RenderMedia(video, video->player() ? video->player()->naturalSize() : IntSize(300, 150)) +{ +} + +RenderVideo::~RenderVideo() +{ + if (MediaPlayer* p = player()) { + p->setVisible(false); + p->setFrameView(0); + } +} + +void RenderVideo::videoSizeChanged() +{ + if (!player()) + return; + IntSize size = player()->naturalSize(); + if (!size.isEmpty() && size != intrinsicSize()) { + setIntrinsicSize(size); + setPrefWidthsDirty(true); + setNeedsLayout(true); + } +} + +IntRect RenderVideo::videoBox() const +{ + IntRect contentRect = contentBox(); + + if (intrinsicSize().isEmpty() || contentRect.isEmpty()) + return IntRect(); + + IntRect resultRect = contentRect; + int ratio = contentRect.width() * intrinsicSize().height() - contentRect.height() * intrinsicSize().width(); + if (ratio > 0) { + int newWidth = contentRect.height() * intrinsicSize().width() / intrinsicSize().height(); + // Just fill the whole area if the difference is one pixel or less (in both sides) + if (resultRect.width() - newWidth > 2) + resultRect.setWidth(newWidth); + resultRect.move((contentRect.width() - resultRect.width()) / 2, 0); + } else if (ratio < 0) { + int newHeight = contentRect.width() * intrinsicSize().height() / intrinsicSize().width(); + if (resultRect.height() - newHeight > 2) + resultRect.setHeight(newHeight); + resultRect.move(0, (contentRect.height() - resultRect.height()) / 2); + } + return resultRect; +} + +void RenderVideo::paintReplaced(PaintInfo& paintInfo, int tx, int ty) +{ + MediaPlayer* mediaPlayer = player(); + if (!mediaPlayer) + return; + updatePlayer(); + IntRect rect = videoBox(); + if (rect.isEmpty()) + return; + rect.move(tx, ty); + mediaPlayer->paint(paintInfo.context, rect); +} + +void RenderVideo::layout() +{ + RenderMedia::layout(); + updatePlayer(); +} + +void RenderVideo::updateFromElement() +{ + RenderMedia::updateFromElement(); + updatePlayer(); +} + +void RenderVideo::updatePlayer() +{ + MediaPlayer* mediaPlayer = player(); + if (!mediaPlayer) + return; + if (!mediaElement()->inActiveDocument()) { + mediaPlayer->setVisible(false); + return; + } + + // FIXME: This doesn't work correctly with transforms. + FloatPoint absPos = localToAbsolute(); + IntRect videoBounds = videoBox(); + videoBounds.move(absPos.x(), absPos.y()); + mediaPlayer->setFrameView(document()->view()); + mediaPlayer->setRect(videoBounds); + mediaPlayer->setVisible(true); +} + +bool RenderVideo::isWidthSpecified() const +{ + switch (style()->width().type()) { + case Fixed: + case Percent: + return true; + case Auto: + case Relative: // FIXME: Shouldn't this case return true? It doesn't for images. + case Static: + case Intrinsic: + case MinIntrinsic: + return false; + } + ASSERT(false); + return false; +} + +bool RenderVideo::isHeightSpecified() const +{ + switch (style()->height().type()) { + case Fixed: + case Percent: + return true; + case Auto: + case Relative: // FIXME: Shouldn't this case return true? It doesn't for images. + case Static: + case Intrinsic: + case MinIntrinsic: + return false; + } + ASSERT(false); + return false; +} + +int RenderVideo::calcReplacedWidth(bool includeMaxWidth) const +{ + int width; + if (isWidthSpecified()) + width = calcReplacedWidthUsing(style()->width()); + else + width = calcAspectRatioWidth() * style()->effectiveZoom(); + + int minW = calcReplacedWidthUsing(style()->minWidth()); + int maxW = !includeMaxWidth || style()->maxWidth().isUndefined() ? width : calcReplacedWidthUsing(style()->maxWidth()); + + return max(minW, min(width, maxW)); +} + +int RenderVideo::calcReplacedHeight() const +{ + int height; + if (isHeightSpecified()) + height = calcReplacedHeightUsing(style()->height()); + else + height = calcAspectRatioHeight() * style()->effectiveZoom(); + + int minH = calcReplacedHeightUsing(style()->minHeight()); + int maxH = style()->maxHeight().isUndefined() ? height : calcReplacedHeightUsing(style()->maxHeight()); + + return max(minH, min(height, maxH)); +} + +int RenderVideo::calcAspectRatioWidth() const +{ + int intrinsicWidth = intrinsicSize().width(); + int intrinsicHeight = intrinsicSize().height(); + if (!intrinsicHeight) + return 0; + return RenderBox::calcReplacedHeight() * intrinsicWidth / intrinsicHeight; +} + +int RenderVideo::calcAspectRatioHeight() const +{ + int intrinsicWidth = intrinsicSize().width(); + int intrinsicHeight = intrinsicSize().height(); + if (!intrinsicWidth) + return 0; + return RenderBox::calcReplacedWidth() * intrinsicHeight / intrinsicWidth; +} + +void RenderVideo::calcPrefWidths() +{ + ASSERT(prefWidthsDirty()); + + int paddingAndBorders = paddingLeft() + paddingRight() + borderLeft() + borderRight(); + m_maxPrefWidth = calcReplacedWidth(false) + paddingAndBorders; + + if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) + m_maxPrefWidth = min(m_maxPrefWidth, style()->maxWidth().value() + (style()->boxSizing() == CONTENT_BOX ? paddingAndBorders : 0)); + + if (style()->width().isPercent() || style()->height().isPercent() || + style()->maxWidth().isPercent() || style()->maxHeight().isPercent() || + style()->minWidth().isPercent() || style()->minHeight().isPercent()) + m_minPrefWidth = 0; + else + m_minPrefWidth = m_maxPrefWidth; + + setPrefWidthsDirty(false); +} + +} // namespace WebCore + +#endif diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderVideo.h b/src/3rdparty/webkit/WebCore/rendering/RenderVideo.h new file mode 100644 index 0000000..43c1e7b --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderVideo.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef RenderVideo_h +#define RenderVideo_h + +#if ENABLE(VIDEO) + +#include "RenderMedia.h" + +namespace WebCore { + +class HTMLMediaElement; + +class RenderVideo : public RenderMedia { +public: + RenderVideo(HTMLMediaElement*); + virtual ~RenderVideo(); + + virtual const char* renderName() const { return "RenderVideo"; } + + virtual void paintReplaced(PaintInfo& paintInfo, int tx, int ty); + + virtual void layout(); + + virtual int calcReplacedWidth(bool includeMaxWidth = true) const; + virtual int calcReplacedHeight() const; + + virtual void calcPrefWidths(); + + void videoSizeChanged(); + + void updateFromElement(); + +protected: + virtual void intrinsicSizeChanged() { videoSizeChanged(); } + +private: + int calcAspectRatioWidth() const; + int calcAspectRatioHeight() const; + + bool isWidthSpecified() const; + bool isHeightSpecified() const; + + IntRect videoBox() const; + + void updatePlayer(); +}; + +} // namespace WebCore + +#endif +#endif // RenderVideo_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderView.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderView.cpp new file mode 100644 index 0000000..1715509 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderView.cpp @@ -0,0 +1,611 @@ +/** + * This file is part of the HTML widget for KDE. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. + * + * 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" +#include "RenderView.h" + +#include "Document.h" +#include "Element.h" +#include "Frame.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "RenderLayer.h" + +namespace WebCore { + +RenderView::RenderView(Node* node, FrameView* view) + : RenderBlock(node) + , m_frameView(view) + , m_selectionStart(0) + , m_selectionEnd(0) + , m_selectionStartPos(-1) + , m_selectionEndPos(-1) + , m_printImages(true) + , m_maximalOutlineSize(0) + , m_layoutState(0) + , m_layoutStateDisableCount(0) +{ + // Clear our anonymous bit, set because RenderObject assumes + // any renderer with document as the node is anonymous. + setIsAnonymous(false); + + // init RenderObject attributes + setInline(false); + + // try to contrain the width to the views width + m_width = 0; + m_height = 0; + m_minPrefWidth = 0; + m_maxPrefWidth = 0; + + setPrefWidthsDirty(true, false); + + setPositioned(true); // to 0,0 :) + + // Create a new root layer for our layer hierarchy. + m_layer = new (node->document()->renderArena()) RenderLayer(this); + setHasLayer(true); +} + +RenderView::~RenderView() +{ +} + +void RenderView::calcHeight() +{ + if (!printing() && m_frameView) + m_height = viewHeight(); +} + +void RenderView::calcWidth() +{ + if (!printing() && m_frameView) + m_width = viewWidth(); + m_marginLeft = 0; + m_marginRight = 0; +} + +void RenderView::calcPrefWidths() +{ + ASSERT(prefWidthsDirty()); + + RenderBlock::calcPrefWidths(); + + m_maxPrefWidth = m_minPrefWidth; +} + +void RenderView::layout() +{ + if (printing()) + m_minPrefWidth = m_maxPrefWidth = m_width; + + // Use calcWidth/Height to get the new width/height, since this will take the full page zoom factor into account. + bool relayoutChildren = !printing() && (!m_frameView || m_width != viewWidth() || m_height != viewHeight()); + if (relayoutChildren) { + setChildNeedsLayout(true, false); + for (RenderObject* child = firstChild(); child; child = child->nextSibling()) { + if (child->style()->height().isPercent() || child->style()->minHeight().isPercent() || child->style()->maxHeight().isPercent()) + child->setChildNeedsLayout(true, false); + } + } + + ASSERT(!m_layoutState); + LayoutState state; + // FIXME: May be better to push a clip and avoid issuing offscreen repaints. + state.m_clipped = false; + m_layoutState = &state; + + if (needsLayout()) + RenderBlock::layout(); + + // Ensure that docWidth() >= width() and docHeight() >= height(). + setOverflowWidth(m_width); + setOverflowHeight(m_height); + + setOverflowWidth(docWidth()); + setOverflowHeight(docHeight()); + + ASSERT(layoutDelta() == IntSize()); + ASSERT(m_layoutStateDisableCount == 0); + ASSERT(m_layoutState == &state); + m_layoutState = 0; + setNeedsLayout(false); +} + +FloatPoint RenderView::localToAbsolute(FloatPoint localPoint, bool fixed, bool) const +{ + if (fixed && m_frameView) + localPoint += m_frameView->scrollOffset(); + + return localPoint; +} + +FloatPoint RenderView::absoluteToLocal(FloatPoint containerPoint, bool fixed, bool) const +{ + if (fixed && m_frameView) + containerPoint -= m_frameView->scrollOffset(); + + return containerPoint; +} + +FloatQuad RenderView::localToAbsoluteQuad(const FloatQuad& localQuad, bool fixed) const +{ + FloatQuad quad = localQuad; + if (fixed && m_frameView) + quad += m_frameView->scrollOffset(); + + return quad; +} + +void RenderView::paint(PaintInfo& paintInfo, int tx, int ty) +{ + // If we ever require layout but receive a paint anyway, something has gone horribly wrong. + ASSERT(!needsLayout()); + + // Cache the print rect because the dirty rect could get changed during painting. + if (printing()) + setPrintRect(paintInfo.rect); + else + setPrintRect(IntRect()); + paintObject(paintInfo, tx, ty); +} + +static inline bool rendererObscuresBackground(RenderObject* object) +{ + return object && object->style()->visibility() == VISIBLE && object->style()->opacity() == 1 && !object->style()->hasTransform(); +} + +void RenderView::paintBoxDecorations(PaintInfo& paintInfo, int, int) +{ + // Check to see if we are enclosed by a layer that requires complex painting rules. If so, we cannot blit + // when scrolling, and we need to use slow repaints. Examples of layers that require this are transparent layers, + // layers with reflections, or transformed layers. + // FIXME: This needs to be dynamic. We should be able to go back to blitting if we ever stop being inside + // a transform, transparency layer, etc. + Element* elt; + for (elt = document()->ownerElement(); view() && elt && elt->renderer(); elt = elt->document()->ownerElement()) { + RenderLayer* layer = elt->renderer()->enclosingLayer(); + if (layer->requiresSlowRepaints()) { + frameView()->setUseSlowRepaints(); + break; + } + } + + // If painting will entirely fill the view, no need to fill the background. + if (elt || rendererObscuresBackground(firstChild()) || !view()) + return; + + // This code typically only executes if the root element's visibility has been set to hidden, + // or there is a transform on the <html>. + // Only fill with the base background color (typically white) if we're the root document, + // since iframes/frames with no background in the child document should show the parent's background. + if (view()->isTransparent()) // FIXME: This needs to be dynamic. We should be able to go back to blitting if we ever stop being transparent. + frameView()->setUseSlowRepaints(); // The parent must show behind the child. + else { + Color baseColor = frameView()->baseBackgroundColor(); + if (baseColor.alpha() > 0) { + paintInfo.context->save(); + paintInfo.context->setCompositeOperation(CompositeCopy); + paintInfo.context->fillRect(paintInfo.rect, baseColor); + paintInfo.context->restore(); + } else + paintInfo.context->clearRect(paintInfo.rect); + } +} + +void RenderView::repaintViewRectangle(const IntRect& ur, bool immediate) +{ + if (printing() || ur.width() == 0 || ur.height() == 0) + return; + + if (!m_frameView) + return; + + // We always just invalidate the root view, since we could be an iframe that is clipped out + // or even invisible. + Element* elt = document()->ownerElement(); + if (!elt) + m_frameView->repaintContentRectangle(ur, immediate); + else if (RenderObject* obj = elt->renderer()) { + IntRect vr = viewRect(); + IntRect r = intersection(ur, vr); + + // Subtract out the contentsX and contentsY offsets to get our coords within the viewing + // rectangle. + r.move(-vr.x(), -vr.y()); + + // FIXME: Hardcoded offsets here are not good. + r.move(obj->borderLeft() + obj->paddingLeft(), + obj->borderTop() + obj->paddingTop()); + obj->repaintRectangle(r, immediate); + } +} + +void RenderView::computeAbsoluteRepaintRect(IntRect& rect, bool fixed) +{ + if (printing()) + return; + + if (fixed && m_frameView) + rect.move(m_frameView->scrollX(), m_frameView->scrollY()); + + // Apply our transform if we have one (because of full page zooming). + if (m_layer && m_layer->transform()) + rect = m_layer->transform()->mapRect(rect); +} + +void RenderView::absoluteRects(Vector<IntRect>& rects, int tx, int ty, bool) +{ + rects.append(IntRect(tx, ty, m_layer->width(), m_layer->height())); +} + +void RenderView::absoluteQuads(Vector<FloatQuad>& quads, bool) +{ + quads.append(FloatRect(0, 0, m_layer->width(), m_layer->height())); +} + +RenderObject* rendererAfterPosition(RenderObject* object, unsigned offset) +{ + if (!object) + return 0; + + RenderObject* child = object->childAt(offset); + return child ? child : object->nextInPreOrderAfterChildren(); +} + +IntRect RenderView::selectionRect(bool clipToVisibleContent) +{ + // The virtual selectionRect() should never be called on the RenderView. + // We assert because there used to be ambiguity between + // RenderView::selectionRect(bool) and + // virtual RenderObject::selectionRect(bool) const + ASSERT_NOT_REACHED(); + return RenderBlock::selectionRect(clipToVisibleContent); +} + +IntRect RenderView::selectionBounds(bool clipToVisibleContent) const +{ + document()->updateRendering(); + + typedef HashMap<RenderObject*, SelectionInfo*> SelectionMap; + SelectionMap selectedObjects; + + RenderObject* os = m_selectionStart; + RenderObject* stop = rendererAfterPosition(m_selectionEnd, m_selectionEndPos); + while (os && os != stop) { + if ((os->canBeSelectionLeaf() || os == m_selectionStart || os == m_selectionEnd) && os->selectionState() != SelectionNone) { + // Blocks are responsible for painting line gaps and margin gaps. They must be examined as well. + selectedObjects.set(os, new SelectionInfo(os, clipToVisibleContent)); + RenderBlock* cb = os->containingBlock(); + while (cb && !cb->isRenderView()) { + SelectionInfo* blockInfo = selectedObjects.get(cb); + if (blockInfo) + break; + selectedObjects.set(cb, new SelectionInfo(cb, clipToVisibleContent)); + cb = cb->containingBlock(); + } + } + + os = os->nextInPreOrder(); + } + + // Now create a single bounding box rect that encloses the whole selection. + IntRect selRect; + SelectionMap::iterator end = selectedObjects.end(); + for (SelectionMap::iterator i = selectedObjects.begin(); i != end; ++i) { + SelectionInfo* info = i->second; + selRect.unite(info->rect()); + delete info; + } + return selRect; +} + +void RenderView::setSelection(RenderObject* start, int startPos, RenderObject* end, int endPos) +{ + // Make sure both our start and end objects are defined. + // Check www.msnbc.com and try clicking around to find the case where this happened. + if ((start && !end) || (end && !start)) + return; + + // Just return if the selection hasn't changed. + if (m_selectionStart == start && m_selectionStartPos == startPos && + m_selectionEnd == end && m_selectionEndPos == endPos) + return; + + // Record the old selected objects. These will be used later + // when we compare against the new selected objects. + int oldStartPos = m_selectionStartPos; + int oldEndPos = m_selectionEndPos; + + // Objects each have a single selection rect to examine. + typedef HashMap<RenderObject*, SelectionInfo*> SelectedObjectMap; + SelectedObjectMap oldSelectedObjects; + SelectedObjectMap newSelectedObjects; + + // Blocks contain selected objects and fill gaps between them, either on the left, right, or in between lines and blocks. + // In order to get the repaint rect right, we have to examine left, middle, and right rects individually, since otherwise + // the union of those rects might remain the same even when changes have occurred. + typedef HashMap<RenderBlock*, BlockSelectionInfo*> SelectedBlockMap; + SelectedBlockMap oldSelectedBlocks; + SelectedBlockMap newSelectedBlocks; + + RenderObject* os = m_selectionStart; + RenderObject* stop = rendererAfterPosition(m_selectionEnd, m_selectionEndPos); + while (os && os != stop) { + if ((os->canBeSelectionLeaf() || os == m_selectionStart || os == m_selectionEnd) && os->selectionState() != SelectionNone) { + // Blocks are responsible for painting line gaps and margin gaps. They must be examined as well. + oldSelectedObjects.set(os, new SelectionInfo(os, true)); + RenderBlock* cb = os->containingBlock(); + while (cb && !cb->isRenderView()) { + BlockSelectionInfo* blockInfo = oldSelectedBlocks.get(cb); + if (blockInfo) + break; + oldSelectedBlocks.set(cb, new BlockSelectionInfo(cb)); + cb = cb->containingBlock(); + } + } + + os = os->nextInPreOrder(); + } + + // Now clear the selection. + SelectedObjectMap::iterator oldObjectsEnd = oldSelectedObjects.end(); + for (SelectedObjectMap::iterator i = oldSelectedObjects.begin(); i != oldObjectsEnd; ++i) + i->first->setSelectionState(SelectionNone); + + // set selection start and end + m_selectionStart = start; + m_selectionStartPos = startPos; + m_selectionEnd = end; + m_selectionEndPos = endPos; + + // Update the selection status of all objects between m_selectionStart and m_selectionEnd + if (start && start == end) + start->setSelectionState(SelectionBoth); + else { + if (start) + start->setSelectionState(SelectionStart); + if (end) + end->setSelectionState(SelectionEnd); + } + + RenderObject* o = start; + stop = rendererAfterPosition(end, endPos); + + while (o && o != stop) { + if (o != start && o != end && o->canBeSelectionLeaf()) + o->setSelectionState(SelectionInside); + o = o->nextInPreOrder(); + } + + // Now that the selection state has been updated for the new objects, walk them again and + // put them in the new objects list. + o = start; + while (o && o != stop) { + if ((o->canBeSelectionLeaf() || o == start || o == end) && o->selectionState() != SelectionNone) { + newSelectedObjects.set(o, new SelectionInfo(o, true)); + RenderBlock* cb = o->containingBlock(); + while (cb && !cb->isRenderView()) { + BlockSelectionInfo* blockInfo = newSelectedBlocks.get(cb); + if (blockInfo) + break; + newSelectedBlocks.set(cb, new BlockSelectionInfo(cb)); + cb = cb->containingBlock(); + } + } + + o = o->nextInPreOrder(); + } + + if (!m_frameView) { + // We built the maps, but we aren't going to use them. + // We need to delete the values, otherwise they'll all leak! + deleteAllValues(oldSelectedObjects); + deleteAllValues(newSelectedObjects); + deleteAllValues(oldSelectedBlocks); + deleteAllValues(newSelectedBlocks); + return; + } + + // Have any of the old selected objects changed compared to the new selection? + for (SelectedObjectMap::iterator i = oldSelectedObjects.begin(); i != oldObjectsEnd; ++i) { + RenderObject* obj = i->first; + SelectionInfo* newInfo = newSelectedObjects.get(obj); + SelectionInfo* oldInfo = i->second; + if (!newInfo || oldInfo->rect() != newInfo->rect() || oldInfo->state() != newInfo->state() || + (m_selectionStart == obj && oldStartPos != m_selectionStartPos) || + (m_selectionEnd == obj && oldEndPos != m_selectionEndPos)) { + repaintViewRectangle(oldInfo->rect()); + if (newInfo) { + repaintViewRectangle(newInfo->rect()); + newSelectedObjects.remove(obj); + delete newInfo; + } + } + delete oldInfo; + } + + // Any new objects that remain were not found in the old objects dict, and so they need to be updated. + SelectedObjectMap::iterator newObjectsEnd = newSelectedObjects.end(); + for (SelectedObjectMap::iterator i = newSelectedObjects.begin(); i != newObjectsEnd; ++i) { + SelectionInfo* newInfo = i->second; + repaintViewRectangle(newInfo->rect()); + delete newInfo; + } + + // Have any of the old blocks changed? + SelectedBlockMap::iterator oldBlocksEnd = oldSelectedBlocks.end(); + for (SelectedBlockMap::iterator i = oldSelectedBlocks.begin(); i != oldBlocksEnd; ++i) { + RenderBlock* block = i->first; + BlockSelectionInfo* newInfo = newSelectedBlocks.get(block); + BlockSelectionInfo* oldInfo = i->second; + if (!newInfo || oldInfo->rects() != newInfo->rects() || oldInfo->state() != newInfo->state()) { + repaintViewRectangle(oldInfo->rects()); + if (newInfo) { + repaintViewRectangle(newInfo->rects()); + newSelectedBlocks.remove(block); + delete newInfo; + } + } + delete oldInfo; + } + + // Any new blocks that remain were not found in the old blocks dict, and so they need to be updated. + SelectedBlockMap::iterator newBlocksEnd = newSelectedBlocks.end(); + for (SelectedBlockMap::iterator i = newSelectedBlocks.begin(); i != newBlocksEnd; ++i) { + BlockSelectionInfo* newInfo = i->second; + repaintViewRectangle(newInfo->rects()); + delete newInfo; + } +} + +void RenderView::clearSelection() +{ + setSelection(0, -1, 0, -1); +} + +void RenderView::selectionStartEnd(int& startPos, int& endPos) const +{ + startPos = m_selectionStartPos; + endPos = m_selectionEndPos; +} + +bool RenderView::printing() const +{ + return document()->printing(); +} + +void RenderView::updateWidgetPositions() +{ + RenderObjectSet::iterator end = m_widgets.end(); + for (RenderObjectSet::iterator it = m_widgets.begin(); it != end; ++it) + (*it)->updateWidgetPosition(); +} + +void RenderView::addWidget(RenderObject* o) +{ + m_widgets.add(o); +} + +void RenderView::removeWidget(RenderObject* o) +{ + m_widgets.remove(o); +} + +IntRect RenderView::viewRect() const +{ + if (printing()) + return IntRect(0, 0, m_width, m_height); + if (m_frameView) + return m_frameView->visibleContentRect(); + return IntRect(); +} + +int RenderView::docHeight() const +{ + int h = m_height; + int lowestPos = lowestPosition(); + if (lowestPos > h) + h = lowestPos; + + // FIXME: This doesn't do any margin collapsing. + // Instead of this dh computation we should keep the result + // when we call RenderBlock::layout. + int dh = 0; + for (RenderObject* c = firstChild(); c; c = c->nextSibling()) + dh += c->height() + c->marginTop() + c->marginBottom(); + + if (dh > h) + h = dh; + + return h; +} + +int RenderView::docWidth() const +{ + int w = m_width; + int rightmostPos = rightmostPosition(); + if (rightmostPos > w) + w = rightmostPos; + + for (RenderObject *c = firstChild(); c; c = c->nextSibling()) { + int dw = c->width() + c->marginLeft() + c->marginRight(); + if (dw > w) + w = dw; + } + + return w; +} + +int RenderView::viewHeight() const +{ + int height = 0; + if (!printing() && m_frameView) { + height = m_frameView->layoutHeight(); + height = m_frameView->useFixedLayout() ? ceilf(style()->effectiveZoom() * float(height)) : height; + } + return height; +} + +int RenderView::viewWidth() const +{ + int width = 0; + if (!printing() && m_frameView) { + width = m_frameView->layoutWidth(); + width = m_frameView->useFixedLayout() ? ceilf(style()->effectiveZoom() * float(width)) : width; + } + return width; +} + +// The idea here is to take into account what object is moving the pagination point, and +// thus choose the best place to chop it. +void RenderView::setBestTruncatedAt(int y, RenderObject* forRenderer, bool forcedBreak) +{ + // Nobody else can set a page break once we have a forced break. + if (m_forcedPageBreak) + return; + + // Forced breaks always win over unforced breaks. + if (forcedBreak) { + m_forcedPageBreak = true; + m_bestTruncatedAt = y; + return; + } + + // prefer the widest object who tries to move the pagination point + int width = forRenderer->width(); + if (width > m_truncatorWidth) { + m_truncatorWidth = width; + m_bestTruncatedAt = y; + } +} + +void RenderView::pushLayoutState(RenderObject* root) +{ + ASSERT(!m_frameView->needsFullRepaint()); + ASSERT(m_layoutStateDisableCount == 0); + ASSERT(m_layoutState == 0); + + m_layoutState = new (renderArena()) LayoutState(root); +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderView.h b/src/3rdparty/webkit/WebCore/rendering/RenderView.h new file mode 100644 index 0000000..96597de --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderView.h @@ -0,0 +1,226 @@ +/* + * This file is part of the HTML widget for KDE. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * Copyright (C) 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef RenderView_h +#define RenderView_h + +#include "FrameView.h" +#include "Frame.h" +#include "LayoutState.h" +#include "RenderBlock.h" + +namespace WebCore { + +class RenderView : public RenderBlock { +public: + RenderView(Node*, FrameView*); + virtual ~RenderView(); + + virtual const char* renderName() const { return "RenderView"; } + + virtual bool isRenderView() const { return true; } + + virtual void layout(); + virtual void calcWidth(); + virtual void calcHeight(); + virtual void calcPrefWidths(); + virtual FloatPoint localToAbsolute(FloatPoint localPoint = FloatPoint(), bool fixed = false, bool useTransforms = false) const; + virtual FloatPoint absoluteToLocal(FloatPoint containerPoint, bool fixed = false, bool useTransforms = false) const; + virtual FloatQuad localToAbsoluteQuad(const FloatQuad&, bool fixed = false) const; + + int docHeight() const; + int docWidth() const; + + // The same as the FrameView's layoutHeight/layoutWidth but with null check guards. + int viewHeight() const; + int viewWidth() const; + + float zoomFactor() const { return m_frameView->frame() && m_frameView->frame()->shouldApplyPageZoom() ? m_frameView->frame()->zoomFactor() : 1.0f; } + + FrameView* frameView() const { return m_frameView; } + + virtual bool hasOverhangingFloats() { return false; } + + virtual void computeAbsoluteRepaintRect(IntRect&, bool fixed = false); + virtual void repaintViewRectangle(const IntRect&, bool immediate = false); + + virtual void paint(PaintInfo&, int tx, int ty); + virtual void paintBoxDecorations(PaintInfo&, int tx, int ty); + + void setSelection(RenderObject* start, int startPos, RenderObject* end, int endPos); + void clearSelection(); + virtual RenderObject* selectionStart() const { return m_selectionStart; } + virtual RenderObject* selectionEnd() const { return m_selectionEnd; } + + bool printing() const; + void setPrintImages(bool enable) { m_printImages = enable; } + bool printImages() const { return m_printImages; } + void setTruncatedAt(int y) { m_truncatedAt = y; m_bestTruncatedAt = m_truncatorWidth = 0; m_forcedPageBreak = false; } + void setBestTruncatedAt(int y, RenderObject *forRenderer, bool forcedBreak = false); + int bestTruncatedAt() const { return m_bestTruncatedAt; } + + int truncatedAt() const { return m_truncatedAt; } + + virtual void absoluteRects(Vector<IntRect>&, int tx, int ty, bool topLevel = true); + virtual void absoluteQuads(Vector<FloatQuad>&, bool topLevel = true); + + IntRect selectionBounds(bool clipToVisibleContent = true) const; + + void setMaximalOutlineSize(int o) { m_maximalOutlineSize = o; } + int maximalOutlineSize() const { return m_maximalOutlineSize; } + + virtual IntRect viewRect() const; + + virtual void selectionStartEnd(int& startPos, int& endPos) const; + + IntRect printRect() const { return m_printRect; } + void setPrintRect(const IntRect& r) { m_printRect = r; } + + void updateWidgetPositions(); + void addWidget(RenderObject*); + void removeWidget(RenderObject*); + + // layoutDelta is used transiently during layout to store how far an object has moved from its + // last layout location, in order to repaint correctly + const IntSize& layoutDelta() const { return m_layoutDelta; } + void addLayoutDelta(const IntSize& delta) { m_layoutDelta += delta; } + + void pushLayoutState(RenderBox* renderer, const IntSize& offset) + { + if (m_layoutStateDisableCount || m_frameView->needsFullRepaint()) + return; + m_layoutState = new (renderArena()) LayoutState(m_layoutState, renderer, offset); + } + + void pushLayoutState(RenderObject*); + + void popLayoutState() + { + if (m_layoutStateDisableCount || m_frameView->needsFullRepaint()) + return; + LayoutState* state = m_layoutState; + m_layoutState = state->m_next; + state->destroy(renderArena()); + } + + LayoutState* layoutState() const { return m_layoutStateDisableCount ? 0 : m_layoutState; } + + // Suspends the LayoutState optimization. Used under transforms that cannot be represented by + // LayoutState (common in SVG) and when manipulating the render tree during layout in ways + // that can trigger repaint of a non-child (e.g. when a list item moves its list marker around). + void disableLayoutState() { m_layoutStateDisableCount++; } + void enableLayoutState() { ASSERT(m_layoutStateDisableCount > 0); m_layoutStateDisableCount--; } + +private: + // selectionRect should never be called on a RenderView + virtual IntRect selectionRect(bool); + +protected: + FrameView* m_frameView; + + RenderObject* m_selectionStart; + RenderObject* m_selectionEnd; + int m_selectionStartPos; + int m_selectionEndPos; + + // used to ignore viewport width when printing to the printer + bool m_printImages; + int m_truncatedAt; + + int m_maximalOutlineSize; // Used to apply a fudge factor to dirty-rect checks on blocks/tables. + IntRect m_printRect; // Used when printing. + + typedef HashSet<RenderObject*> RenderObjectSet; + + RenderObjectSet m_widgets; + +private: + int m_bestTruncatedAt; + int m_truncatorWidth; + bool m_forcedPageBreak; + IntSize m_layoutDelta; + LayoutState* m_layoutState; + unsigned m_layoutStateDisableCount; +}; + +// Stack-based class to assist with LayoutState push/pop +class LayoutStateMaintainer : Noncopyable { +public: + // ctor to push now + LayoutStateMaintainer(RenderView* view, RenderBox* root, IntSize offset, bool shouldPush = true) + : m_view(view) + , m_shouldPushPop(shouldPush) + , m_didStart(false) + , m_didEnd(false) + { + push(root, offset); + } + + // ctor to maybe push later + LayoutStateMaintainer(RenderView* view) + : m_view(view) + , m_shouldPushPop(true) + , m_didStart(false) + , m_didEnd(false) + { + } + + ~LayoutStateMaintainer() + { + ASSERT(m_didStart == m_didEnd); // if this fires, it means that someone did a push(), but forgot to pop(). + } + + void pop() + { + if (m_didStart) { + ASSERT(!m_didEnd); + if (m_shouldPushPop) + m_view->popLayoutState(); + else + m_view->enableLayoutState(); + m_didEnd = true; + } + } + + void push(RenderBox* root, IntSize offset) + { + ASSERT(!m_didStart); + if (m_shouldPushPop) + m_view->pushLayoutState(root, offset); + else + m_view->disableLayoutState(); + m_didStart = true; + } + + bool didPush() const { return m_didStart; } + +private: + RenderView* m_view; + bool m_shouldPushPop : 1; // true if we should push/pop, rather than disable/enable + bool m_didStart : 1; // true if we did a push or disable + bool m_didEnd : 1; // true if we popped or re-enabled +}; + +} // namespace WebCore + +#endif // RenderView_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderWidget.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderWidget.cpp new file mode 100644 index 0000000..1a9744a --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderWidget.cpp @@ -0,0 +1,278 @@ +/** + * This file is part of the HTML widget for KDE. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * Copyright (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2004, 2006 Apple Computer, Inc. + * + * 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" +#include "RenderWidget.h" + +#include "AnimationController.h" +#include "AXObjectCache.h" +#include "Document.h" +#include "Element.h" +#include "Event.h" +#include "FrameView.h" +#include "GraphicsContext.h" +#include "HitTestResult.h" +#include "RenderLayer.h" +#include "RenderView.h" + +using namespace std; + +namespace WebCore { + +static HashMap<const Widget*, RenderWidget*>& widgetRendererMap() +{ + static HashMap<const Widget*, RenderWidget*>* staticWidgetRendererMap = new HashMap<const Widget*, RenderWidget*>; + return *staticWidgetRendererMap; +} + +RenderWidget::RenderWidget(Node* node) + : RenderReplaced(node) + , m_widget(0) + , m_refCount(0) +{ + // a replaced element doesn't support being anonymous + ASSERT(node); + m_view = node->document()->view(); + + view()->addWidget(this); + + // Reference counting is used to prevent the widget from being + // destroyed while inside the Widget code, which might not be + // able to handle that. + ref(); +} + +void RenderWidget::destroy() +{ + // We can't call the base class's destroy because we don't + // want to unconditionally delete ourselves (we're ref-counted). + // So the code below includes copied and pasted contents of + // both RenderBox::destroy() and RenderObject::destroy(). + // Fix originally made for <rdar://problem/4228818>. + animation()->cancelAnimations(this); + + if (RenderView* v = view()) + v->removeWidget(this); + + if (AXObjectCache::accessibilityEnabled()) + document()->axObjectCache()->remove(this); + + remove(); + + if (m_widget) { + if (m_view) + m_view->removeChild(m_widget); + widgetRendererMap().remove(m_widget); + } + + // removes from override size map + if (hasOverrideSize()) + setOverrideSize(-1); + + RenderLayer* layer = m_layer; + RenderArena* arena = renderArena(); + + if (layer) + layer->clearClipRects(); + + if (style() && (style()->height().isPercent() || style()->minHeight().isPercent() || style()->maxHeight().isPercent())) + RenderBlock::removePercentHeightDescendant(this); + + setNode(0); + deref(arena); + + if (layer) + layer->destroy(arena); +} + +RenderWidget::~RenderWidget() +{ + ASSERT(m_refCount <= 0); + deleteWidget(); +} + +void RenderWidget::setWidgetGeometry(const IntRect& frame) +{ + if (element() && m_widget->frameRect() != frame) { + RenderArena* arena = ref(); + RefPtr<Node> protectedElement(element()); + m_widget->setFrameRect(frame); + deref(arena); + } +} + +void RenderWidget::setWidget(Widget* widget) +{ + if (widget != m_widget) { + if (m_widget) { + m_widget->removeFromParent(); + widgetRendererMap().remove(m_widget); + deleteWidget(); + } + m_widget = widget; + if (m_widget) { + widgetRendererMap().add(m_widget, this); + // if we've already received a layout, apply the calculated space to the + // widget immediately, but we have to have really been full constructed (with a non-null + // style pointer). + if (style()) { + if (!needsLayout()) + setWidgetGeometry(absoluteContentBox()); + if (style()->visibility() != VISIBLE) + m_widget->hide(); + else + m_widget->show(); + } + m_view->addChild(m_widget); + } + } +} + +void RenderWidget::layout() +{ + ASSERT(needsLayout()); + + setNeedsLayout(false); +} + +void RenderWidget::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle) +{ + RenderReplaced::styleDidChange(diff, oldStyle); + if (m_widget) { + if (style()->visibility() != VISIBLE) + m_widget->hide(); + else + m_widget->show(); + } +} + +void RenderWidget::paint(PaintInfo& paintInfo, int tx, int ty) +{ + if (!shouldPaint(paintInfo, tx, ty)) + return; + + tx += m_x; + ty += m_y; + + if (hasBoxDecorations() && (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection)) + paintBoxDecorations(paintInfo, tx, ty); + + if (paintInfo.phase == PaintPhaseMask) { + paintMask(paintInfo, tx, ty); + return; + } + + if (!m_view || paintInfo.phase != PaintPhaseForeground || style()->visibility() != VISIBLE) + return; + +#if PLATFORM(MAC) + if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled()) + paintCustomHighlight(tx - m_x, ty - m_y, style()->highlight(), true); +#endif + + if (m_widget) { + // Move the widget if necessary. We normally move and resize widgets during layout, but sometimes + // widgets can move without layout occurring (most notably when you scroll a document that + // contains fixed positioned elements). + m_widget->move(tx + borderLeft() + paddingLeft(), ty + borderTop() + paddingTop()); + + // Tell the widget to paint now. This is the only time the widget is allowed + // to paint itself. That way it will composite properly with z-indexed layers. + m_widget->paint(paintInfo.context, paintInfo.rect); + } + + // Paint a partially transparent wash over selected widgets. + if (isSelected() && !document()->printing()) + paintInfo.context->fillRect(selectionRect(), selectionBackgroundColor()); +} + +void RenderWidget::deref(RenderArena *arena) +{ + if (--m_refCount <= 0) + arenaDelete(arena, this); +} + +void RenderWidget::updateWidgetPosition() +{ + if (!m_widget) + return; + + // FIXME: This doesn't work correctly with transforms. + FloatPoint absPos = localToAbsolute(); + absPos.move(borderLeft() + paddingLeft(), borderTop() + paddingTop()); + + int width = m_width - borderLeft() - borderRight() - paddingLeft() - paddingRight(); + int height = m_height - borderTop() - borderBottom() - paddingTop() - paddingBottom(); + + IntRect newBounds(absPos.x(), absPos.y(), width, height); + IntRect oldBounds(m_widget->frameRect()); + if (newBounds != oldBounds) { + // The widget changed positions. Update the frame geometry. + if (checkForRepaintDuringLayout()) { + RenderView* v = view(); + if (!v->printing()) { + v->repaintViewRectangle(oldBounds); + v->repaintViewRectangle(newBounds); + } + } + + RenderArena* arena = ref(); + element()->ref(); + m_widget->setFrameRect(newBounds); + element()->deref(); + deref(arena); + } +} + +void RenderWidget::setSelectionState(SelectionState state) +{ + if (selectionState() != state) { + RenderReplaced::setSelectionState(state); + if (m_widget) + m_widget->setIsSelected(isSelected()); + } +} + +void RenderWidget::deleteWidget() +{ + delete m_widget; +} + +RenderWidget* RenderWidget::find(const Widget* widget) +{ + return widgetRendererMap().get(widget); +} + +bool RenderWidget::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction action) +{ + bool hadResult = result.innerNode(); + bool inside = RenderReplaced::nodeAtPoint(request, result, x, y, tx, ty, action); + + // Check to see if we are really over the widget itself (and not just in the border/padding area). + if (inside && !hadResult && result.innerNode() == element()) + result.setIsOverWidget(contentBox().contains(result.localPoint())); + return inside; +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderWidget.h b/src/3rdparty/webkit/WebCore/rendering/RenderWidget.h new file mode 100644 index 0000000..a64bb54 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderWidget.h @@ -0,0 +1,77 @@ +/* + * This file is part of the HTML widget for KDE. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef RenderWidget_h +#define RenderWidget_h + +#include "RenderReplaced.h" + +namespace WebCore { + +class Widget; + +class RenderWidget : public RenderReplaced { +public: + RenderWidget(Node*); + virtual ~RenderWidget(); + + virtual bool isWidget() const { return true; } + + virtual void paint(PaintInfo&, int tx, int ty); + + virtual void destroy(); + virtual void layout(); + + Widget* widget() const { return m_widget; } + static RenderWidget* find(const Widget*); + + RenderArena* ref() { ++m_refCount; return renderArena(); } + void deref(RenderArena*); + + virtual void setSelectionState(SelectionState); + + virtual void updateWidgetPosition(); + + virtual void setWidget(Widget*); + + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); + +protected: + virtual void styleDidChange(RenderStyle::Diff, const RenderStyle* oldStyle); + +private: + void setWidgetGeometry(const IntRect&); + + virtual void deleteWidget(); + +protected: + Widget* m_widget; + FrameView* m_view; + +private: + int m_refCount; +}; + +} // namespace WebCore + +#endif // RenderWidget_h diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderWordBreak.cpp b/src/3rdparty/webkit/WebCore/rendering/RenderWordBreak.cpp new file mode 100644 index 0000000..a620560 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderWordBreak.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#include "config.h" +#include "RenderWordBreak.h" + +#include "HTMLElement.h" + +namespace WebCore { + +RenderWordBreak::RenderWordBreak(HTMLElement* element) + : RenderText(element, StringImpl::empty()) +{ +} + +const char* RenderWordBreak::renderName() const +{ + return "RenderWordBreak"; +} + +bool RenderWordBreak::isWordBreak() const +{ + return true; +} + +} diff --git a/src/3rdparty/webkit/WebCore/rendering/RenderWordBreak.h b/src/3rdparty/webkit/WebCore/rendering/RenderWordBreak.h new file mode 100644 index 0000000..acd8179 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RenderWordBreak.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef RenderWordBreak_h +#define RenderWordBreak_h + +#include "RenderText.h" + +namespace WebCore { + +class HTMLElement; + +class RenderWordBreak : public RenderText { +public: + RenderWordBreak(HTMLElement*); + + virtual const char* renderName() const; + virtual bool isWordBreak() const; +}; + +} + +#endif diff --git a/src/3rdparty/webkit/WebCore/rendering/RootInlineBox.cpp b/src/3rdparty/webkit/WebCore/rendering/RootInlineBox.cpp new file mode 100644 index 0000000..bb88f24 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RootInlineBox.cpp @@ -0,0 +1,405 @@ +/* + * Copyright (C) 2003, 2006, 2008 Apple Inc. All rights reserved. + * + * 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" +#include "RootInlineBox.h" + +#include "BidiResolver.h" +#include "ChromeClient.h" +#include "Document.h" +#include "EllipsisBox.h" +#include "Frame.h" +#include "GraphicsContext.h" +#include "HitTestResult.h" +#include "Page.h" +#include "RenderArena.h" +#include "RenderBlock.h" + +using namespace std; + +namespace WebCore { + +typedef WTF::HashMap<const RootInlineBox*, EllipsisBox*> EllipsisBoxMap; +static EllipsisBoxMap* gEllipsisBoxMap = 0; + +void* RootInlineBox::Overflow::operator new(size_t sz, RenderArena* renderArena) throw() +{ + return renderArena->allocate(sz); +} + +void RootInlineBox::Overflow::operator delete(void* ptr, size_t sz) +{ + // Stash size where destroy can find it. + *(size_t *)ptr = sz; +} + +void RootInlineBox::Overflow::destroy(RenderArena* renderArena) +{ + delete this; + // Recover the size left there for us by operator delete and free the memory. + renderArena->free(*(size_t *)this, this); +} + +void RootInlineBox::destroy(RenderArena* arena) +{ + if (m_overflow) + m_overflow->destroy(arena); + detachEllipsisBox(arena); + InlineFlowBox::destroy(arena); +} + +void RootInlineBox::detachEllipsisBox(RenderArena* arena) +{ + if (m_hasEllipsisBox) { + EllipsisBox* box = gEllipsisBoxMap->take(this); + box->setParent(0); + box->destroy(arena); + m_hasEllipsisBox = false; + } +} + +void RootInlineBox::clearTruncation() +{ + if (m_hasEllipsisBox) { + detachEllipsisBox(m_object->renderArena()); + InlineFlowBox::clearTruncation(); + } +} + +bool RootInlineBox::canAccommodateEllipsis(bool ltr, int blockEdge, int lineBoxEdge, int ellipsisWidth) +{ + // First sanity-check the unoverflowed width of the whole line to see if there is sufficient room. + int delta = ltr ? lineBoxEdge - blockEdge : blockEdge - lineBoxEdge; + if (width() - delta < ellipsisWidth) + return false; + + // Next iterate over all the line boxes on the line. If we find a replaced element that intersects + // then we refuse to accommodate the ellipsis. Otherwise we're ok. + return InlineFlowBox::canAccommodateEllipsis(ltr, blockEdge, ellipsisWidth); +} + +void RootInlineBox::placeEllipsis(const AtomicString& ellipsisStr, bool ltr, int blockEdge, int ellipsisWidth, + InlineBox* markupBox) +{ + // Create an ellipsis box. + EllipsisBox* ellipsisBox = new (m_object->renderArena()) EllipsisBox(m_object, ellipsisStr, this, + ellipsisWidth - (markupBox ? markupBox->width() : 0), + yPos(), height(), baseline(), !prevRootBox(), + markupBox); + + if (!gEllipsisBoxMap) + gEllipsisBoxMap = new EllipsisBoxMap(); + gEllipsisBoxMap->add(this, ellipsisBox); + m_hasEllipsisBox = true; + + if (ltr && (xPos() + width() + ellipsisWidth) <= blockEdge) { + ellipsisBox->m_x = xPos() + width(); + return; + } + + // Now attempt to find the nearest glyph horizontally and place just to the right (or left in RTL) + // of that glyph. Mark all of the objects that intersect the ellipsis box as not painting (as being + // truncated). + bool foundBox = false; + ellipsisBox->m_x = placeEllipsisBox(ltr, blockEdge, ellipsisWidth, foundBox); +} + +int RootInlineBox::placeEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth, bool& foundBox) +{ + int result = InlineFlowBox::placeEllipsisBox(ltr, blockEdge, ellipsisWidth, foundBox); + if (result == -1) + result = ltr ? blockEdge - ellipsisWidth : blockEdge; + return result; +} + +void RootInlineBox::paintEllipsisBox(RenderObject::PaintInfo& paintInfo, int tx, int ty) const +{ + if (m_hasEllipsisBox && object()->shouldPaintWithinRoot(paintInfo) && object()->style()->visibility() == VISIBLE && + paintInfo.phase == PaintPhaseForeground) + ellipsisBox()->paint(paintInfo, tx, ty); +} + +#if PLATFORM(MAC) + +void RootInlineBox::addHighlightOverflow() +{ + Frame* frame = object()->document()->frame(); + if (!frame) + return; + Page* page = frame->page(); + if (!page) + return; + + // Highlight acts as a selection inflation. + FloatRect rootRect(0, selectionTop(), width(), selectionHeight()); + IntRect inflatedRect = enclosingIntRect(page->chrome()->client()->customHighlightRect(object()->node(), object()->style()->highlight(), rootRect)); + setHorizontalOverflowPositions(min(leftOverflow(), inflatedRect.x()), max(rightOverflow(), inflatedRect.right())); + setVerticalOverflowPositions(min(topOverflow(), inflatedRect.y()), max(bottomOverflow(), inflatedRect.bottom())); +} + +void RootInlineBox::paintCustomHighlight(RenderObject::PaintInfo& paintInfo, int tx, int ty, const AtomicString& highlightType) +{ + if (!object()->shouldPaintWithinRoot(paintInfo) || object()->style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseForeground) + return; + + Frame* frame = object()->document()->frame(); + if (!frame) + return; + Page* page = frame->page(); + if (!page) + return; + + // Get the inflated rect so that we can properly hit test. + FloatRect rootRect(tx + xPos(), ty + selectionTop(), width(), selectionHeight()); + FloatRect inflatedRect = page->chrome()->client()->customHighlightRect(object()->node(), highlightType, rootRect); + if (inflatedRect.intersects(paintInfo.rect)) + page->chrome()->client()->paintCustomHighlight(object()->node(), highlightType, rootRect, rootRect, false, true); +} + +#endif + +void RootInlineBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) +{ + InlineFlowBox::paint(paintInfo, tx, ty); + paintEllipsisBox(paintInfo, tx, ty); +#if PLATFORM(MAC) + RenderStyle* styleToUse = object()->style(m_firstLine); + if (styleToUse->highlight() != nullAtom && !paintInfo.context->paintingDisabled()) + paintCustomHighlight(paintInfo, tx, ty, styleToUse->highlight()); +#endif +} + +bool RootInlineBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty) +{ + if (m_hasEllipsisBox && visibleToHitTesting()) { + if (ellipsisBox()->nodeAtPoint(request, result, x, y, tx, ty)) { + object()->updateHitTestResult(result, IntPoint(x - tx, y - ty)); + return true; + } + } + return InlineFlowBox::nodeAtPoint(request, result, x, y, tx, ty); +} + +void RootInlineBox::adjustPosition(int dx, int dy) +{ + InlineFlowBox::adjustPosition(dx, dy); + if (m_overflow) { + m_overflow->m_topOverflow += dy; + m_overflow->m_bottomOverflow += dy; + m_overflow->m_selectionTop += dy; + m_overflow->m_selectionBottom += dy; + } + m_blockHeight += dy; +} + +void RootInlineBox::childRemoved(InlineBox* box) +{ + if (box->object() == m_lineBreakObj) + setLineBreakInfo(0, 0, BidiStatus()); + + for (RootInlineBox* prev = prevRootBox(); prev && prev->lineBreakObj() == box->object(); prev = prev->prevRootBox()) { + prev->setLineBreakInfo(0, 0, BidiStatus()); + prev->markDirty(); + } +} + +GapRects RootInlineBox::fillLineSelectionGap(int selTop, int selHeight, RenderBlock* rootBlock, int blockX, int blockY, int tx, int ty, + const RenderObject::PaintInfo* paintInfo) +{ + RenderObject::SelectionState lineState = selectionState(); + + bool leftGap, rightGap; + block()->getHorizontalSelectionGapInfo(lineState, leftGap, rightGap); + + GapRects result; + + InlineBox* firstBox = firstSelectedBox(); + InlineBox* lastBox = lastSelectedBox(); + if (leftGap) + result.uniteLeft(block()->fillLeftSelectionGap(firstBox->parent()->object(), + firstBox->xPos(), selTop, selHeight, + rootBlock, blockX, blockY, tx, ty, paintInfo)); + if (rightGap) + result.uniteRight(block()->fillRightSelectionGap(lastBox->parent()->object(), + lastBox->xPos() + lastBox->width(), selTop, selHeight, + rootBlock, blockX, blockY, tx, ty, paintInfo)); + + if (firstBox && firstBox != lastBox) { + // Now fill in any gaps on the line that occurred between two selected elements. + int lastX = firstBox->xPos() + firstBox->width(); + for (InlineBox* box = firstBox->nextLeafChild(); box; box = box->nextLeafChild()) { + if (box->selectionState() != RenderObject::SelectionNone) { + result.uniteCenter(block()->fillHorizontalSelectionGap(box->parent()->object(), + lastX + tx, selTop + ty, + box->xPos() - lastX, selHeight, paintInfo)); + lastX = box->xPos() + box->width(); + } + if (box == lastBox) + break; + } + } + + return result; +} + +void RootInlineBox::setHasSelectedChildren(bool b) +{ + if (m_hasSelectedChildren == b) + return; + m_hasSelectedChildren = b; +} + +RenderObject::SelectionState RootInlineBox::selectionState() +{ + // Walk over all of the selected boxes. + RenderObject::SelectionState state = RenderObject::SelectionNone; + for (InlineBox* box = firstLeafChild(); box; box = box->nextLeafChild()) { + RenderObject::SelectionState boxState = box->selectionState(); + if ((boxState == RenderObject::SelectionStart && state == RenderObject::SelectionEnd) || + (boxState == RenderObject::SelectionEnd && state == RenderObject::SelectionStart)) + state = RenderObject::SelectionBoth; + else if (state == RenderObject::SelectionNone || + ((boxState == RenderObject::SelectionStart || boxState == RenderObject::SelectionEnd) && + (state == RenderObject::SelectionNone || state == RenderObject::SelectionInside))) + state = boxState; + if (state == RenderObject::SelectionBoth) + break; + } + + return state; +} + +InlineBox* RootInlineBox::firstSelectedBox() +{ + for (InlineBox* box = firstLeafChild(); box; box = box->nextLeafChild()) { + if (box->selectionState() != RenderObject::SelectionNone) + return box; + } + + return 0; +} + +InlineBox* RootInlineBox::lastSelectedBox() +{ + for (InlineBox* box = lastLeafChild(); box; box = box->prevLeafChild()) { + if (box->selectionState() != RenderObject::SelectionNone) + return box; + } + + return 0; +} + +int RootInlineBox::selectionTop() +{ + int selectionTop = m_overflow ? m_overflow->m_selectionTop : m_y; + if (!prevRootBox()) + return selectionTop; + + int prevBottom = prevRootBox()->selectionBottom(); + if (prevBottom < selectionTop && block()->containsFloats()) { + // This line has actually been moved further down, probably from a large line-height, but possibly because the + // line was forced to clear floats. If so, let's check the offsets, and only be willing to use the previous + // line's bottom overflow if the offsets are greater on both sides. + int prevLeft = block()->leftOffset(prevBottom); + int prevRight = block()->rightOffset(prevBottom); + int newLeft = block()->leftOffset(selectionTop); + int newRight = block()->rightOffset(selectionTop); + if (prevLeft > newLeft || prevRight < newRight) + return selectionTop; + } + + return prevBottom; +} + +RenderBlock* RootInlineBox::block() const +{ + return static_cast<RenderBlock*>(m_object); +} + +bool isEditableLeaf(InlineBox* leaf) +{ + return leaf && leaf->object() && leaf->object()->element() && leaf->object()->element()->isContentEditable(); +} + +InlineBox* RootInlineBox::closestLeafChildForXPos(int x, bool onlyEditableLeaves) +{ + InlineBox* firstLeaf = firstLeafChildAfterBox(); + InlineBox* lastLeaf = lastLeafChildBeforeBox(); + if (firstLeaf == lastLeaf && (!onlyEditableLeaves || isEditableLeaf(firstLeaf))) + return firstLeaf; + + // Avoid returning a list marker when possible. + if (x <= firstLeaf->m_x && !firstLeaf->object()->isListMarker() && (!onlyEditableLeaves || isEditableLeaf(firstLeaf))) + // The x coordinate is less or equal to left edge of the firstLeaf. + // Return it. + return firstLeaf; + + if (x >= lastLeaf->m_x + lastLeaf->m_width && !lastLeaf->object()->isListMarker() && (!onlyEditableLeaves || isEditableLeaf(lastLeaf))) + // The x coordinate is greater or equal to right edge of the lastLeaf. + // Return it. + return lastLeaf; + + InlineBox* closestLeaf = 0; + for (InlineBox* leaf = firstLeaf; leaf; leaf = leaf->nextLeafChild()) { + if (!leaf->object()->isListMarker() && (!onlyEditableLeaves || isEditableLeaf(leaf))) { + closestLeaf = leaf; + if (x < leaf->m_x + leaf->m_width) + // The x coordinate is less than the right edge of the box. + // Return it. + return leaf; + } + } + + return closestLeaf ? closestLeaf : lastLeaf; +} + +BidiStatus RootInlineBox::lineBreakBidiStatus() const +{ + return BidiStatus(m_lineBreakBidiStatusEor, m_lineBreakBidiStatusLastStrong, m_lineBreakBidiStatusLast, m_lineBreakContext); +} + +void RootInlineBox::setLineBreakInfo(RenderObject* obj, unsigned breakPos, const BidiStatus& status) +{ + m_lineBreakObj = obj; + m_lineBreakPos = breakPos; + m_lineBreakBidiStatusEor = status.eor; + m_lineBreakBidiStatusLastStrong = status.lastStrong; + m_lineBreakBidiStatusLast = status.last; + m_lineBreakContext = status.context; +} + +EllipsisBox* RootInlineBox::ellipsisBox() const +{ + if (!m_hasEllipsisBox) + return false; + return gEllipsisBoxMap->get(this); +} + +void RootInlineBox::setVerticalOverflowPositions(int top, int bottom) +{ + if (!m_overflow) { + if (top == m_y && bottom == m_y + m_height) + return; + m_overflow = new (m_object->renderArena()) Overflow(this); + } + m_overflow->m_topOverflow = top; + m_overflow->m_bottomOverflow = bottom; +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/RootInlineBox.h b/src/3rdparty/webkit/WebCore/rendering/RootInlineBox.h new file mode 100644 index 0000000..4724110 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/RootInlineBox.h @@ -0,0 +1,206 @@ +/* + * This file is part of the line box implementation for KDE. + * + * Copyright (C) 2003, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef RootInlineBox_h +#define RootInlineBox_h + +#include "BidiContext.h" +#include "InlineFlowBox.h" + +namespace WebCore { + +class BidiStatus; +class EllipsisBox; +class HitTestResult; +struct GapRects; + +class RootInlineBox : public InlineFlowBox { +public: + RootInlineBox(RenderObject* obj) + : InlineFlowBox(obj) + , m_overflow(0) + , m_lineBreakObj(0) + , m_lineBreakPos(0) + { + } + + virtual bool isRootInlineBox() { return true; } + + virtual void destroy(RenderArena*); + void detachEllipsisBox(RenderArena*); + + RootInlineBox* nextRootBox() { return static_cast<RootInlineBox*>(m_nextLine); } + RootInlineBox* prevRootBox() { return static_cast<RootInlineBox*>(m_prevLine); } + + virtual void adjustPosition(int dx, int dy); + + virtual int topOverflow() { return m_overflow ? m_overflow->m_topOverflow : m_y; } + virtual int bottomOverflow() { return m_overflow ? m_overflow->m_bottomOverflow : m_y + m_height; } + virtual int leftOverflow() { return m_overflow ? m_overflow->m_leftOverflow : m_x; } + virtual int rightOverflow() { return m_overflow ? m_overflow->m_rightOverflow : m_x + m_width; } + + virtual void setVerticalOverflowPositions(int top, int bottom); + void setHorizontalOverflowPositions(int left, int right); + + virtual void setVerticalSelectionPositions(int top, int bottom); + +#if ENABLE(SVG) + virtual void computePerCharacterLayoutInformation() { } +#endif + + RenderObject* lineBreakObj() const { return m_lineBreakObj; } + BidiStatus lineBreakBidiStatus() const; + void setLineBreakInfo(RenderObject*, unsigned breakPos, const BidiStatus&); + + unsigned lineBreakPos() const { return m_lineBreakPos; } + void setLineBreakPos(unsigned p) { m_lineBreakPos = p; } + + int blockHeight() const { return m_blockHeight; } + void setBlockHeight(int h) { m_blockHeight = h; } + + bool endsWithBreak() const { return m_endsWithBreak; } + void setEndsWithBreak(bool b) { m_endsWithBreak = b; } + + void childRemoved(InlineBox* box); + + bool canAccommodateEllipsis(bool ltr, int blockEdge, int lineBoxEdge, int ellipsisWidth); + void placeEllipsis(const AtomicString& ellipsisStr, bool ltr, int blockEdge, int ellipsisWidth, InlineBox* markupBox = 0); + virtual int placeEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth, bool& foundBox); + + EllipsisBox* ellipsisBox() const; + + void paintEllipsisBox(RenderObject::PaintInfo&, int tx, int ty) const; + bool hitTestEllipsisBox(HitTestResult&, int x, int y, int tx, int ty, HitTestAction, bool); + + virtual void clearTruncation(); + +#if PLATFORM(MAC) + void addHighlightOverflow(); + void paintCustomHighlight(RenderObject::PaintInfo&, int tx, int ty, const AtomicString& highlightType); +#endif + + virtual void paint(RenderObject::PaintInfo&, int tx, int ty); + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int, int, int, int); + + bool hasSelectedChildren() const { return m_hasSelectedChildren; } + void setHasSelectedChildren(bool); + + virtual RenderObject::SelectionState selectionState(); + InlineBox* firstSelectedBox(); + InlineBox* lastSelectedBox(); + + GapRects fillLineSelectionGap(int selTop, int selHeight, RenderBlock* rootBlock, int blockX, int blockY, + int tx, int ty, const RenderObject::PaintInfo*); + + RenderBlock* block() const; + + int selectionTop(); + int selectionBottom() { return m_overflow ? m_overflow->m_selectionBottom : m_y + m_height; } + int selectionHeight() { return max(0, selectionBottom() - selectionTop()); } + + InlineBox* closestLeafChildForXPos(int x, bool onlyEditableLeaves = false); + + Vector<RenderObject*>& floats() + { + ASSERT(!isDirty()); + if (!m_overflow) + m_overflow = new (m_object->renderArena()) Overflow(this); + return m_overflow->floats; + } + + Vector<RenderObject*>* floatsPtr() { ASSERT(!isDirty()); return m_overflow ? &m_overflow->floats : 0; } + +protected: + // Normally we are only as tall as the style on our block dictates, but we might have content + // that spills out above the height of our font (e.g, a tall image), or something that extends further + // below our line (e.g., a child whose font has a huge descent). + + // Allocated only when some of these fields have non-default values + struct Overflow { + Overflow(RootInlineBox* box) + : m_topOverflow(box->m_y) + , m_bottomOverflow(box->m_y + box->m_height) + , m_leftOverflow(box->m_x) + , m_rightOverflow(box->m_x + box->m_width) + , m_selectionTop(box->m_y) + , m_selectionBottom(box->m_y + box->m_height) + { + } + + void destroy(RenderArena*); + void* operator new(size_t, RenderArena*) throw(); + void operator delete(void*, size_t); + + int m_topOverflow; + int m_bottomOverflow; + int m_leftOverflow; + int m_rightOverflow; + int m_selectionTop; + int m_selectionBottom; + // Floats hanging off the line are pushed into this vector during layout. It is only + // good for as long as the line has not been marked dirty. + Vector<RenderObject*> floats; + private: + void* operator new(size_t) throw(); + }; + + Overflow* m_overflow; + + // Where this line ended. The exact object and the position within that object are stored so that + // we can create an InlineIterator beginning just after the end of this line. + RenderObject* m_lineBreakObj; + unsigned m_lineBreakPos; + RefPtr<BidiContext> m_lineBreakContext; + + // The height of the block at the end of this line. This is where the next line starts. + int m_blockHeight; + + WTF::Unicode::Direction m_lineBreakBidiStatusEor : 5; + WTF::Unicode::Direction m_lineBreakBidiStatusLastStrong : 5; + WTF::Unicode::Direction m_lineBreakBidiStatusLast : 5; +}; + +inline void RootInlineBox::setHorizontalOverflowPositions(int left, int right) +{ + if (!m_overflow) { + if (left == m_x && right == m_x + m_width) + return; + m_overflow = new (m_object->renderArena()) Overflow(this); + } + m_overflow->m_leftOverflow = left; + m_overflow->m_rightOverflow = right; +} + +inline void RootInlineBox::setVerticalSelectionPositions(int top, int bottom) +{ + if (!m_overflow) { + if (top == m_y && bottom == m_y + m_height) + return; + m_overflow = new (m_object->renderArena()) Overflow(this); + } + m_overflow->m_selectionTop = top; + m_overflow->m_selectionBottom = bottom; +} + +} // namespace WebCore + +#endif // RootInlineBox_h diff --git a/src/3rdparty/webkit/WebCore/rendering/SVGCharacterLayoutInfo.cpp b/src/3rdparty/webkit/WebCore/rendering/SVGCharacterLayoutInfo.cpp new file mode 100644 index 0000000..89bab2d --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/SVGCharacterLayoutInfo.cpp @@ -0,0 +1,535 @@ +/* + * This file is part of the WebKit project. + * + * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> + * + * 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(SVG) +#include "SVGCharacterLayoutInfo.h" + +#include "InlineFlowBox.h" +#include "InlineTextBox.h" +#include "SVGLengthList.h" +#include "SVGNumberList.h" +#include "SVGTextPositioningElement.h" +#include "RenderSVGTextPath.h" + +#include <float.h> + +namespace WebCore { + +// Helper function +static float calculateBaselineShift(RenderObject* item) +{ + const Font& font = item->style()->font(); + const SVGRenderStyle* svgStyle = item->style()->svgStyle(); + + float baselineShift = 0.0f; + if (svgStyle->baselineShift() == BS_LENGTH) { + CSSPrimitiveValue* primitive = static_cast<CSSPrimitiveValue*>(svgStyle->baselineShiftValue()); + baselineShift = primitive->getFloatValue(); + + if (primitive->primitiveType() == CSSPrimitiveValue::CSS_PERCENTAGE) + baselineShift = baselineShift / 100.0f * font.pixelSize(); + } else { + float baselineAscent = font.ascent() + font.descent(); + + switch (svgStyle->baselineShift()) { + case BS_BASELINE: + break; + case BS_SUB: + baselineShift = -baselineAscent / 2.0f; + break; + case BS_SUPER: + baselineShift = baselineAscent / 2.0f; + break; + default: + ASSERT_NOT_REACHED(); + } + } + + return baselineShift; +} + +SVGCharacterLayoutInfo::SVGCharacterLayoutInfo(Vector<SVGChar>& chars) + : curx(0.0f) + , cury(0.0f) + , angle(0.0f) + , dx(0.0f) + , dy(0.0f) + , shiftx(0.0f) + , shifty(0.0f) + , pathExtraAdvance(0.0f) + , pathTextLength(0.0f) + , pathChunkLength(0.0f) + , svgChars(chars) + , nextDrawnSeperated(false) + , xStackChanged(false) + , yStackChanged(false) + , dxStackChanged(false) + , dyStackChanged(false) + , angleStackChanged(false) + , baselineShiftStackChanged(false) + , pathLayout(false) + , currentOffset(0.0f) + , startOffset(0.0f) + , layoutPathLength(0.0f) +{ +} + +bool SVGCharacterLayoutInfo::xValueAvailable() const +{ + return xStack.isEmpty() ? false : xStack.last().position() < xStack.last().size(); +} + +bool SVGCharacterLayoutInfo::yValueAvailable() const +{ + return yStack.isEmpty() ? false : yStack.last().position() < yStack.last().size(); +} + +bool SVGCharacterLayoutInfo::dxValueAvailable() const +{ + return dxStack.isEmpty() ? false : dxStack.last().position() < dxStack.last().size(); +} + +bool SVGCharacterLayoutInfo::dyValueAvailable() const +{ + return dyStack.isEmpty() ? false : dyStack.last().position() < dyStack.last().size(); +} + +bool SVGCharacterLayoutInfo::angleValueAvailable() const +{ + return angleStack.isEmpty() ? false : angleStack.last().position() < angleStack.last().size(); +} + +bool SVGCharacterLayoutInfo::baselineShiftValueAvailable() const +{ + return !baselineShiftStack.isEmpty(); +} + +float SVGCharacterLayoutInfo::xValueNext() const +{ + ASSERT(!xStack.isEmpty()); + return xStack.last().valueAtCurrentPosition(); +} + +float SVGCharacterLayoutInfo::yValueNext() const +{ + ASSERT(!yStack.isEmpty()); + return yStack.last().valueAtCurrentPosition(); +} + +float SVGCharacterLayoutInfo::dxValueNext() const +{ + ASSERT(!dxStack.isEmpty()); + return dxStack.last().valueAtCurrentPosition(); +} + +float SVGCharacterLayoutInfo::dyValueNext() const +{ + ASSERT(!dyStack.isEmpty()); + return dyStack.last().valueAtCurrentPosition(); +} + +float SVGCharacterLayoutInfo::angleValueNext() const +{ + ASSERT(!angleStack.isEmpty()); + return angleStack.last().valueAtCurrentPosition(); +} + +float SVGCharacterLayoutInfo::baselineShiftValueNext() const +{ + ASSERT(!baselineShiftStack.isEmpty()); + return baselineShiftStack.last(); +} + +void SVGCharacterLayoutInfo::processedSingleCharacter() +{ + xStackWalk(); + yStackWalk(); + dxStackWalk(); + dyStackWalk(); + angleStackWalk(); + baselineShiftStackWalk(); +} + +void SVGCharacterLayoutInfo::processedChunk(float savedShiftX, float savedShiftY) +{ + // baseline-shift doesn't span across ancestors, unlike dx/dy. + curx += savedShiftX - shiftx; + cury += savedShiftY - shifty; + + if (inPathLayout()) { + shiftx = savedShiftX; + shifty = savedShiftY; + } + + // rotation also doesn't span + angle = 0.0f; + + if (xStackChanged) { + ASSERT(!xStack.isEmpty()); + xStack.removeLast(); + xStackChanged = false; + } + + if (yStackChanged) { + ASSERT(!yStack.isEmpty()); + yStack.removeLast(); + yStackChanged = false; + } + + if (dxStackChanged) { + ASSERT(!dxStack.isEmpty()); + dxStack.removeLast(); + dxStackChanged = false; + } + + if (dyStackChanged) { + ASSERT(!dyStack.isEmpty()); + dyStack.removeLast(); + dyStackChanged = false; + } + + if (angleStackChanged) { + ASSERT(!angleStack.isEmpty()); + angleStack.removeLast(); + angleStackChanged = false; + } + + if (baselineShiftStackChanged) { + ASSERT(!baselineShiftStack.isEmpty()); + baselineShiftStack.removeLast(); + baselineShiftStackChanged = false; + } +} + +bool SVGCharacterLayoutInfo::nextPathLayoutPointAndAngle(float glyphAdvance, float extraAdvance, float newOffset) +{ + if (layoutPathLength <= 0.0f) + return false; + + if (newOffset != FLT_MIN) + currentOffset = startOffset + newOffset; + + // Respect translation along path (extraAdvance is orthogonal to the path) + currentOffset += extraAdvance; + + float offset = currentOffset + glyphAdvance / 2.0f; + currentOffset += glyphAdvance + pathExtraAdvance; + + if (offset < 0.0f || offset > layoutPathLength) + return false; + + bool ok = false; + FloatPoint point = layoutPath.pointAtLength(offset, ok); + ASSERT(ok); + + curx = point.x(); + cury = point.y(); + + angle = layoutPath.normalAngleAtLength(offset, ok); + ASSERT(ok); + + // fprintf(stderr, "t: %f, x: %f, y: %f, angle: %f, glyphAdvance: %f\n", currentOffset, x, y, angle, glyphAdvance); + return true; +} + +bool SVGCharacterLayoutInfo::inPathLayout() const +{ + return pathLayout; +} + +void SVGCharacterLayoutInfo::setInPathLayout(bool value) +{ + pathLayout = value; + + pathExtraAdvance = 0.0f; + pathTextLength = 0.0f; + pathChunkLength = 0.0f; +} + +void SVGCharacterLayoutInfo::addLayoutInformation(InlineFlowBox* flowBox, float textAnchorStartOffset) +{ + bool isInitialLayout = xStack.isEmpty() && yStack.isEmpty() && + dxStack.isEmpty() && dyStack.isEmpty() && + angleStack.isEmpty() && baselineShiftStack.isEmpty() && + curx == 0.0f && cury == 0.0f; + + RenderSVGTextPath* textPath = static_cast<RenderSVGTextPath*>(flowBox->object()); + Path path = textPath->layoutPath(); + + float baselineShift = calculateBaselineShift(textPath); + + layoutPath = path; + layoutPathLength = path.length(); + + if (layoutPathLength <= 0.0f) + return; + + startOffset = textPath->startOffset(); + + if (textPath->startOffset() >= 0.0f && textPath->startOffset() <= 1.0f) + startOffset *= layoutPathLength; + + startOffset += textAnchorStartOffset; + currentOffset = startOffset; + + // Only baseline-shift is handled through the normal layout system + addStackContent(BaselineShiftStack, baselineShift); + + if (isInitialLayout) { + xStackChanged = false; + yStackChanged = false; + dxStackChanged = false; + dyStackChanged = false; + angleStackChanged = false; + baselineShiftStackChanged = false; + } +} + +void SVGCharacterLayoutInfo::addLayoutInformation(SVGTextPositioningElement* element) +{ + bool isInitialLayout = xStack.isEmpty() && yStack.isEmpty() && + dxStack.isEmpty() && dyStack.isEmpty() && + angleStack.isEmpty() && baselineShiftStack.isEmpty() && + curx == 0.0f && cury == 0.0f; + + float baselineShift = calculateBaselineShift(element->renderer()); + + addStackContent(XStack, element->x(), element); + addStackContent(YStack, element->y(), element); + addStackContent(DxStack, element->dx(), element); + addStackContent(DyStack, element->dy(), element); + addStackContent(AngleStack, element->rotate()); + addStackContent(BaselineShiftStack, baselineShift); + + if (isInitialLayout) { + xStackChanged = false; + yStackChanged = false; + dxStackChanged = false; + dyStackChanged = false; + angleStackChanged = false; + baselineShiftStackChanged = false; + } +} + +void SVGCharacterLayoutInfo::addStackContent(StackType type, SVGNumberList* list) +{ + unsigned length = list->numberOfItems(); + if (!length) + return; + + PositionedFloatVector newLayoutInfo; + + // TODO: Convert more efficiently! + ExceptionCode ec = 0; + for (unsigned i = 0; i < length; ++i) { + float value = list->getItem(i, ec); + ASSERT(ec == 0); + + newLayoutInfo.append(value); + } + + addStackContent(type, newLayoutInfo); +} + +void SVGCharacterLayoutInfo::addStackContent(StackType type, SVGLengthList* list, const SVGElement* context) +{ + unsigned length = list->numberOfItems(); + if (!length) + return; + + PositionedFloatVector newLayoutInfo; + + ExceptionCode ec = 0; + for (unsigned i = 0; i < length; ++i) { + float value = list->getItem(i, ec).value(context); + ASSERT(ec == 0); + + newLayoutInfo.append(value); + } + + addStackContent(type, newLayoutInfo); +} + +void SVGCharacterLayoutInfo::addStackContent(StackType type, const PositionedFloatVector& list) +{ + switch (type) { + case XStack: + xStackChanged = true; + xStack.append(list); + break; + case YStack: + yStackChanged = true; + yStack.append(list); + break; + case DxStack: + dxStackChanged = true; + dxStack.append(list); + break; + case DyStack: + dyStackChanged = true; + dyStack.append(list); + break; + case AngleStack: + angleStackChanged = true; + angleStack.append(list); + break; + default: + ASSERT_NOT_REACHED(); + } +} + +void SVGCharacterLayoutInfo::addStackContent(StackType type, float value) +{ + if (value == 0.0f) + return; + + switch (type) { + case BaselineShiftStack: + baselineShiftStackChanged = true; + baselineShiftStack.append(value); + break; + default: + ASSERT_NOT_REACHED(); + } +} + +void SVGCharacterLayoutInfo::xStackWalk() +{ + unsigned i = 1; + + while (!xStack.isEmpty()) { + PositionedFloatVector& cur = xStack.last(); + if (i + cur.position() < cur.size()) { + cur.advance(i); + break; + } + + i += cur.position(); + xStack.removeLast(); + xStackChanged = false; + } +} + +void SVGCharacterLayoutInfo::yStackWalk() +{ + unsigned i = 1; + + while (!yStack.isEmpty()) { + PositionedFloatVector& cur = yStack.last(); + if (i + cur.position() < cur.size()) { + cur.advance(i); + break; + } + + i += cur.position(); + yStack.removeLast(); + yStackChanged = false; + } +} + +void SVGCharacterLayoutInfo::dxStackWalk() +{ + unsigned i = 1; + + while (!dxStack.isEmpty()) { + PositionedFloatVector& cur = dxStack.last(); + if (i + cur.position() < cur.size()) { + cur.advance(i); + break; + } + + i += cur.position(); + dxStack.removeLast(); + dxStackChanged = false; + } +} + +void SVGCharacterLayoutInfo::dyStackWalk() +{ + unsigned i = 1; + + while (!dyStack.isEmpty()) { + PositionedFloatVector& cur = dyStack.last(); + if (i + cur.position() < cur.size()) { + cur.advance(i); + break; + } + + i += cur.position(); + dyStack.removeLast(); + dyStackChanged = false; + } +} + +void SVGCharacterLayoutInfo::angleStackWalk() +{ + unsigned i = 1; + + while (!angleStack.isEmpty()) { + PositionedFloatVector& cur = angleStack.last(); + if (i + cur.position() < cur.size()) { + cur.advance(i); + break; + } + + i += cur.position(); + angleStack.removeLast(); + angleStackChanged = false; + } +} + +void SVGCharacterLayoutInfo::baselineShiftStackWalk() +{ + if (!baselineShiftStack.isEmpty()) { + baselineShiftStack.removeLast(); + baselineShiftStackChanged = false; + } +} + +bool SVGChar::isHidden() const +{ + return pathData && pathData->hidden; +} + +TransformationMatrix SVGChar::characterTransform() const +{ + TransformationMatrix ctm; + + // Rotate character around angle, and possibly scale. + ctm.translate(x, y); + ctm.rotate(angle); + + if (pathData) { + ctm.scale(pathData->xScale, pathData->yScale); + ctm.translate(pathData->xShift, pathData->yShift); + ctm.rotate(pathData->orientationAngle); + } + + ctm.translate(orientationShiftX - x, orientationShiftY - y); + return ctm; +} + +} // namespace WebCore + +#endif // ENABLE(SVG) diff --git a/src/3rdparty/webkit/WebCore/rendering/SVGCharacterLayoutInfo.h b/src/3rdparty/webkit/WebCore/rendering/SVGCharacterLayoutInfo.h new file mode 100644 index 0000000..0188b9d --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/SVGCharacterLayoutInfo.h @@ -0,0 +1,416 @@ +/* + * This file is part of the WebKit project. + * + * Copyright (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> + * + * 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. + * + */ + +#ifndef SVGCharacterLayoutInfo_h +#define SVGCharacterLayoutInfo_h + +#if ENABLE(SVG) +#include <wtf/Assertions.h> +#include <wtf/HashMap.h> +#include <wtf/HashSet.h> +#include <wtf/Vector.h> + +#include "TransformationMatrix.h" +#include <wtf/RefCounted.h> +#include "SVGRenderStyle.h" +#include "SVGTextContentElement.h" + +namespace WebCore { + +class InlineBox; +class InlineFlowBox; +class SVGInlineTextBox; +class SVGLengthList; +class SVGNumberList; +class SVGTextPositioningElement; + +template<class Type> +class PositionedVector : public Vector<Type> +{ +public: + PositionedVector<Type>() + : m_position(0) + { + } + + unsigned position() const + { + return m_position; + } + + void advance(unsigned position) + { + m_position += position; + ASSERT(m_position < Vector<Type>::size()); + } + + Type valueAtCurrentPosition() const + { + ASSERT(m_position < Vector<Type>::size()); + return Vector<Type>::at(m_position); + } + +private: + unsigned m_position; +}; + +class PositionedFloatVector : public PositionedVector<float> { }; +struct SVGChar; + +struct SVGCharacterLayoutInfo { + SVGCharacterLayoutInfo(Vector<SVGChar>&); + + enum StackType { XStack, YStack, DxStack, DyStack, AngleStack, BaselineShiftStack }; + + bool xValueAvailable() const; + bool yValueAvailable() const; + bool dxValueAvailable() const; + bool dyValueAvailable() const; + bool angleValueAvailable() const; + bool baselineShiftValueAvailable() const; + + float xValueNext() const; + float yValueNext() const; + float dxValueNext() const; + float dyValueNext() const; + float angleValueNext() const; + float baselineShiftValueNext() const; + + void processedChunk(float savedShiftX, float savedShiftY); + void processedSingleCharacter(); + + bool nextPathLayoutPointAndAngle(float glyphAdvance, float extraAdvance, float newOffset); + + // Used for text-on-path. + void addLayoutInformation(InlineFlowBox*, float textAnchorOffset = 0.0f); + + bool inPathLayout() const; + void setInPathLayout(bool value); + + // Used for anything else. + void addLayoutInformation(SVGTextPositioningElement*); + + // Global position + float curx; + float cury; + + // Global rotation + float angle; + + // Accumulated dx/dy values + float dx; + float dy; + + // Accumulated baseline-shift values + float shiftx; + float shifty; + + // Path specific advance values to handle lengthAdjust + float pathExtraAdvance; + float pathTextLength; + float pathChunkLength; + + // Result vector + Vector<SVGChar>& svgChars; + bool nextDrawnSeperated : 1; + +private: + // Used for baseline-shift. + void addStackContent(StackType, float); + + // Used for angle. + void addStackContent(StackType, SVGNumberList*); + + // Used for x/y/dx/dy. + void addStackContent(StackType, SVGLengthList*, const SVGElement*); + + void addStackContent(StackType, const PositionedFloatVector&); + + void xStackWalk(); + void yStackWalk(); + void dxStackWalk(); + void dyStackWalk(); + void angleStackWalk(); + void baselineShiftStackWalk(); + +private: + bool xStackChanged : 1; + bool yStackChanged : 1; + bool dxStackChanged : 1; + bool dyStackChanged : 1; + bool angleStackChanged : 1; + bool baselineShiftStackChanged : 1; + + // text on path layout + bool pathLayout : 1; + float currentOffset; + float startOffset; + float layoutPathLength; + Path layoutPath; + + Vector<PositionedFloatVector> xStack; + Vector<PositionedFloatVector> yStack; + Vector<PositionedFloatVector> dxStack; + Vector<PositionedFloatVector> dyStack; + Vector<PositionedFloatVector> angleStack; + Vector<float> baselineShiftStack; +}; + +// Holds extra data, when the character is laid out on a path +struct SVGCharOnPath : RefCounted<SVGCharOnPath> { + static PassRefPtr<SVGCharOnPath> create() { return adoptRef(new SVGCharOnPath); } + + float xScale; + float yScale; + + float xShift; + float yShift; + + float orientationAngle; + + bool hidden : 1; + +private: + SVGCharOnPath() + : xScale(1.0f) + , yScale(1.0f) + , xShift(0.0f) + , yShift(0.0f) + , orientationAngle(0.0f) + , hidden(false) + { + } +}; + +struct SVGChar { + SVGChar() + : x(0.0f) + , y(0.0f) + , angle(0.0f) + , orientationShiftX(0.0f) + , orientationShiftY(0.0f) + , pathData() + , drawnSeperated(false) + , newTextChunk(false) + { + } + + ~SVGChar() + { + } + + float x; + float y; + float angle; + + float orientationShiftX; + float orientationShiftY; + + RefPtr<SVGCharOnPath> pathData; + + // Determines wheter this char needs to be drawn seperated + bool drawnSeperated : 1; + + // Determines wheter this char starts a new chunk + bool newTextChunk : 1; + + // Helper methods + bool isHidden() const; + TransformationMatrix characterTransform() const; +}; + +struct SVGInlineBoxCharacterRange { + SVGInlineBoxCharacterRange() + : startOffset(INT_MIN) + , endOffset(INT_MIN) + , box(0) + { + } + + bool isOpen() const { return (startOffset == endOffset) && (endOffset == INT_MIN); } + bool isClosed() const { return startOffset != INT_MIN && endOffset != INT_MIN; } + + int startOffset; + int endOffset; + + InlineBox* box; +}; + +// Convenience typedef +typedef SVGTextContentElement::SVGLengthAdjustType ELengthAdjust; + +struct SVGTextChunk { + SVGTextChunk() + : anchor(TA_START) + , textLength(0.0f) + , lengthAdjust(SVGTextContentElement::LENGTHADJUST_SPACING) + , ctm() + , isVerticalText(false) + , isTextPath(false) + , start(0) + , end(0) + { } + + // text-anchor support + ETextAnchor anchor; + + // textLength & lengthAdjust support + float textLength; + ELengthAdjust lengthAdjust; + TransformationMatrix ctm; + + // status flags + bool isVerticalText : 1; + bool isTextPath : 1; + + // main chunk data + Vector<SVGChar>::iterator start; + Vector<SVGChar>::iterator end; + + Vector<SVGInlineBoxCharacterRange> boxes; +}; + +struct SVGTextChunkWalkerBase { + virtual ~SVGTextChunkWalkerBase() { } + + virtual void operator()(SVGInlineTextBox* textBox, int startOffset, const TransformationMatrix& chunkCtm, + const Vector<SVGChar>::iterator& start, const Vector<SVGChar>::iterator& end) = 0; + + // Followings methods are only used for painting text chunks + virtual void start(InlineBox*) = 0; + virtual void end(InlineBox*) = 0; + + virtual bool setupFill(InlineBox*) = 0; + virtual bool setupStroke(InlineBox*) = 0; +}; + +template<typename CallbackClass> +struct SVGTextChunkWalker : public SVGTextChunkWalkerBase { +public: + typedef void (CallbackClass::*SVGTextChunkWalkerCallback)(SVGInlineTextBox* textBox, + int startOffset, + const TransformationMatrix& chunkCtm, + const Vector<SVGChar>::iterator& start, + const Vector<SVGChar>::iterator& end); + + // These callbacks are only used for painting! + typedef void (CallbackClass::*SVGTextChunkStartCallback)(InlineBox* box); + typedef void (CallbackClass::*SVGTextChunkEndCallback)(InlineBox* box); + + typedef bool (CallbackClass::*SVGTextChunkSetupFillCallback)(InlineBox* box); + typedef bool (CallbackClass::*SVGTextChunkSetupStrokeCallback)(InlineBox* box); + + SVGTextChunkWalker(CallbackClass* object, + SVGTextChunkWalkerCallback walker, + SVGTextChunkStartCallback start = 0, + SVGTextChunkEndCallback end = 0, + SVGTextChunkSetupFillCallback fill = 0, + SVGTextChunkSetupStrokeCallback stroke = 0) + : m_object(object) + , m_walkerCallback(walker) + , m_startCallback(start) + , m_endCallback(end) + , m_setupFillCallback(fill) + , m_setupStrokeCallback(stroke) + { + ASSERT(object); + ASSERT(walker); + } + + virtual void operator()(SVGInlineTextBox* textBox, int startOffset, const TransformationMatrix& chunkCtm, + const Vector<SVGChar>::iterator& start, const Vector<SVGChar>::iterator& end) + { + (*m_object.*m_walkerCallback)(textBox, startOffset, chunkCtm, start, end); + } + + // Followings methods are only used for painting text chunks + virtual void start(InlineBox* box) + { + if (m_startCallback) + (*m_object.*m_startCallback)(box); + else + ASSERT_NOT_REACHED(); + } + + virtual void end(InlineBox* box) + { + if (m_endCallback) + (*m_object.*m_endCallback)(box); + else + ASSERT_NOT_REACHED(); + } + + virtual bool setupFill(InlineBox* box) + { + if (m_setupFillCallback) + return (*m_object.*m_setupFillCallback)(box); + + ASSERT_NOT_REACHED(); + return false; + } + + virtual bool setupStroke(InlineBox* box) + { + if (m_setupStrokeCallback) + return (*m_object.*m_setupStrokeCallback)(box); + + ASSERT_NOT_REACHED(); + return false; + } + +private: + CallbackClass* m_object; + SVGTextChunkWalkerCallback m_walkerCallback; + SVGTextChunkStartCallback m_startCallback; + SVGTextChunkEndCallback m_endCallback; + SVGTextChunkSetupFillCallback m_setupFillCallback; + SVGTextChunkSetupStrokeCallback m_setupStrokeCallback; +}; + +struct SVGTextChunkLayoutInfo { + SVGTextChunkLayoutInfo(Vector<SVGTextChunk>& textChunks) + : assignChunkProperties(true) + , handlingTextPath(false) + , svgTextChunks(textChunks) + , it(0) + { + } + + bool assignChunkProperties : 1; + bool handlingTextPath : 1; + + Vector<SVGTextChunk>& svgTextChunks; + Vector<SVGChar>::iterator it; + + SVGTextChunk chunk; +}; + +struct SVGTextDecorationInfo { + // ETextDecoration is meant to be used here + HashMap<int, RenderObject*> fillServerMap; + HashMap<int, RenderObject*> strokeServerMap; +}; + +} // namespace WebCore + +#endif // ENABLE(SVG) +#endif // SVGCharacterLayoutInfo_h diff --git a/src/3rdparty/webkit/WebCore/rendering/SVGInlineFlowBox.cpp b/src/3rdparty/webkit/WebCore/rendering/SVGInlineFlowBox.cpp new file mode 100644 index 0000000..3ea1193 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/SVGInlineFlowBox.cpp @@ -0,0 +1,53 @@ +/* + * This file is part of the WebKit project. + * + * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz> + * (C) 2006 Apple Computer Inc. + * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> + * + * 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(SVG) +#include "SVGInlineFlowBox.h" +#include "SVGNames.h" + +namespace WebCore { + +using namespace SVGNames; + +void SVGInlineFlowBox::paint(RenderObject::PaintInfo&, int, int) +{ + ASSERT_NOT_REACHED(); +} + +int SVGInlineFlowBox::placeBoxesHorizontally(int, int&, int&, bool&) +{ + // no-op + return 0; +} + +void SVGInlineFlowBox::verticallyAlignBoxes(int&) +{ + // no-op +} + +} // namespace WebCore + +#endif // ENABLE(SVG) diff --git a/src/3rdparty/webkit/WebCore/rendering/SVGInlineFlowBox.h b/src/3rdparty/webkit/WebCore/rendering/SVGInlineFlowBox.h new file mode 100644 index 0000000..96c5d4a --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/SVGInlineFlowBox.h @@ -0,0 +1,48 @@ +/* + * This file is part of the WebKit project. + * + * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz> + * (C) 2006 Apple Computer Inc. + * + * 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. + * + */ + +#ifndef SVGInlineFlowBox_h +#define SVGInlineFlowBox_h + +#if ENABLE(SVG) +#include "InlineFlowBox.h" + +namespace WebCore { + +class SVGInlineFlowBox : public InlineFlowBox { +public: + SVGInlineFlowBox(RenderObject* obj) + : InlineFlowBox(obj) + { + } + + virtual void paint(RenderObject::PaintInfo&, int tx, int ty); + virtual int placeBoxesHorizontally(int x, int& leftPosition, int& rightPosition, bool& needsWordSpacing); + virtual void verticallyAlignBoxes(int& heightOfBlock); +}; + +} // namespace WebCore + +#endif // ENABLE(SVG) + +#endif // SVGInlineFlowBox_h diff --git a/src/3rdparty/webkit/WebCore/rendering/SVGInlineTextBox.cpp b/src/3rdparty/webkit/WebCore/rendering/SVGInlineTextBox.cpp new file mode 100644 index 0000000..5f59700 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/SVGInlineTextBox.cpp @@ -0,0 +1,548 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2007 Rob Buis <buis@kde.org> + * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> + * + * 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(SVG) +#include "SVGInlineTextBox.h" + +#include "Document.h" +#include "Editor.h" +#include "Frame.h" +#include "GraphicsContext.h" +#include "InlineFlowBox.h" +#include "Range.h" +#include "SVGPaintServer.h" +#include "SVGRootInlineBox.h" +#include "Text.h" + +#include <float.h> + +using std::max; + +namespace WebCore { + +SVGInlineTextBox::SVGInlineTextBox(RenderObject* obj) + : InlineTextBox(obj) +{ +} + +int SVGInlineTextBox::selectionTop() +{ + return m_y; +} + +int SVGInlineTextBox::selectionHeight() +{ + return m_height; +} + +SVGRootInlineBox* SVGInlineTextBox::svgRootInlineBox() const +{ + // Find associated root inline box + InlineFlowBox* parentBox = parent(); + + while (parentBox && !parentBox->isRootInlineBox()) + parentBox = parentBox->parent(); + + ASSERT(parentBox); + ASSERT(parentBox->isRootInlineBox()); + + if (!parentBox->isSVGRootInlineBox()) + return 0; + + return static_cast<SVGRootInlineBox*>(parentBox); +} + +float SVGInlineTextBox::calculateGlyphWidth(RenderStyle* style, int offset, int extraCharsAvailable, int& charsConsumed, String& glyphName) const +{ + ASSERT(style); + return style->font().floatWidth(svgTextRunForInlineTextBox(textObject()->text()->characters() + offset, 1, style, this, 0), extraCharsAvailable, charsConsumed, glyphName); +} + +float SVGInlineTextBox::calculateGlyphHeight(RenderStyle* style, int, int) const +{ + // This is just a guess, and the only purpose of this function is to centralize this hack. + // In real-life top-top-bottom scripts this won't be enough, I fear. + return style->font().ascent() + style->font().descent(); +} + +FloatRect SVGInlineTextBox::calculateGlyphBoundaries(RenderStyle* style, int offset, const SVGChar& svgChar) const +{ + const Font& font = style->font(); + + // Take RTL text into account and pick right glyph width/height. + float glyphWidth = 0.0f; + + // FIXME: account for multi-character glyphs + int charsConsumed; + String glyphName; + if (direction() == LTR) + glyphWidth = calculateGlyphWidth(style, offset, 0, charsConsumed, glyphName); + else + glyphWidth = calculateGlyphWidth(style, start() + end() - offset, 0, charsConsumed, glyphName); + + float x1 = svgChar.x; + float x2 = svgChar.x + glyphWidth; + + float y1 = svgChar.y - font.ascent(); + float y2 = svgChar.y + font.descent(); + + FloatRect glyphRect(x1, y1, x2 - x1, y2 - y1); + + // Take per-character transformations into account + TransformationMatrix ctm = svgChar.characterTransform(); + if (!ctm.isIdentity()) + glyphRect = ctm.mapRect(glyphRect); + + return glyphRect; +} + +// Helper class for closestCharacterToPosition() +struct SVGInlineTextBoxClosestCharacterToPositionWalker { + SVGInlineTextBoxClosestCharacterToPositionWalker(int x, int y) + : m_character(0) + , m_distance(FLT_MAX) + , m_x(x) + , m_y(y) + , m_offset(0) + { + } + + void chunkPortionCallback(SVGInlineTextBox* textBox, int startOffset, const TransformationMatrix& chunkCtm, + const Vector<SVGChar>::iterator& start, const Vector<SVGChar>::iterator& end) + { + RenderStyle* style = textBox->textObject()->style(); + + Vector<SVGChar>::iterator closestCharacter = 0; + unsigned int closestOffset = UINT_MAX; + + for (Vector<SVGChar>::iterator it = start; it != end; ++it) { + if (it->isHidden()) + continue; + + unsigned int newOffset = textBox->start() + (it - start) + startOffset; + FloatRect glyphRect = chunkCtm.mapRect(textBox->calculateGlyphBoundaries(style, newOffset, *it)); + + // Take RTL text into account and pick right glyph width/height. + // NOTE: This offset has to be corrected _after_ calling calculateGlyphBoundaries + if (textBox->direction() == RTL) + newOffset = textBox->start() + textBox->end() - newOffset; + + // Calculate distances relative to the glyph mid-point. I hope this is accurate enough. + float xDistance = glyphRect.x() + glyphRect.width() / 2.0f - m_x; + float yDistance = glyphRect.y() - glyphRect.height() / 2.0f - m_y; + + float newDistance = sqrtf(xDistance * xDistance + yDistance * yDistance); + if (newDistance <= m_distance) { + m_distance = newDistance; + closestOffset = newOffset; + closestCharacter = it; + } + } + + if (closestOffset != UINT_MAX) { + // Record current chunk, if it contains the current closest character next to the mouse. + m_character = closestCharacter; + m_offset = closestOffset; + } + } + + SVGChar* character() const + { + return m_character; + } + + int offset() const + { + if (!m_character) + return 0; + + return m_offset; + } + +private: + Vector<SVGChar>::iterator m_character; + float m_distance; + + int m_x; + int m_y; + int m_offset; +}; + +// Helper class for selectionRect() +struct SVGInlineTextBoxSelectionRectWalker { + SVGInlineTextBoxSelectionRectWalker() + { + } + + void chunkPortionCallback(SVGInlineTextBox* textBox, int startOffset, const TransformationMatrix& chunkCtm, + const Vector<SVGChar>::iterator& start, const Vector<SVGChar>::iterator& end) + { + RenderStyle* style = textBox->textObject()->style(); + + for (Vector<SVGChar>::iterator it = start; it != end; ++it) { + if (it->isHidden()) + continue; + + unsigned int newOffset = textBox->start() + (it - start) + startOffset; + m_selectionRect.unite(textBox->calculateGlyphBoundaries(style, newOffset, *it)); + } + + m_selectionRect = chunkCtm.mapRect(m_selectionRect); + } + + FloatRect selectionRect() const + { + return m_selectionRect; + } + +private: + FloatRect m_selectionRect; +}; + +SVGChar* SVGInlineTextBox::closestCharacterToPosition(int x, int y, int& offset) const +{ + SVGRootInlineBox* rootBox = svgRootInlineBox(); + if (!rootBox) + return 0; + + SVGInlineTextBoxClosestCharacterToPositionWalker walkerCallback(x, y); + SVGTextChunkWalker<SVGInlineTextBoxClosestCharacterToPositionWalker> walker(&walkerCallback, &SVGInlineTextBoxClosestCharacterToPositionWalker::chunkPortionCallback); + + rootBox->walkTextChunks(&walker, this); + + offset = walkerCallback.offset(); + return walkerCallback.character(); +} + +bool SVGInlineTextBox::svgCharacterHitsPosition(int x, int y, int& offset) const +{ + SVGChar* charAtPosPtr = closestCharacterToPosition(x, y, offset); + if (!charAtPosPtr) + return false; + + SVGChar& charAtPos = *charAtPosPtr; + RenderStyle* style = textObject()->style(m_firstLine); + FloatRect glyphRect = calculateGlyphBoundaries(style, offset, charAtPos); + + if (direction() == RTL) + offset++; + + // FIXME: todo list + // (#13910) This code does not handle bottom-to-top/top-to-bottom vertical text. + + // Check whether y position hits the current character + if (y < charAtPos.y - glyphRect.height() || y > charAtPos.y) + return false; + + // Check whether x position hits the current character + if (x < charAtPos.x) { + if (offset > 0 && direction() == LTR) + return true; + else if (offset < (int) end() && direction() == RTL) + return true; + + return false; + } + + // If we are past the last glyph of this box, don't mark it as 'hit' anymore. + if (x >= charAtPos.x + glyphRect.width() && offset == (int) end()) + return false; + + // Snap to character at half of it's advance + if (x >= charAtPos.x + glyphRect.width() / 2.0) + offset += direction() == RTL ? -1 : 1; + + return true; +} + +int SVGInlineTextBox::offsetForPosition(int, bool) const +{ + // SVG doesn't use the offset <-> position selection system. + ASSERT_NOT_REACHED(); + return 0; +} + +int SVGInlineTextBox::positionForOffset(int) const +{ + // SVG doesn't use the offset <-> position selection system. + ASSERT_NOT_REACHED(); + return 0; +} + +bool SVGInlineTextBox::nodeAtPoint(const HitTestRequest&, HitTestResult& result, int x, int y, int tx, int ty) +{ + ASSERT(!isLineBreak()); + + IntRect rect = selectionRect(0, 0, 0, len()); + if (object()->style()->visibility() == VISIBLE && rect.contains(x, y)) { + object()->updateHitTestResult(result, IntPoint(x - tx, y - ty)); + return true; + } + + return false; +} + +IntRect SVGInlineTextBox::selectionRect(int, int, int startPos, int endPos) +{ + if (startPos >= endPos) + return IntRect(); + + // TODO: Actually respect startPos/endPos - we're returning the _full_ selectionRect + // here. This won't lead to visible bugs, but to extra work being done. Investigate. + SVGRootInlineBox* rootBox = svgRootInlineBox(); + if (!rootBox) + return IntRect(); + + SVGInlineTextBoxSelectionRectWalker walkerCallback; + SVGTextChunkWalker<SVGInlineTextBoxSelectionRectWalker> walker(&walkerCallback, &SVGInlineTextBoxSelectionRectWalker::chunkPortionCallback); + + rootBox->walkTextChunks(&walker, this); + return enclosingIntRect(walkerCallback.selectionRect()); +} + +void SVGInlineTextBox::paintCharacters(RenderObject::PaintInfo& paintInfo, int tx, int ty, const SVGChar& svgChar, const UChar* chars, int length, SVGPaintServer* activePaintServer) +{ + if (object()->style()->visibility() != VISIBLE || paintInfo.phase == PaintPhaseOutline) + return; + + ASSERT(paintInfo.phase != PaintPhaseSelfOutline && paintInfo.phase != PaintPhaseChildOutlines); + + RenderText* text = textObject(); + ASSERT(text); + + bool isPrinting = text->document()->printing(); + + // Determine whether or not we're selected. + bool haveSelection = !isPrinting && selectionState() != RenderObject::SelectionNone; + if (!haveSelection && paintInfo.phase == PaintPhaseSelection) + // When only painting the selection, don't bother to paint if there is none. + return; + + // Determine whether or not we have a composition. + bool containsComposition = text->document()->frame()->editor()->compositionNode() == text->node(); + bool useCustomUnderlines = containsComposition && text->document()->frame()->editor()->compositionUsesCustomUnderlines(); + + // Set our font + RenderStyle* styleToUse = text->style(isFirstLineStyle()); + const Font* font = &styleToUse->font(); + if (*font != paintInfo.context->font()) + paintInfo.context->setFont(*font); + + TransformationMatrix ctm = svgChar.characterTransform(); + if (!ctm.isIdentity()) + paintInfo.context->concatCTM(ctm); + + // 1. Paint backgrounds behind text if needed. Examples of such backgrounds include selection + // and marked text. + if (paintInfo.phase != PaintPhaseSelection && !isPrinting) { +#if PLATFORM(MAC) + // Custom highlighters go behind everything else. + if (styleToUse->highlight() != nullAtom && !paintInfo.context->paintingDisabled()) + paintCustomHighlight(tx, ty, styleToUse->highlight()); +#endif + + if (containsComposition && !useCustomUnderlines) + paintCompositionBackground(paintInfo.context, tx, ty, styleToUse, font, + text->document()->frame()->editor()->compositionStart(), + text->document()->frame()->editor()->compositionEnd()); + + paintDocumentMarkers(paintInfo.context, tx, ty, styleToUse, font, true); + + if (haveSelection && !useCustomUnderlines) { + int boxStartOffset = chars - text->characters() - start(); + paintSelection(boxStartOffset, svgChar, chars, length, paintInfo.context, styleToUse, font); + } + } + + // Set a text shadow if we have one. + // FIXME: Support multiple shadow effects. Need more from the CG API before + // we can do this. + bool setShadow = false; + if (styleToUse->textShadow()) { + paintInfo.context->setShadow(IntSize(styleToUse->textShadow()->x, styleToUse->textShadow()->y), + styleToUse->textShadow()->blur, styleToUse->textShadow()->color); + setShadow = true; + } + + IntPoint origin((int) svgChar.x, (int) svgChar.y); + TextRun run = svgTextRunForInlineTextBox(chars, length, styleToUse, this, svgChar.x); + +#if ENABLE(SVG_FONTS) + // SVG Fonts need access to the paint server used to draw the current text chunk. + // They need to be able to call renderPath() on a SVGPaintServer object. + run.setActivePaintServer(activePaintServer); +#endif + + paintInfo.context->drawText(run, origin); + + if (paintInfo.phase != PaintPhaseSelection) { + paintDocumentMarkers(paintInfo.context, tx, ty, styleToUse, font, false); + + if (useCustomUnderlines) { + const Vector<CompositionUnderline>& underlines = text->document()->frame()->editor()->customCompositionUnderlines(); + size_t numUnderlines = underlines.size(); + + for (size_t index = 0; index < numUnderlines; ++index) { + const CompositionUnderline& underline = underlines[index]; + + if (underline.endOffset <= start()) + // underline is completely before this run. This might be an underline that sits + // before the first run we draw, or underlines that were within runs we skipped + // due to truncation. + continue; + + if (underline.startOffset <= end()) { + // underline intersects this run. Paint it. + paintCompositionUnderline(paintInfo.context, tx, ty, underline); + if (underline.endOffset > end() + 1) + // underline also runs into the next run. Bail now, no more marker advancement. + break; + } else + // underline is completely after this run, bail. A later run will paint it. + break; + } + } + + } + + if (setShadow) + paintInfo.context->clearShadow(); + + if (!ctm.isIdentity()) + paintInfo.context->concatCTM(ctm.inverse()); +} + +void SVGInlineTextBox::paintSelection(int boxStartOffset, const SVGChar& svgChar, const UChar*, int length, GraphicsContext* p, RenderStyle* style, const Font* f) +{ + if (selectionState() == RenderObject::SelectionNone) + return; + + int startPos, endPos; + selectionStartEnd(startPos, endPos); + + if (startPos >= endPos) + return; + + Color textColor = style->color(); + Color color = object()->selectionBackgroundColor(); + if (!color.isValid() || color.alpha() == 0) + return; + + // If the text color ends up being the same as the selection background, invert the selection + // background. This should basically never happen, since the selection has transparency. + if (textColor == color) + color = Color(0xff - color.red(), 0xff - color.green(), 0xff - color.blue()); + + // Map from text box positions and a given start offset to chunk positions + // 'boxStartOffset' represents the beginning of the text chunk. + if ((startPos > boxStartOffset && endPos > boxStartOffset + length) || boxStartOffset >= endPos) + return; + + if (endPos > boxStartOffset + length) + endPos = boxStartOffset + length; + + if (startPos < boxStartOffset) + startPos = boxStartOffset; + + ASSERT(startPos >= boxStartOffset); + ASSERT(endPos <= boxStartOffset + length); + ASSERT(startPos < endPos); + + p->save(); + + int adjust = startPos >= boxStartOffset ? boxStartOffset : 0; + p->drawHighlightForText(svgTextRunForInlineTextBox(textObject()->text()->characters() + start() + boxStartOffset, length, style, this, svgChar.x), + IntPoint((int) svgChar.x, (int) svgChar.y - f->ascent()), + f->ascent() + f->descent(), color, startPos - adjust, endPos - adjust); + + p->restore(); +} + +static inline Path pathForDecoration(ETextDecoration decoration, RenderObject* object, float x, float y, float width) +{ + float thickness = SVGRenderStyle::cssPrimitiveToLength(object, object->style()->svgStyle()->strokeWidth(), 1.0f); + + const Font& font = object->style()->font(); + thickness = max(thickness * powf(font.size(), 2.0f) / font.unitsPerEm(), 1.0f); + + if (decoration == UNDERLINE) + y += thickness * 1.5f; // For compatibility with Batik/Opera + else if (decoration == OVERLINE) + y += thickness; + + float halfThickness = thickness / 2.0f; + return Path::createRectangle(FloatRect(x + halfThickness, y, width - 2.0f * halfThickness, thickness)); +} + +void SVGInlineTextBox::paintDecoration(ETextDecoration decoration, GraphicsContext* context, int tx, int ty, int width, const SVGChar& svgChar, const SVGTextDecorationInfo& info) +{ + if (object()->style()->visibility() != VISIBLE) + return; + + // This function does NOT accept combinated text decorations. It's meant to be invoked for just one. + ASSERT(decoration == TDNONE || decoration == UNDERLINE || decoration == OVERLINE || decoration == LINE_THROUGH || decoration == BLINK); + + bool isFilled = info.fillServerMap.contains(decoration); + bool isStroked = info.strokeServerMap.contains(decoration); + + if (!isFilled && !isStroked) + return; + + if (decoration == UNDERLINE) + ty += m_baseline; + else if (decoration == LINE_THROUGH) + ty += 2 * m_baseline / 3; + + context->save(); + context->beginPath(); + + TransformationMatrix ctm = svgChar.characterTransform(); + if (!ctm.isIdentity()) + context->concatCTM(ctm); + + if (isFilled) { + if (RenderObject* fillObject = info.fillServerMap.get(decoration)) { + if (SVGPaintServer* fillPaintServer = SVGPaintServer::fillPaintServer(fillObject->style(), fillObject)) { + context->addPath(pathForDecoration(decoration, fillObject, tx, ty, width)); + fillPaintServer->draw(context, fillObject, ApplyToFillTargetType); + } + } + } + + if (isStroked) { + if (RenderObject* strokeObject = info.strokeServerMap.get(decoration)) { + if (SVGPaintServer* strokePaintServer = SVGPaintServer::strokePaintServer(strokeObject->style(), strokeObject)) { + context->addPath(pathForDecoration(decoration, strokeObject, tx, ty, width)); + strokePaintServer->draw(context, strokeObject, ApplyToStrokeTargetType); + } + } + } + + context->restore(); +} + +} // namespace WebCore + +#endif diff --git a/src/3rdparty/webkit/WebCore/rendering/SVGInlineTextBox.h b/src/3rdparty/webkit/WebCore/rendering/SVGInlineTextBox.h new file mode 100644 index 0000000..1ddc23a --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/SVGInlineTextBox.h @@ -0,0 +1,75 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2007 Rob Buis <buis@kde.org> + * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> + * + * 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. + * + */ + +#ifndef SVGInlineTextBox_h +#define SVGInlineTextBox_h + +#if ENABLE(SVG) +#include "InlineTextBox.h" + +namespace WebCore { + + class SVGChar; + class SVGRootInlineBox; + class SVGTextDecorationInfo; + + class SVGInlineTextBox : public InlineTextBox { + public: + SVGInlineTextBox(RenderObject* obj); + + virtual int selectionTop(); + virtual int selectionHeight(); + + virtual int offsetForPosition(int x, bool includePartialGlyphs = true) const; + virtual int positionForOffset(int offset) const; + + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty); + virtual IntRect selectionRect(int absx, int absy, int startPos, int endPos); + + // SVGs custom paint text method + void paintCharacters(RenderObject::PaintInfo&, int tx, int ty, const SVGChar&, const UChar* chars, int length, SVGPaintServer*); + + // SVGs custom paint selection method + void paintSelection(int boxStartOffset, const SVGChar&, const UChar*, int length, GraphicsContext*, RenderStyle*, const Font*); + + // SVGs custom paint decoration method + void paintDecoration(ETextDecoration, GraphicsContext*, int tx, int ty, int width, const SVGChar&, const SVGTextDecorationInfo&); + + SVGRootInlineBox* svgRootInlineBox() const; + + // Helper functions shared with SVGRootInlineBox + float calculateGlyphWidth(RenderStyle* style, int offset, int extraCharsAvailable, int& charsConsumed, String& glyphName) const; + float calculateGlyphHeight(RenderStyle*, int offset, int extraCharsAvailable) const; + + FloatRect calculateGlyphBoundaries(RenderStyle*, int offset, const SVGChar&) const; + SVGChar* closestCharacterToPosition(int x, int y, int& offset) const; + + private: + friend class RenderSVGInlineText; + bool svgCharacterHitsPosition(int x, int y, int& offset) const; + }; + +} // namespace WebCore + +#endif +#endif // SVGInlineTextBox_h diff --git a/src/3rdparty/webkit/WebCore/rendering/SVGRenderSupport.cpp b/src/3rdparty/webkit/WebCore/rendering/SVGRenderSupport.cpp new file mode 100644 index 0000000..c88e8e8 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/SVGRenderSupport.cpp @@ -0,0 +1,167 @@ +/* + * Copyright (C) 2007, 2008 Rob Buis <buis@kde.org> + * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> + * (C) 2007 Eric Seidel <eric@webkit.org> + * + * 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(SVG) +#include "SVGRenderSupport.h" + +#include "TransformationMatrix.h" +#include "ImageBuffer.h" +#include "RenderObject.h" +#include "RenderSVGContainer.h" +#include "RenderView.h" +#include "SVGResourceClipper.h" +#include "SVGResourceFilter.h" +#include "SVGResourceMasker.h" +#include "SVGStyledElement.h" +#include "SVGURIReference.h" + +namespace WebCore { + +void prepareToRenderSVGContent(RenderObject* object, RenderObject::PaintInfo& paintInfo, const FloatRect& boundingBox, SVGResourceFilter*& filter, SVGResourceFilter* rootFilter) +{ + SVGElement* svgElement = static_cast<SVGElement*>(object->element()); + ASSERT(svgElement && svgElement->document() && svgElement->isStyled()); + ASSERT(object); + + SVGStyledElement* styledElement = static_cast<SVGStyledElement*>(svgElement); + const RenderStyle* style = object->style(); + ASSERT(style); + + const SVGRenderStyle* svgStyle = style->svgStyle(); + ASSERT(svgStyle); + + // Setup transparency layers before setting up filters! + float opacity = style->opacity(); + if (opacity < 1.0f) { + paintInfo.context->clip(enclosingIntRect(boundingBox)); + paintInfo.context->beginTransparencyLayer(opacity); + } + +#if ENABLE(SVG_FILTERS) + AtomicString filterId(svgStyle->filter()); +#endif + + AtomicString clipperId(svgStyle->clipPath()); + AtomicString maskerId(svgStyle->maskElement()); + + Document* document = object->document(); + +#if ENABLE(SVG_FILTERS) + SVGResourceFilter* newFilter = getFilterById(document, filterId); + if (newFilter == rootFilter) { + // Catch <text filter="url(#foo)">Test<tspan filter="url(#foo)">123</tspan></text>. + // The filter is NOT meant to be applied twice in that case! + filter = 0; + filterId = String(); + } else + filter = newFilter; +#endif + + SVGResourceClipper* clipper = getClipperById(document, clipperId); + SVGResourceMasker* masker = getMaskerById(document, maskerId); + +#if ENABLE(SVG_FILTERS) + if (filter) { + filter->addClient(styledElement); + filter->prepareFilter(paintInfo.context, boundingBox); + } else if (!filterId.isEmpty()) + svgElement->document()->accessSVGExtensions()->addPendingResource(filterId, styledElement); +#endif + + if (clipper) { + clipper->addClient(styledElement); + clipper->applyClip(paintInfo.context, boundingBox); + } else if (!clipperId.isEmpty()) + svgElement->document()->accessSVGExtensions()->addPendingResource(clipperId, styledElement); + + if (masker) { + masker->addClient(styledElement); + masker->applyMask(paintInfo.context, boundingBox); + } else if (!maskerId.isEmpty()) + svgElement->document()->accessSVGExtensions()->addPendingResource(maskerId, styledElement); +} + +void finishRenderSVGContent(RenderObject* object, RenderObject::PaintInfo& paintInfo, const FloatRect& boundingBox, SVGResourceFilter*& filter, GraphicsContext* savedContext) +{ + ASSERT(object); + + const RenderStyle* style = object->style(); + ASSERT(style); + +#if ENABLE(SVG_FILTERS) + if (filter) { + filter->applyFilter(paintInfo.context, boundingBox); + paintInfo.context = savedContext; + } +#endif + + float opacity = style->opacity(); + if (opacity < 1.0f) + paintInfo.context->endTransparencyLayer(); +} + +void renderSubtreeToImage(ImageBuffer* image, RenderObject* item) +{ + ASSERT(item); + ASSERT(image); + ASSERT(image->context()); + RenderObject::PaintInfo info(image->context(), IntRect(), PaintPhaseForeground, 0, 0, 0); + + RenderSVGContainer* svgContainer = 0; + if (item && item->isSVGContainer()) + svgContainer = static_cast<RenderSVGContainer*>(item); + + bool drawsContents = svgContainer ? svgContainer->drawsContents() : false; + if (svgContainer && !drawsContents) + svgContainer->setDrawsContents(true); + + item->layoutIfNeeded(); + item->paint(info, 0, 0); + + if (svgContainer && !drawsContents) + svgContainer->setDrawsContents(false); +} + +void clampImageBufferSizeToViewport(RenderObject* object, IntSize& size) +{ + if (!object || !object->isRenderView()) + return; + + RenderView* view = static_cast<RenderView*>(object); + if (!view->frameView()) + return; + + int viewWidth = view->frameView()->visibleWidth(); + int viewHeight = view->frameView()->visibleHeight(); + + if (size.width() > viewWidth) + size.setWidth(viewWidth); + + if (size.height() > viewHeight) + size.setHeight(viewHeight); +} + +} // namespace WebCore + +#endif // ENABLE(SVG) diff --git a/src/3rdparty/webkit/WebCore/rendering/SVGRenderSupport.h b/src/3rdparty/webkit/WebCore/rendering/SVGRenderSupport.h new file mode 100644 index 0000000..7fdfcf8 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/SVGRenderSupport.h @@ -0,0 +1,42 @@ +/** + * This file is part of the DOM implementation for WebKit. + * + * Copyright (C) 2007 Rob Buis <buis@kde.org> + * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> + * (C) 2007 Eric Seidel <eric@webkit.org> + * + * 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. + * + */ + +#if ENABLE(SVG) +#include "RenderObject.h" + +namespace WebCore { + +class SVGResourceFilter; +void prepareToRenderSVGContent(RenderObject*, RenderObject::PaintInfo&, const FloatRect& boundingBox, SVGResourceFilter*&, SVGResourceFilter* rootFilter = 0); +void finishRenderSVGContent(RenderObject*, RenderObject::PaintInfo&, const FloatRect& boundingBox, SVGResourceFilter*&, GraphicsContext* savedContext); + +// This offers a way to render parts of a WebKit rendering tree into a ImageBuffer. +class ImageBuffer; +void renderSubtreeToImage(ImageBuffer*, RenderObject*); + +void clampImageBufferSizeToViewport(RenderObject*, IntSize&); + +} + +#endif diff --git a/src/3rdparty/webkit/WebCore/rendering/SVGRenderTreeAsText.cpp b/src/3rdparty/webkit/WebCore/rendering/SVGRenderTreeAsText.cpp new file mode 100644 index 0000000..eff2fff --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/SVGRenderTreeAsText.cpp @@ -0,0 +1,562 @@ +/* + * Copyright (C) 2004, 2005, 2007 Apple Inc. All rights reserved. + * (C) 2005 Rob Buis <buis@kde.org> + * (C) 2006 Alexander Kellett <lypanov@kde.org> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" + +#if ENABLE(SVG) +#include "SVGRenderTreeAsText.h" + +#include "GraphicsTypes.h" +#include "InlineTextBox.h" +#include "HTMLNames.h" +#include "RenderSVGContainer.h" +#include "RenderSVGInlineText.h" +#include "RenderSVGText.h" +#include "RenderSVGRoot.h" +#include "RenderTreeAsText.h" +#include "SVGCharacterLayoutInfo.h" +#include "SVGInlineTextBox.h" +#include "SVGPaintServerGradient.h" +#include "SVGPaintServerPattern.h" +#include "SVGPaintServerSolid.h" +#include "SVGResourceClipper.h" +#include "SVGRootInlineBox.h" +#include "SVGStyledElement.h" +#include <math.h> + +namespace WebCore { + +/** class + iomanip to help streaming list separators, i.e. ", " in string "a, b, c, d" + * Can be used in cases where you don't know which item in the list is the first + * one to be printed, but still want to avoid strings like ", b, c". + */ +class TextStreamSeparator { +public: + TextStreamSeparator(const String& s) + : m_separator(s) + , m_needToSeparate(false) + { + } + +private: + friend TextStream& operator<<(TextStream&, TextStreamSeparator&); + + String m_separator; + bool m_needToSeparate; +}; + +TextStream& operator<<(TextStream& ts, TextStreamSeparator& sep) +{ + if (sep.m_needToSeparate) + ts << sep.m_separator; + else + sep.m_needToSeparate = true; + return ts; +} + +TextStream& operator<<(TextStream& ts, const IntPoint& p) +{ + return ts << "(" << p.x() << "," << p.y() << ")"; +} + +TextStream& operator<<(TextStream& ts, const IntRect& r) +{ + return ts << "at (" << r.x() << "," << r.y() << ") size " << r.width() << "x" << r.height(); +} + +bool hasFractions(double val) +{ + double epsilon = 0.0001; + int ival = static_cast<int>(val); + double dval = static_cast<double>(ival); + return fabs(val - dval) > epsilon; +} + +TextStream& operator<<(TextStream& ts, const FloatRect &r) +{ + ts << "at ("; + if (hasFractions(r.x())) + ts << r.x(); + else + ts << int(r.x()); + ts << ","; + if (hasFractions(r.y())) + ts << r.y(); + else + ts << int(r.y()); + ts << ") size "; + if (hasFractions(r.width())) + ts << r.width(); + else + ts << int(r.width()); + ts << "x"; + if (hasFractions(r.height())) + ts << r.height(); + else + ts << int(r.height()); + return ts; +} + +TextStream& operator<<(TextStream& ts, const FloatPoint& p) +{ + ts << "("; + if (hasFractions(p.x())) + ts << p.x(); + else + ts << int(p.x()); + ts << ","; + if (hasFractions(p.y())) + ts << p.y(); + else + ts << int(p.y()); + return ts << ")"; +} + +TextStream& operator<<(TextStream& ts, const FloatSize& s) +{ + ts << "width="; + if (hasFractions(s.width())) + ts << s.width(); + else + ts << int(s.width()); + ts << " height="; + if (hasFractions(s.height())) + ts << s.height(); + else + ts << int(s.height()); + return ts; +} + +TextStream& operator<<(TextStream& ts, const TransformationMatrix& transform) +{ + if (transform.isIdentity()) + ts << "identity"; + else + ts << "{m=((" + << transform.a() << "," << transform.b() + << ")(" + << transform.c() << "," << transform.d() + << ")) t=(" + << transform.e() << "," << transform.f() + << ")}"; + + return ts; +} + +TextStream& operator<<(TextStream& ts, const Color& c) +{ + return ts << c.name(); +} + +static void writeIndent(TextStream& ts, int indent) +{ + for (int i = 0; i != indent; ++i) + ts << " "; +} + +// FIXME: Maybe this should be in KCanvasRenderingStyle.cpp +static TextStream& operator<<(TextStream& ts, const DashArray& a) +{ + ts << "{"; + DashArray::const_iterator end = a.end(); + for (DashArray::const_iterator it = a.begin(); it != end; ++it) { + if (it != a.begin()) + ts << ", "; + ts << *it; + } + ts << "}"; + return ts; +} + +// FIXME: Maybe this should be in GraphicsTypes.cpp +static TextStream& operator<<(TextStream& ts, LineCap style) +{ + switch (style) { + case ButtCap: + ts << "BUTT"; + break; + case RoundCap: + ts << "ROUND"; + break; + case SquareCap: + ts << "SQUARE"; + break; + } + return ts; +} + +// FIXME: Maybe this should be in GraphicsTypes.cpp +static TextStream& operator<<(TextStream& ts, LineJoin style) +{ + switch (style) { + case MiterJoin: + ts << "MITER"; + break; + case RoundJoin: + ts << "ROUND"; + break; + case BevelJoin: + ts << "BEVEL"; + break; + } + return ts; +} + +static void writeStyle(TextStream& ts, const RenderObject& object) +{ + const RenderStyle* style = object.style(); + const SVGRenderStyle* svgStyle = style->svgStyle(); + + if (!object.localTransform().isIdentity()) + ts << " [transform=" << object.localTransform() << "]"; + if (svgStyle->imageRendering() != SVGRenderStyle::initialImageRendering()) + ts << " [image rendering=" << svgStyle->imageRendering() << "]"; + if (style->opacity() != RenderStyle::initialOpacity()) + ts << " [opacity=" << style->opacity() << "]"; + if (object.isRenderPath()) { + const RenderPath& path = static_cast<const RenderPath&>(object); + SVGPaintServer* strokePaintServer = SVGPaintServer::strokePaintServer(style, &path); + if (strokePaintServer) { + TextStreamSeparator s(" "); + ts << " [stroke={"; + if (strokePaintServer) + ts << s << *strokePaintServer; + + double dashOffset = SVGRenderStyle::cssPrimitiveToLength(&path, svgStyle->strokeDashOffset(), 0.0f); + const DashArray& dashArray = dashArrayFromRenderingStyle(style); + double strokeWidth = SVGRenderStyle::cssPrimitiveToLength(&path, svgStyle->strokeWidth(), 1.0f); + + if (svgStyle->strokeOpacity() != 1.0f) + ts << s << "[opacity=" << svgStyle->strokeOpacity() << "]"; + if (strokeWidth != 1.0f) + ts << s << "[stroke width=" << strokeWidth << "]"; + if (svgStyle->strokeMiterLimit() != 4) + ts << s << "[miter limit=" << svgStyle->strokeMiterLimit() << "]"; + if (svgStyle->capStyle() != 0) + ts << s << "[line cap=" << svgStyle->capStyle() << "]"; + if (svgStyle->joinStyle() != 0) + ts << s << "[line join=" << svgStyle->joinStyle() << "]"; + if (dashOffset != 0.0f) + ts << s << "[dash offset=" << dashOffset << "]"; + if (!dashArray.isEmpty()) + ts << s << "[dash array=" << dashArray << "]"; + ts << "}]"; + } + SVGPaintServer* fillPaintServer = SVGPaintServer::fillPaintServer(style, &path); + if (fillPaintServer) { + TextStreamSeparator s(" "); + ts << " [fill={"; + if (fillPaintServer) + ts << s << *fillPaintServer; + + if (style->svgStyle()->fillOpacity() != 1.0f) + ts << s << "[opacity=" << style->svgStyle()->fillOpacity() << "]"; + if (style->svgStyle()->fillRule() != RULE_NONZERO) + ts << s << "[fill rule=" << style->svgStyle()->fillRule() << "]"; + ts << "}]"; + } + } + if (!svgStyle->clipPath().isEmpty()) + ts << " [clip path=\"" << svgStyle->clipPath() << "\"]"; + if (!svgStyle->startMarker().isEmpty()) + ts << " [start marker=" << svgStyle->startMarker() << "]"; + if (!svgStyle->midMarker().isEmpty()) + ts << " [middle marker=" << svgStyle->midMarker() << "]"; + if (!svgStyle->endMarker().isEmpty()) + ts << " [end marker=" << svgStyle->endMarker() << "]"; + if (!svgStyle->filter().isEmpty()) + ts << " [filter=" << svgStyle->filter() << "]"; +} + +static TextStream& operator<<(TextStream& ts, const RenderPath& path) +{ + ts << " " << path.absoluteTransform().mapRect(path.relativeBBox()); + + writeStyle(ts, path); + + ts << " [data=\"" << path.path().debugString() << "\"]"; + + return ts; +} + +static TextStream& operator<<(TextStream& ts, const RenderSVGContainer& container) +{ + ts << " " << container.absoluteTransform().mapRect(container.relativeBBox()); + + writeStyle(ts, container); + + return ts; +} + +static TextStream& operator<<(TextStream& ts, const RenderSVGRoot& root) +{ + ts << " " << root.absoluteTransform().mapRect(root.relativeBBox()); + + writeStyle(ts, root); + + return ts; +} + +static TextStream& operator<<(TextStream& ts, const RenderSVGText& text) +{ + SVGRootInlineBox* box = static_cast<SVGRootInlineBox*>(text.firstRootBox()); + + if (!box) + return ts; + + Vector<SVGTextChunk>& chunks = const_cast<Vector<SVGTextChunk>& >(box->svgTextChunks()); + ts << " at (" << text.xPos() << "," << text.yPos() << ") size " << box->width() << "x" << box->height() << " contains " << chunks.size() << " chunk(s)"; + + if (text.parent() && (text.parent()->style()->color() != text.style()->color())) + ts << " [color=" << text.style()->color().name() << "]"; + + return ts; +} + +static inline bool containsInlineTextBox(SVGTextChunk& chunk, SVGInlineTextBox* box) +{ + Vector<SVGInlineBoxCharacterRange>::iterator boxIt = chunk.boxes.begin(); + Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = chunk.boxes.end(); + + bool found = false; + for (; boxIt != boxEnd; ++boxIt) { + SVGInlineBoxCharacterRange& range = *boxIt; + + if (box == static_cast<SVGInlineTextBox*>(range.box)) { + found = true; + break; + } + } + + return found; +} + +static inline void writeSVGInlineTextBox(TextStream& ts, SVGInlineTextBox* textBox, int indent) +{ + SVGRootInlineBox* rootBox = textBox->svgRootInlineBox(); + if (!rootBox) + return; + + Vector<SVGTextChunk>& chunks = const_cast<Vector<SVGTextChunk>& >(rootBox->svgTextChunks()); + + Vector<SVGTextChunk>::iterator it = chunks.begin(); + Vector<SVGTextChunk>::iterator end = chunks.end(); + + // Write text chunks + unsigned int i = 1; + for (; it != end; ++it) { + SVGTextChunk& cur = *it; + + // Write inline box character ranges + Vector<SVGInlineBoxCharacterRange>::iterator boxIt = cur.boxes.begin(); + Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = cur.boxes.end(); + + if (!containsInlineTextBox(cur, textBox)) { + i++; + continue; + } + + writeIndent(ts, indent + 1); + + unsigned int j = 1; + ts << "chunk " << i << " "; + + if (cur.anchor == TA_MIDDLE) { + ts << "(middle anchor"; + if (cur.isVerticalText) + ts << ", vertical"; + ts << ") "; + } else if (cur.anchor == TA_END) { + ts << "(end anchor"; + if (cur.isVerticalText) + ts << ", vertical"; + ts << ") "; + } else if (cur.isVerticalText) + ts << "(vertical) "; + + unsigned int totalOffset = 0; + + for (; boxIt != boxEnd; ++boxIt) { + SVGInlineBoxCharacterRange& range = *boxIt; + + unsigned int offset = range.endOffset - range.startOffset; + ASSERT(cur.start + totalOffset <= cur.end); + + totalOffset += offset; + + if (textBox != static_cast<SVGInlineTextBox*>(range.box)) { + j++; + continue; + } + + FloatPoint topLeft = topLeftPositionOfCharacterRange(cur.start + totalOffset - offset, cur.start + totalOffset); + + ts << "text run " << j << " at (" << topLeft.x() << "," << topLeft.y() << ") "; + ts << "startOffset " << range.startOffset << " endOffset " << range.endOffset; + + if (cur.isVerticalText) + ts << " height " << cummulatedHeightOfInlineBoxCharacterRange(range); + else + ts << " width " << cummulatedWidthOfInlineBoxCharacterRange(range); + + if (textBox->direction() == RTL || textBox->m_dirOverride) { + ts << (textBox->direction() == RTL ? " RTL" : " LTR"); + + if (textBox->m_dirOverride) + ts << " override"; + } + + ts << ": " << quoteAndEscapeNonPrintables(String(textBox->textObject()->text()).substring(textBox->start() + range.startOffset, offset)) << "\n"; + + j++; + } + + i++; + } +} + +static inline void writeSVGInlineText(TextStream& ts, const RenderSVGInlineText& text, int indent) +{ + for (InlineTextBox* box = text.firstTextBox(); box; box = box->nextTextBox()) + writeSVGInlineTextBox(ts, static_cast<SVGInlineTextBox*>(box), indent); +} + +static String getTagName(SVGStyledElement* elem) +{ + if (elem) + return elem->nodeName(); + return ""; +} + +void write(TextStream& ts, const RenderSVGContainer& container, int indent) +{ + writeIndent(ts, indent); + ts << container.renderName(); + + if (container.element()) { + String tagName = getTagName(static_cast<SVGStyledElement*>(container.element())); + if (!tagName.isEmpty()) + ts << " {" << tagName << "}"; + } + + ts << container << "\n"; + + for (RenderObject* child = container.firstChild(); child; child = child->nextSibling()) + write(ts, *child, indent + 1); +} + +void write(TextStream& ts, const RenderSVGRoot& root, int indent) +{ + writeIndent(ts, indent); + ts << root.renderName(); + + if (root.element()) { + String tagName = getTagName(static_cast<SVGStyledElement*>(root.element())); + if (!tagName.isEmpty()) + ts << " {" << tagName << "}"; + } + + ts << root << "\n"; + + for (RenderObject* child = root.firstChild(); child; child = child->nextSibling()) + write(ts, *child, indent + 1); +} + +void write(TextStream& ts, const RenderSVGText& text, int indent) +{ + writeIndent(ts, indent); + ts << text.renderName(); + + if (text.element()) { + String tagName = getTagName(static_cast<SVGStyledElement*>(text.element())); + if (!tagName.isEmpty()) + ts << " {" << tagName << "}"; + } + + ts << text << "\n"; + + for (RenderObject* child = text.firstChild(); child; child = child->nextSibling()) + write(ts, *child, indent + 1); +} + +void write(TextStream& ts, const RenderSVGInlineText& text, int indent) +{ + writeIndent(ts, indent); + ts << text.renderName(); + + if (text.element()) { + String tagName = getTagName(static_cast<SVGStyledElement*>(text.element())); + if (!tagName.isEmpty()) + ts << " {" << tagName << "}"; + } + + ts << " at (" << text.xPos() << "," << text.yPos() << ") size " << text.width() << "x" << text.height() << "\n"; + writeSVGInlineText(ts, text, indent); +} + +void write(TextStream& ts, const RenderPath& path, int indent) +{ + writeIndent(ts, indent); + ts << path.renderName(); + + if (path.element()) { + String tagName = getTagName(static_cast<SVGStyledElement*>(path.element())); + if (!tagName.isEmpty()) + ts << " {" << tagName << "}"; + } + + ts << path << "\n"; +} + +void writeRenderResources(TextStream& ts, Node* parent) +{ + ASSERT(parent); + Node* node = parent; + do { + if (!node->isSVGElement()) + continue; + SVGElement* svgElement = static_cast<SVGElement*>(node); + if (!svgElement->isStyled()) + continue; + + SVGStyledElement* styled = static_cast<SVGStyledElement*>(svgElement); + RefPtr<SVGResource> resource(styled->canvasResource()); + if (!resource) + continue; + + String elementId = svgElement->getAttribute(HTMLNames::idAttr); + if (resource->isPaintServer()) { + RefPtr<SVGPaintServer> paintServer = WTF::static_pointer_cast<SVGPaintServer>(resource); + ts << "KRenderingPaintServer {id=\"" << elementId << "\" " << *paintServer << "}" << "\n"; + } else + ts << "KCanvasResource {id=\"" << elementId << "\" " << *resource << "}" << "\n"; + } while ((node = node->traverseNextNode(parent))); +} + +} // namespace WebCore + +#endif // ENABLE(SVG) diff --git a/src/3rdparty/webkit/WebCore/rendering/SVGRenderTreeAsText.h b/src/3rdparty/webkit/WebCore/rendering/SVGRenderTreeAsText.h new file mode 100644 index 0000000..c4d832d --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/SVGRenderTreeAsText.h @@ -0,0 +1,111 @@ +/* + * Copyright (C) 2004, 2005 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SVGRenderTreeAsText_h +#define SVGRenderTreeAsText_h + +#if ENABLE(SVG) + +#include "TextStream.h" + +namespace WebCore { + + class TransformationMatrix; + class Color; + class FloatPoint; + class FloatRect; + class FloatSize; + class IntPoint; + class IntRect; + class Node; + class RenderPath; + class RenderSVGContainer; + class RenderSVGInlineText; + class RenderSVGRoot; + class RenderSVGText; + +// functions used by the main RenderTreeAsText code +void write(TextStream&, const RenderPath&, int indent = 0); +void write(TextStream&, const RenderSVGContainer&, int indent = 0); +void write(TextStream&, const RenderSVGInlineText&, int ident = 0); +void write(TextStream&, const RenderSVGRoot&, int indent = 0); +void write(TextStream&, const RenderSVGText&, int ident = 0); + +void writeRenderResources(TextStream&, Node* parent); + +// helper operators defined used in various classes to dump the render tree. +TextStream& operator<<(TextStream&, const TransformationMatrix&); +TextStream& operator<<(TextStream&, const IntRect&); +TextStream& operator<<(TextStream&, const Color&); +TextStream& operator<<(TextStream&, const IntPoint&); +TextStream& operator<<(TextStream&, const FloatSize&); +TextStream& operator<<(TextStream&, const FloatRect&); +TextStream& operator<<(TextStream&, const FloatPoint&); + +// helper operators specific to dumping the render tree. these are used in various classes to dump the render tree +// these could be defined in separate namespace to avoid matching these generic signatures unintentionally. + +template<typename Item> +TextStream& operator<<(TextStream& ts, const Vector<Item*>& v) +{ + ts << "["; + + for (unsigned i = 0; i < v.size(); i++) { + ts << *v[i]; + if (i < v.size() - 1) + ts << ", "; + } + + ts << "]"; + return ts; +} + +template<typename Item> +TextStream& operator<<(TextStream& ts, const Vector<Item>& v) +{ + ts << "["; + + for (unsigned i = 0; i < v.size(); i++) { + ts << v[i]; + if (i < v.size() - 1) + ts << ", "; + } + + ts << "]"; + return ts; +} + +template<typename Pointer> +TextStream& operator<<(TextStream& ts, Pointer* t) +{ + ts << reinterpret_cast<intptr_t>(t); + return ts; +} + +} // namespace WebCore + +#endif // ENABLE(SVG) + +#endif // SVGRenderTreeAsText_h diff --git a/src/3rdparty/webkit/WebCore/rendering/SVGRootInlineBox.cpp b/src/3rdparty/webkit/WebCore/rendering/SVGRootInlineBox.cpp new file mode 100644 index 0000000..f7623ba --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/SVGRootInlineBox.cpp @@ -0,0 +1,1718 @@ +/* + * This file is part of the WebKit project. + * + * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz> + * (C) 2006 Apple Computer Inc. + * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> + * + * 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(SVG) +#include "SVGRootInlineBox.h" + +#include "Editor.h" +#include "Frame.h" +#include "GraphicsContext.h" +#include "RenderSVGRoot.h" +#include "SVGInlineFlowBox.h" +#include "SVGInlineTextBox.h" +#include "SVGFontElement.h" +#include "SVGPaintServer.h" +#include "SVGRenderStyleDefs.h" +#include "SVGRenderSupport.h" +#include "SVGResourceFilter.h" +#include "SVGTextPositioningElement.h" +#include "SVGURIReference.h" +#include "Text.h" +#include "UnicodeRange.h" + +#include <float.h> + +// Text chunk creation is complex and the whole process +// can easily be traced by setting this variable > 0. +#define DEBUG_CHUNK_BUILDING 0 + +namespace WebCore { + +static inline bool isVerticalWritingMode(const SVGRenderStyle* style) +{ + return style->writingMode() == WM_TBRL || style->writingMode() == WM_TB; +} + +static inline EAlignmentBaseline dominantBaselineToShift(bool isVerticalText, const RenderObject* text, const Font& font) +{ + ASSERT(text); + + const SVGRenderStyle* style = text->style() ? text->style()->svgStyle() : 0; + ASSERT(style); + + const SVGRenderStyle* parentStyle = text->parent() && text->parent()->style() ? text->parent()->style()->svgStyle() : 0; + + EDominantBaseline baseline = style->dominantBaseline(); + if (baseline == DB_AUTO) { + if (isVerticalText) + baseline = DB_CENTRAL; + else + baseline = DB_ALPHABETIC; + } + + switch (baseline) { + case DB_USE_SCRIPT: + // TODO: The dominant-baseline and the baseline-table components are set by + // determining the predominant script of the character data content. + return AB_ALPHABETIC; + case DB_NO_CHANGE: + { + if (parentStyle) + return dominantBaselineToShift(isVerticalText, text->parent(), font); + + ASSERT_NOT_REACHED(); + return AB_AUTO; + } + case DB_RESET_SIZE: + { + if (parentStyle) + return dominantBaselineToShift(isVerticalText, text->parent(), font); + + ASSERT_NOT_REACHED(); + return AB_AUTO; + } + case DB_IDEOGRAPHIC: + return AB_IDEOGRAPHIC; + case DB_ALPHABETIC: + return AB_ALPHABETIC; + case DB_HANGING: + return AB_HANGING; + case DB_MATHEMATICAL: + return AB_MATHEMATICAL; + case DB_CENTRAL: + return AB_CENTRAL; + case DB_MIDDLE: + return AB_MIDDLE; + case DB_TEXT_AFTER_EDGE: + return AB_TEXT_AFTER_EDGE; + case DB_TEXT_BEFORE_EDGE: + return AB_TEXT_BEFORE_EDGE; + default: + ASSERT_NOT_REACHED(); + return AB_AUTO; + } +} + +static inline float alignmentBaselineToShift(bool isVerticalText, const RenderObject* text, const Font& font) +{ + ASSERT(text); + + const SVGRenderStyle* style = text->style() ? text->style()->svgStyle() : 0; + ASSERT(style); + + const SVGRenderStyle* parentStyle = text->parent() && text->parent()->style() ? text->parent()->style()->svgStyle() : 0; + + EAlignmentBaseline baseline = style->alignmentBaseline(); + if (baseline == AB_AUTO) { + if (parentStyle && style->dominantBaseline() == DB_AUTO) + baseline = dominantBaselineToShift(isVerticalText, text->parent(), font); + else + baseline = dominantBaselineToShift(isVerticalText, text, font); + + ASSERT(baseline != AB_AUTO); + } + + // Note: http://wiki.apache.org/xmlgraphics-fop/LineLayout/AlignmentHandling + switch (baseline) { + case AB_BASELINE: + { + if (parentStyle) + return dominantBaselineToShift(isVerticalText, text->parent(), font); + + return 0.0f; + } + case AB_BEFORE_EDGE: + case AB_TEXT_BEFORE_EDGE: + return font.ascent(); + case AB_MIDDLE: + return font.xHeight() / 2.0f; + case AB_CENTRAL: + // Not needed, we're taking this into account already for vertical text! + // return (font.ascent() - font.descent()) / 2.0f; + return 0.0f; + case AB_AFTER_EDGE: + case AB_TEXT_AFTER_EDGE: + case AB_IDEOGRAPHIC: + return font.descent(); + case AB_ALPHABETIC: + return 0.0f; + case AB_HANGING: + return font.ascent() * 8.0f / 10.0f; + case AB_MATHEMATICAL: + return font.ascent() / 2.0f; + default: + ASSERT_NOT_REACHED(); + return 0.0f; + } +} + +static inline float glyphOrientationToAngle(const SVGRenderStyle* svgStyle, bool isVerticalText, const UChar& character) +{ + switch (isVerticalText ? svgStyle->glyphOrientationVertical() : svgStyle->glyphOrientationHorizontal()) { + case GO_AUTO: + { + // Spec: Fullwidth ideographic and fullwidth Latin text will be set with a glyph-orientation of 0-degrees. + // Text which is not fullwidth will be set with a glyph-orientation of 90-degrees. + unsigned int unicodeRange = findCharUnicodeRange(character); + if (unicodeRange == cRangeSetLatin || unicodeRange == cRangeArabic) + return 90.0f; + + return 0.0f; + } + case GO_90DEG: + return 90.0f; + case GO_180DEG: + return 180.0f; + case GO_270DEG: + return 270.0f; + case GO_0DEG: + default: + return 0.0f; + } +} + +static inline bool glyphOrientationIsMultiplyOf180Degrees(float orientationAngle) +{ + return fabsf(fmodf(orientationAngle, 180.0f)) == 0.0f; +} + +static inline float calculateGlyphAdvanceAndShiftRespectingOrientation(bool isVerticalText, float orientationAngle, float glyphWidth, float glyphHeight, const Font& font, SVGChar& svgChar, float& xOrientationShift, float& yOrientationShift) +{ + bool orientationIsMultiplyOf180Degrees = glyphOrientationIsMultiplyOf180Degrees(orientationAngle); + + // The function is based on spec requirements: + // + // Spec: If the 'glyph-orientation-horizontal' results in an orientation angle that is not a multiple of + // of 180 degrees, then the current text position is incremented according to the vertical metrics of the glyph. + // + // Spec: If if the 'glyph-orientation-vertical' results in an orientation angle that is not a multiple of + // 180 degrees,then the current text position is incremented according to the horizontal metrics of the glyph. + + // vertical orientation handling + if (isVerticalText) { + if (orientationAngle == 0.0f) { + xOrientationShift = -glyphWidth / 2.0f; + yOrientationShift = font.ascent(); + } else if (orientationAngle == 90.0f) { + xOrientationShift = -glyphHeight; + yOrientationShift = font.descent(); + svgChar.orientationShiftY = -font.ascent(); + } else if (orientationAngle == 270.0f) { + xOrientationShift = glyphHeight; + yOrientationShift = font.descent(); + svgChar.orientationShiftX = -glyphWidth; + svgChar.orientationShiftY = -font.ascent(); + } else if (orientationAngle == 180.0f) { + yOrientationShift = font.ascent(); + svgChar.orientationShiftX = -glyphWidth / 2.0f; + svgChar.orientationShiftY = font.ascent() - font.descent(); + } + + // vertical advance calculation + if (orientationAngle != 0.0f && !orientationIsMultiplyOf180Degrees) + return glyphWidth; + + return glyphHeight; + } + + // horizontal orientation handling + if (orientationAngle == 90.0f) { + xOrientationShift = glyphWidth / 2.0f; + yOrientationShift = -font.descent(); + svgChar.orientationShiftX = -glyphWidth / 2.0f - font.descent(); + svgChar.orientationShiftY = font.descent(); + } else if (orientationAngle == 270.0f) { + xOrientationShift = -glyphWidth / 2.0f; + yOrientationShift = -font.descent(); + svgChar.orientationShiftX = -glyphWidth / 2.0f + font.descent(); + svgChar.orientationShiftY = glyphHeight; + } else if (orientationAngle == 180.0f) { + xOrientationShift = glyphWidth / 2.0f; + svgChar.orientationShiftX = -glyphWidth / 2.0f; + svgChar.orientationShiftY = font.ascent() - font.descent(); + } + + // horizontal advance calculation + if (orientationAngle != 0.0f && !orientationIsMultiplyOf180Degrees) + return glyphHeight; + + return glyphWidth; +} + +static inline void startTextChunk(SVGTextChunkLayoutInfo& info) +{ + info.chunk.boxes.clear(); + info.chunk.boxes.append(SVGInlineBoxCharacterRange()); + + info.chunk.start = info.it; + info.assignChunkProperties = true; +} + +static inline void closeTextChunk(SVGTextChunkLayoutInfo& info) +{ + ASSERT(!info.chunk.boxes.last().isOpen()); + ASSERT(info.chunk.boxes.last().isClosed()); + + info.chunk.end = info.it; + ASSERT(info.chunk.end >= info.chunk.start); + + info.svgTextChunks.append(info.chunk); +} + +RenderSVGRoot* findSVGRootObject(RenderObject* start) +{ + // Find associated root inline box + while (start && !start->isSVGRoot()) + start = start->parent(); + + ASSERT(start); + ASSERT(start->isSVGRoot()); + + return static_cast<RenderSVGRoot*>(start); +} + +static inline FloatPoint topLeftPositionOfCharacterRange(Vector<SVGChar>& chars) +{ + return topLeftPositionOfCharacterRange(chars.begin(), chars.end()); +} + +FloatPoint topLeftPositionOfCharacterRange(Vector<SVGChar>::iterator it, Vector<SVGChar>::iterator end) +{ + float lowX = FLT_MAX, lowY = FLT_MAX; + for (; it != end; ++it) { + if (it->isHidden()) + continue; + + float x = (*it).x; + float y = (*it).y; + + if (x < lowX) + lowX = x; + + if (y < lowY) + lowY = y; + } + + return FloatPoint(lowX, lowY); +} + +// Helper function +static float calculateKerning(RenderObject* item) +{ + const Font& font = item->style()->font(); + const SVGRenderStyle* svgStyle = item->style()->svgStyle(); + + float kerning = 0.0f; + if (CSSPrimitiveValue* primitive = static_cast<CSSPrimitiveValue*>(svgStyle->kerning())) { + kerning = primitive->getFloatValue(); + + if (primitive->primitiveType() == CSSPrimitiveValue::CSS_PERCENTAGE && font.pixelSize() > 0) + kerning = kerning / 100.0f * font.pixelSize(); + } + + return kerning; +} + +// Helper class for paint() +struct SVGRootInlineBoxPaintWalker { + SVGRootInlineBoxPaintWalker(SVGRootInlineBox* rootBox, SVGResourceFilter* rootFilter, RenderObject::PaintInfo paintInfo, int tx, int ty) + : m_rootBox(rootBox) + , m_chunkStarted(false) + , m_paintInfo(paintInfo) + , m_savedInfo(paintInfo) + , m_boundingBox(tx + rootBox->xPos(), ty + rootBox->yPos(), rootBox->width(), rootBox->height()) + , m_filter(0) + , m_rootFilter(rootFilter) + , m_fillPaintServer(0) + , m_strokePaintServer(0) + , m_fillPaintServerObject(0) + , m_strokePaintServerObject(0) + , m_tx(tx) + , m_ty(ty) + { + } + + ~SVGRootInlineBoxPaintWalker() + { + ASSERT(!m_filter); + ASSERT(!m_fillPaintServer); + ASSERT(!m_fillPaintServerObject); + ASSERT(!m_strokePaintServer); + ASSERT(!m_strokePaintServerObject); + ASSERT(!m_chunkStarted); + } + + void teardownFillPaintServer() + { + if (!m_fillPaintServer) + return; + + m_fillPaintServer->teardown(m_paintInfo.context, m_fillPaintServerObject, ApplyToFillTargetType, true); + + m_fillPaintServer = 0; + m_fillPaintServerObject = 0; + } + + void teardownStrokePaintServer() + { + if (!m_strokePaintServer) + return; + + m_strokePaintServer->teardown(m_paintInfo.context, m_strokePaintServerObject, ApplyToStrokeTargetType, true); + + m_strokePaintServer = 0; + m_strokePaintServerObject = 0; + } + + void chunkStartCallback(InlineBox* box) + { + ASSERT(!m_chunkStarted); + m_chunkStarted = true; + + InlineFlowBox* flowBox = box->parent(); + + // Initialize text rendering + RenderObject* object = flowBox->object(); + ASSERT(object); + + m_savedInfo = m_paintInfo; + m_paintInfo.context->save(); + + if (!flowBox->isRootInlineBox()) + m_paintInfo.context->concatCTM(m_rootBox->object()->localTransform()); + + m_paintInfo.context->concatCTM(object->localTransform()); + + if (!flowBox->isRootInlineBox()) { + prepareToRenderSVGContent(object, m_paintInfo, m_boundingBox, m_filter, m_rootFilter); + m_paintInfo.rect = object->localTransform().inverse().mapRect(m_paintInfo.rect); + } + } + + void chunkEndCallback(InlineBox* box) + { + ASSERT(m_chunkStarted); + m_chunkStarted = false; + + InlineFlowBox* flowBox = box->parent(); + + RenderObject* object = flowBox->object(); + ASSERT(object); + + // Clean up last used paint server + teardownFillPaintServer(); + teardownStrokePaintServer(); + + // Finalize text rendering + if (!flowBox->isRootInlineBox()) { + finishRenderSVGContent(object, m_paintInfo, m_boundingBox, m_filter, m_savedInfo.context); + m_filter = 0; + } + + // Restore context & repaint rect + m_paintInfo.context->restore(); + m_paintInfo.rect = m_savedInfo.rect; + } + + bool chunkSetupFillCallback(InlineBox* box) + { + InlineFlowBox* flowBox = box->parent(); + + // Setup fill paint server + RenderObject* object = flowBox->object(); + ASSERT(object); + + ASSERT(!m_strokePaintServer); + teardownFillPaintServer(); + + m_fillPaintServer = SVGPaintServer::fillPaintServer(object->style(), object); + if (m_fillPaintServer) { + m_fillPaintServer->setup(m_paintInfo.context, object, ApplyToFillTargetType, true); + m_fillPaintServerObject = object; + return true; + } + + return false; + } + + bool chunkSetupStrokeCallback(InlineBox* box) + { + InlineFlowBox* flowBox = box->parent(); + + // Setup stroke paint server + RenderObject* object = flowBox->object(); + ASSERT(object); + + // If we're both stroked & filled, teardown fill paint server before stroking. + teardownFillPaintServer(); + teardownStrokePaintServer(); + + m_strokePaintServer = SVGPaintServer::strokePaintServer(object->style(), object); + + if (m_strokePaintServer) { + m_strokePaintServer->setup(m_paintInfo.context, object, ApplyToStrokeTargetType, true); + m_strokePaintServerObject = object; + return true; + } + + return false; + } + + void chunkPortionCallback(SVGInlineTextBox* textBox, int startOffset, const TransformationMatrix& chunkCtm, + const Vector<SVGChar>::iterator& start, const Vector<SVGChar>::iterator& end) + { + RenderText* text = textBox->textObject(); + ASSERT(text); + + RenderStyle* styleToUse = text->style(textBox->isFirstLineStyle()); + ASSERT(styleToUse); + + startOffset += textBox->start(); + + int textDecorations = styleToUse->textDecorationsInEffect(); + + int textWidth = 0; + IntPoint decorationOrigin; + SVGTextDecorationInfo info; + + if (!chunkCtm.isIdentity()) + m_paintInfo.context->concatCTM(chunkCtm); + + for (Vector<SVGChar>::iterator it = start; it != end; ++it) { + if (it->isHidden()) + continue; + + // Determine how many characters - starting from the current - can be drawn at once. + Vector<SVGChar>::iterator itSearch = it + 1; + while (itSearch != end) { + if (itSearch->drawnSeperated || itSearch->isHidden()) + break; + + itSearch++; + } + + const UChar* stringStart = text->characters() + startOffset + (it - start); + unsigned int stringLength = itSearch - it; + + // Paint decorations, that have to be drawn before the text gets drawn + if (textDecorations != TDNONE && m_paintInfo.phase != PaintPhaseSelection) { + textWidth = styleToUse->font().width(svgTextRunForInlineTextBox(stringStart, stringLength, styleToUse, textBox, (*it).x)); + decorationOrigin = IntPoint((int) (*it).x, (int) (*it).y - styleToUse->font().ascent()); + info = m_rootBox->retrievePaintServersForTextDecoration(text); + } + + if (textDecorations & UNDERLINE && textWidth != 0.0f) + textBox->paintDecoration(UNDERLINE, m_paintInfo.context, decorationOrigin.x(), decorationOrigin.y(), textWidth, *it, info); + + if (textDecorations & OVERLINE && textWidth != 0.0f) + textBox->paintDecoration(OVERLINE, m_paintInfo.context, decorationOrigin.x(), decorationOrigin.y(), textWidth, *it, info); + + // Paint text + SVGPaintServer* activePaintServer = m_fillPaintServer; + if (!activePaintServer) + activePaintServer = m_strokePaintServer; + + ASSERT(activePaintServer); + textBox->paintCharacters(m_paintInfo, m_tx, m_ty, *it, stringStart, stringLength, activePaintServer); + + // Paint decorations, that have to be drawn afterwards + if (textDecorations & LINE_THROUGH && textWidth != 0.0f) + textBox->paintDecoration(LINE_THROUGH, m_paintInfo.context, decorationOrigin.x(), decorationOrigin.y(), textWidth, *it, info); + + // Skip processed characters + it = itSearch - 1; + } + + if (!chunkCtm.isIdentity()) + m_paintInfo.context->concatCTM(chunkCtm.inverse()); + } + +private: + SVGRootInlineBox* m_rootBox; + bool m_chunkStarted : 1; + + RenderObject::PaintInfo m_paintInfo; + RenderObject::PaintInfo m_savedInfo; + + FloatRect m_boundingBox; + SVGResourceFilter* m_filter; + SVGResourceFilter* m_rootFilter; + + SVGPaintServer* m_fillPaintServer; + SVGPaintServer* m_strokePaintServer; + + RenderObject* m_fillPaintServerObject; + RenderObject* m_strokePaintServerObject; + + int m_tx; + int m_ty; +}; + +void SVGRootInlineBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty) +{ + if (paintInfo.context->paintingDisabled() || paintInfo.phase != PaintPhaseForeground) + return; + + RenderObject::PaintInfo savedInfo(paintInfo); + paintInfo.context->save(); + + SVGResourceFilter* filter = 0; + FloatRect boundingBox(tx + xPos(), ty + yPos(), width(), height()); + + // Initialize text rendering + paintInfo.context->concatCTM(object()->localTransform()); + prepareToRenderSVGContent(object(), paintInfo, boundingBox, filter); + paintInfo.context->concatCTM(object()->localTransform().inverse()); + + // Render text, chunk-by-chunk + SVGRootInlineBoxPaintWalker walkerCallback(this, filter, paintInfo, tx, ty); + SVGTextChunkWalker<SVGRootInlineBoxPaintWalker> walker(&walkerCallback, + &SVGRootInlineBoxPaintWalker::chunkPortionCallback, + &SVGRootInlineBoxPaintWalker::chunkStartCallback, + &SVGRootInlineBoxPaintWalker::chunkEndCallback, + &SVGRootInlineBoxPaintWalker::chunkSetupFillCallback, + &SVGRootInlineBoxPaintWalker::chunkSetupStrokeCallback); + + walkTextChunks(&walker); + + // Finalize text rendering + finishRenderSVGContent(object(), paintInfo, boundingBox, filter, savedInfo.context); + paintInfo.context->restore(); +} + +int SVGRootInlineBox::placeBoxesHorizontally(int, int& leftPosition, int& rightPosition, bool&) +{ + // Remove any offsets caused by RTL text layout + leftPosition = 0; + rightPosition = 0; + return 0; +} + +void SVGRootInlineBox::verticallyAlignBoxes(int& heightOfBlock) +{ + // height is set by layoutInlineBoxes. + heightOfBlock = height(); +} + +float cummulatedWidthOfInlineBoxCharacterRange(SVGInlineBoxCharacterRange& range) +{ + ASSERT(!range.isOpen()); + ASSERT(range.isClosed()); + ASSERT(range.box->isInlineTextBox()); + + InlineTextBox* textBox = static_cast<InlineTextBox*>(range.box); + RenderText* text = textBox->textObject(); + RenderStyle* style = text->style(); + + return style->font().floatWidth(svgTextRunForInlineTextBox(text->characters() + textBox->start() + range.startOffset, range.endOffset - range.startOffset, style, textBox, 0)); +} + +float cummulatedHeightOfInlineBoxCharacterRange(SVGInlineBoxCharacterRange& range) +{ + ASSERT(!range.isOpen()); + ASSERT(range.isClosed()); + ASSERT(range.box->isInlineTextBox()); + + InlineTextBox* textBox = static_cast<InlineTextBox*>(range.box); + RenderText* text = textBox->textObject(); + const Font& font = text->style()->font(); + + return (range.endOffset - range.startOffset) * (font.ascent() + font.descent()); +} + +TextRun svgTextRunForInlineTextBox(const UChar* c, int len, RenderStyle* style, const InlineTextBox* textBox, float xPos) +{ + ASSERT(textBox); + ASSERT(style); + + TextRun run(c, len, false, static_cast<int>(xPos), textBox->toAdd(), textBox->direction() == RTL, textBox->m_dirOverride || style->visuallyOrdered()); + +#if ENABLE(SVG_FONTS) + run.setReferencingRenderObject(textBox->textObject()->parent()); +#endif + + // We handle letter & word spacing ourselves + run.disableSpacing(); + return run; +} + +static float cummulatedWidthOrHeightOfTextChunk(SVGTextChunk& chunk, bool calcWidthOnly) +{ + float length = 0.0f; + Vector<SVGChar>::iterator charIt = chunk.start; + + Vector<SVGInlineBoxCharacterRange>::iterator it = chunk.boxes.begin(); + Vector<SVGInlineBoxCharacterRange>::iterator end = chunk.boxes.end(); + + for (; it != end; ++it) { + SVGInlineBoxCharacterRange& range = *it; + + SVGInlineTextBox* box = static_cast<SVGInlineTextBox*>(range.box); + RenderStyle* style = box->object()->style(); + + for (int i = range.startOffset; i < range.endOffset; ++i) { + ASSERT(charIt <= chunk.end); + + // Determine how many characters - starting from the current - can be measured at once. + // Important for non-absolute positioned non-latin1 text (ie. Arabic) where ie. the width + // of a string is not the sum of the boundaries of all contained glyphs. + Vector<SVGChar>::iterator itSearch = charIt + 1; + Vector<SVGChar>::iterator endSearch = charIt + range.endOffset - i; + while (itSearch != endSearch) { + // No need to check for 'isHidden()' here as this function is not called for text paths. + if (itSearch->drawnSeperated) + break; + + itSearch++; + } + + unsigned int positionOffset = itSearch - charIt; + + // Calculate width/height of subrange + SVGInlineBoxCharacterRange subRange; + subRange.box = range.box; + subRange.startOffset = i; + subRange.endOffset = i + positionOffset; + + if (calcWidthOnly) + length += cummulatedWidthOfInlineBoxCharacterRange(subRange); + else + length += cummulatedHeightOfInlineBoxCharacterRange(subRange); + + // Calculate gap between the previous & current range + // <text x="10 50 70">ABCD</text> - we need to take the gaps between A & B into account + // so add "40" as width, and analogous for B & C, add "20" as width. + if (itSearch > chunk.start && itSearch < chunk.end) { + SVGChar& lastCharacter = *(itSearch - 1); + SVGChar& currentCharacter = *itSearch; + + int offset = box->direction() == RTL ? box->end() - i - positionOffset + 1 : box->start() + i + positionOffset - 1; + + // FIXME: does this need to change to handle multichar glyphs? + int charsConsumed = 1; + String glyphName; + if (calcWidthOnly) { + float lastGlyphWidth = box->calculateGlyphWidth(style, offset, 0, charsConsumed, glyphName); + length += currentCharacter.x - lastCharacter.x - lastGlyphWidth; + } else { + float lastGlyphHeight = box->calculateGlyphHeight(style, offset, 0); + length += currentCharacter.y - lastCharacter.y - lastGlyphHeight; + } + } + + // Advance processed characters + i += positionOffset - 1; + charIt = itSearch; + } + } + + ASSERT(charIt == chunk.end); + return length; +} + +static float cummulatedWidthOfTextChunk(SVGTextChunk& chunk) +{ + return cummulatedWidthOrHeightOfTextChunk(chunk, true); +} + +static float cummulatedHeightOfTextChunk(SVGTextChunk& chunk) +{ + return cummulatedWidthOrHeightOfTextChunk(chunk, false); +} + +static float calculateTextAnchorShiftForTextChunk(SVGTextChunk& chunk, ETextAnchor anchor) +{ + float shift = 0.0f; + + if (chunk.isVerticalText) + shift = cummulatedHeightOfTextChunk(chunk); + else + shift = cummulatedWidthOfTextChunk(chunk); + + if (anchor == TA_MIDDLE) + shift *= -0.5f; + else + shift *= -1.0f; + + return shift; +} + +static void applyTextAnchorToTextChunk(SVGTextChunk& chunk) +{ + // This method is not called for chunks containing chars aligned on a path. + // -> all characters are visible, no need to check for "isHidden()" anywhere. + + if (chunk.anchor == TA_START) + return; + + float shift = calculateTextAnchorShiftForTextChunk(chunk, chunk.anchor); + + // Apply correction to chunk + Vector<SVGChar>::iterator chunkIt = chunk.start; + for (; chunkIt != chunk.end; ++chunkIt) { + SVGChar& curChar = *chunkIt; + + if (chunk.isVerticalText) + curChar.y += shift; + else + curChar.x += shift; + } + + // Move inline boxes + Vector<SVGInlineBoxCharacterRange>::iterator boxIt = chunk.boxes.begin(); + Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = chunk.boxes.end(); + + for (; boxIt != boxEnd; ++boxIt) { + SVGInlineBoxCharacterRange& range = *boxIt; + + InlineBox* curBox = range.box; + ASSERT(curBox->isInlineTextBox()); + ASSERT(curBox->parent() && (curBox->parent()->isRootInlineBox() || curBox->parent()->isInlineFlowBox())); + + // Move target box + if (chunk.isVerticalText) + curBox->setYPos(curBox->yPos() + static_cast<int>(shift)); + else + curBox->setXPos(curBox->xPos() + static_cast<int>(shift)); + } +} + +static float calculateTextLengthCorrectionForTextChunk(SVGTextChunk& chunk, ELengthAdjust lengthAdjust, float& computedLength) +{ + if (chunk.textLength <= 0.0f) + return 0.0f; + + float computedWidth = cummulatedWidthOfTextChunk(chunk); + float computedHeight = cummulatedHeightOfTextChunk(chunk); + + if ((computedWidth <= 0.0f && !chunk.isVerticalText) || + (computedHeight <= 0.0f && chunk.isVerticalText)) + return 0.0f; + + if (chunk.isVerticalText) + computedLength = computedHeight; + else + computedLength = computedWidth; + + if (lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS) { + if (chunk.isVerticalText) + chunk.ctm.scale(1.0f, chunk.textLength / computedLength); + else + chunk.ctm.scale(chunk.textLength / computedLength, 1.0f); + + return 0.0f; + } + + return (chunk.textLength - computedLength) / float(chunk.end - chunk.start); +} + +static void applyTextLengthCorrectionToTextChunk(SVGTextChunk& chunk) +{ + // This method is not called for chunks containing chars aligned on a path. + // -> all characters are visible, no need to check for "isHidden()" anywhere. + + // lengthAdjust="spacingAndGlyphs" is handled by modifying chunk.ctm + float computedLength = 0.0f; + float spacingToApply = calculateTextLengthCorrectionForTextChunk(chunk, chunk.lengthAdjust, computedLength); + + if (!chunk.ctm.isIdentity() && chunk.lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS) { + SVGChar& firstChar = *(chunk.start); + + // Assure we apply the chunk scaling in the right origin + TransformationMatrix newChunkCtm; + newChunkCtm.translate(firstChar.x, firstChar.y); + newChunkCtm = chunk.ctm * newChunkCtm; + newChunkCtm.translate(-firstChar.x, -firstChar.y); + + chunk.ctm = newChunkCtm; + } + + // Apply correction to chunk + if (spacingToApply != 0.0f) { + Vector<SVGChar>::iterator chunkIt = chunk.start; + for (; chunkIt != chunk.end; ++chunkIt) { + SVGChar& curChar = *chunkIt; + curChar.drawnSeperated = true; + + if (chunk.isVerticalText) + curChar.y += (chunkIt - chunk.start) * spacingToApply; + else + curChar.x += (chunkIt - chunk.start) * spacingToApply; + } + } +} + +void SVGRootInlineBox::computePerCharacterLayoutInformation() +{ + // Clean up any previous layout information + m_svgChars.clear(); + m_svgTextChunks.clear(); + + // Build layout information for all contained render objects + SVGCharacterLayoutInfo info(m_svgChars); + buildLayoutInformation(this, info); + + // Now all layout information are available for every character + // contained in any of our child inline/flow boxes. Build list + // of text chunks now, to be able to apply text-anchor shifts. + buildTextChunks(m_svgChars, m_svgTextChunks, this); + + // Layout all text chunks + // text-anchor needs to be applied to individual chunks. + layoutTextChunks(); + + // Finally the top left position of our box is known. + // Propogate this knownledge to our RenderSVGText parent. + FloatPoint topLeft = topLeftPositionOfCharacterRange(m_svgChars); + object()->setPos((int) floorf(topLeft.x()), (int) floorf(topLeft.y())); + + // Layout all InlineText/Flow boxes + // BEWARE: This requires the root top/left position to be set correctly before! + layoutInlineBoxes(); +} + +void SVGRootInlineBox::buildLayoutInformation(InlineFlowBox* start, SVGCharacterLayoutInfo& info) +{ + if (start->isRootInlineBox()) { + ASSERT(start->object()->element()->hasTagName(SVGNames::textTag)); + + SVGTextPositioningElement* positioningElement = static_cast<SVGTextPositioningElement*>(start->object()->element()); + ASSERT(positioningElement); + ASSERT(positioningElement->parentNode()); + + info.addLayoutInformation(positioningElement); + } + + LastGlyphInfo lastGlyph; + + for (InlineBox* curr = start->firstChild(); curr; curr = curr->nextOnLine()) { + if (curr->object()->isText()) + buildLayoutInformationForTextBox(info, static_cast<InlineTextBox*>(curr), lastGlyph); + else { + ASSERT(curr->isInlineFlowBox()); + InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(curr); + + if (!flowBox->object()->element()) + continue; // Skip generated content. + + bool isAnchor = flowBox->object()->element()->hasTagName(SVGNames::aTag); + bool isTextPath = flowBox->object()->element()->hasTagName(SVGNames::textPathTag); + + if (!isTextPath && !isAnchor) { + SVGTextPositioningElement* positioningElement = static_cast<SVGTextPositioningElement*>(flowBox->object()->element()); + ASSERT(positioningElement); + ASSERT(positioningElement->parentNode()); + + info.addLayoutInformation(positioningElement); + } else if (!isAnchor) { + info.setInPathLayout(true); + + // Handle text-anchor/textLength on path, which is special. + SVGTextContentElement* textContent = 0; + Node* node = flowBox->object()->element(); + if (node && node->isSVGElement()) + textContent = static_cast<SVGTextContentElement*>(node); + ASSERT(textContent); + + ELengthAdjust lengthAdjust = (ELengthAdjust) textContent->lengthAdjust(); + ETextAnchor anchor = flowBox->object()->style()->svgStyle()->textAnchor(); + float textAnchorStartOffset = 0.0f; + + // Initialize sub-layout. We need to create text chunks from the textPath + // children using our standard layout code, to be able to measure the + // text length using our normal methods and not textPath specific hacks. + Vector<SVGChar> tempChars; + Vector<SVGTextChunk> tempChunks; + + SVGCharacterLayoutInfo tempInfo(tempChars); + buildLayoutInformation(flowBox, tempInfo); + + buildTextChunks(tempChars, tempChunks, flowBox); + + Vector<SVGTextChunk>::iterator it = tempChunks.begin(); + Vector<SVGTextChunk>::iterator end = tempChunks.end(); + + TransformationMatrix ctm; + float computedLength = 0.0f; + + for (; it != end; ++it) { + SVGTextChunk& chunk = *it; + + // Apply text-length calculation + info.pathExtraAdvance += calculateTextLengthCorrectionForTextChunk(chunk, lengthAdjust, computedLength); + + if (lengthAdjust == SVGTextContentElement::LENGTHADJUST_SPACINGANDGLYPHS) { + info.pathTextLength += computedLength; + info.pathChunkLength += chunk.textLength; + } + + // Calculate text-anchor start offset + if (anchor == TA_START) + continue; + + textAnchorStartOffset += calculateTextAnchorShiftForTextChunk(chunk, anchor); + } + + info.addLayoutInformation(flowBox, textAnchorStartOffset); + } + + float shiftxSaved = info.shiftx; + float shiftySaved = info.shifty; + + buildLayoutInformation(flowBox, info); + info.processedChunk(shiftxSaved, shiftySaved); + + if (isTextPath) + info.setInPathLayout(false); + } + } +} + +void SVGRootInlineBox::layoutInlineBoxes() +{ + int lowX = INT_MAX; + int lowY = INT_MAX; + int highX = INT_MIN; + int highY = INT_MIN; + + // Layout all child boxes + Vector<SVGChar>::iterator it = m_svgChars.begin(); + layoutInlineBoxes(this, it, lowX, highX, lowY, highY); + ASSERT(it == m_svgChars.end()); +} + +void SVGRootInlineBox::layoutInlineBoxes(InlineFlowBox* start, Vector<SVGChar>::iterator& it, int& lowX, int& highX, int& lowY, int& highY) +{ + for (InlineBox* curr = start->firstChild(); curr; curr = curr->nextOnLine()) { + RenderStyle* style = curr->object()->style(); + const Font& font = style->font(); + + if (curr->object()->isText()) { + SVGInlineTextBox* textBox = static_cast<SVGInlineTextBox*>(curr); + unsigned length = textBox->len(); + + SVGChar curChar = *it; + ASSERT(it != m_svgChars.end()); + + FloatRect stringRect; + for (unsigned i = 0; i < length; ++i) { + ASSERT(it != m_svgChars.end()); + + if (it->isHidden()) { + ++it; + continue; + } + + stringRect.unite(textBox->calculateGlyphBoundaries(style, textBox->start() + i, *it)); + ++it; + } + + IntRect enclosedStringRect = enclosingIntRect(stringRect); + + int minX = enclosedStringRect.x(); + int maxX = minX + enclosedStringRect.width(); + + int minY = enclosedStringRect.y(); + int maxY = minY + enclosedStringRect.height(); + + curr->setXPos(minX - object()->xPos()); + curr->setWidth(enclosedStringRect.width()); + + curr->setYPos(minY - object()->yPos()); + curr->setBaseline(font.ascent()); + curr->setHeight(enclosedStringRect.height()); + + if (minX < lowX) + lowX = minX; + + if (maxX > highX) + highX = maxX; + + if (minY < lowY) + lowY = minY; + + if (maxY > highY) + highY = maxY; + } else { + ASSERT(curr->isInlineFlowBox()); + + int minX = INT_MAX; + int minY = INT_MAX; + int maxX = INT_MIN; + int maxY = INT_MIN; + + InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(curr); + + if (!flowBox->object()->element()) + continue; // Skip generated content. + + layoutInlineBoxes(flowBox, it, minX, maxX, minY, maxY); + + curr->setXPos(minX - object()->xPos()); + curr->setWidth(maxX - minX); + + curr->setYPos(minY - object()->yPos()); + curr->setBaseline(font.ascent()); + curr->setHeight(maxY - minY); + + if (minX < lowX) + lowX = minX; + + if (maxX > highX) + highX = maxX; + + if (minY < lowY) + lowY = minY; + + if (maxY > highY) + highY = maxY; + } + } + + if (start->isRootInlineBox()) { + int top = lowY - object()->yPos(); + int bottom = highY - object()->yPos(); + + start->setXPos(lowX - object()->xPos()); + start->setYPos(top); + + start->setWidth(highX - lowX); + start->setHeight(highY - lowY); + + start->setVerticalOverflowPositions(top, bottom); + start->setVerticalSelectionPositions(top, bottom); + } +} + +void SVGRootInlineBox::buildLayoutInformationForTextBox(SVGCharacterLayoutInfo& info, InlineTextBox* textBox, LastGlyphInfo& lastGlyph) +{ + RenderText* text = textBox->textObject(); + ASSERT(text); + + RenderStyle* style = text->style(textBox->isFirstLineStyle()); + ASSERT(style); + + const Font& font = style->font(); + SVGInlineTextBox* svgTextBox = static_cast<SVGInlineTextBox*>(textBox); + + unsigned length = textBox->len(); + + const SVGRenderStyle* svgStyle = style->svgStyle(); + bool isVerticalText = isVerticalWritingMode(svgStyle); + + int charsConsumed = 0; + for (unsigned i = 0; i < length; i += charsConsumed) { + SVGChar svgChar; + + if (info.inPathLayout()) + svgChar.pathData = SVGCharOnPath::create(); + + float glyphWidth = 0.0f; + float glyphHeight = 0.0f; + + int extraCharsAvailable = length - i - 1; + + String unicodeStr; + String glyphName; + if (textBox->direction() == RTL) { + glyphWidth = svgTextBox->calculateGlyphWidth(style, textBox->end() - i, extraCharsAvailable, charsConsumed, glyphName); + glyphHeight = svgTextBox->calculateGlyphHeight(style, textBox->end() - i, extraCharsAvailable); + unicodeStr = String(textBox->textObject()->text()->characters() + textBox->end() - i, charsConsumed); + } else { + glyphWidth = svgTextBox->calculateGlyphWidth(style, textBox->start() + i, extraCharsAvailable, charsConsumed, glyphName); + glyphHeight = svgTextBox->calculateGlyphHeight(style, textBox->start() + i, extraCharsAvailable); + unicodeStr = String(textBox->textObject()->text()->characters() + textBox->start() + i, charsConsumed); + } + + bool assignedX = false; + bool assignedY = false; + + if (info.xValueAvailable() && (!info.inPathLayout() || (info.inPathLayout() && !isVerticalText))) { + if (!isVerticalText) + svgChar.newTextChunk = true; + + assignedX = true; + svgChar.drawnSeperated = true; + info.curx = info.xValueNext(); + } + + if (info.yValueAvailable() && (!info.inPathLayout() || (info.inPathLayout() && isVerticalText))) { + if (isVerticalText) + svgChar.newTextChunk = true; + + assignedY = true; + svgChar.drawnSeperated = true; + info.cury = info.yValueNext(); + } + + float dx = 0.0f; + float dy = 0.0f; + + // Apply x-axis shift + if (info.dxValueAvailable()) { + svgChar.drawnSeperated = true; + + dx = info.dxValueNext(); + info.dx += dx; + + if (!info.inPathLayout()) + info.curx += dx; + } + + // Apply y-axis shift + if (info.dyValueAvailable()) { + svgChar.drawnSeperated = true; + + dy = info.dyValueNext(); + info.dy += dy; + + if (!info.inPathLayout()) + info.cury += dy; + } + + // Take letter & word spacing and kerning into account + float spacing = font.letterSpacing() + calculateKerning(textBox->object()->element()->renderer()); + + const UChar* currentCharacter = text->characters() + (textBox->direction() == RTL ? textBox->end() - i : textBox->start() + i); + const UChar* lastCharacter = 0; + + if (textBox->direction() == RTL) { + if (i < textBox->end()) + lastCharacter = text->characters() + textBox->end() - i + 1; + } else { + if (i > 0) + lastCharacter = text->characters() + textBox->start() + i - 1; + } + + if (info.nextDrawnSeperated || spacing != 0.0f) { + info.nextDrawnSeperated = false; + svgChar.drawnSeperated = true; + } + + if (currentCharacter && Font::treatAsSpace(*currentCharacter) && lastCharacter && !Font::treatAsSpace(*lastCharacter)) { + spacing += font.wordSpacing(); + + if (spacing != 0.0f && !info.inPathLayout()) + info.nextDrawnSeperated = true; + } + + float orientationAngle = glyphOrientationToAngle(svgStyle, isVerticalText, *currentCharacter); + + float xOrientationShift = 0.0f; + float yOrientationShift = 0.0f; + float glyphAdvance = calculateGlyphAdvanceAndShiftRespectingOrientation(isVerticalText, orientationAngle, glyphWidth, glyphHeight, + font, svgChar, xOrientationShift, yOrientationShift); + + // Handle textPath layout mode + if (info.inPathLayout()) { + float extraAdvance = isVerticalText ? dy : dx; + float newOffset = FLT_MIN; + + if (assignedX && !isVerticalText) + newOffset = info.curx; + else if (assignedY && isVerticalText) + newOffset = info.cury; + + float correctedGlyphAdvance = glyphAdvance; + + // Handle lengthAdjust="spacingAndGlyphs" by specifying per-character scale operations + if (info.pathTextLength > 0.0f && info.pathChunkLength > 0.0f) { + if (isVerticalText) { + svgChar.pathData->yScale = info.pathChunkLength / info.pathTextLength; + spacing *= svgChar.pathData->yScale; + correctedGlyphAdvance *= svgChar.pathData->yScale; + } else { + svgChar.pathData->xScale = info.pathChunkLength / info.pathTextLength; + spacing *= svgChar.pathData->xScale; + correctedGlyphAdvance *= svgChar.pathData->xScale; + } + } + + // Handle letter & word spacing on text path + float pathExtraAdvance = info.pathExtraAdvance; + info.pathExtraAdvance += spacing; + + svgChar.pathData->hidden = !info.nextPathLayoutPointAndAngle(correctedGlyphAdvance, extraAdvance, newOffset); + svgChar.drawnSeperated = true; + + info.pathExtraAdvance = pathExtraAdvance; + } + + // Apply rotation + if (info.angleValueAvailable()) + info.angle = info.angleValueNext(); + + // Apply baseline-shift + if (info.baselineShiftValueAvailable()) { + svgChar.drawnSeperated = true; + float shift = info.baselineShiftValueNext(); + + if (isVerticalText) + info.shiftx += shift; + else + info.shifty -= shift; + } + + // Take dominant-baseline / alignment-baseline into account + yOrientationShift += alignmentBaselineToShift(isVerticalText, text, font); + + svgChar.x = info.curx; + svgChar.y = info.cury; + svgChar.angle = info.angle; + + // For text paths any shift (dx/dy/baseline-shift) has to be applied after the rotation + if (!info.inPathLayout()) { + svgChar.x += info.shiftx + xOrientationShift; + svgChar.y += info.shifty + yOrientationShift; + + if (orientationAngle != 0.0f) + svgChar.angle += orientationAngle; + + if (svgChar.angle != 0.0f) + svgChar.drawnSeperated = true; + } else { + svgChar.pathData->orientationAngle = orientationAngle; + + if (isVerticalText) + svgChar.angle -= 90.0f; + + svgChar.pathData->xShift = info.shiftx + xOrientationShift; + svgChar.pathData->yShift = info.shifty + yOrientationShift; + + // Translate to glyph midpoint + if (isVerticalText) { + svgChar.pathData->xShift += info.dx; + svgChar.pathData->yShift -= glyphAdvance / 2.0f; + } else { + svgChar.pathData->xShift -= glyphAdvance / 2.0f; + svgChar.pathData->yShift += info.dy; + } + } + + double kerning = 0.0; +#if ENABLE(SVG_FONTS) + SVGFontElement* svgFont = 0; + if (style->font().isSVGFont()) + svgFont = style->font().svgFont(); + + if (lastGlyph.isValid && style->font().isSVGFont()) { + SVGHorizontalKerningPair kerningPair; + if (svgFont->getHorizontalKerningPairForStringsAndGlyphs(lastGlyph.unicode, lastGlyph.glyphName, unicodeStr, glyphName, kerningPair)) + kerning = kerningPair.kerning; + } + + if (style->font().isSVGFont()) { + lastGlyph.unicode = unicodeStr; + lastGlyph.glyphName = glyphName; + lastGlyph.isValid = true; + } else + lastGlyph.isValid = false; +#endif + + svgChar.x -= (float)kerning; + + // Advance to new position + if (isVerticalText) { + svgChar.drawnSeperated = true; + info.cury += glyphAdvance + spacing; + } else + info.curx += glyphAdvance + spacing - (float)kerning; + + // Advance to next character group + for (int k = 0; k < charsConsumed; ++k) { + info.svgChars.append(svgChar); + info.processedSingleCharacter(); + svgChar.drawnSeperated = false; + svgChar.newTextChunk = false; + } + } +} + +void SVGRootInlineBox::buildTextChunks(Vector<SVGChar>& svgChars, Vector<SVGTextChunk>& svgTextChunks, InlineFlowBox* start) +{ + SVGTextChunkLayoutInfo info(svgTextChunks); + info.it = svgChars.begin(); + info.chunk.start = svgChars.begin(); + info.chunk.end = svgChars.begin(); + + buildTextChunks(svgChars, start, info); + ASSERT(info.it == svgChars.end()); +} + +void SVGRootInlineBox::buildTextChunks(Vector<SVGChar>& svgChars, InlineFlowBox* start, SVGTextChunkLayoutInfo& info) +{ +#if DEBUG_CHUNK_BUILDING > 1 + fprintf(stderr, " -> buildTextChunks(start=%p)\n", start); +#endif + + for (InlineBox* curr = start->firstChild(); curr; curr = curr->nextOnLine()) { + if (curr->object()->isText()) { + InlineTextBox* textBox = static_cast<InlineTextBox*>(curr); + + unsigned length = textBox->len(); + if (!length) + continue; + +#if DEBUG_CHUNK_BUILDING > 1 + fprintf(stderr, " -> Handle inline text box (%p) with %i characters (start: %i, end: %i), handlingTextPath=%i\n", + textBox, length, textBox->start(), textBox->end(), (int) info.handlingTextPath); +#endif + + RenderText* text = textBox->textObject(); + ASSERT(text); + ASSERT(text->element()); + + SVGTextContentElement* textContent = 0; + Node* node = text->element()->parent(); + while (node && node->isSVGElement() && !textContent) { + if (static_cast<SVGElement*>(node)->isTextContent()) + textContent = static_cast<SVGTextContentElement*>(node); + else + node = node->parentNode(); + } + ASSERT(textContent); + + // Start new character range for the first chunk + bool isFirstCharacter = info.svgTextChunks.isEmpty() && info.chunk.start == info.it && info.chunk.start == info.chunk.end; + if (isFirstCharacter) { + ASSERT(info.chunk.boxes.isEmpty()); + info.chunk.boxes.append(SVGInlineBoxCharacterRange()); + } else + ASSERT(!info.chunk.boxes.isEmpty()); + + // Walk string to find out new chunk positions, if existant + for (unsigned i = 0; i < length; ++i) { + ASSERT(info.it != svgChars.end()); + + SVGInlineBoxCharacterRange& range = info.chunk.boxes.last(); + if (range.isOpen()) { + range.box = curr; + range.startOffset = (i == 0 ? 0 : i - 1); + +#if DEBUG_CHUNK_BUILDING > 1 + fprintf(stderr, " | -> Range is open! box=%p, startOffset=%i\n", range.box, range.startOffset); +#endif + } + + // If a new (or the first) chunk has been started, record it's text-anchor and writing mode. + if (info.assignChunkProperties) { + info.assignChunkProperties = false; + + info.chunk.isVerticalText = isVerticalWritingMode(text->style()->svgStyle()); + info.chunk.isTextPath = info.handlingTextPath; + info.chunk.anchor = text->style()->svgStyle()->textAnchor(); + info.chunk.textLength = textContent->textLength().value(textContent); + info.chunk.lengthAdjust = (ELengthAdjust) textContent->lengthAdjust(); + +#if DEBUG_CHUNK_BUILDING > 1 + fprintf(stderr, " | -> Assign chunk properties, isVerticalText=%i, anchor=%i\n", info.chunk.isVerticalText, info.chunk.anchor); +#endif + } + + if (i > 0 && !isFirstCharacter && (*info.it).newTextChunk) { + // Close mid chunk & character range + ASSERT(!range.isOpen()); + ASSERT(!range.isClosed()); + + range.endOffset = i; + closeTextChunk(info); + +#if DEBUG_CHUNK_BUILDING > 1 + fprintf(stderr, " | -> Close mid-text chunk, at endOffset: %i and starting new mid chunk!\n", range.endOffset); +#endif + + // Prepare for next chunk, if we're not at the end + startTextChunk(info); + if (i + 1 == length) { +#if DEBUG_CHUNK_BUILDING > 1 + fprintf(stderr, " | -> Record last chunk of inline text box!\n"); +#endif + + startTextChunk(info); + SVGInlineBoxCharacterRange& range = info.chunk.boxes.last(); + + info.assignChunkProperties = false; + info.chunk.isVerticalText = isVerticalWritingMode(text->style()->svgStyle()); + info.chunk.isTextPath = info.handlingTextPath; + info.chunk.anchor = text->style()->svgStyle()->textAnchor(); + info.chunk.textLength = textContent->textLength().value(textContent); + info.chunk.lengthAdjust = (ELengthAdjust) textContent->lengthAdjust(); + + range.box = curr; + range.startOffset = i; + + ASSERT(!range.isOpen()); + ASSERT(!range.isClosed()); + } + } + + // This should only hold true for the first character of the first chunk + if (isFirstCharacter) + isFirstCharacter = false; + + ++info.it; + } + +#if DEBUG_CHUNK_BUILDING > 1 + fprintf(stderr, " -> Finished inline text box!\n"); +#endif + + SVGInlineBoxCharacterRange& range = info.chunk.boxes.last(); + if (!range.isOpen() && !range.isClosed()) { +#if DEBUG_CHUNK_BUILDING > 1 + fprintf(stderr, " -> Last range not closed - closing with endOffset: %i\n", length); +#endif + + // Current text chunk is not yet closed. Finish the current range, but don't start a new chunk. + range.endOffset = length; + + if (info.it != svgChars.end()) { +#if DEBUG_CHUNK_BUILDING > 1 + fprintf(stderr, " -> Not at last character yet!\n"); +#endif + + // If we're not at the end of the last box to be processed, and if the next + // character starts a new chunk, then close the current chunk and start a new one. + if ((*info.it).newTextChunk) { +#if DEBUG_CHUNK_BUILDING > 1 + fprintf(stderr, " -> Next character starts new chunk! Closing current chunk, and starting a new one...\n"); +#endif + + closeTextChunk(info); + startTextChunk(info); + } else { + // Just start a new character range + info.chunk.boxes.append(SVGInlineBoxCharacterRange()); + +#if DEBUG_CHUNK_BUILDING > 1 + fprintf(stderr, " -> Next character does NOT start a new chunk! Starting new character range...\n"); +#endif + } + } else { +#if DEBUG_CHUNK_BUILDING > 1 + fprintf(stderr, " -> Closing final chunk! Finished processing!\n"); +#endif + + // Close final chunk, once we're at the end of the last box + closeTextChunk(info); + } + } + } else { + ASSERT(curr->isInlineFlowBox()); + InlineFlowBox* flowBox = static_cast<InlineFlowBox*>(curr); + + if (!flowBox->object()->element()) + continue; // Skip generated content. + + bool isTextPath = flowBox->object()->element()->hasTagName(SVGNames::textPathTag); + +#if DEBUG_CHUNK_BUILDING > 1 + fprintf(stderr, " -> Handle inline flow box (%p), isTextPath=%i\n", flowBox, (int) isTextPath); +#endif + + if (isTextPath) + info.handlingTextPath = true; + + buildTextChunks(svgChars, flowBox, info); + + if (isTextPath) + info.handlingTextPath = false; + } + } + +#if DEBUG_CHUNK_BUILDING > 1 + fprintf(stderr, " <- buildTextChunks(start=%p)\n", start); +#endif +} + +const Vector<SVGTextChunk>& SVGRootInlineBox::svgTextChunks() const +{ + return m_svgTextChunks; +} + +void SVGRootInlineBox::layoutTextChunks() +{ + Vector<SVGTextChunk>::iterator it = m_svgTextChunks.begin(); + Vector<SVGTextChunk>::iterator end = m_svgTextChunks.end(); + + for (; it != end; ++it) { + SVGTextChunk& chunk = *it; + +#if DEBUG_CHUNK_BUILDING > 0 + { + fprintf(stderr, "Handle TEXT CHUNK! anchor=%i, textLength=%f, lengthAdjust=%i, isVerticalText=%i, isTextPath=%i start=%p, end=%p -> dist: %i\n", + (int) chunk.anchor, chunk.textLength, (int) chunk.lengthAdjust, (int) chunk.isVerticalText, + (int) chunk.isTextPath, chunk.start, chunk.end, (unsigned int) (chunk.end - chunk.start)); + + Vector<SVGInlineBoxCharacterRange>::iterator boxIt = chunk.boxes.begin(); + Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = chunk.boxes.end(); + + unsigned int i = 0; + for (; boxIt != boxEnd; ++boxIt) { + SVGInlineBoxCharacterRange& range = *boxIt; i++; + fprintf(stderr, " -> RANGE %i STARTOFFSET: %i, ENDOFFSET: %i, BOX: %p\n", i, range.startOffset, range.endOffset, range.box); + } + } +#endif + + if (chunk.isTextPath) + continue; + + // text-path & textLength, with lengthAdjust="spacing" is already handled for textPath layouts. + applyTextLengthCorrectionToTextChunk(chunk); + + // text-anchor is already handled for textPath layouts. + applyTextAnchorToTextChunk(chunk); + } +} + +static inline void addPaintServerToTextDecorationInfo(ETextDecoration decoration, SVGTextDecorationInfo& info, RenderObject* object) +{ + if (object->style()->svgStyle()->hasFill()) + info.fillServerMap.set(decoration, object); + + if (object->style()->svgStyle()->hasStroke()) + info.strokeServerMap.set(decoration, object); +} + +SVGTextDecorationInfo SVGRootInlineBox::retrievePaintServersForTextDecoration(RenderObject* start) +{ + ASSERT(start); + + Vector<RenderObject*> parentChain; + while ((start = start->parent())) { + parentChain.prepend(start); + + // Stop at our direct <text> parent. + if (start->isSVGText()) + break; + } + + Vector<RenderObject*>::iterator it = parentChain.begin(); + Vector<RenderObject*>::iterator end = parentChain.end(); + + SVGTextDecorationInfo info; + + for (; it != end; ++it) { + RenderObject* object = *it; + ASSERT(object); + + RenderStyle* style = object->style(); + ASSERT(style); + + int decorations = style->textDecoration(); + if (decorations != NONE) { + if (decorations & OVERLINE) + addPaintServerToTextDecorationInfo(OVERLINE, info, object); + + if (decorations & UNDERLINE) + addPaintServerToTextDecorationInfo(UNDERLINE, info, object); + + if (decorations & LINE_THROUGH) + addPaintServerToTextDecorationInfo(LINE_THROUGH, info, object); + } + } + + return info; +} + +void SVGRootInlineBox::walkTextChunks(SVGTextChunkWalkerBase* walker, const SVGInlineTextBox* textBox) +{ + ASSERT(walker); + + Vector<SVGTextChunk>::iterator it = m_svgTextChunks.begin(); + Vector<SVGTextChunk>::iterator itEnd = m_svgTextChunks.end(); + + for (; it != itEnd; ++it) { + SVGTextChunk& curChunk = *it; + + Vector<SVGInlineBoxCharacterRange>::iterator boxIt = curChunk.boxes.begin(); + Vector<SVGInlineBoxCharacterRange>::iterator boxEnd = curChunk.boxes.end(); + + InlineBox* lastNotifiedBox = 0; + InlineBox* prevBox = 0; + + unsigned int chunkOffset = 0; + bool startedFirstChunk = false; + + for (; boxIt != boxEnd; ++boxIt) { + SVGInlineBoxCharacterRange& range = *boxIt; + + ASSERT(range.box->isInlineTextBox()); + SVGInlineTextBox* rangeTextBox = static_cast<SVGInlineTextBox*>(range.box); + + if (textBox && rangeTextBox != textBox) { + chunkOffset += range.endOffset - range.startOffset; + continue; + } + + // Eventually notify that we started a new chunk + if (!textBox && !startedFirstChunk) { + startedFirstChunk = true; + + lastNotifiedBox = range.box; + walker->start(range.box); + } else { + // Eventually apply new style, as this chunk spans multiple boxes (with possible different styling) + if (prevBox && prevBox != range.box) { + lastNotifiedBox = range.box; + + walker->end(prevBox); + walker->start(lastNotifiedBox); + } + } + + unsigned int length = range.endOffset - range.startOffset; + + Vector<SVGChar>::iterator itCharBegin = curChunk.start + chunkOffset; + Vector<SVGChar>::iterator itCharEnd = curChunk.start + chunkOffset + length; + ASSERT(itCharEnd <= curChunk.end); + + // Process this chunk portion + if (textBox) + (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd); + else { + if (walker->setupFill(range.box)) + (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd); + + if (walker->setupStroke(range.box)) + (*walker)(rangeTextBox, range.startOffset, curChunk.ctm, itCharBegin, itCharEnd); + } + + chunkOffset += length; + + if (!textBox) + prevBox = range.box; + } + + if (!textBox && startedFirstChunk) + walker->end(lastNotifiedBox); + } +} + +} // namespace WebCore + +#endif // ENABLE(SVG) diff --git a/src/3rdparty/webkit/WebCore/rendering/SVGRootInlineBox.h b/src/3rdparty/webkit/WebCore/rendering/SVGRootInlineBox.h new file mode 100644 index 0000000..800664b --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/SVGRootInlineBox.h @@ -0,0 +1,99 @@ +/* + * This file is part of the WebKit project. + * + * Copyright (C) 2006 Oliver Hunt <ojh16@student.canterbury.ac.nz> + * (C) 2006 Apple Computer Inc. + * (C) 2007 Nikolas Zimmermann <zimmermann@kde.org> + * + * 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. + * + */ + +#ifndef SVGRootInlineBox_h +#define SVGRootInlineBox_h + +#if ENABLE(SVG) +#include "RootInlineBox.h" +#include "SVGCharacterLayoutInfo.h" + +namespace WebCore { + +class InlineTextBox; +class RenderSVGRoot; +class SVGInlineTextBox; + +struct LastGlyphInfo { + LastGlyphInfo() : isValid(false) { } + + String unicode; + String glyphName; + bool isValid; +}; + +class SVGRootInlineBox : public RootInlineBox { +public: + SVGRootInlineBox(RenderObject* obj) + : RootInlineBox(obj) + { + } + + virtual bool isSVGRootInlineBox() { return true; } + + virtual void paint(RenderObject::PaintInfo&, int tx, int ty); + + virtual int placeBoxesHorizontally(int x, int& leftPosition, int& rightPosition, bool& needsWordSpacing); + virtual void verticallyAlignBoxes(int& heightOfBlock); + + virtual void computePerCharacterLayoutInformation(); + + // Used by SVGInlineTextBox + const Vector<SVGTextChunk>& svgTextChunks() const; + + void walkTextChunks(SVGTextChunkWalkerBase*, const SVGInlineTextBox* textBox = 0); + +private: + friend struct SVGRootInlineBoxPaintWalker; + + void layoutInlineBoxes(); + void layoutInlineBoxes(InlineFlowBox* start, Vector<SVGChar>::iterator& it, int& minX, int& maxX, int& minY, int& maxY); + + void buildLayoutInformation(InlineFlowBox* start, SVGCharacterLayoutInfo&); + void buildLayoutInformationForTextBox(SVGCharacterLayoutInfo&, InlineTextBox*, LastGlyphInfo&); + + void buildTextChunks(Vector<SVGChar>&, Vector<SVGTextChunk>&, InlineFlowBox* start); + void buildTextChunks(Vector<SVGChar>&, InlineFlowBox* start, SVGTextChunkLayoutInfo&); + void layoutTextChunks(); + + SVGTextDecorationInfo retrievePaintServersForTextDecoration(RenderObject* start); + +private: + Vector<SVGChar> m_svgChars; + Vector<SVGTextChunk> m_svgTextChunks; +}; + +// Shared with SVGRenderTreeAsText / SVGInlineTextBox +TextRun svgTextRunForInlineTextBox(const UChar*, int len, RenderStyle* style, const InlineTextBox* textBox, float xPos); +FloatPoint topLeftPositionOfCharacterRange(Vector<SVGChar>::iterator start, Vector<SVGChar>::iterator end); +float cummulatedWidthOfInlineBoxCharacterRange(SVGInlineBoxCharacterRange& range); +float cummulatedHeightOfInlineBoxCharacterRange(SVGInlineBoxCharacterRange& range); + +RenderSVGRoot* findSVGRootObject(RenderObject* start); + +} // namespace WebCore + +#endif // ENABLE(SVG) + +#endif // SVGRootInlineBox_h diff --git a/src/3rdparty/webkit/WebCore/rendering/TableLayout.h b/src/3rdparty/webkit/WebCore/rendering/TableLayout.h new file mode 100644 index 0000000..8ae0ce7 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/TableLayout.h @@ -0,0 +1,48 @@ +/* + * This file is part of the HTML rendering engine for KDE. + * + * Copyright (C) 2002 Lars Knoll (knoll@kde.org) + * (C) 2002 Dirk Mueller (mueller@kde.org) + * + * 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. + * + * 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. + */ + +#ifndef TableLayout_h +#define TableLayout_h + +namespace WebCore { + +class RenderTable; + +class TableLayout { +public: + TableLayout(RenderTable* table) + : m_table(table) + { + } + + virtual ~TableLayout() { } + + virtual void calcPrefWidths(int& minWidth, int& maxWidth) = 0; + virtual void layout() = 0; + +protected: + RenderTable* m_table; +}; + +} // namespace WebCore + +#endif // TableLayout_h diff --git a/src/3rdparty/webkit/WebCore/rendering/TextControlInnerElements.cpp b/src/3rdparty/webkit/WebCore/rendering/TextControlInnerElements.cpp new file mode 100644 index 0000000..452333c --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/TextControlInnerElements.cpp @@ -0,0 +1,178 @@ +/* + * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "TextControlInnerElements.h" + +#include "BeforeTextInsertedEvent.h" +#include "Document.h" +#include "EventHandler.h" +#include "EventNames.h" +#include "Frame.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include "HTMLTextAreaElement.h" +#include "MouseEvent.h" +#include "RenderTextControlSingleLine.h" + +namespace WebCore { + +class RenderTextControlInnerBlock : public RenderBlock { +public: + RenderTextControlInnerBlock(Node* node) : RenderBlock(node) { } + + virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, int x, int y, int tx, int ty, HitTestAction); +}; + +bool RenderTextControlInnerBlock::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, HitTestAction hitTestAction) +{ + RenderObject* renderer = node()->shadowAncestorNode()->renderer(); + + bool placeholderIsVisible = false; + if (renderer->isTextField()) + placeholderIsVisible = static_cast<RenderTextControlSingleLine*>(renderer)->placeholderIsVisible(); + + return RenderBlock::nodeAtPoint(request, result, x, y, tx, ty, placeholderIsVisible ? HitTestBlockBackground : hitTestAction); +} + +TextControlInnerElement::TextControlInnerElement(Document* doc, Node* shadowParent) + : HTMLDivElement(HTMLNames::divTag, doc) + , m_shadowParent(shadowParent) +{ +} + +void TextControlInnerElement::attachInnerElement(Node* parent, PassRefPtr<RenderStyle> style, RenderArena* arena) +{ + // When adding these elements, create the renderer & style first before adding to the DOM. + // Otherwise, the render tree will create some anonymous blocks that will mess up our layout. + + // Create the renderer with the specified style + RenderObject* renderer = createRenderer(arena, style.get()); + if (renderer) { + setRenderer(renderer); + renderer->setStyle(style); + } + + // Set these explicitly since this normally happens during an attach() + setAttached(); + setInDocument(true); + + // For elements without a shadow parent, add the node to the DOM normally. + if (!m_shadowParent) + parent->addChild(this); + + // Add the renderer to the render tree + if (renderer) + parent->renderer()->addChild(renderer); +} + +TextControlInnerTextElement::TextControlInnerTextElement(Document* doc, Node* shadowParent) + : TextControlInnerElement(doc, shadowParent) +{ +} + +void TextControlInnerTextElement::defaultEventHandler(Event* evt) +{ + // FIXME: In the future, we should add a way to have default event listeners. Then we would add one to the text field's inner div, and we wouldn't need this subclass. + Node* shadowAncestor = shadowAncestorNode(); + if (shadowAncestor && shadowAncestor->renderer()) { + ASSERT(shadowAncestor->renderer()->isTextField() || shadowAncestor->renderer()->isTextArea()); + if (evt->isBeforeTextInsertedEvent()) + if (shadowAncestor->renderer()->isTextField()) + static_cast<HTMLInputElement*>(shadowAncestor)->defaultEventHandler(evt); + else + static_cast<HTMLTextAreaElement*>(shadowAncestor)->defaultEventHandler(evt); + if (evt->type() == eventNames().webkitEditableContentChangedEvent) + static_cast<RenderTextControl*>(shadowAncestor->renderer())->subtreeHasChanged(); + } + if (!evt->defaultHandled()) + HTMLDivElement::defaultEventHandler(evt); +} + +RenderObject* TextControlInnerTextElement::createRenderer(RenderArena* arena, RenderStyle*) +{ + return new (arena) RenderTextControlInnerBlock(this); +} + +SearchFieldResultsButtonElement::SearchFieldResultsButtonElement(Document* doc) + : TextControlInnerElement(doc) +{ +} + +void SearchFieldResultsButtonElement::defaultEventHandler(Event* evt) +{ + // On mousedown, bring up a menu, if needed + HTMLInputElement* input = static_cast<HTMLInputElement*>(shadowAncestorNode()); + if (evt->type() == eventNames().mousedownEvent && evt->isMouseEvent() && static_cast<MouseEvent*>(evt)->button() == LeftButton) { + input->focus(); + input->select(); + RenderTextControlSingleLine* renderer = static_cast<RenderTextControlSingleLine*>(input->renderer()); + if (renderer->popupIsVisible()) + renderer->hidePopup(); + else if (input->maxResults() > 0) + renderer->showPopup(); + evt->setDefaultHandled(); + } + if (!evt->defaultHandled()) + HTMLDivElement::defaultEventHandler(evt); +} + +SearchFieldCancelButtonElement::SearchFieldCancelButtonElement(Document* doc) + : TextControlInnerElement(doc) + , m_capturing(false) +{ +} + +void SearchFieldCancelButtonElement::defaultEventHandler(Event* evt) +{ + // If the element is visible, on mouseup, clear the value, and set selection + HTMLInputElement* input = static_cast<HTMLInputElement*>(shadowAncestorNode()); + if (evt->type() == eventNames().mousedownEvent && evt->isMouseEvent() && static_cast<MouseEvent*>(evt)->button() == LeftButton) { + input->focus(); + input->select(); + evt->setDefaultHandled(); + if (renderer() && renderer()->visibleToHitTesting()) + if (Frame* frame = document()->frame()) { + frame->eventHandler()->setCapturingMouseEventsNode(this); + m_capturing = true; + } + } else if (evt->type() == eventNames().mouseupEvent && evt->isMouseEvent() && static_cast<MouseEvent*>(evt)->button() == LeftButton) { + if (m_capturing && renderer() && renderer()->visibleToHitTesting()) { + if (hovered()) { + input->setValue(""); + input->onSearch(); + evt->setDefaultHandled(); + } + if (Frame* frame = document()->frame()) { + frame->eventHandler()->setCapturingMouseEventsNode(0); + m_capturing = false; + } + } + } + if (!evt->defaultHandled()) + HTMLDivElement::defaultEventHandler(evt); +} + +} diff --git a/src/3rdparty/webkit/WebCore/rendering/TextControlInnerElements.h b/src/3rdparty/webkit/WebCore/rendering/TextControlInnerElements.h new file mode 100644 index 0000000..9e81ada --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/TextControlInnerElements.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef TextControlInnerElements_h +#define TextControlInnerElements_h + +#include "HTMLDivElement.h" + +namespace WebCore { + +class String; + +class TextControlInnerElement : public HTMLDivElement +{ +public: + TextControlInnerElement(Document*, Node* shadowParent = 0); + + virtual bool isMouseFocusable() const { return false; } + virtual bool isShadowNode() const { return m_shadowParent; } + virtual Node* shadowParentNode() { return m_shadowParent; } + void setShadowParentNode(Node* node) { m_shadowParent = node; } + void attachInnerElement(Node*, PassRefPtr<RenderStyle>, RenderArena*); + +private: + Node* m_shadowParent; +}; + +class TextControlInnerTextElement : public TextControlInnerElement { +public: + TextControlInnerTextElement(Document*, Node* shadowParent); + virtual RenderObject* createRenderer(RenderArena*, RenderStyle*); + virtual void defaultEventHandler(Event*); +}; + +class SearchFieldResultsButtonElement : public TextControlInnerElement { +public: + SearchFieldResultsButtonElement(Document*); + virtual void defaultEventHandler(Event*); +}; + +class SearchFieldCancelButtonElement : public TextControlInnerElement { +public: + SearchFieldCancelButtonElement(Document*); + virtual void defaultEventHandler(Event*); +private: + bool m_capturing; +}; + +} //namespace + +#endif diff --git a/src/3rdparty/webkit/WebCore/rendering/bidi.cpp b/src/3rdparty/webkit/WebCore/rendering/bidi.cpp new file mode 100644 index 0000000..6ff2d59 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/bidi.cpp @@ -0,0 +1,2222 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * Copyright (C) 2004, 2006, 2007, 2008 Apple Inc. All right reserved. + * + * 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" +#include "bidi.h" + +#include "CharacterNames.h" +#include "Document.h" +#include "Element.h" +#include "FrameView.h" +#include "InlineTextBox.h" +#include "Logging.h" +#include "RenderArena.h" +#include "RenderLayer.h" +#include "RenderListMarker.h" +#include "RenderView.h" +#include "break_lines.h" +#include <wtf/AlwaysInline.h> +#include <wtf/RefCountedLeakCounter.h> +#include <wtf/StdLibExtras.h> +#include <wtf/Vector.h> + +using namespace std; +using namespace WTF; +using namespace Unicode; + +namespace WebCore { + +// We don't let our line box tree for a single line get any deeper than this. +const unsigned cMaxLineDepth = 200; + +class InlineIterator { +public: + InlineIterator() + : block(0) + , obj(0) + , pos(0) + , nextBreakablePosition(-1) + { + } + + InlineIterator(RenderBlock* b, RenderObject* o, unsigned p) + : block(b) + , obj(o) + , pos(p) + , nextBreakablePosition(-1) + { + } + + void increment(InlineBidiResolver* resolver = 0); + bool atEnd() const; + + UChar current() const; + WTF::Unicode::Direction direction() const; + + RenderBlock* block; + RenderObject* obj; + unsigned pos; + int nextBreakablePosition; +}; + +// Midpoint globals. The goal is not to do any allocation when dealing with +// these midpoints, so we just keep an array around and never clear it. We track +// the number of items and position using the two other variables. +static Vector<InlineIterator>* smidpoints; +static unsigned sNumMidpoints; +static unsigned sCurrMidpoint; +static bool betweenMidpoints; + +static bool isLineEmpty = true; +static bool previousLineBrokeCleanly = true; + +static int getBorderPaddingMargin(RenderObject* child, bool endOfInline) +{ + bool leftSide = (child->style()->direction() == LTR) ? !endOfInline : endOfInline; + if (leftSide) + return child->marginLeft() + child->paddingLeft() + child->borderLeft(); + return child->marginRight() + child->paddingRight() + child->borderRight(); +} + +static int inlineWidth(RenderObject* child, bool start = true, bool end = true) +{ + unsigned lineDepth = 1; + int extraWidth = 0; + RenderObject* parent = child->parent(); + while (parent->isInline() && !parent->isInlineBlockOrInlineTable() && lineDepth++ < cMaxLineDepth) { + if (start && parent->firstChild() == child) + extraWidth += getBorderPaddingMargin(parent, false); + if (end && parent->lastChild() == child) + extraWidth += getBorderPaddingMargin(parent, true); + child = parent; + parent = child->parent(); + } + return extraWidth; +} + +#ifndef NDEBUG +static WTF::RefCountedLeakCounter bidiRunCounter("BidiRun"); + +static bool inBidiRunDestroy; +#endif + +void BidiRun::destroy() +{ +#ifndef NDEBUG + inBidiRunDestroy = true; +#endif + RenderArena* renderArena = m_object->renderArena(); + delete this; +#ifndef NDEBUG + inBidiRunDestroy = false; +#endif + + // Recover the size left there for us by operator delete and free the memory. + renderArena->free(*reinterpret_cast<size_t*>(this), this); +} + +void* BidiRun::operator new(size_t sz, RenderArena* renderArena) throw() +{ +#ifndef NDEBUG + bidiRunCounter.increment(); +#endif + return renderArena->allocate(sz); +} + +void BidiRun::operator delete(void* ptr, size_t sz) +{ +#ifndef NDEBUG + bidiRunCounter.decrement(); +#endif + ASSERT(inBidiRunDestroy); + + // Stash size where destroy() can find it. + *(size_t*)ptr = sz; +} + +// --------------------------------------------------------------------- + +inline bool operator==(const InlineIterator& it1, const InlineIterator& it2) +{ + return it1.pos == it2.pos && it1.obj == it2.obj; +} + +inline bool operator!=(const InlineIterator& it1, const InlineIterator& it2) +{ + return it1.pos != it2.pos || it1.obj != it2.obj; +} + +static inline RenderObject* bidiNext(RenderBlock* block, RenderObject* current, InlineBidiResolver* resolver = 0, bool skipInlines = true, bool* endOfInlinePtr = 0) +{ + RenderObject* next = 0; + bool oldEndOfInline = endOfInlinePtr ? *endOfInlinePtr : false; + bool endOfInline = false; + + while (current) { + next = 0; + if (!oldEndOfInline && !current->isFloating() && !current->isReplaced() && !current->isPositioned()) { + next = current->firstChild(); + if (next && resolver && next->isInlineFlow()) { + EUnicodeBidi ub = next->style()->unicodeBidi(); + if (ub != UBNormal) { + TextDirection dir = next->style()->direction(); + Direction d = (ub == Embed + ? (dir == RTL ? RightToLeftEmbedding : LeftToRightEmbedding) + : (dir == RTL ? RightToLeftOverride : LeftToRightOverride)); + resolver->embed(d); + } + } + } + + if (!next) { + if (!skipInlines && !oldEndOfInline && current->isInlineFlow()) { + next = current; + endOfInline = true; + break; + } + + while (current && current != block) { + if (resolver && current->isInlineFlow() && current->style()->unicodeBidi() != UBNormal) + resolver->embed(PopDirectionalFormat); + + next = current->nextSibling(); + if (next) { + if (resolver && next->isInlineFlow()) { + EUnicodeBidi ub = next->style()->unicodeBidi(); + if (ub != UBNormal) { + TextDirection dir = next->style()->direction(); + Direction d = (ub == Embed + ? (dir == RTL ? RightToLeftEmbedding: LeftToRightEmbedding) + : (dir == RTL ? RightToLeftOverride : LeftToRightOverride)); + resolver->embed(d); + } + } + break; + } + + current = current->parent(); + if (!skipInlines && current && current != block && current->isInlineFlow()) { + next = current; + endOfInline = true; + break; + } + } + } + + if (!next) + break; + + if (next->isText() || next->isFloating() || next->isReplaced() || next->isPositioned() + || ((!skipInlines || !next->firstChild()) // Always return EMPTY inlines. + && next->isInlineFlow())) + break; + current = next; + } + + if (endOfInlinePtr) + *endOfInlinePtr = endOfInline; + + return next; +} + +static RenderObject* bidiFirst(RenderBlock* block, InlineBidiResolver* resolver, bool skipInlines = true) +{ + if (!block->firstChild()) + return 0; + + RenderObject* o = block->firstChild(); + if (o->isInlineFlow()) { + if (resolver) { + EUnicodeBidi ub = o->style()->unicodeBidi(); + if (ub != UBNormal) { + TextDirection dir = o->style()->direction(); + Direction d = (ub == Embed + ? (dir == RTL ? RightToLeftEmbedding : LeftToRightEmbedding) + : (dir == RTL ? RightToLeftOverride : LeftToRightOverride)); + resolver->embed(d); + } + } + if (skipInlines && o->firstChild()) + o = bidiNext(block, o, resolver, skipInlines); + else { + // Never skip empty inlines. + if (resolver) + resolver->commitExplicitEmbedding(); + return o; + } + } + + if (o && !o->isText() && !o->isReplaced() && !o->isFloating() && !o->isPositioned()) + o = bidiNext(block, o, resolver, skipInlines); + + if (resolver) + resolver->commitExplicitEmbedding(); + return o; +} + +inline void InlineIterator::increment(InlineBidiResolver* resolver) +{ + if (!obj) + return; + if (obj->isText()) { + pos++; + if (pos >= static_cast<RenderText*>(obj)->textLength()) { + obj = bidiNext(block, obj, resolver); + pos = 0; + nextBreakablePosition = -1; + } + } else { + obj = bidiNext(block, obj, resolver); + pos = 0; + nextBreakablePosition = -1; + } +} + +template<> +inline void InlineBidiResolver::increment() +{ + current.increment(this); +} + +inline bool InlineIterator::atEnd() const +{ + return !obj; +} + +inline UChar InlineIterator::current() const +{ + if (!obj || !obj->isText()) + return 0; + + RenderText* text = static_cast<RenderText*>(obj); + if (pos >= text->textLength()) + return 0; + + return text->characters()[pos]; +} + +ALWAYS_INLINE Direction InlineIterator::direction() const +{ + if (UChar c = current()) + return Unicode::direction(c); + + if (obj && obj->isListMarker()) + return obj->style()->direction() == LTR ? LeftToRight : RightToLeft; + + return OtherNeutral; +} + +// ------------------------------------------------------------------------------------------------- + +static void chopMidpointsAt(RenderObject* obj, unsigned pos) +{ + if (!sNumMidpoints) + return; + InlineIterator* midpoints = smidpoints->data(); + for (int i = sNumMidpoints - 1; i >= 0; i--) { + const InlineIterator& point = midpoints[i]; + if (point.obj == obj && point.pos == pos) { + sNumMidpoints = i; + break; + } + } +} + +static void checkMidpoints(InlineIterator& lBreak) +{ + // Check to see if our last midpoint is a start point beyond the line break. If so, + // shave it off the list, and shave off a trailing space if the previous end point doesn't + // preserve whitespace. + if (lBreak.obj && sNumMidpoints && sNumMidpoints % 2 == 0) { + InlineIterator* midpoints = smidpoints->data(); + InlineIterator& endpoint = midpoints[sNumMidpoints-2]; + const InlineIterator& startpoint = midpoints[sNumMidpoints-1]; + InlineIterator currpoint = endpoint; + while (!currpoint.atEnd() && currpoint != startpoint && currpoint != lBreak) + currpoint.increment(); + if (currpoint == lBreak) { + // We hit the line break before the start point. Shave off the start point. + sNumMidpoints--; + if (endpoint.obj->style()->collapseWhiteSpace()) { + if (endpoint.obj->isText()) { + // Don't shave a character off the endpoint if it was from a soft hyphen. + RenderText* textObj = static_cast<RenderText*>(endpoint.obj); + if (endpoint.pos + 1 < textObj->textLength()) { + if (textObj->characters()[endpoint.pos+1] == softHyphen) + return; + } else if (startpoint.obj->isText()) { + RenderText *startText = static_cast<RenderText*>(startpoint.obj); + if (startText->textLength() && startText->characters()[0] == softHyphen) + return; + } + } + endpoint.pos--; + } + } + } +} + +static void addMidpoint(const InlineIterator& midpoint) +{ + if (smidpoints->size() <= sNumMidpoints) + smidpoints->grow(sNumMidpoints + 10); + + InlineIterator* midpoints = smidpoints->data(); + midpoints[sNumMidpoints++] = midpoint; +} + +static void appendRunsForObject(int start, int end, RenderObject* obj, InlineBidiResolver& resolver) +{ + if (start > end || obj->isFloating() || + (obj->isPositioned() && !obj->hasStaticX() && !obj->hasStaticY() && !obj->container()->isInlineFlow())) + return; + + bool haveNextMidpoint = (sCurrMidpoint < sNumMidpoints); + InlineIterator nextMidpoint; + if (haveNextMidpoint) + nextMidpoint = smidpoints->at(sCurrMidpoint); + if (betweenMidpoints) { + if (!(haveNextMidpoint && nextMidpoint.obj == obj)) + return; + // This is a new start point. Stop ignoring objects and + // adjust our start. + betweenMidpoints = false; + start = nextMidpoint.pos; + sCurrMidpoint++; + if (start < end) + return appendRunsForObject(start, end, obj, resolver); + } else { + if (!haveNextMidpoint || (obj != nextMidpoint.obj)) { + resolver.addRun(new (obj->renderArena()) BidiRun(start, end, obj, resolver.context(), resolver.dir())); + return; + } + + // An end midpoint has been encountered within our object. We + // need to go ahead and append a run with our endpoint. + if (static_cast<int>(nextMidpoint.pos + 1) <= end) { + betweenMidpoints = true; + sCurrMidpoint++; + if (nextMidpoint.pos != UINT_MAX) { // UINT_MAX means stop at the object and don't include any of it. + if (static_cast<int>(nextMidpoint.pos + 1) > start) + resolver.addRun(new (obj->renderArena()) + BidiRun(start, nextMidpoint.pos + 1, obj, resolver.context(), resolver.dir())); + return appendRunsForObject(nextMidpoint.pos + 1, end, obj, resolver); + } + } else + resolver.addRun(new (obj->renderArena()) BidiRun(start, end, obj, resolver.context(), resolver.dir())); + } +} + +template <> +void InlineBidiResolver::appendRun() +{ + if (!emptyRun && !eor.atEnd()) { + int start = sor.pos; + RenderObject *obj = sor.obj; + while (obj && obj != eor.obj && obj != endOfLine.obj) { + appendRunsForObject(start, obj->length(), obj, *this); + start = 0; + obj = bidiNext(sor.block, obj); + } + if (obj) { + unsigned pos = obj == eor.obj ? eor.pos : UINT_MAX; + if (obj == endOfLine.obj && endOfLine.pos <= pos) { + reachedEndOfLine = true; + pos = endOfLine.pos; + } + // It's OK to add runs for zero-length RenderObjects, just don't make the run larger than it should be + int end = obj->length() ? pos+1 : 0; + appendRunsForObject(start, end, obj, *this); + } + + eor.increment(); + sor = eor; + } + + m_direction = OtherNeutral; + m_status.eor = OtherNeutral; +} + +InlineFlowBox* RenderBlock::createLineBoxes(RenderObject* obj) +{ + // See if we have an unconstructed line box for this object that is also + // the last item on the line. + unsigned lineDepth = 1; + InlineFlowBox* childBox = 0; + InlineFlowBox* parentBox = 0; + InlineFlowBox* result = 0; + do { + ASSERT(obj->isInlineFlow() || obj == this); + RenderFlow* flow = static_cast<RenderFlow*>(obj); + + // Get the last box we made for this render object. + parentBox = flow->lastLineBox(); + + // If this box is constructed then it is from a previous line, and we need + // to make a new box for our line. If this box is unconstructed but it has + // something following it on the line, then we know we have to make a new box + // as well. In this situation our inline has actually been split in two on + // the same line (this can happen with very fancy language mixtures). + bool constructedNewBox = false; + if (!parentBox || parentBox->isConstructed() || parentBox->nextOnLine()) { + // We need to make a new box for this render object. Once + // made, we need to place it at the end of the current line. + InlineBox* newBox = obj->createInlineBox(false, obj == this); + ASSERT(newBox->isInlineFlowBox()); + parentBox = static_cast<InlineFlowBox*>(newBox); + parentBox->setFirstLineStyleBit(m_firstLine); + constructedNewBox = true; + } + + if (!result) + result = parentBox; + + // If we have hit the block itself, then |box| represents the root + // inline box for the line, and it doesn't have to be appended to any parent + // inline. + if (childBox) + parentBox->addToLine(childBox); + + if (!constructedNewBox || obj == this) + break; + + childBox = parentBox; + + // If we've exceeded our line depth, then jump straight to the root and skip all the remaining + // intermediate inline flows. + obj = (++lineDepth >= cMaxLineDepth) ? this : obj->parent(); + + } while (true); + + return result; +} + +RootInlineBox* RenderBlock::constructLine(unsigned runCount, BidiRun* firstRun, BidiRun* lastRun, bool lastLine, RenderObject* endObject) +{ + ASSERT(firstRun); + + InlineFlowBox* parentBox = 0; + for (BidiRun* r = firstRun; r; r = r->next()) { + // Create a box for our object. + bool isOnlyRun = (runCount == 1); + if (runCount == 2 && !r->m_object->isListMarker()) + isOnlyRun = ((style()->direction() == RTL) ? lastRun : firstRun)->m_object->isListMarker(); + + InlineBox* box = r->m_object->createInlineBox(r->m_object->isPositioned(), false, isOnlyRun); + r->m_box = box; + + if (box) { + // If we have no parent box yet, or if the run is not simply a sibling, + // then we need to construct inline boxes as necessary to properly enclose the + // run's inline box. + if (!parentBox || parentBox->object() != r->m_object->parent()) + // Create new inline boxes all the way back to the appropriate insertion point. + parentBox = createLineBoxes(r->m_object->parent()); + + // Append the inline box to this line. + parentBox->addToLine(box); + + bool visuallyOrdered = r->m_object->style()->visuallyOrdered(); + box->setBidiLevel(visuallyOrdered ? 0 : r->level()); + + if (box->isInlineTextBox()) { + InlineTextBox* text = static_cast<InlineTextBox*>(box); + text->setStart(r->m_start); + text->setLen(r->m_stop - r->m_start); + text->m_dirOverride = r->dirOverride(visuallyOrdered); + } + } + } + + // We should have a root inline box. It should be unconstructed and + // be the last continuation of our line list. + ASSERT(lastLineBox() && !lastLineBox()->isConstructed()); + + // Set bits on our inline flow boxes that indicate which sides should + // paint borders/margins/padding. This knowledge will ultimately be used when + // we determine the horizontal positions and widths of all the inline boxes on + // the line. + lastLineBox()->determineSpacingForFlowBoxes(lastLine, endObject); + + // Now mark the line boxes as being constructed. + lastLineBox()->setConstructed(); + + // Return the last line. + return lastRootBox(); +} + +void RenderBlock::computeHorizontalPositionsForLine(RootInlineBox* lineBox, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd) +{ + // First determine our total width. + int availableWidth = lineWidth(m_height); + int totWidth = lineBox->getFlowSpacingWidth(); + bool needsWordSpacing = false; + unsigned numSpaces = 0; + ETextAlign textAlign = style()->textAlign(); + + for (BidiRun* r = firstRun; r; r = r->next()) { + if (!r->m_box || r->m_object->isPositioned() || r->m_box->isLineBreak()) + continue; // Positioned objects are only participating to figure out their + // correct static x position. They have no effect on the width. + // Similarly, line break boxes have no effect on the width. + if (r->m_object->isText()) { + RenderText* rt = static_cast<RenderText*>(r->m_object); + + if (textAlign == JUSTIFY && r != trailingSpaceRun) { + const UChar* characters = rt->characters(); + for (int i = r->m_start; i < r->m_stop; i++) { + UChar c = characters[i]; + if (c == ' ' || c == '\n' || c == '\t') + numSpaces++; + } + } + + if (int length = rt->textLength()) { + if (!r->m_compact && !r->m_start && needsWordSpacing && isSpaceOrNewline(rt->characters()[r->m_start])) + totWidth += rt->style(m_firstLine)->font().wordSpacing(); + needsWordSpacing = !isSpaceOrNewline(rt->characters()[r->m_stop - 1]) && r->m_stop == length; + } + r->m_box->setWidth(rt->width(r->m_start, r->m_stop - r->m_start, totWidth, m_firstLine)); + } else if (!r->m_object->isInlineFlow()) { + r->m_object->calcWidth(); + r->m_box->setWidth(r->m_object->width()); + if (!r->m_compact) + totWidth += r->m_object->marginLeft() + r->m_object->marginRight(); + } + + // Compacts don't contribute to the width of the line, since they are placed in the margin. + if (!r->m_compact) + totWidth += r->m_box->width(); + } + + // Armed with the total width of the line (without justification), + // we now examine our text-align property in order to determine where to position the + // objects horizontally. The total width of the line can be increased if we end up + // justifying text. + int x = leftOffset(m_height); + switch(textAlign) { + case LEFT: + case WEBKIT_LEFT: + // The direction of the block should determine what happens with wide lines. In + // particular with RTL blocks, wide lines should still spill out to the left. + if (style()->direction() == LTR) { + if (totWidth > availableWidth && trailingSpaceRun) + trailingSpaceRun->m_box->setWidth(trailingSpaceRun->m_box->width() - totWidth + availableWidth); + } else { + if (trailingSpaceRun) + trailingSpaceRun->m_box->setWidth(0); + else if (totWidth > availableWidth) + x -= (totWidth - availableWidth); + } + break; + case JUSTIFY: + if (numSpaces && !reachedEnd && !lineBox->endsWithBreak()) { + if (trailingSpaceRun) { + totWidth -= trailingSpaceRun->m_box->width(); + trailingSpaceRun->m_box->setWidth(0); + } + break; + } + // fall through + case TAAUTO: + numSpaces = 0; + // for right to left fall through to right aligned + if (style()->direction() == LTR) { + if (totWidth > availableWidth && trailingSpaceRun) + trailingSpaceRun->m_box->setWidth(trailingSpaceRun->m_box->width() - totWidth + availableWidth); + break; + } + case RIGHT: + case WEBKIT_RIGHT: + // Wide lines spill out of the block based off direction. + // So even if text-align is right, if direction is LTR, wide lines should overflow out of the right + // side of the block. + if (style()->direction() == LTR) { + if (trailingSpaceRun) { + totWidth -= trailingSpaceRun->m_box->width(); + trailingSpaceRun->m_box->setWidth(0); + } + if (totWidth < availableWidth) + x += availableWidth - totWidth; + } else { + if (totWidth > availableWidth && trailingSpaceRun) { + trailingSpaceRun->m_box->setWidth(trailingSpaceRun->m_box->width() - totWidth + availableWidth); + totWidth -= trailingSpaceRun->m_box->width(); + } else + x += availableWidth - totWidth; + } + break; + case CENTER: + case WEBKIT_CENTER: + int trailingSpaceWidth = 0; + if (trailingSpaceRun) { + totWidth -= trailingSpaceRun->m_box->width(); + trailingSpaceWidth = min(trailingSpaceRun->m_box->width(), (availableWidth - totWidth + 1) / 2); + trailingSpaceRun->m_box->setWidth(trailingSpaceWidth); + } + if (style()->direction() == LTR) + x += max((availableWidth - totWidth) / 2, 0); + else + x += totWidth > availableWidth ? (availableWidth - totWidth) : (availableWidth - totWidth) / 2 - trailingSpaceWidth; + break; + } + + if (numSpaces) { + for (BidiRun* r = firstRun; r; r = r->next()) { + if (!r->m_box || r == trailingSpaceRun) + continue; + + int spaceAdd = 0; + if (r->m_object->isText() && !r->m_compact) { + unsigned spaces = 0; + const UChar* characters = static_cast<RenderText*>(r->m_object)->characters(); + for (int i = r->m_start; i < r->m_stop; i++) { + UChar c = characters[i]; + if (c == ' ' || c == '\n' || c == '\t') + spaces++; + } + + ASSERT(spaces <= numSpaces); + + // Only justify text if whitespace is collapsed. + if (r->m_object->style()->collapseWhiteSpace()) { + spaceAdd = (availableWidth - totWidth) * spaces / numSpaces; + static_cast<InlineTextBox*>(r->m_box)->setSpaceAdd(spaceAdd); + totWidth += spaceAdd; + } + numSpaces -= spaces; + if (!numSpaces) + break; + } + } + } + + // The widths of all runs are now known. We can now place every inline box (and + // compute accurate widths for the inline flow boxes). + int leftPosition = x; + int rightPosition = x; + needsWordSpacing = false; + lineBox->placeBoxesHorizontally(x, leftPosition, rightPosition, needsWordSpacing); + lineBox->setHorizontalOverflowPositions(leftPosition, rightPosition); +} + +void RenderBlock::computeVerticalPositionsForLine(RootInlineBox* lineBox, BidiRun* firstRun) +{ + lineBox->verticallyAlignBoxes(m_height); + lineBox->setBlockHeight(m_height); + + // See if the line spilled out. If so set overflow height accordingly. + int bottomOfLine = lineBox->bottomOverflow(); + if (bottomOfLine > m_height && bottomOfLine > m_overflowHeight) + m_overflowHeight = bottomOfLine; + + // Now make sure we place replaced render objects correctly. + for (BidiRun* r = firstRun; r; r = r->next()) { + if (!r->m_box) + continue; // Skip runs with no line boxes. + + // Align positioned boxes with the top of the line box. This is + // a reasonable approximation of an appropriate y position. + if (r->m_object->isPositioned()) + r->m_box->setYPos(m_height); + + // Position is used to properly position both replaced elements and + // to update the static normal flow x/y of positioned elements. + r->m_object->position(r->m_box); + } + // Positioned objects and zero-length text nodes destroy their boxes in + // position(), which unnecessarily dirties the line. + lineBox->markDirty(false); +} + +// collects one line of the paragraph and transforms it to visual order +void RenderBlock::bidiReorderLine(InlineBidiResolver& resolver, const InlineIterator& end) +{ + resolver.createBidiRunsForLine(end, style()->visuallyOrdered(), previousLineBrokeCleanly); +} + +static void buildCompactRuns(RenderObject* compactObj, InlineBidiResolver& resolver) +{ + ASSERT(compactObj->isRenderBlock()); + ASSERT(!resolver.firstRun()); + + // Format the compact like it is its own single line. We build up all the runs for + // the little compact and then reorder them for bidi. + RenderBlock* compactBlock = static_cast<RenderBlock*>(compactObj); + + InlineIterator start(compactBlock, bidiFirst(compactBlock, &resolver), 0); + resolver.setPosition(start); + + betweenMidpoints = false; + isLineEmpty = true; + previousLineBrokeCleanly = true; + + InlineIterator end = compactBlock->findNextLineBreak(resolver); + if (!isLineEmpty) + compactBlock->bidiReorderLine(resolver, end); + + for (BidiRun* run = resolver.firstRun(); run; run = run->next()) + run->m_compact = true; + + sNumMidpoints = 0; + sCurrMidpoint = 0; + betweenMidpoints = false; +} + +static inline bool isCollapsibleSpace(UChar character, RenderText* renderer) +{ + if (character == ' ' || character == '\t' || character == softHyphen) + return true; + if (character == '\n') + return !renderer->style()->preserveNewline(); + if (character == noBreakSpace) + return renderer->style()->nbspMode() == SPACE; + return false; +} + +void RenderBlock::layoutInlineChildren(bool relayoutChildren, int& repaintTop, int& repaintBottom) +{ + bool useRepaintBounds = false; + + invalidateVerticalPosition(); + + m_overflowHeight = 0; + + m_height = borderTop() + paddingTop(); + int toAdd = borderBottom() + paddingBottom() + horizontalScrollbarHeight(); + + // Figure out if we should clear out our line boxes. + // FIXME: Handle resize eventually! + // FIXME: Do something better when floats are present. + bool fullLayout = !firstLineBox() || !firstChild() || selfNeedsLayout() || relayoutChildren; + if (fullLayout) + deleteLineBoxes(); + + // Text truncation only kicks in if your overflow isn't visible and your text-overflow-mode isn't + // clip. + // FIXME: CSS3 says that descendants that are clipped must also know how to truncate. This is insanely + // difficult to figure out (especially in the middle of doing layout), and is really an esoteric pile of nonsense + // anyway, so we won't worry about following the draft here. + bool hasTextOverflow = style()->textOverflow() && hasOverflowClip(); + + // Walk all the lines and delete our ellipsis line boxes if they exist. + if (hasTextOverflow) + deleteEllipsisLineBoxes(); + + if (firstChild()) { + // layout replaced elements + bool endOfInline = false; + RenderObject* o = bidiFirst(this, 0, false); + Vector<FloatWithRect> floats; + int containerWidth = max(0, containingBlockWidth()); + while (o) { + o->invalidateVerticalPosition(); + if (o->isReplaced() || o->isFloating() || o->isPositioned()) { + if (relayoutChildren || o->style()->width().isPercent() || o->style()->height().isPercent()) + o->setChildNeedsLayout(true, false); + + // If relayoutChildren is set and we have percentage padding, we also need to invalidate the child's pref widths. + if (relayoutChildren && (o->style()->paddingLeft().isPercent() || o->style()->paddingRight().isPercent())) + o->setPrefWidthsDirty(true, false); + + if (o->isPositioned()) + o->containingBlock()->insertPositionedObject(o); + else { + if (o->isFloating()) + floats.append(FloatWithRect(o)); + else if (fullLayout || o->needsLayout()) // Replaced elements + o->dirtyLineBoxes(fullLayout); + + o->layoutIfNeeded(); + } + } else if (o->isText() || (o->isInlineFlow() && !endOfInline)) { + if (fullLayout || o->selfNeedsLayout()) + o->dirtyLineBoxes(fullLayout); + + // Calculate margins of inline flows so that they can be used later by line layout. + if (o->isInlineFlow()) + static_cast<RenderFlow*>(o)->calcMargins(containerWidth); + o->setNeedsLayout(false); + } + o = bidiNext(this, o, 0, false, &endOfInline); + } + + // We want to skip ahead to the first dirty line + InlineBidiResolver resolver; + unsigned floatIndex; + RootInlineBox* startLine = determineStartPosition(fullLayout, resolver, floats, floatIndex); + + if (fullLayout && !selfNeedsLayout()) { + setNeedsLayout(true, false); // Mark ourselves as needing a full layout. This way we'll repaint like + // we're supposed to. + if (!document()->view()->needsFullRepaint() && m_layer) { + // Because we waited until we were already inside layout to discover + // that the block really needed a full layout, we missed our chance to repaint the layer + // before layout started. Luckily the layer has cached the repaint rect for its original + // position and size, and so we can use that to make a repaint happen now. + RenderView* c = view(); + if (c && !c->printing()) + c->repaintViewRectangle(m_layer->repaintRect()); + } + } + + FloatingObject* lastFloat = m_floatingObjects ? m_floatingObjects->last() : 0; + + if (!smidpoints) + smidpoints = new Vector<InlineIterator>(); + + sNumMidpoints = 0; + sCurrMidpoint = 0; + + // We also find the first clean line and extract these lines. We will add them back + // if we determine that we're able to synchronize after handling all our dirty lines. + InlineIterator cleanLineStart; + BidiStatus cleanLineBidiStatus; + int endLineYPos = 0; + RootInlineBox* endLine = (fullLayout || !startLine) ? + 0 : determineEndPosition(startLine, cleanLineStart, cleanLineBidiStatus, endLineYPos); + + if (startLine) { + useRepaintBounds = true; + repaintTop = m_height; + repaintBottom = m_height; + RenderArena* arena = renderArena(); + RootInlineBox* box = startLine; + while (box) { + repaintTop = min(repaintTop, box->topOverflow()); + repaintBottom = max(repaintBottom, box->bottomOverflow()); + RootInlineBox* next = box->nextRootBox(); + box->deleteLine(arena); + box = next; + } + } + + InlineIterator end = resolver.position(); + + if (!fullLayout && lastRootBox() && lastRootBox()->endsWithBreak()) { + // If the last line before the start line ends with a line break that clear floats, + // adjust the height accordingly. + // A line break can be either the first or the last object on a line, depending on its direction. + if (InlineBox* lastLeafChild = lastRootBox()->lastLeafChild()) { + RenderObject* lastObject = lastLeafChild->object(); + if (!lastObject->isBR()) + lastObject = lastRootBox()->firstLeafChild()->object(); + if (lastObject->isBR()) { + EClear clear = lastObject->style()->clear(); + if (clear != CNONE) + newLine(clear); + } + } + } + + bool endLineMatched = false; + bool checkForEndLineMatch = endLine; + bool checkForFloatsFromLastLine = false; + int lastHeight = m_height; + + while (!end.atEnd()) { + // FIXME: Is this check necessary before the first iteration or can it be moved to the end? + if (checkForEndLineMatch && (endLineMatched = matchedEndLine(resolver, cleanLineStart, cleanLineBidiStatus, endLine, endLineYPos, repaintBottom, repaintTop))) + break; + + betweenMidpoints = false; + isLineEmpty = true; + if (m_firstLine && firstChild()->isCompact() && firstChild()->isRenderBlock()) { + buildCompactRuns(firstChild(), resolver); + resolver.setPosition(InlineIterator(this, firstChild()->nextSibling(), 0)); + } + EClear clear = CNONE; + end = findNextLineBreak(resolver, &clear); + if (resolver.position().atEnd()) { + resolver.deleteRuns(); + checkForFloatsFromLastLine = true; + break; + } + ASSERT(end != resolver.position()); + + if (!isLineEmpty) { + bidiReorderLine(resolver, end); + ASSERT(resolver.position() == end); + + BidiRun* trailingSpaceRun = 0; + if (!previousLineBrokeCleanly && resolver.runCount() && resolver.logicallyLastRun()->m_object->style()->breakOnlyAfterWhiteSpace()) { + trailingSpaceRun = resolver.logicallyLastRun(); + RenderObject* lastObject = trailingSpaceRun->m_object; + if (lastObject->isText()) { + RenderText* lastText = static_cast<RenderText*>(lastObject); + const UChar* characters = lastText->characters(); + int firstSpace = trailingSpaceRun->stop(); + while (firstSpace > trailingSpaceRun->start()) { + UChar current = characters[firstSpace - 1]; + if (!isCollapsibleSpace(current, lastText)) + break; + firstSpace--; + } + if (firstSpace == trailingSpaceRun->stop()) + trailingSpaceRun = 0; + else { + TextDirection direction = style()->direction(); + bool shouldReorder = trailingSpaceRun != (direction == LTR ? resolver.lastRun() : resolver.firstRun()); + if (firstSpace != trailingSpaceRun->start()) { + ETextAlign textAlign = style()->textAlign(); + // If the trailing white space is at the right hand side of a left-aligned line, then computeHorizontalPositionsForLine() + // does not care if trailingSpaceRun includes non-spaces at the beginning. In all other cases, trailingSpaceRun has to + // contain only the spaces, either because we re-order them or because computeHorizontalPositionsForLine() needs to know + // their width. + bool shouldSeparateSpaces = textAlign != LEFT && textAlign != WEBKIT_LEFT && textAlign != TAAUTO || trailingSpaceRun->m_level % 2 || direction == RTL || shouldReorder; + if (shouldSeparateSpaces) { + BidiContext* baseContext = resolver.context(); + while (BidiContext* parent = baseContext->parent()) + baseContext = parent; + + BidiRun* newTrailingRun = new (renderArena()) BidiRun(firstSpace, trailingSpaceRun->m_stop, trailingSpaceRun->m_object, baseContext, OtherNeutral); + trailingSpaceRun->m_stop = firstSpace; + if (direction == LTR) + resolver.addRun(newTrailingRun); + else + resolver.prependRun(newTrailingRun); + trailingSpaceRun = newTrailingRun; + shouldReorder = false; + } + } + if (shouldReorder) { + if (direction == LTR) { + resolver.moveRunToEnd(trailingSpaceRun); + trailingSpaceRun->m_level = 0; + } else { + resolver.moveRunToBeginning(trailingSpaceRun); + trailingSpaceRun->m_level = 1; + } + } + } + } else + trailingSpaceRun = 0; + } + + // Now that the runs have been ordered, we create the line boxes. + // At the same time we figure out where border/padding/margin should be applied for + // inline flow boxes. + + RootInlineBox* lineBox = 0; + if (resolver.runCount()) { + lineBox = constructLine(resolver.runCount(), resolver.firstRun(), resolver.lastRun(), !end.obj, end.obj && !end.pos ? end.obj : 0); + if (lineBox) { + lineBox->setEndsWithBreak(previousLineBrokeCleanly); + + // Now we position all of our text runs horizontally. + computeHorizontalPositionsForLine(lineBox, resolver.firstRun(), trailingSpaceRun, end.atEnd()); + + // Now position our text runs vertically. + computeVerticalPositionsForLine(lineBox, resolver.firstRun()); + +#if ENABLE(SVG) + // Special SVG text layout code + lineBox->computePerCharacterLayoutInformation(); +#endif + +#if PLATFORM(MAC) + // Highlight acts as an overflow inflation. + if (style()->highlight() != nullAtom) + lineBox->addHighlightOverflow(); +#endif + } + } + + resolver.deleteRuns(); + + if (lineBox) { + lineBox->setLineBreakInfo(end.obj, end.pos, resolver.status()); + if (useRepaintBounds) { + repaintTop = min(repaintTop, lineBox->topOverflow()); + repaintBottom = max(repaintBottom, lineBox->bottomOverflow()); + } + } + + m_firstLine = false; + newLine(clear); + } + + if (m_floatingObjects && lastRootBox()) { + if (lastFloat) { + for (FloatingObject* f = m_floatingObjects->last(); f != lastFloat; f = m_floatingObjects->prev()) { + } + m_floatingObjects->next(); + } else + m_floatingObjects->first(); + for (FloatingObject* f = m_floatingObjects->current(); f; f = m_floatingObjects->next()) { + if (f->m_bottom > lastHeight) + lastRootBox()->floats().append(f->m_renderer); + ASSERT(f->m_renderer == floats[floatIndex].object); + // If a float's geometry has changed, give up on syncing with clean lines. + if (floats[floatIndex].rect != IntRect(f->m_left, f->m_top, f->m_width, f->m_bottom - f->m_top)) + checkForEndLineMatch = false; + floatIndex++; + } + lastFloat = m_floatingObjects->last(); + } + + lastHeight = m_height; + sNumMidpoints = 0; + sCurrMidpoint = 0; + resolver.setPosition(end); + } + + if (endLine) { + if (endLineMatched) { + // Attach all the remaining lines, and then adjust their y-positions as needed. + int delta = m_height - endLineYPos; + for (RootInlineBox* line = endLine; line; line = line->nextRootBox()) { + line->attachLine(); + if (delta) { + repaintTop = min(repaintTop, line->topOverflow() + min(delta, 0)); + repaintBottom = max(repaintBottom, line->bottomOverflow() + max(delta, 0)); + line->adjustPosition(0, delta); + } + if (Vector<RenderObject*>* cleanLineFloats = line->floatsPtr()) { + Vector<RenderObject*>::iterator end = cleanLineFloats->end(); + for (Vector<RenderObject*>::iterator f = cleanLineFloats->begin(); f != end; ++f) { + int floatTop = (*f)->yPos() - (*f)->marginTop(); + insertFloatingObject(*f); + m_height = floatTop + delta; + positionNewFloats(); + } + } + } + m_height = lastRootBox()->blockHeight(); + } else { + // Delete all the remaining lines. + InlineRunBox* line = endLine; + RenderArena* arena = renderArena(); + while (line) { + repaintTop = min(repaintTop, line->topOverflow()); + repaintBottom = max(repaintBottom, line->bottomOverflow()); + InlineRunBox* next = line->nextLineBox(); + line->deleteLine(arena); + line = next; + } + } + } + if (m_floatingObjects && (checkForFloatsFromLastLine || positionNewFloats()) && lastRootBox()) { + // In case we have a float on the last line, it might not be positioned up to now. + // This has to be done before adding in the bottom border/padding, or the float will + // include the padding incorrectly. -dwh + if (lastFloat) { + for (FloatingObject* f = m_floatingObjects->last(); f != lastFloat; f = m_floatingObjects->prev()) { + } + m_floatingObjects->next(); + } else + m_floatingObjects->first(); + for (FloatingObject* f = m_floatingObjects->current(); f; f = m_floatingObjects->next()) { + if (f->m_bottom > lastHeight) + lastRootBox()->floats().append(f->m_renderer); + } + lastFloat = m_floatingObjects->last(); + } + } + + sNumMidpoints = 0; + sCurrMidpoint = 0; + + // Now add in the bottom border/padding. + m_height += toAdd; + + // Always make sure this is at least our height. + m_overflowHeight = max(m_height, m_overflowHeight); + + // See if any lines spill out of the block. If so, we need to update our overflow width. + checkLinesForOverflow(); + + if (!firstLineBox() && hasLineIfEmpty()) + m_height += lineHeight(true, true); + + // See if we have any lines that spill out of our block. If we do, then we will possibly need to + // truncate text. + if (hasTextOverflow) + checkLinesForTextOverflow(); +} + +RootInlineBox* RenderBlock::determineStartPosition(bool& fullLayout, InlineBidiResolver& resolver, Vector<FloatWithRect>& floats, unsigned& numCleanFloats) +{ + RootInlineBox* curr = 0; + RootInlineBox* last = 0; + + bool dirtiedByFloat = false; + if (!fullLayout) { + size_t floatIndex = 0; + for (curr = firstRootBox(); curr && !curr->isDirty(); curr = curr->nextRootBox()) { + if (Vector<RenderObject*>* cleanLineFloats = curr->floatsPtr()) { + Vector<RenderObject*>::iterator end = cleanLineFloats->end(); + for (Vector<RenderObject*>::iterator o = cleanLineFloats->begin(); o != end; ++o) { + RenderObject* f = *o; + IntSize newSize(f->width() + f->marginLeft() +f->marginRight(), f->height() + f->marginTop() + f->marginBottom()); + ASSERT(floatIndex < floats.size()); + if (floats[floatIndex].object != f) { + // A new float has been inserted before this line or before its last known float. + // Just do a full layout. + fullLayout = true; + break; + } + if (floats[floatIndex].rect.size() != newSize) { + int floatTop = floats[floatIndex].rect.y(); + curr->markDirty(); + markLinesDirtyInVerticalRange(curr->blockHeight(), floatTop + max(floats[floatIndex].rect.height(), newSize.height())); + floats[floatIndex].rect.setSize(newSize); + dirtiedByFloat = true; + } + floatIndex++; + } + } + if (dirtiedByFloat || fullLayout) + break; + } + // Check if a new float has been inserted after the last known float. + if (!curr && floatIndex < floats.size()) + fullLayout = true; + } + + if (fullLayout) { + // Nuke all our lines. + if (firstRootBox()) { + RenderArena* arena = renderArena(); + curr = firstRootBox(); + while (curr) { + RootInlineBox* next = curr->nextRootBox(); + curr->deleteLine(arena); + curr = next; + } + ASSERT(!firstLineBox() && !lastLineBox()); + } + } else { + if (curr) { + // We have a dirty line. + if (RootInlineBox* prevRootBox = curr->prevRootBox()) { + // We have a previous line. + if (!dirtiedByFloat && (!prevRootBox->endsWithBreak() || prevRootBox->lineBreakObj()->isText() && prevRootBox->lineBreakPos() >= static_cast<RenderText*>(prevRootBox->lineBreakObj())->textLength())) + // The previous line didn't break cleanly or broke at a newline + // that has been deleted, so treat it as dirty too. + curr = prevRootBox; + } + } else { + // No dirty lines were found. + // If the last line didn't break cleanly, treat it as dirty. + if (lastRootBox() && !lastRootBox()->endsWithBreak()) + curr = lastRootBox(); + } + + // If we have no dirty lines, then last is just the last root box. + last = curr ? curr->prevRootBox() : lastRootBox(); + } + + numCleanFloats = 0; + if (!floats.isEmpty()) { + int savedHeight = m_height; + // Restore floats from clean lines. + RootInlineBox* line = firstRootBox(); + while (line != curr) { + if (Vector<RenderObject*>* cleanLineFloats = line->floatsPtr()) { + Vector<RenderObject*>::iterator end = cleanLineFloats->end(); + for (Vector<RenderObject*>::iterator f = cleanLineFloats->begin(); f != end; ++f) { + insertFloatingObject(*f); + m_height = (*f)->yPos() - (*f)->marginTop(); + positionNewFloats(); + ASSERT(floats[numCleanFloats].object == *f); + numCleanFloats++; + } + } + line = line->nextRootBox(); + } + m_height = savedHeight; + } + + m_firstLine = !last; + previousLineBrokeCleanly = !last || last->endsWithBreak(); + + RenderObject* startObj; + int pos = 0; + if (last) { + m_height = last->blockHeight(); + startObj = last->lineBreakObj(); + pos = last->lineBreakPos(); + resolver.setStatus(last->lineBreakBidiStatus()); + } else { + bool ltr = style()->direction() == LTR + #if ENABLE(SVG) + || (style()->unicodeBidi() == UBNormal && isSVGText()) + #endif + ; + + BidiContext* context = new BidiContext(ltr ? 0 : 1, ltr ? LeftToRight : RightToLeft, style()->unicodeBidi() == Override); + + resolver.setLastStrongDir(context->dir()); + resolver.setLastDir(context->dir()); + resolver.setEorDir(context->dir()); + resolver.setContext(context); + startObj = bidiFirst(this, &resolver); + } + + resolver.setPosition(InlineIterator(this, startObj, pos)); + + return curr; +} + +RootInlineBox* RenderBlock::determineEndPosition(RootInlineBox* startLine, InlineIterator& cleanLineStart, BidiStatus& cleanLineBidiStatus, int& yPos) +{ + RootInlineBox* last = 0; + if (!startLine) + last = 0; + else { + for (RootInlineBox* curr = startLine->nextRootBox(); curr; curr = curr->nextRootBox()) { + if (curr->isDirty()) + last = 0; + else if (!last) + last = curr; + } + } + + if (!last) + return 0; + + RootInlineBox* prev = last->prevRootBox(); + cleanLineStart = InlineIterator(this, prev->lineBreakObj(), prev->lineBreakPos()); + cleanLineBidiStatus = prev->lineBreakBidiStatus(); + yPos = prev->blockHeight(); + + for (RootInlineBox* line = last; line; line = line->nextRootBox()) + line->extractLine(); // Disconnect all line boxes from their render objects while preserving + // their connections to one another. + + return last; +} + +bool RenderBlock::matchedEndLine(const InlineBidiResolver& resolver, const InlineIterator& endLineStart, const BidiStatus& endLineStatus, RootInlineBox*& endLine, int& endYPos, int& repaintBottom, int& repaintTop) +{ + if (resolver.position() == endLineStart) { + if (resolver.status() != endLineStatus) + return false; + + int delta = m_height - endYPos; + if (!delta || !m_floatingObjects) + return true; + + // See if any floats end in the range along which we want to shift the lines vertically. + int top = min(m_height, endYPos); + + RootInlineBox* lastLine = endLine; + while (RootInlineBox* nextLine = lastLine->nextRootBox()) + lastLine = nextLine; + + int bottom = lastLine->blockHeight() + abs(delta); + + for (FloatingObject* f = m_floatingObjects->first(); f; f = m_floatingObjects->next()) { + if (f->m_bottom >= top && f->m_bottom < bottom) + return false; + } + + return true; + } + + // The first clean line doesn't match, but we can check a handful of following lines to try + // to match back up. + static int numLines = 8; // The # of lines we're willing to match against. + RootInlineBox* line = endLine; + for (int i = 0; i < numLines && line; i++, line = line->nextRootBox()) { + if (line->lineBreakObj() == resolver.position().obj && line->lineBreakPos() == resolver.position().pos) { + // We have a match. + if (line->lineBreakBidiStatus() != resolver.status()) + return false; // ...but the bidi state doesn't match. + RootInlineBox* result = line->nextRootBox(); + + // Set our yPos to be the block height of endLine. + if (result) + endYPos = line->blockHeight(); + + int delta = m_height - endYPos; + if (delta && m_floatingObjects) { + // See if any floats end in the range along which we want to shift the lines vertically. + int top = min(m_height, endYPos); + + RootInlineBox* lastLine = endLine; + while (RootInlineBox* nextLine = lastLine->nextRootBox()) + lastLine = nextLine; + + int bottom = lastLine->blockHeight() + abs(delta); + + for (FloatingObject* f = m_floatingObjects->first(); f; f = m_floatingObjects->next()) { + if (f->m_bottom >= top && f->m_bottom < bottom) + return false; + } + } + + // Now delete the lines that we failed to sync. + RootInlineBox* boxToDelete = endLine; + RenderArena* arena = renderArena(); + while (boxToDelete && boxToDelete != result) { + repaintTop = min(repaintTop, boxToDelete->topOverflow()); + repaintBottom = max(repaintBottom, boxToDelete->bottomOverflow()); + RootInlineBox* next = boxToDelete->nextRootBox(); + boxToDelete->deleteLine(arena); + boxToDelete = next; + } + + endLine = result; + return result; + } + } + + return false; +} + +static inline bool skipNonBreakingSpace(const InlineIterator& it) +{ + if (it.obj->style()->nbspMode() != SPACE || it.current() != noBreakSpace) + return false; + + // FIXME: This is bad. It makes nbsp inconsistent with space and won't work correctly + // with m_minWidth/m_maxWidth. + // Do not skip a non-breaking space if it is the first character + // on a line after a clean line break (or on the first line, since previousLineBrokeCleanly starts off + // |true|). + if (isLineEmpty && previousLineBrokeCleanly) + return false; + + return true; +} + +static inline bool shouldCollapseWhiteSpace(const RenderStyle* style) +{ + return style->collapseWhiteSpace() || (style->whiteSpace() == PRE_WRAP && (!isLineEmpty || !previousLineBrokeCleanly)); +} + +static inline bool shouldPreserveNewline(RenderObject* object) +{ +#if ENABLE(SVG) + if (object->isSVGText()) + return false; +#endif + + return object->style()->preserveNewline(); +} + +static bool inlineFlowRequiresLineBox(RenderObject* flow) +{ + // FIXME: Right now, we only allow line boxes for inlines that are truly empty. + // We need to fix this, though, because at the very least, inlines containing only + // ignorable whitespace should should also have line boxes. + return flow->isInlineFlow() && !flow->firstChild() && flow->hasHorizontalBordersPaddingOrMargin(); +} + +static inline bool requiresLineBox(const InlineIterator& it) +{ + if (it.obj->isFloatingOrPositioned()) + return false; + + if (it.obj->isInlineFlow() && !inlineFlowRequiresLineBox(it.obj)) + return false; + + if (!shouldCollapseWhiteSpace(it.obj->style()) || it.obj->isBR()) + return true; + + UChar current = it.current(); + return current != ' ' && current != '\t' && current != softHyphen && (current != '\n' || shouldPreserveNewline(it.obj)) && !skipNonBreakingSpace(it); +} + +bool RenderBlock::generatesLineBoxesForInlineChild(RenderObject* inlineObj) +{ + ASSERT(inlineObj->parent() == this); + + InlineIterator it(this, inlineObj, 0); + while (!it.atEnd() && !requiresLineBox(it)) + it.increment(); + + return !it.atEnd(); +} + +// FIXME: The entire concept of the skipTrailingWhitespace function is flawed, since we really need to be building +// line boxes even for containers that may ultimately collapse away. Otherwise we'll never get positioned +// elements quite right. In other words, we need to build this function's work into the normal line +// object iteration process. +// NB. this function will insert any floating elements that would otherwise +// be skipped but it will not position them. +void RenderBlock::skipTrailingWhitespace(InlineIterator& iterator) +{ + while (!iterator.atEnd() && !requiresLineBox(iterator)) { + RenderObject* object = iterator.obj; + if (object->isFloating()) { + insertFloatingObject(object); + } else if (object->isPositioned()) { + // FIXME: The math here is actually not really right. It's a best-guess approximation that + // will work for the common cases + RenderObject* c = object->container(); + if (c->isInlineFlow()) { + // A relative positioned inline encloses us. In this case, we also have to determine our + // position as though we were an inline. Set |staticX| and |staticY| on the relative positioned + // inline so that we can obtain the value later. + c->setStaticX(style()->direction() == LTR ? leftOffset(m_height) : rightOffset(m_height)); + c->setStaticY(m_height); + } + + if (object->hasStaticX()) { + if (object->style()->isOriginalDisplayInlineType()) + object->setStaticX(style()->direction() == LTR ? leftOffset(m_height) : width() - rightOffset(m_height)); + else + object->setStaticX(style()->direction() == LTR ? borderLeft() + paddingLeft() : borderRight() + paddingRight()); + } + + if (object->hasStaticY()) + object->setStaticY(m_height); + } + iterator.increment(); + } +} + +int RenderBlock::skipLeadingWhitespace(InlineBidiResolver& resolver) +{ + int availableWidth = lineWidth(m_height); + while (!resolver.position().atEnd() && !requiresLineBox(resolver.position())) { + RenderObject* object = resolver.position().obj; + if (object->isFloating()) { + insertFloatingObject(object); + positionNewFloats(); + availableWidth = lineWidth(m_height); + } else if (object->isPositioned()) { + // FIXME: The math here is actually not really right. It's a best-guess approximation that + // will work for the common cases + RenderObject* c = object->container(); + if (c->isInlineFlow()) { + // A relative positioned inline encloses us. In this case, we also have to determine our + // position as though we were an inline. Set |staticX| and |staticY| on the relative positioned + // inline so that we can obtain the value later. + c->setStaticX(style()->direction() == LTR ? leftOffset(m_height) : rightOffset(m_height)); + c->setStaticY(m_height); + } + + if (object->hasStaticX()) { + if (object->style()->isOriginalDisplayInlineType()) + object->setStaticX(style()->direction() == LTR ? leftOffset(m_height) : width() - rightOffset(m_height)); + else + object->setStaticX(style()->direction() == LTR ? borderLeft() + paddingLeft() : borderRight() + paddingRight()); + } + + if (object->hasStaticY()) + object->setStaticY(m_height); + } + resolver.increment(); + } + resolver.commitExplicitEmbedding(); + return availableWidth; +} + +// This is currently just used for list markers and inline flows that have line boxes. Neither should +// have an effect on whitespace at the start of the line. +static bool shouldSkipWhitespaceAfterStartObject(RenderBlock* block, RenderObject* o) +{ + RenderObject* next = bidiNext(block, o); + if (next && !next->isBR() && next->isText() && static_cast<RenderText*>(next)->textLength() > 0) { + RenderText* nextText = static_cast<RenderText*>(next); + UChar nextChar = nextText->characters()[0]; + if (nextText->style()->isCollapsibleWhiteSpace(nextChar)) { + addMidpoint(InlineIterator(0, o, 0)); + return true; + } + } + + return false; +} + +void RenderBlock::fitBelowFloats(int widthToFit, int& availableWidth) +{ + ASSERT(widthToFit > availableWidth); + + int floatBottom; + int lastFloatBottom = m_height; + int newLineWidth = availableWidth; + while (true) { + floatBottom = nextFloatBottomBelow(lastFloatBottom); + if (!floatBottom) + break; + + newLineWidth = lineWidth(floatBottom); + lastFloatBottom = floatBottom; + if (newLineWidth >= widthToFit) + break; + } + + if (newLineWidth > availableWidth) { + m_height = lastFloatBottom; + availableWidth = newLineWidth; + } +} + +InlineIterator RenderBlock::findNextLineBreak(InlineBidiResolver& resolver, EClear* clear) +{ + ASSERT(resolver.position().block == this); + + bool appliedStartWidth = resolver.position().pos > 0; + + int width = skipLeadingWhitespace(resolver); + + int w = 0; + int tmpW = 0; + + if (resolver.position().atEnd()) + return resolver.position(); + + // This variable is used only if whitespace isn't set to PRE, and it tells us whether + // or not we are currently ignoring whitespace. + bool ignoringSpaces = false; + InlineIterator ignoreStart; + + // This variable tracks whether the very last character we saw was a space. We use + // this to detect when we encounter a second space so we know we have to terminate + // a run. + bool currentCharacterIsSpace = false; + bool currentCharacterIsWS = false; + RenderObject* trailingSpaceObject = 0; + + InlineIterator lBreak = resolver.position(); + + RenderObject *o = resolver.position().obj; + RenderObject *last = o; + unsigned pos = resolver.position().pos; + int nextBreakable = resolver.position().nextBreakablePosition; + bool atStart = true; + + bool prevLineBrokeCleanly = previousLineBrokeCleanly; + previousLineBrokeCleanly = false; + + bool autoWrapWasEverTrueOnLine = false; + bool floatsFitOnLine = true; + + // Firefox and Opera will allow a table cell to grow to fit an image inside it under + // very specific circumstances (in order to match common WinIE renderings). + // Not supporting the quirk has caused us to mis-render some real sites. (See Bugzilla 10517.) + bool allowImagesToBreak = !style()->htmlHacks() || !isTableCell() || !style()->width().isIntrinsicOrAuto(); + + EWhiteSpace currWS = style()->whiteSpace(); + EWhiteSpace lastWS = currWS; + while (o) { + currWS = o->isReplaced() ? o->parent()->style()->whiteSpace() : o->style()->whiteSpace(); + lastWS = last->isReplaced() ? last->parent()->style()->whiteSpace() : last->style()->whiteSpace(); + + bool autoWrap = RenderStyle::autoWrap(currWS); + autoWrapWasEverTrueOnLine = autoWrapWasEverTrueOnLine || autoWrap; + +#if ENABLE(SVG) + bool preserveNewline = o->isSVGText() ? false : RenderStyle::preserveNewline(currWS); +#else + bool preserveNewline = RenderStyle::preserveNewline(currWS); +#endif + + bool collapseWhiteSpace = RenderStyle::collapseWhiteSpace(currWS); + + if (o->isBR()) { + if (w + tmpW <= width) { + lBreak.obj = o; + lBreak.pos = 0; + lBreak.nextBreakablePosition = -1; + lBreak.increment(); + + // A <br> always breaks a line, so don't let the line be collapsed + // away. Also, the space at the end of a line with a <br> does not + // get collapsed away. It only does this if the previous line broke + // cleanly. Otherwise the <br> has no effect on whether the line is + // empty or not. + if (prevLineBrokeCleanly) + isLineEmpty = false; + trailingSpaceObject = 0; + previousLineBrokeCleanly = true; + + if (!isLineEmpty && clear) + *clear = o->style()->clear(); + } + goto end; + } + + if (o->isFloatingOrPositioned()) { + // add to special objects... + if (o->isFloating()) { + insertFloatingObject(o); + // check if it fits in the current line. + // If it does, position it now, otherwise, position + // it after moving to next line (in newLine() func) + if (floatsFitOnLine && o->width() + o->marginLeft() + o->marginRight() + w + tmpW <= width) { + positionNewFloats(); + width = lineWidth(m_height); + } else + floatsFitOnLine = false; + } else if (o->isPositioned()) { + // If our original display wasn't an inline type, then we can + // go ahead and determine our static x position now. + bool isInlineType = o->style()->isOriginalDisplayInlineType(); + bool needToSetStaticX = o->hasStaticX(); + if (o->hasStaticX() && !isInlineType) { + o->setStaticX(o->parent()->style()->direction() == LTR ? + borderLeft() + paddingLeft() : + borderRight() + paddingRight()); + needToSetStaticX = false; + } + + // If our original display was an INLINE type, then we can go ahead + // and determine our static y position now. + bool needToSetStaticY = o->hasStaticY(); + if (o->hasStaticY() && isInlineType) { + o->setStaticY(m_height); + needToSetStaticY = false; + } + + bool needToCreateLineBox = needToSetStaticX || needToSetStaticY; + RenderObject* c = o->container(); + if (c->isInlineFlow() && (!needToSetStaticX || !needToSetStaticY)) + needToCreateLineBox = true; + + // If we're ignoring spaces, we have to stop and include this object and + // then start ignoring spaces again. + if (needToCreateLineBox) { + trailingSpaceObject = 0; + ignoreStart.obj = o; + ignoreStart.pos = 0; + if (ignoringSpaces) { + addMidpoint(ignoreStart); // Stop ignoring spaces. + addMidpoint(ignoreStart); // Start ignoring again. + } + + } + } + } else if (o->isInlineFlow()) { + // Right now, we should only encounter empty inlines here. + ASSERT(!o->firstChild()); + + // Now that some inline flows have line boxes, if we are already ignoring spaces, we need + // to make sure that we stop to include this object and then start ignoring spaces again. + // If this object is at the start of the line, we need to behave like list markers and + // start ignoring spaces. + if (inlineFlowRequiresLineBox(o)) { + isLineEmpty = false; + if (ignoringSpaces) { + trailingSpaceObject = 0; + addMidpoint(InlineIterator(0, o, 0)); // Stop ignoring spaces. + addMidpoint(InlineIterator(0, o, 0)); // Start ignoring again. + } else if (style()->collapseWhiteSpace() && resolver.position().obj == o + && shouldSkipWhitespaceAfterStartObject(this, o)) { + // Like with list markers, we start ignoring spaces to make sure that any + // additional spaces we see will be discarded. + currentCharacterIsSpace = true; + currentCharacterIsWS = true; + ignoringSpaces = true; + } + } + + tmpW += o->marginLeft() + o->borderLeft() + o->paddingLeft() + + o->marginRight() + o->borderRight() + o->paddingRight(); + } else if (o->isReplaced()) { + // Break on replaced elements if either has normal white-space. + if ((autoWrap || RenderStyle::autoWrap(lastWS)) && (!o->isImage() || allowImagesToBreak)) { + w += tmpW; + tmpW = 0; + lBreak.obj = o; + lBreak.pos = 0; + lBreak.nextBreakablePosition = -1; + } + + if (ignoringSpaces) + addMidpoint(InlineIterator(0, o, 0)); + + isLineEmpty = false; + ignoringSpaces = false; + currentCharacterIsSpace = false; + currentCharacterIsWS = false; + trailingSpaceObject = 0; + + // Optimize for a common case. If we can't find whitespace after the list + // item, then this is all moot. -dwh + if (o->isListMarker() && !static_cast<RenderListMarker*>(o)->isInside()) { + if (style()->collapseWhiteSpace() && shouldSkipWhitespaceAfterStartObject(this, o)) { + // Like with inline flows, we start ignoring spaces to make sure that any + // additional spaces we see will be discarded. + currentCharacterIsSpace = true; + currentCharacterIsWS = true; + ignoringSpaces = true; + } + } else + tmpW += o->width() + o->marginLeft() + o->marginRight() + inlineWidth(o); + } else if (o->isText()) { + if (!pos) + appliedStartWidth = false; + + RenderText* t = static_cast<RenderText*>(o); + + int strlen = t->textLength(); + int len = strlen - pos; + const UChar* str = t->characters(); + + const Font& f = t->style(m_firstLine)->font(); + + int lastSpace = pos; + int wordSpacing = o->style()->wordSpacing(); + int lastSpaceWordSpacing = 0; + + int wrapW = tmpW + inlineWidth(o, !appliedStartWidth, true); + int charWidth = 0; + bool breakNBSP = autoWrap && o->style()->nbspMode() == SPACE; + // Auto-wrapping text should wrap in the middle of a word only if it could not wrap before the word, + // which is only possible if the word is the first thing on the line, that is, if |w| is zero. + bool breakWords = o->style()->breakWords() && ((autoWrap && !w) || currWS == PRE); + bool midWordBreak = false; + bool breakAll = o->style()->wordBreak() == BreakAllWordBreak && autoWrap; + + if (t->isWordBreak()) { + w += tmpW; + tmpW = 0; + lBreak.obj = o; + lBreak.pos = 0; + lBreak.nextBreakablePosition = -1; + ASSERT(!len); + } + + while (len) { + bool previousCharacterIsSpace = currentCharacterIsSpace; + bool previousCharacterIsWS = currentCharacterIsWS; + UChar c = str[pos]; + currentCharacterIsSpace = c == ' ' || c == '\t' || (!preserveNewline && (c == '\n')); + + if (!collapseWhiteSpace || !currentCharacterIsSpace) + isLineEmpty = false; + + // Check for soft hyphens. Go ahead and ignore them. + if (c == softHyphen) { + if (!ignoringSpaces) { + // Ignore soft hyphens + InlineIterator beforeSoftHyphen; + if (pos) + beforeSoftHyphen = InlineIterator(0, o, pos - 1); + else + beforeSoftHyphen = InlineIterator(0, last, last->isText() ? static_cast<RenderText*>(last)->textLength() - 1 : 0); + // Two consecutive soft hyphens. Avoid overlapping midpoints. + if (sNumMidpoints && smidpoints->at(sNumMidpoints - 1).obj == o && smidpoints->at(sNumMidpoints - 1).pos == pos) + sNumMidpoints--; + else + addMidpoint(beforeSoftHyphen); + + // Add the width up to but not including the hyphen. + tmpW += t->width(lastSpace, pos - lastSpace, f, w + tmpW) + lastSpaceWordSpacing; + + // For wrapping text only, include the hyphen. We need to ensure it will fit + // on the line if it shows when we break. + if (autoWrap) + tmpW += t->width(pos, 1, f, w + tmpW); + + InlineIterator afterSoftHyphen(0, o, pos); + afterSoftHyphen.increment(); + addMidpoint(afterSoftHyphen); + } + + pos++; + len--; + lastSpaceWordSpacing = 0; + lastSpace = pos; // Cheesy hack to prevent adding in widths of the run twice. + continue; + } + + bool applyWordSpacing = false; + + currentCharacterIsWS = currentCharacterIsSpace || (breakNBSP && c == noBreakSpace); + + if ((breakAll || breakWords) && !midWordBreak) { + wrapW += charWidth; + charWidth = t->width(pos, 1, f, w + wrapW); + midWordBreak = w + wrapW + charWidth > width; + } + + bool betweenWords = c == '\n' || (currWS != PRE && !atStart && isBreakable(str, pos, strlen, nextBreakable, breakNBSP)); + + if (betweenWords || midWordBreak) { + bool stoppedIgnoringSpaces = false; + if (ignoringSpaces) { + if (!currentCharacterIsSpace) { + // Stop ignoring spaces and begin at this + // new point. + ignoringSpaces = false; + lastSpaceWordSpacing = 0; + lastSpace = pos; // e.g., "Foo goo", don't add in any of the ignored spaces. + addMidpoint(InlineIterator(0, o, pos)); + stoppedIgnoringSpaces = true; + } else { + // Just keep ignoring these spaces. + pos++; + len--; + continue; + } + } + + int additionalTmpW = t->width(lastSpace, pos - lastSpace, f, w+tmpW) + lastSpaceWordSpacing; + tmpW += additionalTmpW; + if (!appliedStartWidth) { + tmpW += inlineWidth(o, true, false); + appliedStartWidth = true; + } + + applyWordSpacing = wordSpacing && currentCharacterIsSpace && !previousCharacterIsSpace; + + if (!w && autoWrap && tmpW > width) + fitBelowFloats(tmpW, width); + + if (autoWrap || breakWords) { + // If we break only after white-space, consider the current character + // as candidate width for this line. + bool lineWasTooWide = false; + if (w + tmpW <= width && currentCharacterIsWS && o->style()->breakOnlyAfterWhiteSpace() && !midWordBreak) { + int charWidth = t->width(pos, 1, f, w + tmpW) + (applyWordSpacing ? wordSpacing : 0); + // Check if line is too big even without the extra space + // at the end of the line. If it is not, do nothing. + // If the line needs the extra whitespace to be too long, + // then move the line break to the space and skip all + // additional whitespace. + if (w + tmpW + charWidth > width) { + lineWasTooWide = true; + lBreak.obj = o; + lBreak.pos = pos; + lBreak.nextBreakablePosition = nextBreakable; + skipTrailingWhitespace(lBreak); + } + } + if (lineWasTooWide || w + tmpW > width) { + if (lBreak.obj && shouldPreserveNewline(lBreak.obj) && lBreak.obj->isText() && !static_cast<RenderText*>(lBreak.obj)->isWordBreak() && static_cast<RenderText*>(lBreak.obj)->characters()[lBreak.pos] == '\n') { + if (!stoppedIgnoringSpaces && pos > 0) { + // We need to stop right before the newline and then start up again. + addMidpoint(InlineIterator(0, o, pos - 1)); // Stop + addMidpoint(InlineIterator(0, o, pos)); // Start + } + lBreak.increment(); + previousLineBrokeCleanly = true; + } + goto end; // Didn't fit. Jump to the end. + } else { + if (!betweenWords || (midWordBreak && !autoWrap)) + tmpW -= additionalTmpW; + if (pos > 0 && str[pos-1] == softHyphen) + // Subtract the width of the soft hyphen out since we fit on a line. + tmpW -= t->width(pos-1, 1, f, w+tmpW); + } + } + + if (c == '\n' && preserveNewline) { + if (!stoppedIgnoringSpaces && pos > 0) { + // We need to stop right before the newline and then start up again. + addMidpoint(InlineIterator(0, o, pos - 1)); // Stop + addMidpoint(InlineIterator(0, o, pos)); // Start + } + lBreak.obj = o; + lBreak.pos = pos; + lBreak.nextBreakablePosition = nextBreakable; + lBreak.increment(); + previousLineBrokeCleanly = true; + return lBreak; + } + + if (autoWrap && betweenWords) { + w += tmpW; + wrapW = 0; + tmpW = 0; + lBreak.obj = o; + lBreak.pos = pos; + lBreak.nextBreakablePosition = nextBreakable; + // Auto-wrapping text should not wrap in the middle of a word once it has had an + // opportunity to break after a word. + breakWords = false; + } + + if (midWordBreak) { + // Remember this as a breakable position in case + // adding the end width forces a break. + lBreak.obj = o; + lBreak.pos = pos; + lBreak.nextBreakablePosition = nextBreakable; + midWordBreak &= (breakWords || breakAll); + } + + if (betweenWords) { + lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0; + lastSpace = pos; + } + + if (!ignoringSpaces && o->style()->collapseWhiteSpace()) { + // If we encounter a newline, or if we encounter a + // second space, we need to go ahead and break up this + // run and enter a mode where we start collapsing spaces. + if (currentCharacterIsSpace && previousCharacterIsSpace) { + ignoringSpaces = true; + + // We just entered a mode where we are ignoring + // spaces. Create a midpoint to terminate the run + // before the second space. + addMidpoint(ignoreStart); + } + } + } else if (ignoringSpaces) { + // Stop ignoring spaces and begin at this + // new point. + ignoringSpaces = false; + lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0; + lastSpace = pos; // e.g., "Foo goo", don't add in any of the ignored spaces. + addMidpoint(InlineIterator(0, o, pos)); + } + + if (currentCharacterIsSpace && !previousCharacterIsSpace) { + ignoreStart.obj = o; + ignoreStart.pos = pos; + } + + if (!currentCharacterIsWS && previousCharacterIsWS) { + if (autoWrap && o->style()->breakOnlyAfterWhiteSpace()) { + lBreak.obj = o; + lBreak.pos = pos; + lBreak.nextBreakablePosition = nextBreakable; + } + } + + if (collapseWhiteSpace && currentCharacterIsSpace && !ignoringSpaces) + trailingSpaceObject = o; + else if (!o->style()->collapseWhiteSpace() || !currentCharacterIsSpace) + trailingSpaceObject = 0; + + pos++; + len--; + atStart = false; + } + + // IMPORTANT: pos is > length here! + if (!ignoringSpaces) + tmpW += t->width(lastSpace, pos - lastSpace, f, w+tmpW) + lastSpaceWordSpacing; + tmpW += inlineWidth(o, !appliedStartWidth, true); + } else + ASSERT_NOT_REACHED(); + + RenderObject* next = bidiNext(this, o); + bool checkForBreak = autoWrap; + if (w && w + tmpW > width && lBreak.obj && currWS == NOWRAP) + checkForBreak = true; + else if (next && o->isText() && next->isText() && !next->isBR()) { + if (autoWrap || (next->style()->autoWrap())) { + if (currentCharacterIsSpace) + checkForBreak = true; + else { + checkForBreak = false; + RenderText* nextText = static_cast<RenderText*>(next); + if (nextText->textLength()) { + UChar c = nextText->characters()[0]; + if (c == ' ' || c == '\t' || (c == '\n' && !shouldPreserveNewline(next))) + // If the next item on the line is text, and if we did not end with + // a space, then the next text run continues our word (and so it needs to + // keep adding to |tmpW|. Just update and continue. + checkForBreak = true; + } else if (nextText->isWordBreak()) + checkForBreak = true; + bool willFitOnLine = w + tmpW <= width; + if (!willFitOnLine && !w) { + fitBelowFloats(tmpW, width); + willFitOnLine = tmpW <= width; + } + bool canPlaceOnLine = willFitOnLine || !autoWrapWasEverTrueOnLine; + if (canPlaceOnLine && checkForBreak) { + w += tmpW; + tmpW = 0; + lBreak.obj = next; + lBreak.pos = 0; + lBreak.nextBreakablePosition = -1; + } + } + } + } + + if (checkForBreak && (w + tmpW > width)) { + // if we have floats, try to get below them. + if (currentCharacterIsSpace && !ignoringSpaces && o->style()->collapseWhiteSpace()) + trailingSpaceObject = 0; + + if (w) + goto end; + + fitBelowFloats(tmpW, width); + + // |width| may have been adjusted because we got shoved down past a float (thus + // giving us more room), so we need to retest, and only jump to + // the end label if we still don't fit on the line. -dwh + if (w + tmpW > width) + goto end; + } + + if (!o->isFloatingOrPositioned()) { + last = o; + if (last->isReplaced() && autoWrap && (!last->isImage() || allowImagesToBreak) && (!last->isListMarker() || static_cast<RenderListMarker*>(last)->isInside())) { + w += tmpW; + tmpW = 0; + lBreak.obj = next; + lBreak.pos = 0; + lBreak.nextBreakablePosition = -1; + } + } + + o = next; + nextBreakable = -1; + + // Clear out our character space bool, since inline <pre>s don't collapse whitespace + // with adjacent inline normal/nowrap spans. + if (!collapseWhiteSpace) + currentCharacterIsSpace = false; + + pos = 0; + atStart = false; + } + + + if (w + tmpW <= width || lastWS == NOWRAP) { + lBreak.obj = 0; + lBreak.pos = 0; + lBreak.nextBreakablePosition = -1; + } + + end: + + if (lBreak == resolver.position() && !lBreak.obj->isBR()) { + // we just add as much as possible + if (style()->whiteSpace() == PRE) { + // FIXME: Don't really understand this case. + if (pos != 0) { + lBreak.obj = o; + lBreak.pos = pos - 1; + } else { + lBreak.obj = last; + lBreak.pos = last->isText() ? last->length() : 0; + lBreak.nextBreakablePosition = -1; + } + } else if (lBreak.obj) { + if (last != o && !last->isListMarker()) { + // better to break between object boundaries than in the middle of a word (except for list markers) + lBreak.obj = o; + lBreak.pos = 0; + lBreak.nextBreakablePosition = -1; + } else { + // Don't ever break in the middle of a word if we can help it. + // There's no room at all. We just have to be on this line, + // even though we'll spill out. + lBreak.obj = o; + lBreak.pos = pos; + lBreak.nextBreakablePosition = -1; + } + } + } + + // make sure we consume at least one char/object. + if (lBreak == resolver.position()) + lBreak.increment(); + + // Sanity check our midpoints. + checkMidpoints(lBreak); + + if (trailingSpaceObject) { + // This object is either going to be part of the last midpoint, or it is going + // to be the actual endpoint. In both cases we just decrease our pos by 1 level to + // exclude the space, allowing it to - in effect - collapse into the newline. + if (sNumMidpoints%2==1) { + InlineIterator* midpoints = smidpoints->data(); + midpoints[sNumMidpoints-1].pos--; + } + //else if (lBreak.pos > 0) + // lBreak.pos--; + else if (lBreak.obj == 0 && trailingSpaceObject->isText()) { + // Add a new end midpoint that stops right at the very end. + RenderText* text = static_cast<RenderText *>(trailingSpaceObject); + unsigned length = text->textLength(); + unsigned pos = length >= 2 ? length - 2 : UINT_MAX; + InlineIterator endMid(0, trailingSpaceObject, pos); + addMidpoint(endMid); + } + } + + // We might have made lBreak an iterator that points past the end + // of the object. Do this adjustment to make it point to the start + // of the next object instead to avoid confusing the rest of the + // code. + if (lBreak.pos > 0) { + lBreak.pos--; + lBreak.increment(); + } + + if (lBreak.obj && lBreak.pos >= 2 && lBreak.obj->isText()) { + // For soft hyphens on line breaks, we have to chop out the midpoints that made us + // ignore the hyphen so that it will render at the end of the line. + UChar c = static_cast<RenderText*>(lBreak.obj)->characters()[lBreak.pos-1]; + if (c == softHyphen) + chopMidpointsAt(lBreak.obj, lBreak.pos-2); + } + + return lBreak; +} + +void RenderBlock::checkLinesForOverflow() +{ + m_overflowWidth = m_width; + for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) { + m_overflowLeft = min(curr->leftOverflow(), m_overflowLeft); + m_overflowTop = min(curr->topOverflow(), m_overflowTop); + m_overflowWidth = max(curr->rightOverflow(), m_overflowWidth); + m_overflowHeight = max(curr->bottomOverflow(), m_overflowHeight); + } +} + +void RenderBlock::deleteEllipsisLineBoxes() +{ + for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) + curr->clearTruncation(); +} + +void RenderBlock::checkLinesForTextOverflow() +{ + // Determine the width of the ellipsis using the current font. + // FIXME: CSS3 says this is configurable, also need to use 0x002E (FULL STOP) if horizontal ellipsis is "not renderable" + TextRun ellipsisRun(&horizontalEllipsis, 1); + DEFINE_STATIC_LOCAL(AtomicString, ellipsisStr, (&horizontalEllipsis, 1)); + const Font& firstLineFont = firstLineStyle()->font(); + const Font& font = style()->font(); + int firstLineEllipsisWidth = firstLineFont.width(ellipsisRun); + int ellipsisWidth = (font == firstLineFont) ? firstLineEllipsisWidth : font.width(ellipsisRun); + + // For LTR text truncation, we want to get the right edge of our padding box, and then we want to see + // if the right edge of a line box exceeds that. For RTL, we use the left edge of the padding box and + // check the left edge of the line box to see if it is less + // Include the scrollbar for overflow blocks, which means we want to use "contentWidth()" + bool ltr = style()->direction() == LTR; + for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) { + int blockEdge = ltr ? rightOffset(curr->yPos()) : leftOffset(curr->yPos()); + int lineBoxEdge = ltr ? curr->xPos() + curr->width() : curr->xPos(); + if ((ltr && lineBoxEdge > blockEdge) || (!ltr && lineBoxEdge < blockEdge)) { + // This line spills out of our box in the appropriate direction. Now we need to see if the line + // can be truncated. In order for truncation to be possible, the line must have sufficient space to + // accommodate our truncation string, and no replaced elements (images, tables) can overlap the ellipsis + // space. + int width = curr == firstRootBox() ? firstLineEllipsisWidth : ellipsisWidth; + if (curr->canAccommodateEllipsis(ltr, blockEdge, lineBoxEdge, width)) + curr->placeEllipsis(ellipsisStr, ltr, blockEdge, width); + } + } +} + +} diff --git a/src/3rdparty/webkit/WebCore/rendering/bidi.h b/src/3rdparty/webkit/WebCore/rendering/bidi.h new file mode 100644 index 0000000..fc6de5b --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/bidi.h @@ -0,0 +1,67 @@ +/* + * This file is part of the html renderer for KDE. + * + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * Copyright (C) 2003 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef bidi_h +#define bidi_h + +#include "BidiResolver.h" + +namespace WebCore { + +class RenderArena; +class RenderBlock; +class RenderObject; +class InlineBox; + +struct BidiRun : BidiCharacterRun { + BidiRun(int start, int stop, RenderObject* object, BidiContext* context, WTF::Unicode::Direction dir) + : BidiCharacterRun(start, stop, context, dir) + , m_object(object) + , m_box(0) + , m_compact(false) + { + } + + void destroy(); + + // Overloaded new operator. + void* operator new(size_t, RenderArena*) throw(); + + // Overridden to prevent the normal delete from being called. + void operator delete(void*, size_t); + + BidiRun* next() { return static_cast<BidiRun*>(m_next); } + +private: + // The normal operator new is disallowed. + void* operator new(size_t) throw(); + +public: + RenderObject* m_object; + InlineBox* m_box; + bool m_compact; +}; + +} // namespace WebCore + +#endif // bidi_h diff --git a/src/3rdparty/webkit/WebCore/rendering/break_lines.cpp b/src/3rdparty/webkit/WebCore/rendering/break_lines.cpp new file mode 100644 index 0000000..05748ac --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/break_lines.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2005, 2007 Apple Inc. All rights reserved. + * + * 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" +#include "break_lines.h" + +#include "CharacterNames.h" +#include "TextBreakIterator.h" + +#if PLATFORM(MAC) +#include <CoreServices/CoreServices.h> +#endif + +namespace WebCore { + +static inline bool isBreakableSpace(UChar ch, bool treatNoBreakSpaceAsBreak) +{ + switch (ch) { + case ' ': + case '\n': + case '\t': + return true; + case noBreakSpace: + return treatNoBreakSpaceAsBreak; + default: + return false; + } +} + +static inline bool shouldBreakAfter(UChar ch) +{ + // Match WinIE's breaking strategy, which is to always allow breaks after hyphens and question marks. + // FIXME: it appears that IE behavior is more complex, see <http://bugs.webkit.org/show_bug.cgi?id=17475>. + switch (ch) { + case '-': + case '?': + case softHyphen: + // FIXME: cases for ideographicComma and ideographicFullStop are a workaround for an issue in Unicode 5.0 + // which is likely to be resolved in Unicode 5.1 <http://bugs.webkit.org/show_bug.cgi?id=17411>. + // We may want to remove or conditionalize this workaround at some point. + case ideographicComma: + case ideographicFullStop: + return true; + default: + return false; + } +} + +static inline bool needsLineBreakIterator(UChar ch) +{ + return ch > 0x7F && ch != noBreakSpace; +} + +#ifdef BUILDING_ON_TIGER +static inline TextBreakLocatorRef lineBreakLocator() +{ + TextBreakLocatorRef locator = 0; + UCCreateTextBreakLocator(0, 0, kUCTextBreakLineMask, &locator); + return locator; +} +#endif + +int nextBreakablePosition(const UChar* str, int pos, int len, bool treatNoBreakSpaceAsBreak) +{ +#ifndef BUILDING_ON_TIGER + TextBreakIterator* breakIterator = 0; +#endif + int nextBreak = -1; + + UChar lastCh = pos > 0 ? str[pos - 1] : 0; + for (int i = pos; i < len; i++) { + UChar ch = str[i]; + + if (isBreakableSpace(ch, treatNoBreakSpaceAsBreak) || shouldBreakAfter(lastCh)) + return i; + + if (needsLineBreakIterator(ch) || needsLineBreakIterator(lastCh)) { + if (nextBreak < i && i) { +#ifndef BUILDING_ON_TIGER + if (!breakIterator) + breakIterator = lineBreakIterator(str, len); + if (breakIterator) + nextBreak = textBreakFollowing(breakIterator, i - 1); +#else + static TextBreakLocatorRef breakLocator = lineBreakLocator(); + if (breakLocator) { + UniCharArrayOffset nextUCBreak; + if (UCFindTextBreak(breakLocator, kUCTextBreakLineMask, 0, str, len, i, &nextUCBreak) == 0) + nextBreak = nextUCBreak; + } +#endif + } + if (i == nextBreak && !isBreakableSpace(lastCh, treatNoBreakSpaceAsBreak)) + return i; + } + + lastCh = ch; + } + + return len; +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/break_lines.h b/src/3rdparty/webkit/WebCore/rendering/break_lines.h new file mode 100644 index 0000000..14f740f --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/break_lines.h @@ -0,0 +1,41 @@ +/* + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 2005 Apple Computer, Inc. + * + * 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. + * + */ + +#ifndef break_lines_h +#define break_lines_h + +#include <wtf/unicode/Unicode.h> + +namespace WebCore { + + int nextBreakablePosition(const UChar*, int pos, int len, bool breakNBSP = false); + + inline bool isBreakable(const UChar* str, int pos, int len, int& nextBreakable, bool breakNBSP = false) + { + if (pos > nextBreakable) + nextBreakable = nextBreakablePosition(str, pos, len, breakNBSP); + return pos == nextBreakable; + } + +} // namespace WebCore + +#endif // break_lines_h diff --git a/src/3rdparty/webkit/WebCore/rendering/style/BindingURI.cpp b/src/3rdparty/webkit/WebCore/rendering/style/BindingURI.cpp new file mode 100644 index 0000000..fd96de4 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/BindingURI.cpp @@ -0,0 +1,71 @@ +/* + * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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" +#include "BindingURI.h" + +#if ENABLE(XBL) + +namespace WebCore { + +BindingURI::BindingURI(StringImpl* uri) + : m_next(0) +{ + m_uri = uri; + if (uri) + uri->ref(); +} + +BindingURI::~BindingURI() +{ + if (m_uri) + m_uri->deref(); + delete m_next; +} + +BindingURI* BindingURI::copy() +{ + BindingURI* newBinding = new BindingURI(m_uri); + if (next()) { + BindingURI* nextCopy = next()->copy(); + newBinding->setNext(nextCopy); + } + + return newBinding; +} + +bool BindingURI::operator==(const BindingURI& o) const +{ + if ((m_next && !o.m_next) || (!m_next && o.m_next) || + (m_next && o.m_next && *m_next != *o.m_next)) + return false; + + if (m_uri == o.m_uri) + return true; + if (!m_uri || !o.m_uri) + return false; + + return String(m_uri) == String(o.m_uri); +} + +} // namespace WebCore + +#endif // ENABLE(XBL) diff --git a/src/3rdparty/webkit/WebCore/rendering/style/BindingURI.h b/src/3rdparty/webkit/WebCore/rendering/style/BindingURI.h new file mode 100644 index 0000000..923f1aa --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/BindingURI.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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. + * + */ + +#ifndef BindingURI_h +#define BindingURI_h +#if ENABLE(XBL) + +#include "StringImpl.h" + +namespace WebCore { + +// This struct holds information about shadows for the text-shadow and box-shadow properties. + +struct BindingURI { + BindingURI(StringImpl*); + ~BindingURI(); + + BindingURI* copy(); + + bool operator==(const BindingURI& o) const; + bool operator!=(const BindingURI& o) const + { + return !(*this == o); + } + + BindingURI* next() { return m_next; } + void setNext(BindingURI* n) { m_next = n; } + + StringImpl* uri() { return m_uri; } + + BindingURI* m_next; + StringImpl* m_uri; +}; + +} // namespace WebCore + +#endif // ENABLE(XBL) +#endif // BindingURI_h diff --git a/src/3rdparty/webkit/WebCore/rendering/style/BorderData.h b/src/3rdparty/webkit/WebCore/rendering/style/BorderData.h new file mode 100644 index 0000000..8ca0d65 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/BorderData.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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. + * + */ + +#ifndef BorderData_h +#define BorderData_h + +#include "BorderValue.h" +#include "IntSize.h" +#include "NinePieceImage.h" + +namespace WebCore { + +class BorderData { +public: + BorderValue left; + BorderValue right; + BorderValue top; + BorderValue bottom; + + NinePieceImage image; + + IntSize topLeft; + IntSize topRight; + IntSize bottomLeft; + IntSize bottomRight; + + bool hasBorder() const + { + bool haveImage = image.hasImage(); + return left.nonZero(!haveImage) || right.nonZero(!haveImage) || top.nonZero(!haveImage) || bottom.nonZero(!haveImage); + } + + bool hasBorderRadius() const + { + if (topLeft.width() > 0) + return true; + if (topRight.width() > 0) + return true; + if (bottomLeft.width() > 0) + return true; + if (bottomRight.width() > 0) + return true; + return false; + } + + unsigned short borderLeftWidth() const + { + if (!image.hasImage() && (left.style() == BNONE || left.style() == BHIDDEN)) + return 0; + return left.width; + } + + unsigned short borderRightWidth() const + { + if (!image.hasImage() && (right.style() == BNONE || right.style() == BHIDDEN)) + return 0; + return right.width; + } + + unsigned short borderTopWidth() const + { + if (!image.hasImage() && (top.style() == BNONE || top.style() == BHIDDEN)) + return 0; + return top.width; + } + + unsigned short borderBottomWidth() const + { + if (!image.hasImage() && (bottom.style() == BNONE || bottom.style() == BHIDDEN)) + return 0; + return bottom.width; + } + + bool operator==(const BorderData& o) const + { + return left == o.left && right == o.right && top == o.top && bottom == o.bottom && image == o.image && + topLeft == o.topLeft && topRight == o.topRight && bottomLeft == o.bottomLeft && bottomRight == o.bottomRight; + } + + bool operator!=(const BorderData& o) const + { + return !(*this == o); + } +}; + +} // namespace WebCore + +#endif // BorderData_h diff --git a/src/3rdparty/webkit/WebCore/rendering/style/BorderValue.h b/src/3rdparty/webkit/WebCore/rendering/style/BorderValue.h new file mode 100644 index 0000000..e61e708 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/BorderValue.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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. + * + */ + +#ifndef BorderValue_h +#define BorderValue_h + +#include "Color.h" +#include "RenderStyleConstants.h" + +namespace WebCore { + +class BorderValue { +public: + BorderValue() + : width(3) + , m_style(BNONE) + { + } + + Color color; + unsigned width : 12; + unsigned m_style : 4; // EBorderStyle + + EBorderStyle style() const { return static_cast<EBorderStyle>(m_style); } + + bool nonZero(bool checkStyle = true) const + { + return width != 0 && (!checkStyle || m_style != BNONE); + } + + bool isTransparent() const + { + return color.isValid() && color.alpha() == 0; + } + + bool isVisible(bool checkStyle = true) const + { + return nonZero(checkStyle) && !isTransparent() && (!checkStyle || m_style != BHIDDEN); + } + + bool operator==(const BorderValue& o) const + { + return width == o.width && m_style == o.m_style && color == o.color; + } + + bool operator!=(const BorderValue& o) const + { + return !(*this == o); + } +}; + +} // namespace WebCore + +#endif // BorderValue_h diff --git a/src/3rdparty/webkit/WebCore/rendering/style/CollapsedBorderValue.h b/src/3rdparty/webkit/WebCore/rendering/style/CollapsedBorderValue.h new file mode 100644 index 0000000..805f474 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/CollapsedBorderValue.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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. + * + */ + +#ifndef CollapsedBorderValue_h +#define CollapsedBorderValue_h + +#include "BorderValue.h" + +namespace WebCore { + +struct CollapsedBorderValue { + CollapsedBorderValue() + : border(0) + , precedence(BOFF) + { + } + + CollapsedBorderValue(const BorderValue* b, EBorderPrecedence p) + : border(b) + , precedence(p) + { + } + + int width() const { return border && border->nonZero() ? border->width : 0; } + EBorderStyle style() const { return border ? border->style() : BHIDDEN; } + bool exists() const { return border; } + Color color() const { return border ? border->color : Color(); } + bool isTransparent() const { return border ? border->isTransparent() : true; } + + bool operator==(const CollapsedBorderValue& o) const + { + if (!border) + return !o.border; + if (!o.border) + return false; + return *border == *o.border && precedence == o.precedence; + } + + const BorderValue* border; + EBorderPrecedence precedence; +}; + +} // namespace WebCore + +#endif // CollapsedBorderValue_h diff --git a/src/3rdparty/webkit/WebCore/rendering/style/ContentData.cpp b/src/3rdparty/webkit/WebCore/rendering/style/ContentData.cpp new file mode 100644 index 0000000..b38cc49 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/ContentData.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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" +#include "ContentData.h" + +#include "CounterContent.h" +#include "StringImpl.h" +#include "StyleImage.h" + +namespace WebCore { + +void ContentData::clear() +{ + switch (m_type) { + case CONTENT_NONE: + break; + case CONTENT_OBJECT: + m_content.m_image->deref(); + break; + case CONTENT_TEXT: + m_content.m_text->deref(); + break; + case CONTENT_COUNTER: + delete m_content.m_counter; + break; + } + + ContentData* n = m_next; + m_type = CONTENT_NONE; + m_next = 0; + + // Reverse the list so we can delete without recursing. + ContentData* last = 0; + ContentData* c; + while ((c = n)) { + n = c->m_next; + c->m_next = last; + last = c; + } + for (c = last; c; c = n) { + n = c->m_next; + c->m_next = 0; + delete c; + } +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/style/ContentData.h b/src/3rdparty/webkit/WebCore/rendering/style/ContentData.h new file mode 100644 index 0000000..d924d1a --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/ContentData.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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. + * + */ + +#ifndef ContentData_h +#define ContentData_h + +#include "RenderStyleConstants.h" +#include <wtf/Noncopyable.h> + +namespace WebCore { + +class CounterContent; +class StringImpl; +class StyleImage; + +struct ContentData : Noncopyable { + ContentData() + : m_type(CONTENT_NONE) + , m_next(0) + { + } + + ~ContentData() + { + clear(); + } + + void clear(); + + ContentType m_type; + union { + StyleImage* m_image; + StringImpl* m_text; + CounterContent* m_counter; + } m_content; + ContentData* m_next; +}; + +} // namespace WebCore + +#endif // ContentData_h diff --git a/src/3rdparty/webkit/WebCore/rendering/style/CounterContent.h b/src/3rdparty/webkit/WebCore/rendering/style/CounterContent.h new file mode 100644 index 0000000..06440ad --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/CounterContent.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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. + * + */ + +#ifndef CounterContent_h +#define CounterContent_h + +#include "AtomicString.h" +#include "RenderStyleConstants.h" + +namespace WebCore { + +class CounterContent { +public: + CounterContent(const AtomicString& identifier, EListStyleType style, const AtomicString& separator) + : m_identifier(identifier) + , m_listStyle(style) + , m_separator(separator) + { + } + + const AtomicString& identifier() const { return m_identifier; } + EListStyleType listStyle() const { return m_listStyle; } + const AtomicString& separator() const { return m_separator; } + +private: + AtomicString m_identifier; + EListStyleType m_listStyle; + AtomicString m_separator; +}; + +static inline bool operator!=(const CounterContent& a, const CounterContent& b) +{ + return a.identifier() != b.identifier() + || a.listStyle() != b.listStyle() + || a.separator() != b.separator(); +} + + +} // namespace WebCore + +#endif // CounterContent_h diff --git a/src/3rdparty/webkit/WebCore/rendering/style/CounterDirectives.cpp b/src/3rdparty/webkit/WebCore/rendering/style/CounterDirectives.cpp new file mode 100644 index 0000000..a0ff52f --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/CounterDirectives.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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" +#include "CounterDirectives.h" + +namespace WebCore { + +bool operator==(const CounterDirectives& a, const CounterDirectives& b) +{ + if (a.m_reset != b.m_reset || a.m_increment != b.m_increment) + return false; + if (a.m_reset && a.m_resetValue != b.m_resetValue) + return false; + if (a.m_increment && a.m_incrementValue != b.m_incrementValue) + return false; + return true; +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/style/CounterDirectives.h b/src/3rdparty/webkit/WebCore/rendering/style/CounterDirectives.h new file mode 100644 index 0000000..9cbdeae --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/CounterDirectives.h @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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. + * + */ + +#ifndef CounterDirectives_h +#define CounterDirectives_h + +#include "AtomicStringImpl.h" +#include <wtf/HashMap.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +struct CounterDirectives { + CounterDirectives() + : m_reset(false) + , m_increment(false) + { + } + + bool m_reset; + int m_resetValue; + bool m_increment; + int m_incrementValue; +}; + +bool operator==(const CounterDirectives&, const CounterDirectives&); +inline bool operator!=(const CounterDirectives& a, const CounterDirectives& b) { return !(a == b); } + +typedef HashMap<RefPtr<AtomicStringImpl>, CounterDirectives> CounterDirectiveMap; + +} // namespace WebCore + +#endif // CounterDirectives_h diff --git a/src/3rdparty/webkit/WebCore/rendering/style/CursorData.h b/src/3rdparty/webkit/WebCore/rendering/style/CursorData.h new file mode 100644 index 0000000..7c6b31d --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/CursorData.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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. + * + */ + +#ifndef CursorData_h +#define CursorData_h + +#include "CachedImage.h" +#include "CachedResourceHandle.h" +#include "IntPoint.h" + +namespace WebCore { + +struct CursorData { + CursorData() + : cursorImage(0) + { + } + + bool operator==(const CursorData& o) const + { + return hotSpot == o.hotSpot && cursorImage == o.cursorImage; + } + + bool operator!=(const CursorData& o) const + { + return !(*this == o); + } + + IntPoint hotSpot; // for CSS3 support + CachedResourceHandle<CachedImage> cursorImage; +}; + +} // namespace WebCore + +#endif // CursorData_h diff --git a/src/3rdparty/webkit/WebCore/rendering/style/CursorList.h b/src/3rdparty/webkit/WebCore/rendering/style/CursorList.h new file mode 100644 index 0000000..bdd65d4 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/CursorList.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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. + * + */ + +#ifndef CursorList_h +#define CursorList_h + +#include "CursorData.h" +#include <wtf/RefCounted.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class CursorList : public RefCounted<CursorList> { +public: + static PassRefPtr<CursorList> create() + { + return adoptRef(new CursorList); + } + + const CursorData& operator[](int i) const { return m_vector[i]; } + + bool operator==(const CursorList& o) const { return m_vector == o.m_vector; } + bool operator!=(const CursorList& o) const { return m_vector != o.m_vector; } + + size_t size() const { return m_vector.size(); } + void append(const CursorData& cursorData) { m_vector.append(cursorData); } + +private: + CursorList() + { + } + + Vector<CursorData> m_vector; +}; + +} // namespace WebCore + +#endif // CursorList_h diff --git a/src/3rdparty/webkit/WebCore/rendering/style/DataRef.h b/src/3rdparty/webkit/WebCore/rendering/style/DataRef.h new file mode 100644 index 0000000..c8d8072 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/DataRef.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2008 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef DataRef_h +#define DataRef_h + +#include <wtf/RefPtr.h> + +namespace WebCore { + +template <typename T> class DataRef { +public: + const T* get() const { return m_data.get(); } + + const T& operator*() const { return *get(); } + const T* operator->() const { return get(); } + + T* access() + { + if (!m_data->hasOneRef()) + m_data = m_data->copy(); + return m_data.get(); + } + + void init() + { + ASSERT(!m_data); + m_data = T::create(); + } + + bool operator==(const DataRef<T>& o) const + { + ASSERT(m_data); + ASSERT(o.m_data); + return m_data == o.m_data || *m_data == *o.m_data; + } + + bool operator!=(const DataRef<T>& o) const + { + ASSERT(m_data); + ASSERT(o.m_data); + return m_data != o.m_data && *m_data != *o.m_data; + } + +private: + RefPtr<T> m_data; +}; + +} // namespace WebCore + +#endif // DataRef_h diff --git a/src/3rdparty/webkit/WebCore/rendering/style/FillLayer.cpp b/src/3rdparty/webkit/WebCore/rendering/style/FillLayer.cpp new file mode 100644 index 0000000..9c491aa --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/FillLayer.cpp @@ -0,0 +1,254 @@ +/* + * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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" +#include "FillLayer.h" + +namespace WebCore { + +FillLayer::FillLayer(EFillLayerType type) + : m_image(FillLayer::initialFillImage(type)) + , m_xPosition(FillLayer::initialFillXPosition(type)) + , m_yPosition(FillLayer::initialFillYPosition(type)) + , m_attachment(FillLayer::initialFillAttachment(type)) + , m_clip(FillLayer::initialFillClip(type)) + , m_origin(FillLayer::initialFillOrigin(type)) + , m_repeat(FillLayer::initialFillRepeat(type)) + , m_composite(FillLayer::initialFillComposite(type)) + , m_size(FillLayer::initialFillSize(type)) + , m_imageSet(false) + , m_attachmentSet(false) + , m_clipSet(false) + , m_originSet(false) + , m_repeatSet(false) + , m_xPosSet(false) + , m_yPosSet(false) + , m_compositeSet(type == MaskFillLayer) + , m_sizeSet(false) + , m_type(type) + , m_next(0) +{ +} + +FillLayer::FillLayer(const FillLayer& o) + : m_image(o.m_image) + , m_xPosition(o.m_xPosition) + , m_yPosition(o.m_yPosition) + , m_attachment(o.m_attachment) + , m_clip(o.m_clip) + , m_origin(o.m_origin) + , m_repeat(o.m_repeat) + , m_composite(o.m_composite) + , m_size(o.m_size) + , m_imageSet(o.m_imageSet) + , m_attachmentSet(o.m_attachmentSet) + , m_clipSet(o.m_clipSet) + , m_originSet(o.m_originSet) + , m_repeatSet(o.m_repeatSet) + , m_xPosSet(o.m_xPosSet) + , m_yPosSet(o.m_yPosSet) + , m_compositeSet(o.m_compositeSet) + , m_sizeSet(o.m_sizeSet) + , m_type(o.m_type) + , m_next(o.m_next ? new FillLayer(*o.m_next) : 0) +{ +} + +FillLayer::~FillLayer() +{ + delete m_next; +} + +FillLayer& FillLayer::operator=(const FillLayer& o) +{ + if (m_next != o.m_next) { + delete m_next; + m_next = o.m_next ? new FillLayer(*o.m_next) : 0; + } + + m_image = o.m_image; + m_xPosition = o.m_xPosition; + m_yPosition = o.m_yPosition; + m_attachment = o.m_attachment; + m_clip = o.m_clip; + m_composite = o.m_composite; + m_origin = o.m_origin; + m_repeat = o.m_repeat; + m_size = o.m_size; + + m_imageSet = o.m_imageSet; + m_attachmentSet = o.m_attachmentSet; + m_clipSet = o.m_clipSet; + m_compositeSet = o.m_compositeSet; + m_originSet = o.m_originSet; + m_repeatSet = o.m_repeatSet; + m_xPosSet = o.m_xPosSet; + m_yPosSet = o.m_yPosSet; + m_sizeSet = o.m_sizeSet; + + m_type = o.m_type; + + return *this; +} + +bool FillLayer::operator==(const FillLayer& o) const +{ + // We do not check the "isSet" booleans for each property, since those are only used during initial construction + // to propagate patterns into layers. All layer comparisons happen after values have all been filled in anyway. + return StyleImage::imagesEquivalent(m_image.get(), o.m_image.get()) && m_xPosition == o.m_xPosition && m_yPosition == o.m_yPosition && + m_attachment == o.m_attachment && m_clip == o.m_clip && + m_composite == o.m_composite && m_origin == o.m_origin && m_repeat == o.m_repeat && + m_size == o.m_size && m_type == o.m_type && + ((m_next && o.m_next) ? *m_next == *o.m_next : m_next == o.m_next); +} + +void FillLayer::fillUnsetProperties() +{ + FillLayer* curr; + for (curr = this; curr && curr->isImageSet(); curr = curr->next()) { } + if (curr && curr != this) { + // We need to fill in the remaining values with the pattern specified. + for (FillLayer* pattern = this; curr; curr = curr->next()) { + curr->m_image = pattern->m_image; + pattern = pattern->next(); + if (pattern == curr || !pattern) + pattern = this; + } + } + + for (curr = this; curr && curr->isXPositionSet(); curr = curr->next()) { } + if (curr && curr != this) { + // We need to fill in the remaining values with the pattern specified. + for (FillLayer* pattern = this; curr; curr = curr->next()) { + curr->m_xPosition = pattern->m_xPosition; + pattern = pattern->next(); + if (pattern == curr || !pattern) + pattern = this; + } + } + + for (curr = this; curr && curr->isYPositionSet(); curr = curr->next()) { } + if (curr && curr != this) { + // We need to fill in the remaining values with the pattern specified. + for (FillLayer* pattern = this; curr; curr = curr->next()) { + curr->m_yPosition = pattern->m_yPosition; + pattern = pattern->next(); + if (pattern == curr || !pattern) + pattern = this; + } + } + + for (curr = this; curr && curr->isAttachmentSet(); curr = curr->next()) { } + if (curr && curr != this) { + // We need to fill in the remaining values with the pattern specified. + for (FillLayer* pattern = this; curr; curr = curr->next()) { + curr->m_attachment = pattern->m_attachment; + pattern = pattern->next(); + if (pattern == curr || !pattern) + pattern = this; + } + } + + for (curr = this; curr && curr->isClipSet(); curr = curr->next()) { } + if (curr && curr != this) { + // We need to fill in the remaining values with the pattern specified. + for (FillLayer* pattern = this; curr; curr = curr->next()) { + curr->m_clip = pattern->m_clip; + pattern = pattern->next(); + if (pattern == curr || !pattern) + pattern = this; + } + } + + for (curr = this; curr && curr->isCompositeSet(); curr = curr->next()) { } + if (curr && curr != this) { + // We need to fill in the remaining values with the pattern specified. + for (FillLayer* pattern = this; curr; curr = curr->next()) { + curr->m_composite = pattern->m_composite; + pattern = pattern->next(); + if (pattern == curr || !pattern) + pattern = this; + } + } + + for (curr = this; curr && curr->isOriginSet(); curr = curr->next()) { } + if (curr && curr != this) { + // We need to fill in the remaining values with the pattern specified. + for (FillLayer* pattern = this; curr; curr = curr->next()) { + curr->m_origin = pattern->m_origin; + pattern = pattern->next(); + if (pattern == curr || !pattern) + pattern = this; + } + } + + for (curr = this; curr && curr->isRepeatSet(); curr = curr->next()) { } + if (curr && curr != this) { + // We need to fill in the remaining values with the pattern specified. + for (FillLayer* pattern = this; curr; curr = curr->next()) { + curr->m_repeat = pattern->m_repeat; + pattern = pattern->next(); + if (pattern == curr || !pattern) + pattern = this; + } + } + + for (curr = this; curr && curr->isSizeSet(); curr = curr->next()) { } + if (curr && curr != this) { + // We need to fill in the remaining values with the pattern specified. + for (FillLayer* pattern = this; curr; curr = curr->next()) { + curr->m_size = pattern->m_size; + pattern = pattern->next(); + if (pattern == curr || !pattern) + pattern = this; + } + } +} + +void FillLayer::cullEmptyLayers() +{ + FillLayer* next; + for (FillLayer* p = this; p; p = next) { + next = p->m_next; + if (next && !next->isImageSet() && + !next->isXPositionSet() && !next->isYPositionSet() && + !next->isAttachmentSet() && !next->isClipSet() && + !next->isCompositeSet() && !next->isOriginSet() && + !next->isRepeatSet() && !next->isSizeSet()) { + delete next; + p->m_next = 0; + break; + } + } +} + +bool FillLayer::containsImage(StyleImage* s) const +{ + if (!s) + return false; + if (m_image && *s == *m_image) + return true; + if (m_next) + return m_next->containsImage(s); + return false; +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/style/FillLayer.h b/src/3rdparty/webkit/WebCore/rendering/style/FillLayer.h new file mode 100644 index 0000000..2dc5871 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/FillLayer.h @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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. + * + */ + +#ifndef FillLayer_h +#define FillLayer_h + +#include "GraphicsTypes.h" +#include "Length.h" +#include "LengthSize.h" +#include "RenderStyleConstants.h" +#include "StyleImage.h" +#include <wtf/RefPtr.h> + +namespace WebCore { + +struct FillLayer { +public: + FillLayer(EFillLayerType); + ~FillLayer(); + + StyleImage* image() const { return m_image.get(); } + Length xPosition() const { return m_xPosition; } + Length yPosition() const { return m_yPosition; } + bool attachment() const { return m_attachment; } + EFillBox clip() const { return static_cast<EFillBox>(m_clip); } + EFillBox origin() const { return static_cast<EFillBox>(m_origin); } + EFillRepeat repeat() const { return static_cast<EFillRepeat>(m_repeat); } + CompositeOperator composite() const { return static_cast<CompositeOperator>(m_composite); } + LengthSize size() const { return m_size; } + + const FillLayer* next() const { return m_next; } + FillLayer* next() { return m_next; } + + bool isImageSet() const { return m_imageSet; } + bool isXPositionSet() const { return m_xPosSet; } + bool isYPositionSet() const { return m_yPosSet; } + bool isAttachmentSet() const { return m_attachmentSet; } + bool isClipSet() const { return m_clipSet; } + bool isOriginSet() const { return m_originSet; } + bool isRepeatSet() const { return m_repeatSet; } + bool isCompositeSet() const { return m_compositeSet; } + bool isSizeSet() const { return m_sizeSet; } + + void setImage(StyleImage* i) { m_image = i; m_imageSet = true; } + void setXPosition(const Length& l) { m_xPosition = l; m_xPosSet = true; } + void setYPosition(const Length& l) { m_yPosition = l; m_yPosSet = true; } + void setAttachment(bool b) { m_attachment = b; m_attachmentSet = true; } + void setClip(EFillBox b) { m_clip = b; m_clipSet = true; } + void setOrigin(EFillBox b) { m_origin = b; m_originSet = true; } + void setRepeat(EFillRepeat r) { m_repeat = r; m_repeatSet = true; } + void setComposite(CompositeOperator c) { m_composite = c; m_compositeSet = true; } + void setSize(const LengthSize& b) { m_size = b; m_sizeSet = true; } + + void clearImage() { m_imageSet = false; } + void clearXPosition() { m_xPosSet = false; } + void clearYPosition() { m_yPosSet = false; } + void clearAttachment() { m_attachmentSet = false; } + void clearClip() { m_clipSet = false; } + void clearOrigin() { m_originSet = false; } + void clearRepeat() { m_repeatSet = false; } + void clearComposite() { m_compositeSet = false; } + void clearSize() { m_sizeSet = false; } + + void setNext(FillLayer* n) { if (m_next != n) { delete m_next; m_next = n; } } + + FillLayer& operator=(const FillLayer& o); + FillLayer(const FillLayer& o); + + bool operator==(const FillLayer& o) const; + bool operator!=(const FillLayer& o) const + { + return !(*this == o); + } + + bool containsImage(StyleImage*) const; + + bool hasImage() const + { + if (m_image) + return true; + return m_next ? m_next->hasImage() : false; + } + + bool hasFixedImage() const + { + if (m_image && !m_attachment) + return true; + return m_next ? m_next->hasFixedImage() : false; + } + + EFillLayerType type() const { return static_cast<EFillLayerType>(m_type); } + + void fillUnsetProperties(); + void cullEmptyLayers(); + + static bool initialFillAttachment(EFillLayerType) { return true; } + static EFillBox initialFillClip(EFillLayerType) { return BorderFillBox; } + static EFillBox initialFillOrigin(EFillLayerType type) { return type == BackgroundFillLayer ? PaddingFillBox : BorderFillBox; } + static EFillRepeat initialFillRepeat(EFillLayerType) { return RepeatFill; } + static CompositeOperator initialFillComposite(EFillLayerType) { return CompositeSourceOver; } + static LengthSize initialFillSize(EFillLayerType) { return LengthSize(); } + static Length initialFillXPosition(EFillLayerType) { return Length(0.0, Percent); } + static Length initialFillYPosition(EFillLayerType) { return Length(0.0, Percent); } + static StyleImage* initialFillImage(EFillLayerType) { return 0; } + +private: + FillLayer() { } + +public: + RefPtr<StyleImage> m_image; + + Length m_xPosition; + Length m_yPosition; + + bool m_attachment : 1; + unsigned m_clip : 2; // EFillBox + unsigned m_origin : 2; // EFillBox + unsigned m_repeat : 2; // EFillRepeat + unsigned m_composite : 4; // CompositeOperator + + LengthSize m_size; + + bool m_imageSet : 1; + bool m_attachmentSet : 1; + bool m_clipSet : 1; + bool m_originSet : 1; + bool m_repeatSet : 1; + bool m_xPosSet : 1; + bool m_yPosSet : 1; + bool m_compositeSet : 1; + bool m_sizeSet : 1; + + unsigned m_type : 1; // EFillLayerType + + FillLayer* m_next; +}; + +} // namespace WebCore + +#endif // FillLayer_h diff --git a/src/3rdparty/webkit/WebCore/rendering/style/KeyframeList.cpp b/src/3rdparty/webkit/WebCore/rendering/style/KeyframeList.cpp new file mode 100644 index 0000000..41fbbe2 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/KeyframeList.cpp @@ -0,0 +1,88 @@ +/* + * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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" +#include "KeyframeList.h" +#include "RenderObject.h" + +namespace WebCore { + +KeyframeList::~KeyframeList() +{ + clear(); +} + +void KeyframeList::clear() +{ + m_keyframes.clear(); + m_properties.clear(); +} + +bool KeyframeList::operator==(const KeyframeList& o) const +{ + if (m_keyframes.size() != o.m_keyframes.size()) + return false; + + Vector<KeyframeValue>::const_iterator it2 = o.m_keyframes.begin(); + for (Vector<KeyframeValue>::const_iterator it1 = m_keyframes.begin(); it1 != m_keyframes.end(); ++it1) { + if (it1->m_key != it2->m_key) + return false; + const RenderStyle& style1 = *it1->m_style; + const RenderStyle& style2 = *it2->m_style; + if (style1 != style2) + return false; + ++it2; + } + + return true; +} + +void KeyframeList::insert(float key, PassRefPtr<RenderStyle> style) +{ + if (key < 0 || key > 1) + return; + + int index = -1; + + for (size_t i = 0; i < m_keyframes.size(); ++i) { + if (m_keyframes[i].m_key == key) { + index = (int) i; + break; + } + if (m_keyframes[i].m_key > key) { + // insert before + m_keyframes.insert(i, KeyframeValue()); + index = (int) i; + break; + } + } + + if (index < 0) { + // append + index = (int) m_keyframes.size(); + m_keyframes.append(KeyframeValue()); + } + + m_keyframes[index].m_key = key; + m_keyframes[index].m_style = style; +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/style/KeyframeList.h b/src/3rdparty/webkit/WebCore/rendering/style/KeyframeList.h new file mode 100644 index 0000000..b1009d2 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/KeyframeList.h @@ -0,0 +1,90 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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. + * + */ + +#ifndef KeyframeList_h +#define KeyframeList_h + +#include "AtomicString.h" +#include <wtf/Vector.h> +#include <wtf/HashSet.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class RenderObject; +class RenderStyle; + +class KeyframeValue { +public: + KeyframeValue() + : m_key(-1) + { + } + + float key() const { return m_key; } + const RenderStyle* style() const { return m_style.get(); } + + float m_key; + RefPtr<RenderStyle> m_style; +}; + +class KeyframeList { +public: + KeyframeList(RenderObject* renderer, const AtomicString& animationName) + : m_animationName(animationName) + , m_renderer(renderer) + { + insert(0, 0); + insert(1, 0); + } + ~KeyframeList(); + + bool operator==(const KeyframeList& o) const; + bool operator!=(const KeyframeList& o) const { return !(*this == o); } + + const AtomicString& animationName() const { return m_animationName; } + + void insert(float key, PassRefPtr<RenderStyle> style); + + void addProperty(int prop) { m_properties.add(prop); } + bool containsProperty(int prop) const { return m_properties.contains(prop); } + HashSet<int>::const_iterator beginProperties() const { return m_properties.begin(); } + HashSet<int>::const_iterator endProperties() const { return m_properties.end(); } + + void clear(); + bool isEmpty() const { return m_keyframes.isEmpty(); } + size_t size() const { return m_keyframes.size(); } + Vector<KeyframeValue>::const_iterator beginKeyframes() const { return m_keyframes.begin(); } + Vector<KeyframeValue>::const_iterator endKeyframes() const { return m_keyframes.end(); } + +private: + AtomicString m_animationName; + Vector<KeyframeValue> m_keyframes; + HashSet<int> m_properties; // the properties being animated + RenderObject* m_renderer; +}; + +} // namespace WebCore + +#endif // KeyframeList_h diff --git a/src/3rdparty/webkit/WebCore/rendering/style/NinePieceImage.cpp b/src/3rdparty/webkit/WebCore/rendering/style/NinePieceImage.cpp new file mode 100644 index 0000000..d585e8f --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/NinePieceImage.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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" +#include "NinePieceImage.h" + +namespace WebCore { + +bool NinePieceImage::operator==(const NinePieceImage& o) const +{ + return StyleImage::imagesEquivalent(m_image.get(), o.m_image.get()) && m_slices == o.m_slices && m_horizontalRule == o.m_horizontalRule && + m_verticalRule == o.m_verticalRule; +} + +} diff --git a/src/3rdparty/webkit/WebCore/rendering/style/NinePieceImage.h b/src/3rdparty/webkit/WebCore/rendering/style/NinePieceImage.h new file mode 100644 index 0000000..bf47ce6 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/NinePieceImage.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef NinePieceImage_h +#define NinePieceImage_h + +#include "LengthBox.h" +#include "StyleImage.h" + +namespace WebCore { + +enum ENinePieceImageRule { + StretchImageRule, RoundImageRule, RepeatImageRule +}; + +class NinePieceImage { +public: + NinePieceImage() + : m_image(0) + , m_horizontalRule(StretchImageRule) + , m_verticalRule(StretchImageRule) + { + } + + NinePieceImage(StyleImage* image, LengthBox slices, ENinePieceImageRule h, ENinePieceImageRule v) + : m_image(image) + , m_slices(slices) + , m_horizontalRule(h) + , m_verticalRule(v) + { + } + + bool operator==(const NinePieceImage& o) const; + bool operator!=(const NinePieceImage& o) const { return !(*this == o); } + + bool hasImage() const { return m_image != 0; } + StyleImage* image() const { return m_image.get(); } + + ENinePieceImageRule horizontalRule() const { return static_cast<ENinePieceImageRule>(m_horizontalRule); } + ENinePieceImageRule verticalRule() const { return static_cast<ENinePieceImageRule>(m_verticalRule); } + + RefPtr<StyleImage> m_image; + LengthBox m_slices; + unsigned m_horizontalRule : 2; // ENinePieceImageRule + unsigned m_verticalRule : 2; // ENinePieceImageRule +}; + +} // namespace WebCore + +#endif // NinePieceImage_h diff --git a/src/3rdparty/webkit/WebCore/rendering/style/OutlineValue.h b/src/3rdparty/webkit/WebCore/rendering/style/OutlineValue.h new file mode 100644 index 0000000..2628b7f --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/OutlineValue.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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. + * + */ + +#ifndef OutlineValue_h +#define OutlineValue_h + +#include "BorderValue.h" + +namespace WebCore { + +class OutlineValue : public BorderValue { +public: + OutlineValue() + : _offset(0) + , _auto(false) + { + } + + bool operator==(const OutlineValue& o) const + { + return width == o.width && m_style == o.m_style && color == o.color && _offset == o._offset && _auto == o._auto; + } + + bool operator!=(const OutlineValue& o) const + { + return !(*this == o); + } + + int _offset; + bool _auto; +}; + +} // namespace WebCore + +#endif // OutlineValue_h diff --git a/src/3rdparty/webkit/WebCore/rendering/style/RenderStyle.cpp b/src/3rdparty/webkit/WebCore/rendering/style/RenderStyle.cpp new file mode 100644 index 0000000..872ee9c --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/RenderStyle.cpp @@ -0,0 +1,844 @@ +/* + * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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" +#include "RenderStyle.h" + +#include "CSSStyleSelector.h" +#include "CachedImage.h" +#include "CounterContent.h" +#include "FontSelector.h" +#include "RenderArena.h" +#include "RenderObject.h" +#include "StyleImage.h" +#include <wtf/StdLibExtras.h> +#include <algorithm> + +namespace WebCore { + +TransformOperations RenderStyle::s_initialTransform; + +inline RenderStyle* defaultStyle() +{ + static RenderStyle* s_defaultStyle = RenderStyle::createDefaultStyle().releaseRef(); + return s_defaultStyle; +} + +PassRefPtr<RenderStyle> RenderStyle::create() +{ + return adoptRef(new RenderStyle()); +} + +PassRefPtr<RenderStyle> RenderStyle::createDefaultStyle() +{ + return adoptRef(new RenderStyle(true)); +} + +PassRefPtr<RenderStyle> RenderStyle::clone(const RenderStyle* other) +{ + return adoptRef(new RenderStyle(*other)); +} + +RenderStyle::RenderStyle() + : box(defaultStyle()->box) + , visual(defaultStyle()->visual) + , background(defaultStyle()->background) + , surround(defaultStyle()->surround) + , rareNonInheritedData(defaultStyle()->rareNonInheritedData) + , rareInheritedData(defaultStyle()->rareInheritedData) + , inherited(defaultStyle()->inherited) + , m_pseudoState(PseudoUnknown) + , m_affectedByAttributeSelectors(false) + , m_unique(false) + , m_affectedByEmpty(false) + , m_emptyState(false) + , m_childrenAffectedByFirstChildRules(false) + , m_childrenAffectedByLastChildRules(false) + , m_childrenAffectedByDirectAdjacentRules(false) + , m_childrenAffectedByForwardPositionalRules(false) + , m_childrenAffectedByBackwardPositionalRules(false) + , m_firstChildState(false) + , m_lastChildState(false) + , m_childIndex(0) +#if ENABLE(SVG) + , m_svgStyle(defaultStyle()->m_svgStyle) +#endif +{ + setBitDefaults(); // Would it be faster to copy this from the default style? +} + +RenderStyle::RenderStyle(bool) + : m_pseudoState(PseudoUnknown) + , m_affectedByAttributeSelectors(false) + , m_unique(false) + , m_affectedByEmpty(false) + , m_emptyState(false) + , m_childrenAffectedByFirstChildRules(false) + , m_childrenAffectedByLastChildRules(false) + , m_childrenAffectedByDirectAdjacentRules(false) + , m_childrenAffectedByForwardPositionalRules(false) + , m_childrenAffectedByBackwardPositionalRules(false) + , m_firstChildState(false) + , m_lastChildState(false) + , m_childIndex(0) +{ + setBitDefaults(); + + box.init(); + visual.init(); + background.init(); + surround.init(); + rareNonInheritedData.init(); + rareNonInheritedData.access()->flexibleBox.init(); + rareNonInheritedData.access()->marquee.init(); + rareNonInheritedData.access()->m_multiCol.init(); + rareNonInheritedData.access()->m_transform.init(); + rareInheritedData.init(); + inherited.init(); + +#if ENABLE(SVG) + m_svgStyle.init(); +#endif +} + +RenderStyle::RenderStyle(const RenderStyle& o) + : RefCounted<RenderStyle>() + , inherited_flags(o.inherited_flags) + , noninherited_flags(o.noninherited_flags) + , box(o.box) + , visual(o.visual) + , background(o.background) + , surround(o.surround) + , rareNonInheritedData(o.rareNonInheritedData) + , rareInheritedData(o.rareInheritedData) + , inherited(o.inherited) + , m_pseudoState(o.m_pseudoState) + , m_affectedByAttributeSelectors(false) + , m_unique(false) + , m_affectedByEmpty(false) + , m_emptyState(false) + , m_childrenAffectedByFirstChildRules(false) + , m_childrenAffectedByLastChildRules(false) + , m_childrenAffectedByDirectAdjacentRules(false) + , m_childrenAffectedByForwardPositionalRules(false) + , m_childrenAffectedByBackwardPositionalRules(false) + , m_firstChildState(false) + , m_lastChildState(false) + , m_childIndex(0) +#if ENABLE(SVG) + , m_svgStyle(o.m_svgStyle) +#endif +{ +} + +void RenderStyle::inheritFrom(const RenderStyle* inheritParent) +{ + rareInheritedData = inheritParent->rareInheritedData; + inherited = inheritParent->inherited; + inherited_flags = inheritParent->inherited_flags; +#if ENABLE(SVG) + if (m_svgStyle != inheritParent->m_svgStyle) + m_svgStyle.access()->inheritFrom(inheritParent->m_svgStyle.get()); +#endif +} + +RenderStyle::~RenderStyle() +{ +} + +bool RenderStyle::operator==(const RenderStyle& o) const +{ + // compare everything except the pseudoStyle pointer + return inherited_flags == o.inherited_flags && + noninherited_flags == o.noninherited_flags && + box == o.box && + visual == o.visual && + background == o.background && + surround == o.surround && + rareNonInheritedData == o.rareNonInheritedData && + rareInheritedData == o.rareInheritedData && + inherited == o.inherited +#if ENABLE(SVG) + && m_svgStyle == o.m_svgStyle +#endif + ; +} + +bool RenderStyle::isStyleAvailable() const +{ + return this != CSSStyleSelector::styleNotYetAvailable(); +} + +static inline int pseudoBit(RenderStyle::PseudoId pseudo) +{ + return 1 << (pseudo - 1); +} + +bool RenderStyle::hasPseudoStyle(PseudoId pseudo) const +{ + ASSERT(pseudo > NOPSEUDO); + ASSERT(pseudo < FIRST_INTERNAL_PSEUDOID); + return pseudoBit(pseudo) & noninherited_flags._pseudoBits; +} + +void RenderStyle::setHasPseudoStyle(PseudoId pseudo) +{ + ASSERT(pseudo > NOPSEUDO); + ASSERT(pseudo < FIRST_INTERNAL_PSEUDOID); + noninherited_flags._pseudoBits |= pseudoBit(pseudo); +} + +RenderStyle* RenderStyle::getCachedPseudoStyle(PseudoId pid) +{ + if (!m_cachedPseudoStyle || styleType() != NOPSEUDO) + return 0; + RenderStyle* ps = m_cachedPseudoStyle.get(); + while (ps && ps->styleType() != pid) + ps = ps->m_cachedPseudoStyle.get(); + return ps; +} + +RenderStyle* RenderStyle::addCachedPseudoStyle(PassRefPtr<RenderStyle> pseudo) +{ + if (!pseudo) + return 0; + pseudo->m_cachedPseudoStyle = m_cachedPseudoStyle; + m_cachedPseudoStyle = pseudo; + return m_cachedPseudoStyle.get(); +} + +bool RenderStyle::inheritedNotEqual(RenderStyle* other) const +{ + return inherited_flags != other->inherited_flags || + inherited != other->inherited || +#if ENABLE(SVG) + m_svgStyle->inheritedNotEqual(other->m_svgStyle.get()) || +#endif + rareInheritedData != other->rareInheritedData; +} + +bool positionedObjectMoved(const LengthBox& a, const LengthBox& b) +{ + // If any unit types are different, then we can't guarantee + // that this was just a movement. + if (a.left().type() != b.left().type() || + a.right().type() != b.right().type() || + a.top().type() != b.top().type() || + a.bottom().type() != b.bottom().type()) + return false; + + // Only one unit can be non-auto in the horizontal direction and + // in the vertical direction. Otherwise the adjustment of values + // is changing the size of the box. + if (!a.left().isIntrinsicOrAuto() && !a.right().isIntrinsicOrAuto()) + return false; + if (!a.top().isIntrinsicOrAuto() && !a.bottom().isIntrinsicOrAuto()) + return false; + + // One of the units is fixed or percent in both directions and stayed + // that way in the new style. Therefore all we are doing is moving. + return true; +} + +/* + compares two styles. The result gives an idea of the action that + needs to be taken when replacing the old style with a new one. + + CbLayout: The containing block of the object needs a relayout. + Layout: the RenderObject needs a relayout after the style change + Visible: The change is visible, but no relayout is needed + NonVisible: The object does need neither repaint nor relayout after + the change. + + ### TODO: + A lot can be optimised here based on the display type, lots of + optimisations are unimplemented, and currently result in the + worst case result causing a relayout of the containing block. +*/ +RenderStyle::Diff RenderStyle::diff(const RenderStyle* other) const +{ +#if ENABLE(SVG) + // This is horribly inefficient. Eventually we'll have to integrate + // this more directly by calling: Diff svgDiff = svgStyle->diff(other) + // and then checking svgDiff and returning from the appropriate places below. + if (m_svgStyle != other->m_svgStyle) + return Layout; +#endif + + if (box->width != other->box->width || + box->min_width != other->box->min_width || + box->max_width != other->box->max_width || + box->height != other->box->height || + box->min_height != other->box->min_height || + box->max_height != other->box->max_height) + return Layout; + + if (box->vertical_align != other->box->vertical_align || noninherited_flags._vertical_align != other->noninherited_flags._vertical_align) + return Layout; + + if (box->boxSizing != other->box->boxSizing) + return Layout; + + if (surround->margin != other->surround->margin) + return Layout; + + if (surround->padding != other->surround->padding) + return Layout; + + if (rareNonInheritedData.get() != other->rareNonInheritedData.get()) { + if (rareNonInheritedData->m_appearance != other->rareNonInheritedData->m_appearance || + rareNonInheritedData->marginTopCollapse != other->rareNonInheritedData->marginTopCollapse || + rareNonInheritedData->marginBottomCollapse != other->rareNonInheritedData->marginBottomCollapse || + rareNonInheritedData->lineClamp != other->rareNonInheritedData->lineClamp || + rareNonInheritedData->textOverflow != other->rareNonInheritedData->textOverflow) + return Layout; + + if (rareNonInheritedData->flexibleBox.get() != other->rareNonInheritedData->flexibleBox.get() && + *rareNonInheritedData->flexibleBox.get() != *other->rareNonInheritedData->flexibleBox.get()) + return Layout; + + if (!rareNonInheritedData->shadowDataEquivalent(*other->rareNonInheritedData.get())) + return Layout; + + if (!rareNonInheritedData->reflectionDataEquivalent(*other->rareNonInheritedData.get())) + return Layout; + + if (rareNonInheritedData->m_multiCol.get() != other->rareNonInheritedData->m_multiCol.get() && + *rareNonInheritedData->m_multiCol.get() != *other->rareNonInheritedData->m_multiCol.get()) + return Layout; + + if (rareNonInheritedData->m_transform.get() != other->rareNonInheritedData->m_transform.get() && + *rareNonInheritedData->m_transform.get() != *other->rareNonInheritedData->m_transform.get()) + return Layout; + +#if ENABLE(DASHBOARD_SUPPORT) + // If regions change, trigger a relayout to re-calc regions. + if (rareNonInheritedData->m_dashboardRegions != other->rareNonInheritedData->m_dashboardRegions) + return Layout; +#endif + } + + if (rareInheritedData.get() != other->rareInheritedData.get()) { + if (rareInheritedData->highlight != other->rareInheritedData->highlight || + rareInheritedData->textSizeAdjust != other->rareInheritedData->textSizeAdjust || + rareInheritedData->wordBreak != other->rareInheritedData->wordBreak || + rareInheritedData->wordWrap != other->rareInheritedData->wordWrap || + rareInheritedData->nbspMode != other->rareInheritedData->nbspMode || + rareInheritedData->khtmlLineBreak != other->rareInheritedData->khtmlLineBreak || + rareInheritedData->textSecurity != other->rareInheritedData->textSecurity) + return Layout; + + if (!rareInheritedData->shadowDataEquivalent(*other->rareInheritedData.get())) + return Layout; + + if (textStrokeWidth() != other->textStrokeWidth()) + return Layout; + } + + if (inherited->indent != other->inherited->indent || + inherited->line_height != other->inherited->line_height || + inherited->list_style_image != other->inherited->list_style_image || + inherited->font != other->inherited->font || + inherited->horizontal_border_spacing != other->inherited->horizontal_border_spacing || + inherited->vertical_border_spacing != other->inherited->vertical_border_spacing || + inherited_flags._box_direction != other->inherited_flags._box_direction || + inherited_flags._visuallyOrdered != other->inherited_flags._visuallyOrdered || + inherited_flags._htmlHacks != other->inherited_flags._htmlHacks || + noninherited_flags._position != other->noninherited_flags._position || + noninherited_flags._floating != other->noninherited_flags._floating || + noninherited_flags._originalDisplay != other->noninherited_flags._originalDisplay) + return Layout; + + + if (((int)noninherited_flags._effectiveDisplay) >= TABLE) { + if (inherited_flags._border_collapse != other->inherited_flags._border_collapse || + inherited_flags._empty_cells != other->inherited_flags._empty_cells || + inherited_flags._caption_side != other->inherited_flags._caption_side || + noninherited_flags._table_layout != other->noninherited_flags._table_layout) + return Layout; + + // In the collapsing border model, 'hidden' suppresses other borders, while 'none' + // does not, so these style differences can be width differences. + if (inherited_flags._border_collapse && + (borderTopStyle() == BHIDDEN && other->borderTopStyle() == BNONE || + borderTopStyle() == BNONE && other->borderTopStyle() == BHIDDEN || + borderBottomStyle() == BHIDDEN && other->borderBottomStyle() == BNONE || + borderBottomStyle() == BNONE && other->borderBottomStyle() == BHIDDEN || + borderLeftStyle() == BHIDDEN && other->borderLeftStyle() == BNONE || + borderLeftStyle() == BNONE && other->borderLeftStyle() == BHIDDEN || + borderRightStyle() == BHIDDEN && other->borderRightStyle() == BNONE || + borderRightStyle() == BNONE && other->borderRightStyle() == BHIDDEN)) + return Layout; + } + + if (noninherited_flags._effectiveDisplay == LIST_ITEM) { + if (inherited_flags._list_style_type != other->inherited_flags._list_style_type || + inherited_flags._list_style_position != other->inherited_flags._list_style_position) + return Layout; + } + + if (inherited_flags._text_align != other->inherited_flags._text_align || + inherited_flags._text_transform != other->inherited_flags._text_transform || + inherited_flags._direction != other->inherited_flags._direction || + inherited_flags._white_space != other->inherited_flags._white_space || + noninherited_flags._clear != other->noninherited_flags._clear) + return Layout; + + // Overflow returns a layout hint. + if (noninherited_flags._overflowX != other->noninherited_flags._overflowX || + noninherited_flags._overflowY != other->noninherited_flags._overflowY) + return Layout; + + // If our border widths change, then we need to layout. Other changes to borders + // only necessitate a repaint. + if (borderLeftWidth() != other->borderLeftWidth() || + borderTopWidth() != other->borderTopWidth() || + borderBottomWidth() != other->borderBottomWidth() || + borderRightWidth() != other->borderRightWidth()) + return Layout; + + // If the counter directives change, trigger a relayout to re-calculate counter values and rebuild the counter node tree. + const CounterDirectiveMap* mapA = rareNonInheritedData->m_counterDirectives.get(); + const CounterDirectiveMap* mapB = other->rareNonInheritedData->m_counterDirectives.get(); + if (!(mapA == mapB || (mapA && mapB && *mapA == *mapB))) + return Layout; + if (visual->counterIncrement != other->visual->counterIncrement || + visual->counterReset != other->visual->counterReset) + return Layout; + + if (inherited->m_effectiveZoom != other->inherited->m_effectiveZoom) + return Layout; + + // Make sure these left/top/right/bottom checks stay below all layout checks and above + // all visible checks. + if (position() != StaticPosition) { + if (surround->offset != other->surround->offset) { + // Optimize for the case where a positioned layer is moving but not changing size. + if (position() == AbsolutePosition && positionedObjectMoved(surround->offset, other->surround->offset)) + return LayoutPositionedMovementOnly; + + // FIXME: We will need to do a bit of work in RenderObject/Box::setStyle before we + // can stop doing a layout when relative positioned objects move. In particular, we'll need + // to update scrolling positions and figure out how to do a repaint properly of the updated layer. + //if (other->position() == RelativePosition) + // return RepaintLayer; + //else + return Layout; + } else if (box->z_index != other->box->z_index || box->z_auto != other->box->z_auto || + visual->clip != other->visual->clip || visual->hasClip != other->visual->hasClip) + return RepaintLayer; + } + + if (rareNonInheritedData->opacity != other->rareNonInheritedData->opacity || + rareNonInheritedData->m_mask != other->rareNonInheritedData->m_mask || + rareNonInheritedData->m_maskBoxImage != other->rareNonInheritedData->m_maskBoxImage) + return RepaintLayer; + + if (inherited->color != other->inherited->color || + inherited_flags._visibility != other->inherited_flags._visibility || + inherited_flags._text_decorations != other->inherited_flags._text_decorations || + inherited_flags._force_backgrounds_to_white != other->inherited_flags._force_backgrounds_to_white || + surround->border != other->surround->border || + *background.get() != *other->background.get() || + visual->textDecoration != other->visual->textDecoration || + rareInheritedData->userModify != other->rareInheritedData->userModify || + rareInheritedData->userSelect != other->rareInheritedData->userSelect || + rareNonInheritedData->userDrag != other->rareNonInheritedData->userDrag || + rareNonInheritedData->m_borderFit != other->rareNonInheritedData->m_borderFit || + rareInheritedData->textFillColor != other->rareInheritedData->textFillColor || + rareInheritedData->textStrokeColor != other->rareInheritedData->textStrokeColor) + return Repaint; + + // Cursors are not checked, since they will be set appropriately in response to mouse events, + // so they don't need to cause any repaint or layout. + + // Animations don't need to be checked either. We always set the new style on the RenderObject, so we will get a chance to fire off + // the resulting transition properly. + return Equal; +} + +void RenderStyle::setClip(Length top, Length right, Length bottom, Length left) +{ + StyleVisualData *data = visual.access(); + data->clip.m_top = top; + data->clip.m_right = right; + data->clip.m_bottom = bottom; + data->clip.m_left = left; +} + +void RenderStyle::addCursor(CachedImage* image, const IntPoint& hotSpot) +{ + CursorData data; + data.cursorImage = image; + data.hotSpot = hotSpot; + if (!inherited.access()->cursorData) + inherited.access()->cursorData = CursorList::create(); + inherited.access()->cursorData->append(data); +} + +void RenderStyle::setCursorList(PassRefPtr<CursorList> other) +{ + inherited.access()->cursorData = other; +} + +void RenderStyle::clearCursorList() +{ + inherited.access()->cursorData = CursorList::create(); +} + +bool RenderStyle::contentDataEquivalent(const RenderStyle* otherStyle) const +{ + ContentData* c1 = rareNonInheritedData->m_content.get(); + ContentData* c2 = otherStyle->rareNonInheritedData->m_content.get(); + + while (c1 && c2) { + if (c1->m_type != c2->m_type) + return false; + + switch (c1->m_type) { + case CONTENT_NONE: + break; + case CONTENT_TEXT: + if (!equal(c1->m_content.m_text, c2->m_content.m_text)) + return false; + break; + case CONTENT_OBJECT: + if (!StyleImage::imagesEquivalent(c1->m_content.m_image, c2->m_content.m_image)) + return false; + break; + case CONTENT_COUNTER: + if (*c1->m_content.m_counter != *c2->m_content.m_counter) + return false; + break; + } + + c1 = c1->m_next; + c2 = c2->m_next; + } + + return !c1 && !c2; +} + +void RenderStyle::clearContent() +{ + if (rareNonInheritedData->m_content) + rareNonInheritedData->m_content->clear(); +} + +void RenderStyle::setContent(PassRefPtr<StyleImage> image, bool add) +{ + if (!image) + return; // The object is null. Nothing to do. Just bail. + + OwnPtr<ContentData>& content = rareNonInheritedData.access()->m_content; + ContentData* lastContent = content.get(); + while (lastContent && lastContent->m_next) + lastContent = lastContent->m_next; + + bool reuseContent = !add; + ContentData* newContentData; + if (reuseContent && content) { + content->clear(); + newContentData = content.release(); + } else + newContentData = new ContentData; + + if (lastContent && !reuseContent) + lastContent->m_next = newContentData; + else + content.set(newContentData); + + newContentData->m_content.m_image = image.releaseRef(); + newContentData->m_type = CONTENT_OBJECT; +} + +void RenderStyle::setContent(StringImpl* s, bool add) +{ + if (!s) + return; // The string is null. Nothing to do. Just bail. + + OwnPtr<ContentData>& content = rareNonInheritedData.access()->m_content; + ContentData* lastContent = content.get(); + while (lastContent && lastContent->m_next) + lastContent = lastContent->m_next; + + bool reuseContent = !add; + if (add && lastContent) { + if (lastContent->m_type == CONTENT_TEXT) { + // We can augment the existing string and share this ContentData node. + StringImpl* oldStr = lastContent->m_content.m_text; + String newStr = oldStr; + newStr.append(s); + newStr.impl()->ref(); + oldStr->deref(); + lastContent->m_content.m_text = newStr.impl(); + return; + } + } + + ContentData* newContentData = 0; + if (reuseContent && content) { + content->clear(); + newContentData = content.release(); + } else + newContentData = new ContentData; + + if (lastContent && !reuseContent) + lastContent->m_next = newContentData; + else + content.set(newContentData); + + newContentData->m_content.m_text = s; + newContentData->m_content.m_text->ref(); + newContentData->m_type = CONTENT_TEXT; +} + +void RenderStyle::setContent(CounterContent* c, bool add) +{ + if (!c) + return; + + OwnPtr<ContentData>& content = rareNonInheritedData.access()->m_content; + ContentData* lastContent = content.get(); + while (lastContent && lastContent->m_next) + lastContent = lastContent->m_next; + + bool reuseContent = !add; + ContentData* newContentData = 0; + if (reuseContent && content) { + content->clear(); + newContentData = content.release(); + } else + newContentData = new ContentData; + + if (lastContent && !reuseContent) + lastContent->m_next = newContentData; + else + content.set(newContentData); + + newContentData->m_content.m_counter = c; + newContentData->m_type = CONTENT_COUNTER; +} + +void RenderStyle::applyTransform(TransformationMatrix& transform, const IntSize& borderBoxSize, bool includeTransformOrigin) const +{ + // transform-origin brackets the transform with translate operations. + // Optimize for the case where the only transform is a translation, since the transform-origin is irrelevant + // in that case. + bool applyTransformOrigin = false; + unsigned s = rareNonInheritedData->m_transform->m_operations.operations().size(); + unsigned i; + if (includeTransformOrigin) { + for (i = 0; i < s; i++) { + TransformOperation::OperationType type = rareNonInheritedData->m_transform->m_operations.operations()[i]->getOperationType(); + if (type != TransformOperation::TRANSLATE_X && + type != TransformOperation::TRANSLATE_Y && + type != TransformOperation::TRANSLATE) { + applyTransformOrigin = true; + break; + } + } + } + + if (applyTransformOrigin) + transform.translate(transformOriginX().calcFloatValue(borderBoxSize.width()), transformOriginY().calcFloatValue(borderBoxSize.height())); + + for (i = 0; i < s; i++) + rareNonInheritedData->m_transform->m_operations.operations()[i]->apply(transform, borderBoxSize); + + if (applyTransformOrigin) + transform.translate(-transformOriginX().calcFloatValue(borderBoxSize.width()), -transformOriginY().calcFloatValue(borderBoxSize.height())); +} + +#if ENABLE(XBL) +void RenderStyle::addBindingURI(StringImpl* uri) +{ + BindingURI* binding = new BindingURI(uri); + if (!bindingURIs()) + SET_VAR(rareNonInheritedData, bindingURI, binding) + else + for (BindingURI* b = bindingURIs(); b; b = b->next()) { + if (!b->next()) + b->setNext(binding); + } +} +#endif + +void RenderStyle::setTextShadow(ShadowData* val, bool add) +{ + StyleRareInheritedData* rareData = rareInheritedData.access(); + if (!add) { + delete rareData->textShadow; + rareData->textShadow = val; + return; + } + + val->next = rareData->textShadow; + rareData->textShadow = val; +} + +void RenderStyle::setBoxShadow(ShadowData* shadowData, bool add) +{ + StyleRareNonInheritedData* rareData = rareNonInheritedData.access(); + if (!add) { + rareData->m_boxShadow.set(shadowData); + return; + } + + shadowData->next = rareData->m_boxShadow.release(); + rareData->m_boxShadow.set(shadowData); +} + +const CounterDirectiveMap* RenderStyle::counterDirectives() const +{ + return rareNonInheritedData->m_counterDirectives.get(); +} + +CounterDirectiveMap& RenderStyle::accessCounterDirectives() +{ + OwnPtr<CounterDirectiveMap>& map = rareNonInheritedData.access()->m_counterDirectives; + if (!map) + map.set(new CounterDirectiveMap); + return *map.get(); +} + +#if ENABLE(DASHBOARD_SUPPORT) +const Vector<StyleDashboardRegion>& RenderStyle::initialDashboardRegions() +{ + DEFINE_STATIC_LOCAL(Vector<StyleDashboardRegion>, emptyList, ()); + return emptyList; +} + +const Vector<StyleDashboardRegion>& RenderStyle::noneDashboardRegions() +{ + DEFINE_STATIC_LOCAL(Vector<StyleDashboardRegion>, noneList, ()); + static bool noneListInitialized = false; + + if (!noneListInitialized) { + StyleDashboardRegion region; + region.label = ""; + region.offset.m_top = Length(); + region.offset.m_right = Length(); + region.offset.m_bottom = Length(); + region.offset.m_left = Length(); + region.type = StyleDashboardRegion::None; + noneList.append(region); + noneListInitialized = true; + } + return noneList; +} +#endif + +void RenderStyle::adjustAnimations() +{ + AnimationList* animationList = rareNonInheritedData->m_animations.get(); + if (!animationList) + return; + + // get rid of empty transitions and anything beyond them + for (size_t i = 0; i < animationList->size(); ++i) { + if (animationList->animation(i)->isEmpty()) { + animationList->resize(i); + break; + } + } + + if (animationList->isEmpty()) { + clearAnimations(); + return; + } + + // Repeat patterns into layers that don't have some properties set. + animationList->fillUnsetProperties(); +} + +void RenderStyle::adjustTransitions() +{ + AnimationList* transitionList = rareNonInheritedData->m_transitions.get(); + if (!transitionList) + return; + + // get rid of empty transitions and anything beyond them + for (size_t i = 0; i < transitionList->size(); ++i) { + if (transitionList->animation(i)->isEmpty()) { + transitionList->resize(i); + break; + } + } + + if (transitionList->isEmpty()) { + clearTransitions(); + return; + } + + // Repeat patterns into layers that don't have some properties set. + transitionList->fillUnsetProperties(); + + // Make sure there are no duplicate properties. This is an O(n^2) algorithm + // but the lists tend to be very short, so it is probably ok + for (size_t i = 0; i < transitionList->size(); ++i) { + for (size_t j = i+1; j < transitionList->size(); ++j) { + if (transitionList->animation(i)->property() == transitionList->animation(j)->property()) { + // toss i + transitionList->remove(i); + j = i; + } + } + } +} + +AnimationList* RenderStyle::accessAnimations() +{ + if (!rareNonInheritedData.access()->m_animations) + rareNonInheritedData.access()->m_animations.set(new AnimationList()); + return rareNonInheritedData->m_animations.get(); +} + +AnimationList* RenderStyle::accessTransitions() +{ + if (!rareNonInheritedData.access()->m_transitions) + rareNonInheritedData.access()->m_transitions.set(new AnimationList()); + return rareNonInheritedData->m_transitions.get(); +} + +const Animation* RenderStyle::transitionForProperty(int property) +{ + if (transitions()) { + for (size_t i = 0; i < transitions()->size(); ++i) { + const Animation* p = transitions()->animation(i); + if (p->property() == cAnimateAll || p->property() == property) { + return p; + } + } + } + return 0; +} + +void RenderStyle::setBlendedFontSize(int size) +{ + FontDescription desc(fontDescription()); + desc.setSpecifiedSize(size); + desc.setComputedSize(size); + setFontDescription(desc); + font().update(font().fontSelector()); +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/style/RenderStyle.h b/src/3rdparty/webkit/WebCore/rendering/style/RenderStyle.h new file mode 100644 index 0000000..f38f943 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/RenderStyle.h @@ -0,0 +1,1129 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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. + * + */ + +#ifndef RenderStyle_h +#define RenderStyle_h + +#include "TransformationMatrix.h" +#include "AnimationList.h" +#include "BorderData.h" +#include "BorderValue.h" +#include "CSSHelper.h" +#include "CSSImageGeneratorValue.h" +#include "CSSPrimitiveValue.h" +#include "CSSReflectionDirection.h" +#include "CSSValueList.h" +#include "CachedImage.h" +#include "CollapsedBorderValue.h" +#include "Color.h" +#include "ContentData.h" +#include "CounterDirectives.h" +#include "CursorList.h" +#include "DataRef.h" +#include "FillLayer.h" +#include "FloatPoint.h" +#include "Font.h" +#include "GraphicsTypes.h" +#include "IntRect.h" +#include "Length.h" +#include "LengthBox.h" +#include "LengthSize.h" +#include "NinePieceImage.h" +#include "OutlineValue.h" +#include "Pair.h" +#include "RenderStyleConstants.h" +#include "ShadowData.h" +#include "StyleBackgroundData.h" +#include "StyleBoxData.h" +#include "StyleFlexibleBoxData.h" +#include "StyleInheritedData.h" +#include "StyleMarqueeData.h" +#include "StyleMultiColData.h" +#include "StyleRareInheritedData.h" +#include "StyleRareNonInheritedData.h" +#include "StyleReflection.h" +#include "StyleSurroundData.h" +#include "StyleTransformData.h" +#include "StyleVisualData.h" +#include "TextDirection.h" +#include "ThemeTypes.h" +#include "TimingFunction.h" +#include "TransformOperations.h" +#include <wtf/OwnPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/StdLibExtras.h> +#include <wtf/Vector.h> + +#if ENABLE(DASHBOARD_SUPPORT) +#include "StyleDashboardRegion.h" +#endif + +#if ENABLE(SVG) +#include "SVGRenderStyle.h" +#endif + +#if ENABLE(XBL) +#include "BindingURI.h" +#endif + +template<typename T, typename U> inline bool compareEqual(const T& t, const U& u) { return t == static_cast<T>(u); } + +#define SET_VAR(group, variable, value) \ + if (!compareEqual(group->variable, value)) \ + group.access()->variable = value; + +namespace WebCore { + +using std::max; + +class CSSStyleSelector; +class CSSValueList; +class CachedImage; +class Pair; +class StringImpl; +class StyleImage; + +class RenderStyle: public RefCounted<RenderStyle> { + friend class CSSStyleSelector; + +public: + // static pseudo styles. Dynamic ones are produced on the fly. + enum PseudoId { NOPSEUDO, FIRST_LINE, FIRST_LETTER, BEFORE, AFTER, SELECTION, FIRST_LINE_INHERITED, SCROLLBAR, FILE_UPLOAD_BUTTON, INPUT_PLACEHOLDER, + SLIDER_THUMB, SEARCH_CANCEL_BUTTON, SEARCH_DECORATION, SEARCH_RESULTS_DECORATION, SEARCH_RESULTS_BUTTON, MEDIA_CONTROLS_PANEL, + MEDIA_CONTROLS_PLAY_BUTTON, MEDIA_CONTROLS_MUTE_BUTTON, MEDIA_CONTROLS_TIMELINE, MEDIA_CONTROLS_TIME_DISPLAY, + MEDIA_CONTROLS_SEEK_BACK_BUTTON, MEDIA_CONTROLS_SEEK_FORWARD_BUTTON , MEDIA_CONTROLS_FULLSCREEN_BUTTON, + SCROLLBAR_THUMB, SCROLLBAR_BUTTON, SCROLLBAR_TRACK, SCROLLBAR_TRACK_PIECE, SCROLLBAR_CORNER, RESIZER }; + static const int FIRST_INTERNAL_PSEUDOID = FILE_UPLOAD_BUTTON; + +private: + static TransformOperations s_initialTransform; + +protected: + +// !START SYNC!: Keep this in sync with the copy constructor in RenderStyle.cpp + + // inherit + struct InheritedFlags { + bool operator==(const InheritedFlags& other) const + { + return (_empty_cells == other._empty_cells) && + (_caption_side == other._caption_side) && + (_list_style_type == other._list_style_type) && + (_list_style_position == other._list_style_position) && + (_visibility == other._visibility) && + (_text_align == other._text_align) && + (_text_transform == other._text_transform) && + (_text_decorations == other._text_decorations) && + (_text_transform == other._text_transform) && + (_cursor_style == other._cursor_style) && + (_direction == other._direction) && + (_border_collapse == other._border_collapse) && + (_white_space == other._white_space) && + (_box_direction == other._box_direction) && + (_visuallyOrdered == other._visuallyOrdered) && + (_htmlHacks == other._htmlHacks) && + (_force_backgrounds_to_white == other._force_backgrounds_to_white) && + (_pointerEvents == other._pointerEvents); + } + + bool operator!=(const InheritedFlags& other) const { return !(*this == other); } + + unsigned _empty_cells : 1; // EEmptyCell + unsigned _caption_side : 2; // ECaptionSide + unsigned _list_style_type : 5 ; // EListStyleType + unsigned _list_style_position : 1; // EListStylePosition + unsigned _visibility : 2; // EVisibility + unsigned _text_align : 3; // ETextAlign + unsigned _text_transform : 2; // ETextTransform + unsigned _text_decorations : 4; + unsigned _cursor_style : 6; // ECursor + unsigned _direction : 1; // TextDirection + bool _border_collapse : 1 ; + unsigned _white_space : 3; // EWhiteSpace + unsigned _box_direction : 1; // EBoxDirection (CSS3 box_direction property, flexible box layout module) + + // non CSS2 inherited + bool _visuallyOrdered : 1; + bool _htmlHacks :1; + bool _force_backgrounds_to_white : 1; + unsigned _pointerEvents : 4; // EPointerEvents + } inherited_flags; + +// don't inherit + struct NonInheritedFlags { + bool operator==(const NonInheritedFlags& other) const + { + return (_effectiveDisplay == other._effectiveDisplay) && + (_originalDisplay == other._originalDisplay) && + (_overflowX == other._overflowX) && + (_overflowY == other._overflowY) && + (_vertical_align == other._vertical_align) && + (_clear == other._clear) && + (_position == other._position) && + (_floating == other._floating) && + (_table_layout == other._table_layout) && + (_page_break_before == other._page_break_before) && + (_page_break_after == other._page_break_after) && + (_styleType == other._styleType) && + (_affectedByHover == other._affectedByHover) && + (_affectedByActive == other._affectedByActive) && + (_affectedByDrag == other._affectedByDrag) && + (_pseudoBits == other._pseudoBits) && + (_unicodeBidi == other._unicodeBidi); + } + + bool operator!=(const NonInheritedFlags& other) const { return !(*this == other); } + + unsigned _effectiveDisplay : 5; // EDisplay + unsigned _originalDisplay : 5; // EDisplay + unsigned _overflowX : 3; // EOverflow + unsigned _overflowY : 3; // EOverflow + unsigned _vertical_align : 4; // EVerticalAlign + unsigned _clear : 2; // EClear + unsigned _position : 2; // EPosition + unsigned _floating : 2; // EFloat + unsigned _table_layout : 1; // ETableLayout + + unsigned _page_break_before : 2; // EPageBreak + unsigned _page_break_after : 2; // EPageBreak + + unsigned _styleType : 5; // PseudoId + bool _affectedByHover : 1; + bool _affectedByActive : 1; + bool _affectedByDrag : 1; + unsigned _pseudoBits : 7; + unsigned _unicodeBidi : 2; // EUnicodeBidi + } noninherited_flags; + + // non-inherited attributes + DataRef<StyleBoxData> box; + DataRef<StyleVisualData> visual; + DataRef<StyleBackgroundData> background; + DataRef<StyleSurroundData> surround; + DataRef<StyleRareNonInheritedData> rareNonInheritedData; + + // inherited attributes + DataRef<StyleRareInheritedData> rareInheritedData; + DataRef<StyleInheritedData> inherited; + + // list of associated pseudo styles + RefPtr<RenderStyle> m_cachedPseudoStyle; + + unsigned m_pseudoState : 3; // PseudoState + bool m_affectedByAttributeSelectors : 1; + bool m_unique : 1; + + // Bits for dynamic child matching. + bool m_affectedByEmpty : 1; + bool m_emptyState : 1; + + // We optimize for :first-child and :last-child. The other positional child selectors like nth-child or + // *-child-of-type, we will just give up and re-evaluate whenever children change at all. + bool m_childrenAffectedByFirstChildRules : 1; + bool m_childrenAffectedByLastChildRules : 1; + bool m_childrenAffectedByDirectAdjacentRules : 1; + bool m_childrenAffectedByForwardPositionalRules : 1; + bool m_childrenAffectedByBackwardPositionalRules : 1; + bool m_firstChildState : 1; + bool m_lastChildState : 1; + unsigned m_childIndex : 18; // Plenty of bits to cache an index. + +#if ENABLE(SVG) + DataRef<SVGRenderStyle> m_svgStyle; +#endif + +// !END SYNC! + +protected: + void setBitDefaults() + { + inherited_flags._empty_cells = initialEmptyCells(); + inherited_flags._caption_side = initialCaptionSide(); + inherited_flags._list_style_type = initialListStyleType(); + inherited_flags._list_style_position = initialListStylePosition(); + inherited_flags._visibility = initialVisibility(); + inherited_flags._text_align = initialTextAlign(); + inherited_flags._text_transform = initialTextTransform(); + inherited_flags._text_decorations = initialTextDecoration(); + inherited_flags._cursor_style = initialCursor(); + inherited_flags._direction = initialDirection(); + inherited_flags._border_collapse = initialBorderCollapse(); + inherited_flags._white_space = initialWhiteSpace(); + inherited_flags._visuallyOrdered = initialVisuallyOrdered(); + inherited_flags._htmlHacks=false; + inherited_flags._box_direction = initialBoxDirection(); + inherited_flags._force_backgrounds_to_white = false; + inherited_flags._pointerEvents = initialPointerEvents(); + + noninherited_flags._effectiveDisplay = noninherited_flags._originalDisplay = initialDisplay(); + noninherited_flags._overflowX = initialOverflowX(); + noninherited_flags._overflowY = initialOverflowY(); + noninherited_flags._vertical_align = initialVerticalAlign(); + noninherited_flags._clear = initialClear(); + noninherited_flags._position = initialPosition(); + noninherited_flags._floating = initialFloating(); + noninherited_flags._table_layout = initialTableLayout(); + noninherited_flags._page_break_before = initialPageBreak(); + noninherited_flags._page_break_after = initialPageBreak(); + noninherited_flags._styleType = NOPSEUDO; + noninherited_flags._affectedByHover = false; + noninherited_flags._affectedByActive = false; + noninherited_flags._affectedByDrag = false; + noninherited_flags._pseudoBits = 0; + noninherited_flags._unicodeBidi = initialUnicodeBidi(); + } + +protected: + RenderStyle(); + // used to create the default style. + RenderStyle(bool); + RenderStyle(const RenderStyle&); + +public: + static PassRefPtr<RenderStyle> create(); + static PassRefPtr<RenderStyle> createDefaultStyle(); + static PassRefPtr<RenderStyle> clone(const RenderStyle*); + + ~RenderStyle(); + + void inheritFrom(const RenderStyle* inheritParent); + + PseudoId styleType() { return static_cast<PseudoId>(noninherited_flags._styleType); } + void setStyleType(PseudoId styleType) { noninherited_flags._styleType = styleType; } + + RenderStyle* getCachedPseudoStyle(PseudoId); + RenderStyle* addCachedPseudoStyle(PassRefPtr<RenderStyle>); + + bool affectedByHoverRules() const { return noninherited_flags._affectedByHover; } + bool affectedByActiveRules() const { return noninherited_flags._affectedByActive; } + bool affectedByDragRules() const { return noninherited_flags._affectedByDrag; } + + void setAffectedByHoverRules(bool b) { noninherited_flags._affectedByHover = b; } + void setAffectedByActiveRules(bool b) { noninherited_flags._affectedByActive = b; } + void setAffectedByDragRules(bool b) { noninherited_flags._affectedByDrag = b; } + + bool operator==(const RenderStyle& other) const; + bool operator!=(const RenderStyle& other) const { return !(*this == other); } + bool isFloating() const { return !(noninherited_flags._floating == FNONE); } + bool hasMargin() const { return surround->margin.nonZero(); } + bool hasBorder() const { return surround->border.hasBorder(); } + bool hasPadding() const { return surround->padding.nonZero(); } + bool hasOffset() const { return surround->offset.nonZero(); } + + bool hasBackground() const + { + if (backgroundColor().isValid() && backgroundColor().alpha() > 0) + return true; + return background->m_background.hasImage(); + } + bool hasFixedBackgroundImage() const { return background->m_background.hasFixedImage(); } + bool hasAppearance() const { return appearance() != NoControlPart; } + + bool visuallyOrdered() const { return inherited_flags._visuallyOrdered; } + void setVisuallyOrdered(bool b) { inherited_flags._visuallyOrdered = b; } + + bool isStyleAvailable() const; + + bool hasPseudoStyle(PseudoId pseudo) const; + void setHasPseudoStyle(PseudoId pseudo); + + // attribute getter methods + + EDisplay display() const { return static_cast<EDisplay>(noninherited_flags._effectiveDisplay); } + EDisplay originalDisplay() const { return static_cast<EDisplay>(noninherited_flags._originalDisplay); } + + Length left() const { return surround->offset.left(); } + Length right() const { return surround->offset.right(); } + Length top() const { return surround->offset.top(); } + Length bottom() const { return surround->offset.bottom(); } + + EPosition position() const { return static_cast<EPosition>(noninherited_flags._position); } + EFloat floating() const { return static_cast<EFloat>(noninherited_flags._floating); } + + Length width() const { return box->width; } + Length height() const { return box->height; } + Length minWidth() const { return box->min_width; } + Length maxWidth() const { return box->max_width; } + Length minHeight() const { return box->min_height; } + Length maxHeight() const { return box->max_height; } + + const BorderData& border() const { return surround->border; } + const BorderValue& borderLeft() const { return surround->border.left; } + const BorderValue& borderRight() const { return surround->border.right; } + const BorderValue& borderTop() const { return surround->border.top; } + const BorderValue& borderBottom() const { return surround->border.bottom; } + + const NinePieceImage& borderImage() const { return surround->border.image; } + + const IntSize& borderTopLeftRadius() const { return surround->border.topLeft; } + const IntSize& borderTopRightRadius() const { return surround->border.topRight; } + const IntSize& borderBottomLeftRadius() const { return surround->border.bottomLeft; } + const IntSize& borderBottomRightRadius() const { return surround->border.bottomRight; } + bool hasBorderRadius() const { return surround->border.hasBorderRadius(); } + + unsigned short borderLeftWidth() const { return surround->border.borderLeftWidth(); } + EBorderStyle borderLeftStyle() const { return surround->border.left.style(); } + const Color& borderLeftColor() const { return surround->border.left.color; } + bool borderLeftIsTransparent() const { return surround->border.left.isTransparent(); } + unsigned short borderRightWidth() const { return surround->border.borderRightWidth(); } + EBorderStyle borderRightStyle() const { return surround->border.right.style(); } + const Color& borderRightColor() const { return surround->border.right.color; } + bool borderRightIsTransparent() const { return surround->border.right.isTransparent(); } + unsigned short borderTopWidth() const { return surround->border.borderTopWidth(); } + EBorderStyle borderTopStyle() const { return surround->border.top.style(); } + const Color& borderTopColor() const { return surround->border.top.color; } + bool borderTopIsTransparent() const { return surround->border.top.isTransparent(); } + unsigned short borderBottomWidth() const { return surround->border.borderBottomWidth(); } + EBorderStyle borderBottomStyle() const { return surround->border.bottom.style(); } + const Color& borderBottomColor() const { return surround->border.bottom.color; } + bool borderBottomIsTransparent() const { return surround->border.bottom.isTransparent(); } + + unsigned short outlineSize() const { return max(0, outlineWidth() + outlineOffset()); } + unsigned short outlineWidth() const + { + if (background->m_outline.style() == BNONE) + return 0; + return background->m_outline.width; + } + bool hasOutline() const { return outlineWidth() > 0 && outlineStyle() > BHIDDEN; } + EBorderStyle outlineStyle() const { return background->m_outline.style(); } + bool outlineStyleIsAuto() const { return background->m_outline._auto; } + const Color& outlineColor() const { return background->m_outline.color; } + + EOverflow overflowX() const { return static_cast<EOverflow>(noninherited_flags._overflowX); } + EOverflow overflowY() const { return static_cast<EOverflow>(noninherited_flags._overflowY); } + + EVisibility visibility() const { return static_cast<EVisibility>(inherited_flags._visibility); } + EVerticalAlign verticalAlign() const { return static_cast<EVerticalAlign>(noninherited_flags._vertical_align); } + Length verticalAlignLength() const { return box->vertical_align; } + + Length clipLeft() const { return visual->clip.left(); } + Length clipRight() const { return visual->clip.right(); } + Length clipTop() const { return visual->clip.top(); } + Length clipBottom() const { return visual->clip.bottom(); } + LengthBox clip() const { return visual->clip; } + bool hasClip() const { return visual->hasClip; } + + EUnicodeBidi unicodeBidi() const { return static_cast<EUnicodeBidi>(noninherited_flags._unicodeBidi); } + + EClear clear() const { return static_cast<EClear>(noninherited_flags._clear); } + ETableLayout tableLayout() const { return static_cast<ETableLayout>(noninherited_flags._table_layout); } + + const Font& font() const { return inherited->font; } + const FontDescription& fontDescription() const { return inherited->font.fontDescription(); } + int fontSize() const { return inherited->font.pixelSize(); } + + const Color& color() const { return inherited->color; } + Length textIndent() const { return inherited->indent; } + ETextAlign textAlign() const { return static_cast<ETextAlign>(inherited_flags._text_align); } + ETextTransform textTransform() const { return static_cast<ETextTransform>(inherited_flags._text_transform); } + int textDecorationsInEffect() const { return inherited_flags._text_decorations; } + int textDecoration() const { return visual->textDecoration; } + int wordSpacing() const { return inherited->font.wordSpacing(); } + int letterSpacing() const { return inherited->font.letterSpacing(); } + + float zoom() const { return visual->m_zoom; } + float effectiveZoom() const { return inherited->m_effectiveZoom; } + + TextDirection direction() const { return static_cast<TextDirection>(inherited_flags._direction); } + Length lineHeight() const { return inherited->line_height; } + + EWhiteSpace whiteSpace() const { return static_cast<EWhiteSpace>(inherited_flags._white_space); } + static bool autoWrap(EWhiteSpace ws) + { + // Nowrap and pre don't automatically wrap. + return ws != NOWRAP && ws != PRE; + } + + bool autoWrap() const + { + return autoWrap(whiteSpace()); + } + + static bool preserveNewline(EWhiteSpace ws) + { + // Normal and nowrap do not preserve newlines. + return ws != NORMAL && ws != NOWRAP; + } + + bool preserveNewline() const + { + return preserveNewline(whiteSpace()); + } + + static bool collapseWhiteSpace(EWhiteSpace ws) + { + // Pre and prewrap do not collapse whitespace. + return ws != PRE && ws != PRE_WRAP; + } + + bool collapseWhiteSpace() const + { + return collapseWhiteSpace(whiteSpace()); + } + + bool isCollapsibleWhiteSpace(UChar c) const + { + switch (c) { + case ' ': + case '\t': + return collapseWhiteSpace(); + case '\n': + return !preserveNewline(); + } + return false; + } + + bool breakOnlyAfterWhiteSpace() const + { + return whiteSpace() == PRE_WRAP || khtmlLineBreak() == AFTER_WHITE_SPACE; + } + + bool breakWords() const + { + return wordBreak() == BreakWordBreak || wordWrap() == BreakWordWrap; + } + + const Color& backgroundColor() const { return background->m_color; } + StyleImage* backgroundImage() const { return background->m_background.m_image.get(); } + EFillRepeat backgroundRepeat() const { return static_cast<EFillRepeat>(background->m_background.m_repeat); } + CompositeOperator backgroundComposite() const { return static_cast<CompositeOperator>(background->m_background.m_composite); } + bool backgroundAttachment() const { return background->m_background.m_attachment; } + EFillBox backgroundClip() const { return static_cast<EFillBox>(background->m_background.m_clip); } + EFillBox backgroundOrigin() const { return static_cast<EFillBox>(background->m_background.m_origin); } + Length backgroundXPosition() const { return background->m_background.m_xPosition; } + Length backgroundYPosition() const { return background->m_background.m_yPosition; } + LengthSize backgroundSize() const { return background->m_background.m_size; } + FillLayer* accessBackgroundLayers() { return &(background.access()->m_background); } + const FillLayer* backgroundLayers() const { return &(background->m_background); } + + StyleImage* maskImage() const { return rareNonInheritedData->m_mask.m_image.get(); } + EFillRepeat maskRepeat() const { return static_cast<EFillRepeat>(rareNonInheritedData->m_mask.m_repeat); } + CompositeOperator maskComposite() const { return static_cast<CompositeOperator>(rareNonInheritedData->m_mask.m_composite); } + bool maskAttachment() const { return rareNonInheritedData->m_mask.m_attachment; } + EFillBox maskClip() const { return static_cast<EFillBox>(rareNonInheritedData->m_mask.m_clip); } + EFillBox maskOrigin() const { return static_cast<EFillBox>(rareNonInheritedData->m_mask.m_origin); } + Length maskXPosition() const { return rareNonInheritedData->m_mask.m_xPosition; } + Length maskYPosition() const { return rareNonInheritedData->m_mask.m_yPosition; } + LengthSize maskSize() const { return rareNonInheritedData->m_mask.m_size; } + FillLayer* accessMaskLayers() { return &(rareNonInheritedData.access()->m_mask); } + const FillLayer* maskLayers() const { return &(rareNonInheritedData->m_mask); } + const NinePieceImage& maskBoxImage() const { return rareNonInheritedData->m_maskBoxImage; } + + // returns true for collapsing borders, false for separate borders + bool borderCollapse() const { return inherited_flags._border_collapse; } + short horizontalBorderSpacing() const { return inherited->horizontal_border_spacing; } + short verticalBorderSpacing() const { return inherited->vertical_border_spacing; } + EEmptyCell emptyCells() const { return static_cast<EEmptyCell>(inherited_flags._empty_cells); } + ECaptionSide captionSide() const { return static_cast<ECaptionSide>(inherited_flags._caption_side); } + + short counterIncrement() const { return visual->counterIncrement; } + short counterReset() const { return visual->counterReset; } + + EListStyleType listStyleType() const { return static_cast<EListStyleType>(inherited_flags._list_style_type); } + StyleImage* listStyleImage() const { return inherited->list_style_image.get(); } + EListStylePosition listStylePosition() const { return static_cast<EListStylePosition>(inherited_flags._list_style_position); } + + Length marginTop() const { return surround->margin.top(); } + Length marginBottom() const { return surround->margin.bottom(); } + Length marginLeft() const { return surround->margin.left(); } + Length marginRight() const { return surround->margin.right(); } + + LengthBox paddingBox() const { return surround->padding; } + Length paddingTop() const { return surround->padding.top(); } + Length paddingBottom() const { return surround->padding.bottom(); } + Length paddingLeft() const { return surround->padding.left(); } + Length paddingRight() const { return surround->padding.right(); } + + ECursor cursor() const { return static_cast<ECursor>(inherited_flags._cursor_style); } + + CursorList* cursors() const { return inherited->cursorData.get(); } + + short widows() const { return inherited->widows; } + short orphans() const { return inherited->orphans; } + EPageBreak pageBreakInside() const { return static_cast<EPageBreak>(inherited->page_break_inside); } + EPageBreak pageBreakBefore() const { return static_cast<EPageBreak>(noninherited_flags._page_break_before); } + EPageBreak pageBreakAfter() const { return static_cast<EPageBreak>(noninherited_flags._page_break_after); } + + // CSS3 Getter Methods +#if ENABLE(XBL) + BindingURI* bindingURIs() const { return rareNonInheritedData->bindingURI; } +#endif + + int outlineOffset() const + { + if (background->m_outline.style() == BNONE) + return 0; + return background->m_outline._offset; + } + + ShadowData* textShadow() const { return rareInheritedData->textShadow; } + const Color& textStrokeColor() const { return rareInheritedData->textStrokeColor; } + float textStrokeWidth() const { return rareInheritedData->textStrokeWidth; } + const Color& textFillColor() const { return rareInheritedData->textFillColor; } + float opacity() const { return rareNonInheritedData->opacity; } + ControlPart appearance() const { return static_cast<ControlPart>(rareNonInheritedData->m_appearance); } + EBoxAlignment boxAlign() const { return static_cast<EBoxAlignment>(rareNonInheritedData->flexibleBox->align); } + EBoxDirection boxDirection() const { return static_cast<EBoxDirection>(inherited_flags._box_direction); } + float boxFlex() { return rareNonInheritedData->flexibleBox->flex; } + unsigned int boxFlexGroup() const { return rareNonInheritedData->flexibleBox->flex_group; } + EBoxLines boxLines() { return static_cast<EBoxLines>(rareNonInheritedData->flexibleBox->lines); } + unsigned int boxOrdinalGroup() const { return rareNonInheritedData->flexibleBox->ordinal_group; } + EBoxOrient boxOrient() const { return static_cast<EBoxOrient>(rareNonInheritedData->flexibleBox->orient); } + EBoxAlignment boxPack() const { return static_cast<EBoxAlignment>(rareNonInheritedData->flexibleBox->pack); } + ShadowData* boxShadow() const { return rareNonInheritedData->m_boxShadow.get(); } + StyleReflection* boxReflect() const { return rareNonInheritedData->m_boxReflect.get(); } + EBoxSizing boxSizing() const { return static_cast<EBoxSizing>(box->boxSizing); } + Length marqueeIncrement() const { return rareNonInheritedData->marquee->increment; } + int marqueeSpeed() const { return rareNonInheritedData->marquee->speed; } + int marqueeLoopCount() const { return rareNonInheritedData->marquee->loops; } + EMarqueeBehavior marqueeBehavior() const { return static_cast<EMarqueeBehavior>(rareNonInheritedData->marquee->behavior); } + EMarqueeDirection marqueeDirection() const { return static_cast<EMarqueeDirection>(rareNonInheritedData->marquee->direction); } + EUserModify userModify() const { return static_cast<EUserModify>(rareInheritedData->userModify); } + EUserDrag userDrag() const { return static_cast<EUserDrag>(rareNonInheritedData->userDrag); } + EUserSelect userSelect() const { return static_cast<EUserSelect>(rareInheritedData->userSelect); } + bool textOverflow() const { return rareNonInheritedData->textOverflow; } + EMarginCollapse marginTopCollapse() const { return static_cast<EMarginCollapse>(rareNonInheritedData->marginTopCollapse); } + EMarginCollapse marginBottomCollapse() const { return static_cast<EMarginCollapse>(rareNonInheritedData->marginBottomCollapse); } + EWordBreak wordBreak() const { return static_cast<EWordBreak>(rareInheritedData->wordBreak); } + EWordWrap wordWrap() const { return static_cast<EWordWrap>(rareInheritedData->wordWrap); } + ENBSPMode nbspMode() const { return static_cast<ENBSPMode>(rareInheritedData->nbspMode); } + EKHTMLLineBreak khtmlLineBreak() const { return static_cast<EKHTMLLineBreak>(rareInheritedData->khtmlLineBreak); } + EMatchNearestMailBlockquoteColor matchNearestMailBlockquoteColor() const { return static_cast<EMatchNearestMailBlockquoteColor>(rareNonInheritedData->matchNearestMailBlockquoteColor); } + const AtomicString& highlight() const { return rareInheritedData->highlight; } + EBorderFit borderFit() const { return static_cast<EBorderFit>(rareNonInheritedData->m_borderFit); } + EResize resize() const { return static_cast<EResize>(rareInheritedData->resize); } + float columnWidth() const { return rareNonInheritedData->m_multiCol->m_width; } + bool hasAutoColumnWidth() const { return rareNonInheritedData->m_multiCol->m_autoWidth; } + unsigned short columnCount() const { return rareNonInheritedData->m_multiCol->m_count; } + bool hasAutoColumnCount() const { return rareNonInheritedData->m_multiCol->m_autoCount; } + float columnGap() const { return rareNonInheritedData->m_multiCol->m_gap; } + bool hasNormalColumnGap() const { return rareNonInheritedData->m_multiCol->m_normalGap; } + const Color& columnRuleColor() const { return rareNonInheritedData->m_multiCol->m_rule.color; } + EBorderStyle columnRuleStyle() const { return rareNonInheritedData->m_multiCol->m_rule.style(); } + unsigned short columnRuleWidth() const { return rareNonInheritedData->m_multiCol->ruleWidth(); } + bool columnRuleIsTransparent() const { return rareNonInheritedData->m_multiCol->m_rule.isTransparent(); } + EPageBreak columnBreakBefore() const { return static_cast<EPageBreak>(rareNonInheritedData->m_multiCol->m_breakBefore); } + EPageBreak columnBreakInside() const { return static_cast<EPageBreak>(rareNonInheritedData->m_multiCol->m_breakInside); } + EPageBreak columnBreakAfter() const { return static_cast<EPageBreak>(rareNonInheritedData->m_multiCol->m_breakAfter); } + const TransformOperations& transform() const { return rareNonInheritedData->m_transform->m_operations; } + Length transformOriginX() const { return rareNonInheritedData->m_transform->m_x; } + Length transformOriginY() const { return rareNonInheritedData->m_transform->m_y; } + bool hasTransform() const { return !rareNonInheritedData->m_transform->m_operations.operations().isEmpty(); } + void applyTransform(TransformationMatrix&, const IntSize& borderBoxSize, bool includeTransformOrigin = true) const; + bool hasMask() const { return rareNonInheritedData->m_mask.hasImage() || rareNonInheritedData->m_maskBoxImage.hasImage(); } + // End CSS3 Getters + + // Apple-specific property getter methods + EPointerEvents pointerEvents() const { return static_cast<EPointerEvents>(inherited_flags._pointerEvents); } + const AnimationList* animations() const { return rareNonInheritedData->m_animations.get(); } + const AnimationList* transitions() const { return rareNonInheritedData->m_transitions.get(); } + + AnimationList* accessAnimations(); + AnimationList* accessTransitions(); + + bool hasAnimations() const { return rareNonInheritedData->m_animations && rareNonInheritedData->m_animations->size() > 0; } + bool hasTransitions() const { return rareNonInheritedData->m_transitions && rareNonInheritedData->m_transitions->size() > 0; } + + // return the first found Animation (including 'all' transitions) + const Animation* transitionForProperty(int property); + + int lineClamp() const { return rareNonInheritedData->lineClamp; } + bool textSizeAdjust() const { return rareInheritedData->textSizeAdjust; } + ETextSecurity textSecurity() const { return static_cast<ETextSecurity>(rareInheritedData->textSecurity); } + +// attribute setter methods + + void setDisplay(EDisplay v) { noninherited_flags._effectiveDisplay = v; } + void setOriginalDisplay(EDisplay v) { noninherited_flags._originalDisplay = v; } + void setPosition(EPosition v) { noninherited_flags._position = v; } + void setFloating(EFloat v) { noninherited_flags._floating = v; } + + void setLeft(Length v) { SET_VAR(surround, offset.m_left, v) } + void setRight(Length v) { SET_VAR(surround, offset.m_right, v) } + void setTop(Length v) { SET_VAR(surround, offset.m_top, v) } + void setBottom(Length v) { SET_VAR(surround, offset.m_bottom, v) } + + void setWidth(Length v) { SET_VAR(box, width, v) } + void setHeight(Length v) { SET_VAR(box, height, v) } + + void setMinWidth(Length v) { SET_VAR(box, min_width, v) } + void setMaxWidth(Length v) { SET_VAR(box, max_width, v) } + void setMinHeight(Length v) { SET_VAR(box, min_height, v) } + void setMaxHeight(Length v) { SET_VAR(box, max_height, v) } + +#if ENABLE(DASHBOARD_SUPPORT) + Vector<StyleDashboardRegion> dashboardRegions() const { return rareNonInheritedData->m_dashboardRegions; } + void setDashboardRegions(Vector<StyleDashboardRegion> regions) { SET_VAR(rareNonInheritedData, m_dashboardRegions, regions); } + + void setDashboardRegion(int type, const String& label, Length t, Length r, Length b, Length l, bool append) + { + StyleDashboardRegion region; + region.label = label; + region.offset.m_top = t; + region.offset.m_right = r; + region.offset.m_bottom = b; + region.offset.m_left = l; + region.type = type; + if (!append) + rareNonInheritedData.access()->m_dashboardRegions.clear(); + rareNonInheritedData.access()->m_dashboardRegions.append(region); + } +#endif + + void resetBorder() { resetBorderImage(); resetBorderTop(); resetBorderRight(); resetBorderBottom(); resetBorderLeft(); resetBorderRadius(); } + void resetBorderTop() { SET_VAR(surround, border.top, BorderValue()) } + void resetBorderRight() { SET_VAR(surround, border.right, BorderValue()) } + void resetBorderBottom() { SET_VAR(surround, border.bottom, BorderValue()) } + void resetBorderLeft() { SET_VAR(surround, border.left, BorderValue()) } + void resetBorderImage() { SET_VAR(surround, border.image, NinePieceImage()) } + void resetBorderRadius() { resetBorderTopLeftRadius(); resetBorderTopRightRadius(); resetBorderBottomLeftRadius(); resetBorderBottomRightRadius(); } + void resetBorderTopLeftRadius() { SET_VAR(surround, border.topLeft, initialBorderRadius()) } + void resetBorderTopRightRadius() { SET_VAR(surround, border.topRight, initialBorderRadius()) } + void resetBorderBottomLeftRadius() { SET_VAR(surround, border.bottomLeft, initialBorderRadius()) } + void resetBorderBottomRightRadius() { SET_VAR(surround, border.bottomRight, initialBorderRadius()) } + + void resetOutline() { SET_VAR(background, m_outline, OutlineValue()) } + + void setBackgroundColor(const Color& v) { SET_VAR(background, m_color, v) } + + void setBorderImage(const NinePieceImage& b) { SET_VAR(surround, border.image, b) } + + void setBorderTopLeftRadius(const IntSize& s) { SET_VAR(surround, border.topLeft, s) } + void setBorderTopRightRadius(const IntSize& s) { SET_VAR(surround, border.topRight, s) } + void setBorderBottomLeftRadius(const IntSize& s) { SET_VAR(surround, border.bottomLeft, s) } + void setBorderBottomRightRadius(const IntSize& s) { SET_VAR(surround, border.bottomRight, s) } + + void setBorderRadius(const IntSize& s) + { + setBorderTopLeftRadius(s); + setBorderTopRightRadius(s); + setBorderBottomLeftRadius(s); + setBorderBottomRightRadius(s); + } + + void setBorderLeftWidth(unsigned short v) { SET_VAR(surround, border.left.width, v) } + void setBorderLeftStyle(EBorderStyle v) { SET_VAR(surround, border.left.m_style, v) } + void setBorderLeftColor(const Color& v) { SET_VAR(surround, border.left.color, v) } + void setBorderRightWidth(unsigned short v) { SET_VAR(surround, border.right.width, v) } + void setBorderRightStyle(EBorderStyle v) { SET_VAR(surround, border.right.m_style, v) } + void setBorderRightColor(const Color& v) { SET_VAR(surround, border.right.color, v) } + void setBorderTopWidth(unsigned short v) { SET_VAR(surround, border.top.width, v) } + void setBorderTopStyle(EBorderStyle v) { SET_VAR(surround, border.top.m_style, v) } + void setBorderTopColor(const Color& v) { SET_VAR(surround, border.top.color, v) } + void setBorderBottomWidth(unsigned short v) { SET_VAR(surround, border.bottom.width, v) } + void setBorderBottomStyle(EBorderStyle v) { SET_VAR(surround, border.bottom.m_style, v) } + void setBorderBottomColor(const Color& v) { SET_VAR(surround, border.bottom.color, v) } + void setOutlineWidth(unsigned short v) { SET_VAR(background, m_outline.width, v) } + + void setOutlineStyle(EBorderStyle v, bool isAuto = false) + { + SET_VAR(background, m_outline.m_style, v) + SET_VAR(background, m_outline._auto, isAuto) + } + + void setOutlineColor(const Color& v) { SET_VAR(background, m_outline.color, v) } + + void setOverflowX(EOverflow v) { noninherited_flags._overflowX = v; } + void setOverflowY(EOverflow v) { noninherited_flags._overflowY = v; } + void setVisibility(EVisibility v) { inherited_flags._visibility = v; } + void setVerticalAlign(EVerticalAlign v) { noninherited_flags._vertical_align = v; } + void setVerticalAlignLength(Length l) { SET_VAR(box, vertical_align, l ) } + + void setHasClip(bool b = true) { SET_VAR(visual, hasClip, b) } + void setClipLeft(Length v) { SET_VAR(visual, clip.m_left, v) } + void setClipRight(Length v) { SET_VAR(visual, clip.m_right, v) } + void setClipTop(Length v) { SET_VAR(visual, clip.m_top, v) } + void setClipBottom(Length v) { SET_VAR(visual, clip.m_bottom, v) } + void setClip(Length top, Length right, Length bottom, Length left); + + void setUnicodeBidi( EUnicodeBidi b ) { noninherited_flags._unicodeBidi = b; } + + void setClear(EClear v) { noninherited_flags._clear = v; } + void setTableLayout(ETableLayout v) { noninherited_flags._table_layout = v; } + + bool setFontDescription(const FontDescription& v) + { + if (inherited->font.fontDescription() != v) { + inherited.access()->font = Font(v, inherited->font.letterSpacing(), inherited->font.wordSpacing()); + return true; + } + return false; + } + + // Only used for blending font sizes when animating. + void setBlendedFontSize(int); + + void setColor(const Color& v) { SET_VAR(inherited, color, v) } + void setTextIndent(Length v) { SET_VAR(inherited, indent, v) } + void setTextAlign(ETextAlign v) { inherited_flags._text_align = v; } + void setTextTransform(ETextTransform v) { inherited_flags._text_transform = v; } + void addToTextDecorationsInEffect(int v) { inherited_flags._text_decorations |= v; } + void setTextDecorationsInEffect(int v) { inherited_flags._text_decorations = v; } + void setTextDecoration(int v) { SET_VAR(visual, textDecoration, v); } + void setDirection(TextDirection v) { inherited_flags._direction = v; } + void setLineHeight(Length v) { SET_VAR(inherited, line_height, v) } + void setZoom(float f) { SET_VAR(visual, m_zoom, f); setEffectiveZoom(effectiveZoom() * zoom()); } + void setEffectiveZoom(float f) { SET_VAR(inherited, m_effectiveZoom, f) } + + void setWhiteSpace(EWhiteSpace v) { inherited_flags._white_space = v; } + + void setWordSpacing(int v) { inherited.access()->font.setWordSpacing(v); } + void setLetterSpacing(int v) { inherited.access()->font.setLetterSpacing(v); } + + void clearBackgroundLayers() { background.access()->m_background = FillLayer(BackgroundFillLayer); } + void inheritBackgroundLayers(const FillLayer& parent) { background.access()->m_background = parent; } + + void adjustBackgroundLayers() + { + if (backgroundLayers()->next()) { + accessBackgroundLayers()->cullEmptyLayers(); + accessBackgroundLayers()->fillUnsetProperties(); + } + } + + void clearMaskLayers() { rareNonInheritedData.access()->m_mask = FillLayer(MaskFillLayer); } + void inheritMaskLayers(const FillLayer& parent) { rareNonInheritedData.access()->m_mask = parent; } + + void adjustMaskLayers() + { + if (maskLayers()->next()) { + accessMaskLayers()->cullEmptyLayers(); + accessMaskLayers()->fillUnsetProperties(); + } + } + + void setMaskBoxImage(const NinePieceImage& b) { SET_VAR(rareNonInheritedData, m_maskBoxImage, b) } + + void setBorderCollapse(bool collapse) { inherited_flags._border_collapse = collapse; } + void setHorizontalBorderSpacing(short v) { SET_VAR(inherited, horizontal_border_spacing, v) } + void setVerticalBorderSpacing(short v) { SET_VAR(inherited, vertical_border_spacing, v) } + void setEmptyCells(EEmptyCell v) { inherited_flags._empty_cells = v; } + void setCaptionSide(ECaptionSide v) { inherited_flags._caption_side = v; } + + void setCounterIncrement(short v) { SET_VAR(visual, counterIncrement, v) } + void setCounterReset(short v) { SET_VAR(visual, counterReset, v) } + + void setListStyleType(EListStyleType v) { inherited_flags._list_style_type = v; } + void setListStyleImage(StyleImage* v) { if (inherited->list_style_image != v) inherited.access()->list_style_image = v; } + void setListStylePosition(EListStylePosition v) { inherited_flags._list_style_position = v; } + + void resetMargin() { SET_VAR(surround, margin, LengthBox(Fixed)) } + void setMarginTop(Length v) { SET_VAR(surround, margin.m_top, v) } + void setMarginBottom(Length v) { SET_VAR(surround, margin.m_bottom, v) } + void setMarginLeft(Length v) { SET_VAR(surround, margin.m_left, v) } + void setMarginRight(Length v) { SET_VAR(surround, margin.m_right, v) } + + void resetPadding() { SET_VAR(surround, padding, LengthBox(Auto)) } + void setPaddingBox(const LengthBox& b) { SET_VAR(surround, padding, b) } + void setPaddingTop(Length v) { SET_VAR(surround, padding.m_top, v) } + void setPaddingBottom(Length v) { SET_VAR(surround, padding.m_bottom, v) } + void setPaddingLeft(Length v) { SET_VAR(surround, padding.m_left, v) } + void setPaddingRight(Length v) { SET_VAR(surround, padding.m_right, v) } + + void setCursor( ECursor c ) { inherited_flags._cursor_style = c; } + void addCursor(CachedImage*, const IntPoint& = IntPoint()); + void setCursorList(PassRefPtr<CursorList>); + void clearCursorList(); + + bool forceBackgroundsToWhite() const { return inherited_flags._force_backgrounds_to_white; } + void setForceBackgroundsToWhite(bool b=true) { inherited_flags._force_backgrounds_to_white = b; } + + bool htmlHacks() const { return inherited_flags._htmlHacks; } + void setHtmlHacks(bool b=true) { inherited_flags._htmlHacks = b; } + + bool hasAutoZIndex() const { return box->z_auto; } + void setHasAutoZIndex() { SET_VAR(box, z_auto, true); SET_VAR(box, z_index, 0) } + int zIndex() const { return box->z_index; } + void setZIndex(int v) { SET_VAR(box, z_auto, false); SET_VAR(box, z_index, v) } + + void setWidows(short w) { SET_VAR(inherited, widows, w); } + void setOrphans(short o) { SET_VAR(inherited, orphans, o); } + void setPageBreakInside(EPageBreak b) { SET_VAR(inherited, page_break_inside, b); } + void setPageBreakBefore(EPageBreak b) { noninherited_flags._page_break_before = b; } + void setPageBreakAfter(EPageBreak b) { noninherited_flags._page_break_after = b; } + + // CSS3 Setters +#if ENABLE(XBL) + void deleteBindingURIs() { SET_VAR(rareNonInheritedData, bindingURI, static_cast<BindingURI*>(0)); } + void inheritBindingURIs(BindingURI* other) { SET_VAR(rareNonInheritedData, bindingURI, other->copy()); } + void addBindingURI(StringImpl* uri); +#endif + + void setOutlineOffset(int v) { SET_VAR(background, m_outline._offset, v) } + void setTextShadow(ShadowData* val, bool add=false); + void setTextStrokeColor(const Color& c) { SET_VAR(rareInheritedData, textStrokeColor, c) } + void setTextStrokeWidth(float w) { SET_VAR(rareInheritedData, textStrokeWidth, w) } + void setTextFillColor(const Color& c) { SET_VAR(rareInheritedData, textFillColor, c) } + void setOpacity(float f) { SET_VAR(rareNonInheritedData, opacity, f); } + void setAppearance(ControlPart a) { SET_VAR(rareNonInheritedData, m_appearance, a); } + void setBoxAlign(EBoxAlignment a) { SET_VAR(rareNonInheritedData.access()->flexibleBox, align, a); } + void setBoxDirection(EBoxDirection d) { inherited_flags._box_direction = d; } + void setBoxFlex(float f) { SET_VAR(rareNonInheritedData.access()->flexibleBox, flex, f); } + void setBoxFlexGroup(unsigned int fg) { SET_VAR(rareNonInheritedData.access()->flexibleBox, flex_group, fg); } + void setBoxLines(EBoxLines l) { SET_VAR(rareNonInheritedData.access()->flexibleBox, lines, l); } + void setBoxOrdinalGroup(unsigned int og) { SET_VAR(rareNonInheritedData.access()->flexibleBox, ordinal_group, og); } + void setBoxOrient(EBoxOrient o) { SET_VAR(rareNonInheritedData.access()->flexibleBox, orient, o); } + void setBoxPack(EBoxAlignment p) { SET_VAR(rareNonInheritedData.access()->flexibleBox, pack, p); } + void setBoxShadow(ShadowData* val, bool add=false); + void setBoxReflect(PassRefPtr<StyleReflection> reflect) { if (rareNonInheritedData->m_boxReflect != reflect) rareNonInheritedData.access()->m_boxReflect = reflect; } + void setBoxSizing(EBoxSizing s) { SET_VAR(box, boxSizing, s); } + void setMarqueeIncrement(const Length& f) { SET_VAR(rareNonInheritedData.access()->marquee, increment, f); } + void setMarqueeSpeed(int f) { SET_VAR(rareNonInheritedData.access()->marquee, speed, f); } + void setMarqueeDirection(EMarqueeDirection d) { SET_VAR(rareNonInheritedData.access()->marquee, direction, d); } + void setMarqueeBehavior(EMarqueeBehavior b) { SET_VAR(rareNonInheritedData.access()->marquee, behavior, b); } + void setMarqueeLoopCount(int i) { SET_VAR(rareNonInheritedData.access()->marquee, loops, i); } + void setUserModify(EUserModify u) { SET_VAR(rareInheritedData, userModify, u); } + void setUserDrag(EUserDrag d) { SET_VAR(rareNonInheritedData, userDrag, d); } + void setUserSelect(EUserSelect s) { SET_VAR(rareInheritedData, userSelect, s); } + void setTextOverflow(bool b) { SET_VAR(rareNonInheritedData, textOverflow, b); } + void setMarginTopCollapse(EMarginCollapse c) { SET_VAR(rareNonInheritedData, marginTopCollapse, c); } + void setMarginBottomCollapse(EMarginCollapse c) { SET_VAR(rareNonInheritedData, marginBottomCollapse, c); } + void setWordBreak(EWordBreak b) { SET_VAR(rareInheritedData, wordBreak, b); } + void setWordWrap(EWordWrap b) { SET_VAR(rareInheritedData, wordWrap, b); } + void setNBSPMode(ENBSPMode b) { SET_VAR(rareInheritedData, nbspMode, b); } + void setKHTMLLineBreak(EKHTMLLineBreak b) { SET_VAR(rareInheritedData, khtmlLineBreak, b); } + void setMatchNearestMailBlockquoteColor(EMatchNearestMailBlockquoteColor c) { SET_VAR(rareNonInheritedData, matchNearestMailBlockquoteColor, c); } + void setHighlight(const AtomicString& h) { SET_VAR(rareInheritedData, highlight, h); } + void setBorderFit(EBorderFit b) { SET_VAR(rareNonInheritedData, m_borderFit, b); } + void setResize(EResize r) { SET_VAR(rareInheritedData, resize, r); } + void setColumnWidth(float f) { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_autoWidth, false); SET_VAR(rareNonInheritedData.access()->m_multiCol, m_width, f); } + void setHasAutoColumnWidth() { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_autoWidth, true); SET_VAR(rareNonInheritedData.access()->m_multiCol, m_width, 0); } + void setColumnCount(unsigned short c) { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_autoCount, false); SET_VAR(rareNonInheritedData.access()->m_multiCol, m_count, c); } + void setHasAutoColumnCount() { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_autoCount, true); SET_VAR(rareNonInheritedData.access()->m_multiCol, m_count, 0); } + void setColumnGap(float f) { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_normalGap, false); SET_VAR(rareNonInheritedData.access()->m_multiCol, m_gap, f); } + void setHasNormalColumnGap() { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_normalGap, true); SET_VAR(rareNonInheritedData.access()->m_multiCol, m_gap, 0); } + void setColumnRuleColor(const Color& c) { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_rule.color, c); } + void setColumnRuleStyle(EBorderStyle b) { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_rule.m_style, b); } + void setColumnRuleWidth(unsigned short w) { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_rule.width, w); } + void resetColumnRule() { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_rule, BorderValue()) } + void setColumnBreakBefore(EPageBreak p) { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_breakBefore, p); } + void setColumnBreakInside(EPageBreak p) { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_breakInside, p); } + void setColumnBreakAfter(EPageBreak p) { SET_VAR(rareNonInheritedData.access()->m_multiCol, m_breakAfter, p); } + void setTransform(const TransformOperations& ops) { SET_VAR(rareNonInheritedData.access()->m_transform, m_operations, ops); } + void setTransformOriginX(Length l) { SET_VAR(rareNonInheritedData.access()->m_transform, m_x, l); } + void setTransformOriginY(Length l) { SET_VAR(rareNonInheritedData.access()->m_transform, m_y, l); } + // End CSS3 Setters + + // Apple-specific property setters + void setPointerEvents(EPointerEvents p) { inherited_flags._pointerEvents = p; } + + void clearAnimations() + { + rareNonInheritedData.access()->m_animations.clear(); + } + + void clearTransitions() + { + rareNonInheritedData.access()->m_transitions.clear(); + } + + void inheritAnimations(const AnimationList* parent) { rareNonInheritedData.access()->m_animations.set(parent ? new AnimationList(*parent) : 0); } + void inheritTransitions(const AnimationList* parent) { rareNonInheritedData.access()->m_transitions.set(parent ? new AnimationList(*parent) : 0); } + void adjustAnimations(); + void adjustTransitions(); + + void setLineClamp(int c) { SET_VAR(rareNonInheritedData, lineClamp, c); } + void setTextSizeAdjust(bool b) { SET_VAR(rareInheritedData, textSizeAdjust, b); } + void setTextSecurity(ETextSecurity aTextSecurity) { SET_VAR(rareInheritedData, textSecurity, aTextSecurity); } + +#if ENABLE(SVG) + const SVGRenderStyle* svgStyle() const { return m_svgStyle.get(); } + SVGRenderStyle* accessSVGStyle() { return m_svgStyle.access(); } + + float fillOpacity() const { return svgStyle()->fillOpacity(); } + void setFillOpacity(float f) { accessSVGStyle()->setFillOpacity(f); } + + float strokeOpacity() const { return svgStyle()->strokeOpacity(); } + void setStrokeOpacity(float f) { accessSVGStyle()->setStrokeOpacity(f); } + + float floodOpacity() const { return svgStyle()->floodOpacity(); } + void setFloodOpacity(float f) { accessSVGStyle()->setFloodOpacity(f); } +#endif + + const ContentData* contentData() const { return rareNonInheritedData->m_content.get(); } + bool contentDataEquivalent(const RenderStyle* otherStyle) const; + void clearContent(); + void setContent(StringImpl*, bool add = false); + void setContent(PassRefPtr<StyleImage>, bool add = false); + void setContent(CounterContent*, bool add = false); + + const CounterDirectiveMap* counterDirectives() const; + CounterDirectiveMap& accessCounterDirectives(); + + bool inheritedNotEqual(RenderStyle*) const; + + // The difference between two styles. The following values are used: + // (1) Equal - The two styles are identical + // (2) Repaint - The object just needs to be repainted. + // (3) RepaintLayer - The layer and its descendant layers needs to be repainted. + // (4) Layout - A layout is required. + enum Diff { Equal, Repaint, RepaintLayer, LayoutPositionedMovementOnly, Layout }; + Diff diff(const RenderStyle*) const; + + bool isDisplayReplacedType() const + { + return display() == INLINE_BLOCK || display() == INLINE_BOX || display() == INLINE_TABLE; + } + + bool isDisplayInlineType() const + { + return display() == INLINE || isDisplayReplacedType(); + } + + bool isOriginalDisplayInlineType() const + { + return originalDisplay() == INLINE || originalDisplay() == INLINE_BLOCK || + originalDisplay() == INLINE_BOX || originalDisplay() == INLINE_TABLE; + } + + // To obtain at any time the pseudo state for a given link. + PseudoState pseudoState() const { return static_cast<PseudoState>(m_pseudoState); } + void setPseudoState(PseudoState s) { m_pseudoState = s; } + + // To tell if this style matched attribute selectors. This makes it impossible to share. + bool affectedByAttributeSelectors() const { return m_affectedByAttributeSelectors; } + void setAffectedByAttributeSelectors() { m_affectedByAttributeSelectors = true; } + + bool unique() const { return m_unique; } + void setUnique() { m_unique = true; } + + // Methods for indicating the style is affected by dynamic updates (e.g., children changing, our position changing in our sibling list, etc.) + bool affectedByEmpty() const { return m_affectedByEmpty; } + bool emptyState() const { return m_emptyState; } + void setEmptyState(bool b) { m_affectedByEmpty = true; m_unique = true; m_emptyState = b; } + bool childrenAffectedByPositionalRules() const { return childrenAffectedByForwardPositionalRules() || childrenAffectedByBackwardPositionalRules(); } + bool childrenAffectedByFirstChildRules() const { return m_childrenAffectedByFirstChildRules; } + void setChildrenAffectedByFirstChildRules() { m_childrenAffectedByFirstChildRules = true; } + bool childrenAffectedByLastChildRules() const { return m_childrenAffectedByLastChildRules; } + void setChildrenAffectedByLastChildRules() { m_childrenAffectedByLastChildRules = true; } + bool childrenAffectedByDirectAdjacentRules() const { return m_childrenAffectedByDirectAdjacentRules; } + void setChildrenAffectedByDirectAdjacentRules() { m_childrenAffectedByDirectAdjacentRules = true; } + bool childrenAffectedByForwardPositionalRules() const { return m_childrenAffectedByForwardPositionalRules; } + void setChildrenAffectedByForwardPositionalRules() { m_childrenAffectedByForwardPositionalRules = true; } + bool childrenAffectedByBackwardPositionalRules() const { return m_childrenAffectedByBackwardPositionalRules; } + void setChildrenAffectedByBackwardPositionalRules() { m_childrenAffectedByBackwardPositionalRules = true; } + bool firstChildState() const { return m_firstChildState; } + void setFirstChildState() { m_firstChildState = true; } + bool lastChildState() const { return m_lastChildState; } + void setLastChildState() { m_lastChildState = true; } + unsigned childIndex() const { return m_childIndex; } + void setChildIndex(unsigned index) { m_childIndex = index; } + + // Initial values for all the properties + static bool initialBorderCollapse() { return false; } + static EBorderStyle initialBorderStyle() { return BNONE; } + static NinePieceImage initialNinePieceImage() { return NinePieceImage(); } + static IntSize initialBorderRadius() { return IntSize(0, 0); } + static ECaptionSide initialCaptionSide() { return CAPTOP; } + static EClear initialClear() { return CNONE; } + static TextDirection initialDirection() { return LTR; } + static EDisplay initialDisplay() { return INLINE; } + static EEmptyCell initialEmptyCells() { return SHOW; } + static EFloat initialFloating() { return FNONE; } + static EListStylePosition initialListStylePosition() { return OUTSIDE; } + static EListStyleType initialListStyleType() { return DISC; } + static EOverflow initialOverflowX() { return OVISIBLE; } + static EOverflow initialOverflowY() { return OVISIBLE; } + static EPageBreak initialPageBreak() { return PBAUTO; } + static EPosition initialPosition() { return StaticPosition; } + static ETableLayout initialTableLayout() { return TAUTO; } + static EUnicodeBidi initialUnicodeBidi() { return UBNormal; } + static ETextTransform initialTextTransform() { return TTNONE; } + static EVisibility initialVisibility() { return VISIBLE; } + static EWhiteSpace initialWhiteSpace() { return NORMAL; } + static short initialHorizontalBorderSpacing() { return 0; } + static short initialVerticalBorderSpacing() { return 0; } + static ECursor initialCursor() { return CURSOR_AUTO; } + static Color initialColor() { return Color::black; } + static StyleImage* initialListStyleImage() { return 0; } + static unsigned short initialBorderWidth() { return 3; } + static int initialLetterWordSpacing() { return 0; } + static Length initialSize() { return Length(); } + static Length initialMinSize() { return Length(0, Fixed); } + static Length initialMaxSize() { return Length(undefinedLength, Fixed); } + static Length initialOffset() { return Length(); } + static Length initialMargin() { return Length(Fixed); } + static Length initialPadding() { return Length(Fixed); } + static Length initialTextIndent() { return Length(Fixed); } + static EVerticalAlign initialVerticalAlign() { return BASELINE; } + static int initialWidows() { return 2; } + static int initialOrphans() { return 2; } + static Length initialLineHeight() { return Length(-100.0, Percent); } + static ETextAlign initialTextAlign() { return TAAUTO; } + static ETextDecoration initialTextDecoration() { return TDNONE; } + static float initialZoom() { return 1.0f; } + static int initialOutlineOffset() { return 0; } + static float initialOpacity() { return 1.0f; } + static EBoxAlignment initialBoxAlign() { return BSTRETCH; } + static EBoxDirection initialBoxDirection() { return BNORMAL; } + static EBoxLines initialBoxLines() { return SINGLE; } + static EBoxOrient initialBoxOrient() { return HORIZONTAL; } + static EBoxAlignment initialBoxPack() { return BSTART; } + static float initialBoxFlex() { return 0.0f; } + static int initialBoxFlexGroup() { return 1; } + static int initialBoxOrdinalGroup() { return 1; } + static EBoxSizing initialBoxSizing() { return CONTENT_BOX; } + static StyleReflection* initialBoxReflect() { return 0; } + static int initialMarqueeLoopCount() { return -1; } + static int initialMarqueeSpeed() { return 85; } + static Length initialMarqueeIncrement() { return Length(6, Fixed); } + static EMarqueeBehavior initialMarqueeBehavior() { return MSCROLL; } + static EMarqueeDirection initialMarqueeDirection() { return MAUTO; } + static EUserModify initialUserModify() { return READ_ONLY; } + static EUserDrag initialUserDrag() { return DRAG_AUTO; } + static EUserSelect initialUserSelect() { return SELECT_TEXT; } + static bool initialTextOverflow() { return false; } + static EMarginCollapse initialMarginTopCollapse() { return MCOLLAPSE; } + static EMarginCollapse initialMarginBottomCollapse() { return MCOLLAPSE; } + static EWordBreak initialWordBreak() { return NormalWordBreak; } + static EWordWrap initialWordWrap() { return NormalWordWrap; } + static ENBSPMode initialNBSPMode() { return NBNORMAL; } + static EKHTMLLineBreak initialKHTMLLineBreak() { return LBNORMAL; } + static EMatchNearestMailBlockquoteColor initialMatchNearestMailBlockquoteColor() { return BCNORMAL; } + static const AtomicString& initialHighlight() { return nullAtom; } + static EBorderFit initialBorderFit() { return BorderFitBorder; } + static EResize initialResize() { return RESIZE_NONE; } + static ControlPart initialAppearance() { return NoControlPart; } + static bool initialVisuallyOrdered() { return false; } + static float initialTextStrokeWidth() { return 0; } + static unsigned short initialColumnCount() { return 1; } + static const TransformOperations& initialTransform() { return s_initialTransform; } + static Length initialTransformOriginX() { return Length(50.0, Percent); } + static Length initialTransformOriginY() { return Length(50.0, Percent); } + static EPointerEvents initialPointerEvents() { return PE_AUTO; } + + // Keep these at the end. + static int initialLineClamp() { return -1; } + static bool initialTextSizeAdjust() { return true; } + static ETextSecurity initialTextSecurity() { return TSNONE; } +#if ENABLE(DASHBOARD_SUPPORT) + static const Vector<StyleDashboardRegion>& initialDashboardRegions(); + static const Vector<StyleDashboardRegion>& noneDashboardRegions(); +#endif +}; + +} // namespace WebCore + +#endif // RenderStyle_h diff --git a/src/3rdparty/webkit/WebCore/rendering/style/RenderStyleConstants.h b/src/3rdparty/webkit/WebCore/rendering/style/RenderStyleConstants.h new file mode 100644 index 0000000..40ad3cc --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/RenderStyleConstants.h @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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. + * + */ + +#ifndef RenderStyleConstants_h +#define RenderStyleConstants_h + +namespace WebCore { + +/* + * WARNING: + * -------- + * + * The order of the values in the enums have to agree with the order specified + * in CSSValueKeywords.in, otherwise some optimizations in the parser will fail, + * and produce invalid results. + */ + + +// These have been defined in the order of their precedence for border-collapsing. Do +// not change this order! +enum EBorderStyle { BNONE, BHIDDEN, INSET, GROOVE, RIDGE, OUTSET, DOTTED, DASHED, SOLID, DOUBLE }; + +enum EBorderPrecedence { BOFF, BTABLE, BCOLGROUP, BCOL, BROWGROUP, BROW, BCELL }; + +enum PseudoState { PseudoUnknown, PseudoNone, PseudoAnyLink, PseudoLink, PseudoVisited}; + +enum EPosition { + StaticPosition, RelativePosition, AbsolutePosition, FixedPosition +}; + +enum EFloat { + FNONE = 0, FLEFT, FRIGHT +}; + + +enum EMarginCollapse { MCOLLAPSE, MSEPARATE, MDISCARD }; + +// Box attributes. Not inherited. + +enum EBoxSizing { CONTENT_BOX, BORDER_BOX }; + +// Random visual rendering model attributes. Not inherited. + +enum EOverflow { + OVISIBLE, OHIDDEN, OSCROLL, OAUTO, OOVERLAY, OMARQUEE +}; + +enum EVerticalAlign { + BASELINE, MIDDLE, SUB, SUPER, TEXT_TOP, + TEXT_BOTTOM, TOP, BOTTOM, BASELINE_MIDDLE, LENGTH +}; + +enum EClear{ + CNONE = 0, CLEFT = 1, CRIGHT = 2, CBOTH = 3 +}; + +enum ETableLayout { + TAUTO, TFIXED +}; + +enum EUnicodeBidi { + UBNormal, Embed, Override +}; + +enum EFillBox { + BorderFillBox, PaddingFillBox, ContentFillBox, TextFillBox +}; + +enum EFillRepeat { + RepeatFill, RepeatXFill, RepeatYFill, NoRepeatFill +}; + +enum EFillLayerType { + BackgroundFillLayer, MaskFillLayer +}; + +// CSS3 Marquee Properties + +enum EMarqueeBehavior { MNONE, MSCROLL, MSLIDE, MALTERNATE }; +enum EMarqueeDirection { MAUTO = 0, MLEFT = 1, MRIGHT = -1, MUP = 2, MDOWN = -2, MFORWARD = 3, MBACKWARD = -3 }; + +// CSS3 Flexible Box Properties + +enum EBoxAlignment { BSTRETCH, BSTART, BCENTER, BEND, BJUSTIFY, BBASELINE }; +enum EBoxOrient { HORIZONTAL, VERTICAL }; +enum EBoxLines { SINGLE, MULTIPLE }; +enum EBoxDirection { BNORMAL, BREVERSE }; + +enum ETextSecurity { + TSNONE, TSDISC, TSCIRCLE, TSSQUARE +}; + +// CSS3 User Modify Properties + +enum EUserModify { + READ_ONLY, READ_WRITE, READ_WRITE_PLAINTEXT_ONLY +}; + +// CSS3 User Drag Values + +enum EUserDrag { + DRAG_AUTO, DRAG_NONE, DRAG_ELEMENT +}; + +// CSS3 User Select Values + +enum EUserSelect { + SELECT_NONE, SELECT_TEXT +}; + +// Word Break Values. Matches WinIE, rather than CSS3 + +enum EWordBreak { + NormalWordBreak, BreakAllWordBreak, BreakWordBreak +}; + +enum EWordWrap { + NormalWordWrap, BreakWordWrap +}; + +enum ENBSPMode { + NBNORMAL, SPACE +}; + +enum EKHTMLLineBreak { + LBNORMAL, AFTER_WHITE_SPACE +}; + +enum EMatchNearestMailBlockquoteColor { + BCNORMAL, MATCH +}; + +enum EResize { + RESIZE_NONE, RESIZE_BOTH, RESIZE_HORIZONTAL, RESIZE_VERTICAL +}; + +enum EListStyleType { + DISC, CIRCLE, SQUARE, LDECIMAL, DECIMAL_LEADING_ZERO, + LOWER_ROMAN, UPPER_ROMAN, LOWER_GREEK, + LOWER_ALPHA, LOWER_LATIN, UPPER_ALPHA, UPPER_LATIN, + HEBREW, ARMENIAN, GEORGIAN, CJK_IDEOGRAPHIC, + HIRAGANA, KATAKANA, HIRAGANA_IROHA, KATAKANA_IROHA, LNONE +}; + +enum ContentType { + CONTENT_NONE, CONTENT_OBJECT, CONTENT_TEXT, CONTENT_COUNTER +}; + +enum EBorderFit { BorderFitBorder, BorderFitLines }; + +enum ETimingFunctionType { LinearTimingFunction, CubicBezierTimingFunction }; + +enum EAnimPlayState { + AnimPlayStatePlaying = 0x0, + AnimPlayStatePaused = 0x1 +}; + +enum EWhiteSpace { + NORMAL, PRE, PRE_WRAP, PRE_LINE, NOWRAP, KHTML_NOWRAP +}; + +enum ETextAlign { + TAAUTO, LEFT, RIGHT, CENTER, JUSTIFY, WEBKIT_LEFT, WEBKIT_RIGHT, WEBKIT_CENTER +}; + +enum ETextTransform { + CAPITALIZE, UPPERCASE, LOWERCASE, TTNONE +}; + +enum ETextDecoration { + TDNONE = 0x0 , UNDERLINE = 0x1, OVERLINE = 0x2, LINE_THROUGH= 0x4, BLINK = 0x8 +}; + +enum EPageBreak { + PBAUTO, PBALWAYS, PBAVOID +}; + +enum EEmptyCell { + SHOW, HIDE +}; + +enum ECaptionSide { + CAPTOP, CAPBOTTOM, CAPLEFT, CAPRIGHT +}; + +enum EListStylePosition { OUTSIDE, INSIDE }; + +enum EVisibility { VISIBLE, HIDDEN, COLLAPSE }; + +enum ECursor { + // The following must match the order in CSSValueKeywords.in. + CURSOR_AUTO, + CURSOR_CROSS, + CURSOR_DEFAULT, + CURSOR_POINTER, + CURSOR_MOVE, + CURSOR_VERTICAL_TEXT, + CURSOR_CELL, + CURSOR_CONTEXT_MENU, + CURSOR_ALIAS, + CURSOR_PROGRESS, + CURSOR_NO_DROP, + CURSOR_NOT_ALLOWED, + CURSOR_WEBKIT_ZOOM_IN, + CURSOR_WEBKIT_ZOOM_OUT, + CURSOR_E_RESIZE, + CURSOR_NE_RESIZE, + CURSOR_NW_RESIZE, + CURSOR_N_RESIZE, + CURSOR_SE_RESIZE, + CURSOR_SW_RESIZE, + CURSOR_S_RESIZE, + CURSOR_W_RESIZE, + CURSOR_EW_RESIZE, + CURSOR_NS_RESIZE, + CURSOR_NESW_RESIZE, + CURSOR_NWSE_RESIZE, + CURSOR_COL_RESIZE, + CURSOR_ROW_RESIZE, + CURSOR_TEXT, + CURSOR_WAIT, + CURSOR_HELP, + CURSOR_ALL_SCROLL, + CURSOR_WEBKIT_GRAB, + CURSOR_WEBKIT_GRABBING, + + // The following are handled as exceptions so don't need to match. + CURSOR_COPY, + CURSOR_NONE +}; + +enum EDisplay { + INLINE, BLOCK, LIST_ITEM, RUN_IN, COMPACT, INLINE_BLOCK, + TABLE, INLINE_TABLE, TABLE_ROW_GROUP, + TABLE_HEADER_GROUP, TABLE_FOOTER_GROUP, TABLE_ROW, + TABLE_COLUMN_GROUP, TABLE_COLUMN, TABLE_CELL, + TABLE_CAPTION, BOX, INLINE_BOX, NONE +}; + +enum EPointerEvents { + PE_NONE, PE_AUTO, PE_STROKE, PE_FILL, PE_PAINTED, PE_VISIBLE, + PE_VISIBLE_STROKE, PE_VISIBLE_FILL, PE_VISIBLE_PAINTED, PE_ALL +}; + +} // namespace WebCore + +#endif // RenderStyleConstants_h diff --git a/src/3rdparty/webkit/WebCore/rendering/style/SVGRenderStyle.cpp b/src/3rdparty/webkit/WebCore/rendering/style/SVGRenderStyle.cpp new file mode 100644 index 0000000..1749978 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/SVGRenderStyle.cpp @@ -0,0 +1,146 @@ +/* + Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org> + 2004, 2005 Rob Buis <buis@kde.org> + + Based on khtml code by: + Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) + Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org) + Copyright (C) 2002-2003 Dirk Mueller (mueller@kde.org) + Copyright (C) 2002 Apple Computer, Inc. + + This file is part of the KDE project + + 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(SVG) +#include "SVGRenderStyle.h" + +#include "CSSPrimitiveValue.h" +#include "CSSValueList.h" +#include "RenderObject.h" +#include "RenderStyle.h" +#include "SVGStyledElement.h" + +namespace WebCore { + +SVGRenderStyle::SVGRenderStyle() +{ + static SVGRenderStyle* defaultStyle = new SVGRenderStyle(CreateDefault); + + fill = defaultStyle->fill; + stroke = defaultStyle->stroke; + text = defaultStyle->text; + stops = defaultStyle->stops; + clip = defaultStyle->clip; + mask = defaultStyle->mask; + misc = defaultStyle->misc; + markers = defaultStyle->markers; + + setBitDefaults(); +} + +SVGRenderStyle::SVGRenderStyle(CreateDefaultType) +{ + setBitDefaults(); + + fill.init(); + stroke.init(); + text.init(); + stops.init(); + clip.init(); + mask.init(); + misc.init(); + markers.init(); +} + +SVGRenderStyle::SVGRenderStyle(const SVGRenderStyle& other) + : RefCounted<SVGRenderStyle>() +{ + fill = other.fill; + stroke = other.stroke; + text = other.text; + stops = other.stops; + clip = other.clip; + mask = other.mask; + misc = other.misc; + markers = other.markers; + + svg_inherited_flags = other.svg_inherited_flags; + svg_noninherited_flags = other.svg_noninherited_flags; +} + +SVGRenderStyle::~SVGRenderStyle() +{ +} + +bool SVGRenderStyle::operator==(const SVGRenderStyle& o) const +{ + return (fill == o.fill && stroke == o.stroke && text == o.text && + stops == o.stops && clip == o.clip && mask == o.mask && + misc == o.misc && markers == o.markers && + svg_inherited_flags == o.svg_inherited_flags && + svg_noninherited_flags == o.svg_noninherited_flags); +} + +bool SVGRenderStyle::inheritedNotEqual(const SVGRenderStyle* other) const +{ + return (fill != other->fill + || stroke != other->stroke + || markers != other->markers + || text != other->text + || svg_inherited_flags != other->svg_inherited_flags); +} + +void SVGRenderStyle::inheritFrom(const SVGRenderStyle* svgInheritParent) +{ + if (!svgInheritParent) + return; + + fill = svgInheritParent->fill; + stroke = svgInheritParent->stroke; + markers = svgInheritParent->markers; + text = svgInheritParent->text; + + svg_inherited_flags = svgInheritParent->svg_inherited_flags; +} + +float SVGRenderStyle::cssPrimitiveToLength(const RenderObject* item, CSSValue* value, float defaultValue) +{ + CSSPrimitiveValue* primitive = static_cast<CSSPrimitiveValue*>(value); + + unsigned short cssType = (primitive ? primitive->primitiveType() : (unsigned short) CSSPrimitiveValue::CSS_UNKNOWN); + if (!(cssType > CSSPrimitiveValue::CSS_UNKNOWN && cssType <= CSSPrimitiveValue::CSS_PC)) + return defaultValue; + + if (cssType == CSSPrimitiveValue::CSS_PERCENTAGE) { + SVGStyledElement* element = static_cast<SVGStyledElement*>(item->element()); + SVGElement* viewportElement = (element ? element->viewportElement() : 0); + if (viewportElement) { + float result = primitive->getFloatValue() / 100.0f; + return SVGLength::PercentageOfViewport(result, element, LengthModeOther); + } + } + + return primitive->computeLengthFloat(const_cast<RenderStyle*>(item->style())); +} + +} + +#endif // ENABLE(SVG) + +// vim:ts=4:noet diff --git a/src/3rdparty/webkit/WebCore/rendering/style/SVGRenderStyle.h b/src/3rdparty/webkit/WebCore/rendering/style/SVGRenderStyle.h new file mode 100644 index 0000000..0e9dae4 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/SVGRenderStyle.h @@ -0,0 +1,215 @@ +/* + Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org> + 2004, 2005 Rob Buis <buis@kde.org> + Copyright (C) 2005, 2006 Apple Computer, Inc. + + This file is part of the KDE project + + 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. +*/ + +#ifndef SVGRenderStyle_h +#define SVGRenderStyle_h + +#if ENABLE(SVG) +#include "CSSValueList.h" +#include "DataRef.h" +#include "GraphicsTypes.h" +#include "SVGPaint.h" +#include "SVGRenderStyleDefs.h" + +#include <wtf/Platform.h> + +namespace WebCore { + + class RenderObject; + class RenderStyle; + + class SVGRenderStyle : public RefCounted<SVGRenderStyle> { + public: + static PassRefPtr<SVGRenderStyle> create() { return adoptRef(new SVGRenderStyle); } + PassRefPtr<SVGRenderStyle> copy() const { return adoptRef(new SVGRenderStyle(*this));} + ~SVGRenderStyle(); + + bool inheritedNotEqual(const SVGRenderStyle*) const; + void inheritFrom(const SVGRenderStyle*); + + bool operator==(const SVGRenderStyle&) const; + bool operator!=(const SVGRenderStyle& o) const { return !(*this == o); } + + // SVG CSS Properties + SVG_RS_DEFINE_ATTRIBUTE(EAlignmentBaseline, AlignmentBaseline, alignmentBaseline, AB_AUTO) + SVG_RS_DEFINE_ATTRIBUTE(EDominantBaseline, DominantBaseline, dominantBaseline, DB_AUTO) + SVG_RS_DEFINE_ATTRIBUTE(EBaselineShift, BaselineShift, baselineShift, BS_BASELINE) + + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(LineCap, CapStyle, capStyle, ButtCap) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(WindRule, ClipRule, clipRule, RULE_NONZERO) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EColorInterpolation, ColorInterpolation, colorInterpolation, CI_SRGB) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EColorInterpolation, ColorInterpolationFilters, colorInterpolationFilters, CI_LINEARRGB) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EColorRendering, ColorRendering, colorRendering, CR_AUTO) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(WindRule, FillRule, fillRule, RULE_NONZERO) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EImageRendering, ImageRendering, imageRendering, IR_AUTO) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(LineJoin, JoinStyle, joinStyle, MiterJoin) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EShapeRendering, ShapeRendering, shapeRendering, SR_AUTO) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(ETextAnchor, TextAnchor, textAnchor, TA_START) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(ETextRendering, TextRendering, textRendering, TR_AUTO) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EWritingMode, WritingMode, writingMode, WM_LRTB) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EGlyphOrientation, GlyphOrientationHorizontal, glyphOrientationHorizontal, GO_0DEG) + SVG_RS_DEFINE_ATTRIBUTE_INHERITED(EGlyphOrientation, GlyphOrientationVertical, glyphOrientationVertical, GO_AUTO) + + // SVG CSS Properties (using DataRef's) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(float, fill, opacity, FillOpacity, fillOpacity, 1.0f) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(SVGPaint, fill, paint, FillPaint, fillPaint, SVGPaint::defaultFill()) + + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(float, stroke, opacity, StrokeOpacity, strokeOpacity, 1.0f) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(SVGPaint, stroke, paint, StrokePaint, strokePaint, SVGPaint::defaultStroke()) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(CSSValueList, stroke, dashArray, StrokeDashArray, strokeDashArray, 0) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(float, stroke, miterLimit, StrokeMiterLimit, strokeMiterLimit, 4.0f) + + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(CSSValue, stroke, width, StrokeWidth, strokeWidth, 0) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(CSSValue, stroke, dashOffset, StrokeDashOffset, strokeDashOffset, 0); + + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(CSSValue, text, kerning, Kerning, kerning, 0) + + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(float, stops, opacity, StopOpacity, stopOpacity, 1.0f) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(Color, stops, color, StopColor, stopColor, Color(0, 0, 0)) + + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(String, clip, clipPath, ClipPath, clipPath, String()) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(String, mask, maskElement, MaskElement, maskElement, String()) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(String, markers, startMarker, StartMarker, startMarker, String()) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(String, markers, midMarker, MidMarker, midMarker, String()) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(String, markers, endMarker, EndMarker, endMarker, String()) + + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(String, misc, filter, Filter, filter, String()) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(float, misc, floodOpacity, FloodOpacity, floodOpacity, 1.0f) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(Color, misc, floodColor, FloodColor, floodColor, Color(0, 0, 0)) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(Color, misc, lightingColor, LightingColor, lightingColor, Color(255, 255, 255)) + SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(CSSValue, misc, baselineShiftValue, BaselineShiftValue, baselineShiftValue, 0) + + // convenience + bool hasStroke() const { return (strokePaint()->paintType() != SVGPaint::SVG_PAINTTYPE_NONE); } + bool hasFill() const { return (fillPaint()->paintType() != SVGPaint::SVG_PAINTTYPE_NONE); } + + static float cssPrimitiveToLength(const RenderObject*, CSSValue*, float defaultValue = 0.0f); + + protected: + // inherit + struct InheritedFlags { + bool operator==(const InheritedFlags& other) const + { + return (_colorRendering == other._colorRendering) && + (_imageRendering == other._imageRendering) && + (_shapeRendering == other._shapeRendering) && + (_textRendering == other._textRendering) && + (_clipRule == other._clipRule) && + (_fillRule == other._fillRule) && + (_capStyle == other._capStyle) && + (_joinStyle == other._joinStyle) && + (_textAnchor == other._textAnchor) && + (_colorInterpolation == other._colorInterpolation) && + (_colorInterpolationFilters == other._colorInterpolationFilters) && + (_writingMode == other._writingMode) && + (_glyphOrientationHorizontal == other._glyphOrientationHorizontal) && + (_glyphOrientationVertical == other._glyphOrientationVertical); + } + + bool operator!=(const InheritedFlags& other) const + { + return !(*this == other); + } + + unsigned _colorRendering : 2; // EColorRendering + unsigned _imageRendering : 2; // EImageRendering + unsigned _shapeRendering : 2; // EShapeRendering + unsigned _textRendering : 2; // ETextRendering + unsigned _clipRule : 1; // WindRule + unsigned _fillRule : 1; // WindRule + unsigned _capStyle : 2; // LineCap + unsigned _joinStyle : 2; // LineJoin + unsigned _textAnchor : 2; // ETextAnchor + unsigned _colorInterpolation : 2; // EColorInterpolation + unsigned _colorInterpolationFilters : 2; // EColorInterpolation + unsigned _writingMode : 3; // EWritingMode + unsigned _glyphOrientationHorizontal : 3; // EGlyphOrientation + unsigned _glyphOrientationVertical : 3; // EGlyphOrientation + } svg_inherited_flags; + + // don't inherit + struct NonInheritedFlags { + // 32 bit non-inherited, don't add to the struct, or the operator will break. + bool operator==(const NonInheritedFlags &other) const { return _niflags == other._niflags; } + bool operator!=(const NonInheritedFlags &other) const { return _niflags != other._niflags; } + + union { + struct { + unsigned _alignmentBaseline : 4; // EAlignmentBaseline + unsigned _dominantBaseline : 4; // EDominantBaseline + unsigned _baselineShift : 2; // EBaselineShift + // 22 bits unused + } f; + uint32_t _niflags; + }; + } svg_noninherited_flags; + + // inherited attributes + DataRef<StyleFillData> fill; + DataRef<StyleStrokeData> stroke; + DataRef<StyleMarkerData> markers; + DataRef<StyleTextData> text; + + // non-inherited attributes + DataRef<StyleStopData> stops; + DataRef<StyleClipData> clip; + DataRef<StyleMaskData> mask; + DataRef<StyleMiscData> misc; + + private: + enum CreateDefaultType { CreateDefault }; + + SVGRenderStyle(); + SVGRenderStyle(const SVGRenderStyle&); + SVGRenderStyle(CreateDefaultType); // Used to create the default style. + + void setBitDefaults() + { + svg_inherited_flags._clipRule = initialClipRule(); + svg_inherited_flags._colorRendering = initialColorRendering(); + svg_inherited_flags._fillRule = initialFillRule(); + svg_inherited_flags._imageRendering = initialImageRendering(); + svg_inherited_flags._shapeRendering = initialShapeRendering(); + svg_inherited_flags._textRendering = initialTextRendering(); + svg_inherited_flags._textAnchor = initialTextAnchor(); + svg_inherited_flags._capStyle = initialCapStyle(); + svg_inherited_flags._joinStyle = initialJoinStyle(); + svg_inherited_flags._colorInterpolation = initialColorInterpolation(); + svg_inherited_flags._colorInterpolationFilters = initialColorInterpolationFilters(); + svg_inherited_flags._writingMode = initialWritingMode(); + svg_inherited_flags._glyphOrientationHorizontal = initialGlyphOrientationHorizontal(); + svg_inherited_flags._glyphOrientationVertical = initialGlyphOrientationVertical(); + + svg_noninherited_flags._niflags = 0; + svg_noninherited_flags.f._alignmentBaseline = initialAlignmentBaseline(); + svg_noninherited_flags.f._dominantBaseline = initialDominantBaseline(); + svg_noninherited_flags.f._baselineShift = initialBaselineShift(); + } + }; + +} // namespace WebCore + +#endif // ENABLE(SVG) +#endif // SVGRenderStyle_h + +// vim:ts=4:noet diff --git a/src/3rdparty/webkit/WebCore/rendering/style/SVGRenderStyleDefs.cpp b/src/3rdparty/webkit/WebCore/rendering/style/SVGRenderStyleDefs.cpp new file mode 100644 index 0000000..f5faad3 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/SVGRenderStyleDefs.cpp @@ -0,0 +1,218 @@ +/* + Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org> + 2004, 2005, 2007 Rob Buis <buis@kde.org> + + Based on khtml code by: + Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) + Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org) + Copyright (C) 2002-2003 Dirk Mueller (mueller@kde.org) + Copyright (C) 2002 Apple Computer, Inc. + + This file is part of the KDE project + + 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(SVG) +#include "SVGRenderStyleDefs.h" + +#include "RenderStyle.h" +#include "SVGRenderStyle.h" + +using namespace WebCore; + +StyleFillData::StyleFillData() +{ + paint = SVGRenderStyle::initialFillPaint(); + opacity = SVGRenderStyle::initialFillOpacity(); +} + +StyleFillData::StyleFillData(const StyleFillData& other) + : RefCounted<StyleFillData>() +{ + paint = other.paint; + opacity = other.opacity; +} + +bool StyleFillData::operator==(const StyleFillData &other) const +{ + if (opacity != other.opacity) + return false; + + if (!paint || !other.paint) + return paint == other.paint; + + if (paint->paintType() != other.paint->paintType()) + return false; + + if (paint->paintType() == SVGPaint::SVG_PAINTTYPE_URI) + return paint->uri() == other.paint->uri(); + + if (paint->paintType() == SVGPaint::SVG_PAINTTYPE_RGBCOLOR) + return paint->color() == other.paint->color(); + + return paint == other.paint; +} + +StyleStrokeData::StyleStrokeData() +{ + width = SVGRenderStyle::initialStrokeWidth(); + paint = SVGRenderStyle::initialStrokePaint(); + opacity = SVGRenderStyle::initialStrokeOpacity(); + miterLimit = SVGRenderStyle::initialStrokeMiterLimit(); + dashOffset = SVGRenderStyle::initialStrokeDashOffset(); + dashArray = SVGRenderStyle::initialStrokeDashArray(); +} + +StyleStrokeData::StyleStrokeData(const StyleStrokeData& other) + : RefCounted<StyleStrokeData>() +{ + width = other.width; + paint = other.paint; + opacity = other.opacity; + miterLimit = other.miterLimit; + dashOffset = other.dashOffset; + dashArray = other.dashArray; +} + +bool StyleStrokeData::operator==(const StyleStrokeData &other) const +{ + return (paint == other.paint) && + (width == other.width) && + (opacity == other.opacity) && + (miterLimit == other.miterLimit) && + (dashOffset == other.dashOffset) && + (dashArray == other.dashArray); +} + +StyleStopData::StyleStopData() +{ + color = SVGRenderStyle::initialStopColor(); + opacity = SVGRenderStyle::initialStopOpacity(); +} + +StyleStopData::StyleStopData(const StyleStopData& other) + : RefCounted<StyleStopData>() +{ + color = other.color; + opacity = other.opacity; +} + +bool StyleStopData::operator==(const StyleStopData &other) const +{ + return (color == other.color) && + (opacity == other.opacity); +} + +StyleTextData::StyleTextData() +{ + kerning = SVGRenderStyle::initialKerning(); +} + +StyleTextData::StyleTextData(const StyleTextData& other) + : RefCounted<StyleTextData>() +{ + kerning = other.kerning; +} + +bool StyleTextData::operator==(const StyleTextData& other) const +{ + return kerning == other.kerning; +} + +StyleClipData::StyleClipData() +{ + clipPath = SVGRenderStyle::initialClipPath(); +} + +StyleClipData::StyleClipData(const StyleClipData& other) + : RefCounted<StyleClipData>() +{ + clipPath = other.clipPath; +} + +bool StyleClipData::operator==(const StyleClipData &other) const +{ + return (clipPath == other.clipPath); +} + +StyleMaskData::StyleMaskData() +{ + maskElement = SVGRenderStyle::initialMaskElement(); +} + +StyleMaskData::StyleMaskData(const StyleMaskData& other) + : RefCounted<StyleMaskData>() +{ + maskElement = other.maskElement; +} + +bool StyleMaskData::operator==(const StyleMaskData &other) const +{ + return (maskElement == other.maskElement); +} + +StyleMarkerData::StyleMarkerData() +{ + startMarker = SVGRenderStyle::initialStartMarker(); + midMarker = SVGRenderStyle::initialMidMarker(); + endMarker = SVGRenderStyle::initialEndMarker(); +} + +StyleMarkerData::StyleMarkerData(const StyleMarkerData& other) + : RefCounted<StyleMarkerData>() +{ + startMarker = other.startMarker; + midMarker = other.midMarker; + endMarker = other.endMarker; +} + +bool StyleMarkerData::operator==(const StyleMarkerData &other) const +{ + return (startMarker == other.startMarker && midMarker == other.midMarker && endMarker == other.endMarker); +} + +StyleMiscData::StyleMiscData() +{ + floodColor = SVGRenderStyle::initialFloodColor(); + floodOpacity = SVGRenderStyle::initialFloodOpacity(); + lightingColor = SVGRenderStyle::initialLightingColor(); + baselineShiftValue = SVGRenderStyle::initialBaselineShiftValue(); +} + +StyleMiscData::StyleMiscData(const StyleMiscData& other) + : RefCounted<StyleMiscData>() +{ + filter = other.filter; + floodColor = other.floodColor; + floodOpacity = other.floodOpacity; + lightingColor = other.lightingColor; + baselineShiftValue = other.baselineShiftValue; +} + +bool StyleMiscData::operator==(const StyleMiscData &other) const +{ + return filter == other.filter + && floodOpacity == other.floodOpacity + && floodColor == other.floodColor + && lightingColor == other.lightingColor + && baselineShiftValue == other.baselineShiftValue; +} + +#endif // ENABLE(SVG) + +// vim:ts=4:noet diff --git a/src/3rdparty/webkit/WebCore/rendering/style/SVGRenderStyleDefs.h b/src/3rdparty/webkit/WebCore/rendering/style/SVGRenderStyleDefs.h new file mode 100644 index 0000000..cb504d2 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/SVGRenderStyleDefs.h @@ -0,0 +1,292 @@ +/* + Copyright (C) 2004, 2005, 2007 Nikolas Zimmermann <zimmermann@kde.org> + 2004, 2005 Rob Buis <buis@kde.org> + + Based on khtml code by: + Copyright (C) 2000-2003 Lars Knoll (knoll@kde.org) + (C) 2000 Antti Koivisto (koivisto@kde.org) + (C) 2000-2003 Dirk Mueller (mueller@kde.org) + (C) 2002-2003 Apple Computer, Inc. + + This file is part of the KDE project + + 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. +*/ + +#ifndef SVGRenderStyleDefs_h +#define SVGRenderStyleDefs_h + +#if ENABLE(SVG) +#include "Color.h" +#include "Path.h" +#include "PlatformString.h" +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +// Helper macros for 'SVGRenderStyle' +#define SVG_RS_DEFINE_ATTRIBUTE(Data, Type, Name, Initial) \ + void set##Type(Data val) { svg_noninherited_flags.f._##Name = val; } \ + Data Name() const { return (Data) svg_noninherited_flags.f._##Name; } \ + static Data initial##Type() { return Initial; } + +#define SVG_RS_DEFINE_ATTRIBUTE_INHERITED(Data, Type, Name, Initial) \ + void set##Type(Data val) { svg_inherited_flags._##Name = val; } \ + Data Name() const { return (Data) svg_inherited_flags._##Name; } \ + static Data initial##Type() { return Initial; } + +// "Helper" macros for SVG's RenderStyle properties +// FIXME: These are impossible to work with or debug. +#define SVG_RS_DEFINE_ATTRIBUTE_DATAREF(Data, Group, Variable, Type, Name) \ + Data Name() const { return Group->Variable; } \ + void set##Type(Data obj) { SVG_RS_SET_VARIABLE(Group, Variable, obj) } + +#define SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL(Data, Group, Variable, Type, Name, Initial) \ + SVG_RS_DEFINE_ATTRIBUTE_DATAREF(Data, Group, Variable, Type, Name) \ + static Data initial##Type() { return Initial; } + +#define SVG_RS_DEFINE_ATTRIBUTE_DATAREF_WITH_INITIAL_REFCOUNTED(Data, Group, Variable, Type, Name, Initial) \ + Data* Name() const { return Group->Variable.get(); } \ + void set##Type(PassRefPtr<Data> obj) { \ + if(!(Group->Variable == obj)) \ + Group.access()->Variable = obj; \ + } \ + static Data* initial##Type() { return Initial; } + +#define SVG_RS_SET_VARIABLE(Group, Variable, Value) \ + if(!(Group->Variable == Value)) \ + Group.access()->Variable = Value; + +namespace WebCore { + + enum EBaselineShift { + BS_BASELINE, BS_SUB, BS_SUPER, BS_LENGTH + }; + + enum ETextAnchor { + TA_START, TA_MIDDLE, TA_END + }; + + enum EColorInterpolation { + CI_AUTO, CI_SRGB, CI_LINEARRGB + }; + + enum EColorRendering { + CR_AUTO, CR_OPTIMIZESPEED, CR_OPTIMIZEQUALITY + }; + + enum EImageRendering { + IR_AUTO, IR_OPTIMIZESPEED, IR_OPTIMIZEQUALITY + }; + + enum EShapeRendering { + SR_AUTO, SR_OPTIMIZESPEED, SR_CRISPEDGES, SR_GEOMETRICPRECISION + }; + + enum ETextRendering { + TR_AUTO, TR_OPTIMIZESPEED, TR_OPTIMIZELEGIBILITY, TR_GEOMETRICPRECISION + }; + + enum EWritingMode { + WM_LRTB, WM_LR, WM_RLTB, WM_RL, WM_TBRL, WM_TB + }; + + enum EGlyphOrientation { + GO_0DEG, GO_90DEG, GO_180DEG, GO_270DEG, GO_AUTO + }; + + enum EAlignmentBaseline { + AB_AUTO, AB_BASELINE, AB_BEFORE_EDGE, AB_TEXT_BEFORE_EDGE, + AB_MIDDLE, AB_CENTRAL, AB_AFTER_EDGE, AB_TEXT_AFTER_EDGE, + AB_IDEOGRAPHIC, AB_ALPHABETIC, AB_HANGING, AB_MATHEMATICAL + }; + + enum EDominantBaseline { + DB_AUTO, DB_USE_SCRIPT, DB_NO_CHANGE, DB_RESET_SIZE, + DB_IDEOGRAPHIC, DB_ALPHABETIC, DB_HANGING, DB_MATHEMATICAL, + DB_CENTRAL, DB_MIDDLE, DB_TEXT_AFTER_EDGE, DB_TEXT_BEFORE_EDGE + }; + + class CSSValue; + class CSSValueList; + class SVGPaint; + + // Inherited/Non-Inherited Style Datastructures + class StyleFillData : public RefCounted<StyleFillData> { + public: + static PassRefPtr<StyleFillData> create() { return adoptRef(new StyleFillData); } + PassRefPtr<StyleFillData> copy() const { return adoptRef(new StyleFillData(*this)); } + + bool operator==(const StyleFillData &other) const; + bool operator!=(const StyleFillData &other) const + { + return !(*this == other); + } + + float opacity; + RefPtr<SVGPaint> paint; + + private: + StyleFillData(); + StyleFillData(const StyleFillData&); + }; + + class StyleStrokeData : public RefCounted<StyleStrokeData> { + public: + static PassRefPtr<StyleStrokeData> create() { return adoptRef(new StyleStrokeData); } + PassRefPtr<StyleStrokeData> copy() const { return adoptRef(new StyleStrokeData(*this)); } + + bool operator==(const StyleStrokeData&) const; + bool operator!=(const StyleStrokeData& other) const + { + return !(*this == other); + } + + float opacity; + float miterLimit; + + RefPtr<CSSValue> width; + RefPtr<CSSValue> dashOffset; + + RefPtr<SVGPaint> paint; + RefPtr<CSSValueList> dashArray; + + private: + StyleStrokeData(); + StyleStrokeData(const StyleStrokeData&); + }; + + class StyleStopData : public RefCounted<StyleStopData> { + public: + static PassRefPtr<StyleStopData> create() { return adoptRef(new StyleStopData); } + PassRefPtr<StyleStopData> copy() const { return adoptRef(new StyleStopData(*this)); } + + bool operator==(const StyleStopData &other) const; + bool operator!=(const StyleStopData &other) const + { + return !(*this == other); + } + + float opacity; + Color color; + + private: + StyleStopData(); + StyleStopData(const StyleStopData&); + }; + + class StyleTextData : public RefCounted<StyleTextData> { + public: + static PassRefPtr<StyleTextData> create() { return adoptRef(new StyleTextData); } + PassRefPtr<StyleTextData> copy() const { return adoptRef(new StyleTextData(*this)); } + + bool operator==(const StyleTextData& other) const; + bool operator!=(const StyleTextData& other) const + { + return !(*this == other); + } + + RefPtr<CSSValue> kerning; + + private: + StyleTextData(); + StyleTextData(const StyleTextData& other); + }; + + class StyleClipData : public RefCounted<StyleClipData> { + public: + static PassRefPtr<StyleClipData> create() { return adoptRef(new StyleClipData); } + PassRefPtr<StyleClipData> copy() const { return adoptRef(new StyleClipData(*this)); } + + bool operator==(const StyleClipData &other) const; + bool operator!=(const StyleClipData &other) const + { + return !(*this == other); + } + + String clipPath; + + private: + StyleClipData(); + StyleClipData(const StyleClipData&); + }; + + class StyleMaskData : public RefCounted<StyleMaskData> { + public: + static PassRefPtr<StyleMaskData> create() { return adoptRef(new StyleMaskData); } + PassRefPtr<StyleMaskData> copy() const { return adoptRef(new StyleMaskData(*this)); } + + bool operator==(const StyleMaskData &other) const; + bool operator!=(const StyleMaskData &other) const { return !(*this == other); } + + String maskElement; + + private: + StyleMaskData(); + StyleMaskData(const StyleMaskData&); + }; + + class StyleMarkerData : public RefCounted<StyleMarkerData> { + public: + static PassRefPtr<StyleMarkerData> create() { return adoptRef(new StyleMarkerData); } + PassRefPtr<StyleMarkerData> copy() const { return adoptRef(new StyleMarkerData(*this)); } + + bool operator==(const StyleMarkerData &other) const; + bool operator!=(const StyleMarkerData &other) const + { + return !(*this == other); + } + + String startMarker; + String midMarker; + String endMarker; + + private: + StyleMarkerData(); + StyleMarkerData(const StyleMarkerData&); + }; + + // Note : the rule for this class is, *no inheritance* of these props + class StyleMiscData : public RefCounted<StyleMiscData> { + public: + static PassRefPtr<StyleMiscData> create() { return adoptRef(new StyleMiscData); } + PassRefPtr<StyleMiscData> copy() const { return adoptRef(new StyleMiscData(*this)); } + + bool operator==(const StyleMiscData &other) const; + bool operator!=(const StyleMiscData &other) const + { + return !(*this == other); + } + + String filter; + Color floodColor; + float floodOpacity; + + Color lightingColor; + + // non-inherited text stuff lives here not in StyleTextData. + RefPtr<CSSValue> baselineShiftValue; + + private: + StyleMiscData(); + StyleMiscData(const StyleMiscData&); + }; + +} // namespace WebCore + +#endif // ENABLE(SVG) +#endif // SVGRenderStyleDefs_h + +// vim:ts=4:noet diff --git a/src/3rdparty/webkit/WebCore/rendering/style/ShadowData.cpp b/src/3rdparty/webkit/WebCore/rendering/style/ShadowData.cpp new file mode 100644 index 0000000..75fb9dc --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/ShadowData.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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" +#include "ShadowData.h" + +namespace WebCore { + +ShadowData::ShadowData(const ShadowData& o) + : x(o.x) + , y(o.y) + , blur(o.blur) + , color(o.color) +{ + next = o.next ? new ShadowData(*o.next) : 0; +} + +bool ShadowData::operator==(const ShadowData& o) const +{ + if ((next && !o.next) || (!next && o.next) || + (next && o.next && *next != *o.next)) + return false; + + return x == o.x && y == o.y && blur == o.blur && color == o.color; +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/style/ShadowData.h b/src/3rdparty/webkit/WebCore/rendering/style/ShadowData.h new file mode 100644 index 0000000..dac2b18 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/ShadowData.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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. + * + */ + +#ifndef ShadowData_h +#define ShadowData_h + +#include "Color.h" + +namespace WebCore { + +// This struct holds information about shadows for the text-shadow and box-shadow properties. + +struct ShadowData { + ShadowData() + : x(0) + , y(0) + , blur(0) + , next(0) + { + } + + ShadowData(int _x, int _y, int _blur, const Color& _color) + : x(_x) + , y(_y) + , blur(_blur) + , color(_color) + , next(0) + { + } + + ShadowData(const ShadowData& o); + + ~ShadowData() { delete next; } + + bool operator==(const ShadowData& o) const; + bool operator!=(const ShadowData& o) const + { + return !(*this == o); + } + + int x; + int y; + int blur; + Color color; + ShadowData* next; +}; + +} // namespace WebCore + +#endif // ShadowData_h diff --git a/src/3rdparty/webkit/WebCore/rendering/style/StyleBackgroundData.cpp b/src/3rdparty/webkit/WebCore/rendering/style/StyleBackgroundData.cpp new file mode 100644 index 0000000..68a9ddd --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/StyleBackgroundData.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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" +#include "StyleBackgroundData.h" + +#include "RenderStyleConstants.h" + +namespace WebCore { + +StyleBackgroundData::StyleBackgroundData() + : m_background(BackgroundFillLayer) +{ +} + +StyleBackgroundData::StyleBackgroundData(const StyleBackgroundData& o) + : RefCounted<StyleBackgroundData>() + , m_background(o.m_background) + , m_color(o.m_color) + , m_outline(o.m_outline) +{ +} + +bool StyleBackgroundData::operator==(const StyleBackgroundData& o) const +{ + return m_background == o.m_background && m_color == o.m_color && m_outline == o.m_outline; +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/style/StyleBackgroundData.h b/src/3rdparty/webkit/WebCore/rendering/style/StyleBackgroundData.h new file mode 100644 index 0000000..8f2da36 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/StyleBackgroundData.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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. + * + */ + +#ifndef StyleBackgroundData_h +#define StyleBackgroundData_h + +#include "Color.h" +#include "FillLayer.h" +#include "OutlineValue.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class StyleBackgroundData : public RefCounted<StyleBackgroundData> { +public: + static PassRefPtr<StyleBackgroundData> create() { return adoptRef(new StyleBackgroundData); } + PassRefPtr<StyleBackgroundData> copy() const { return adoptRef(new StyleBackgroundData(*this)); } + ~StyleBackgroundData() { } + + bool operator==(const StyleBackgroundData& o) const; + bool operator!=(const StyleBackgroundData& o) const + { + return !(*this == o); + } + + FillLayer m_background; + Color m_color; + OutlineValue m_outline; + +private: + StyleBackgroundData(); + StyleBackgroundData(const StyleBackgroundData&); +}; + +} // namespace WebCore + +#endif // StyleBackgroundData_h diff --git a/src/3rdparty/webkit/WebCore/rendering/style/StyleBoxData.cpp b/src/3rdparty/webkit/WebCore/rendering/style/StyleBoxData.cpp new file mode 100644 index 0000000..d9734d1 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/StyleBoxData.cpp @@ -0,0 +1,67 @@ +/* + * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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" +#include "StyleBoxData.h" + +#include "RenderStyle.h" +#include "RenderStyleConstants.h" + +namespace WebCore { + +StyleBoxData::StyleBoxData() + : z_index(0) + , z_auto(true) + , boxSizing(CONTENT_BOX) +{ + // Initialize our min/max widths/heights. + min_width = min_height = RenderStyle::initialMinSize(); + max_width = max_height = RenderStyle::initialMaxSize(); +} + +StyleBoxData::StyleBoxData(const StyleBoxData& o) + : RefCounted<StyleBoxData>() + , width(o.width) + , height(o.height) + , min_width(o.min_width) + , max_width(o.max_width) + , min_height(o.min_height) + , max_height(o.max_height) + , z_index(o.z_index) + , z_auto(o.z_auto) + , boxSizing(o.boxSizing) +{ +} + +bool StyleBoxData::operator==(const StyleBoxData& o) const +{ + return width == o.width && + height == o.height && + min_width == o.min_width && + max_width == o.max_width && + min_height == o.min_height && + max_height == o.max_height && + z_index == o.z_index && + z_auto == o.z_auto && + boxSizing == o.boxSizing; +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/style/StyleBoxData.h b/src/3rdparty/webkit/WebCore/rendering/style/StyleBoxData.h new file mode 100644 index 0000000..a5bd2ff --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/StyleBoxData.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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. + * + */ + +#ifndef StyleBoxData_h +#define StyleBoxData_h + +#include "Length.h" +#include <wtf/RefCounted.h> +#include <wtf/PassRefPtr.h> + +namespace WebCore { + +class StyleBoxData : public RefCounted<StyleBoxData> { +public: + static PassRefPtr<StyleBoxData> create() { return adoptRef(new StyleBoxData); } + PassRefPtr<StyleBoxData> copy() const { return adoptRef(new StyleBoxData(*this)); } + + bool operator==(const StyleBoxData& o) const; + bool operator!=(const StyleBoxData& o) const + { + return !(*this == o); + } + + Length width; + Length height; + + Length min_width; + Length max_width; + + Length min_height; + Length max_height; + + Length vertical_align; + + int z_index; + bool z_auto : 1; + unsigned boxSizing : 1; // EBoxSizing + +private: + StyleBoxData(); + StyleBoxData(const StyleBoxData&); +}; + +} // namespace WebCore + +#endif // StyleBoxData_h diff --git a/src/3rdparty/webkit/WebCore/rendering/style/StyleCachedImage.cpp b/src/3rdparty/webkit/WebCore/rendering/style/StyleCachedImage.cpp new file mode 100644 index 0000000..b55c5b9 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/StyleCachedImage.cpp @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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" +#include "StyleCachedImage.h" + +#include "CachedImage.h" +#include "RenderObject.h" + +namespace WebCore { + +PassRefPtr<CSSValue> StyleCachedImage::cssValue() +{ + return CSSPrimitiveValue::create(m_image->url(), CSSPrimitiveValue::CSS_URI); +} + +bool StyleCachedImage::canRender(float multiplier) const +{ + return m_image->canRender(multiplier); +} + +bool StyleCachedImage::isLoaded() const +{ + return m_image->isLoaded(); +} + +bool StyleCachedImage::errorOccurred() const +{ + return m_image->errorOccurred(); +} + +IntSize StyleCachedImage::imageSize(const RenderObject* /*renderer*/, float multiplier) const +{ + return m_image->imageSize(multiplier); +} + +bool StyleCachedImage::imageHasRelativeWidth() const +{ + return m_image->imageHasRelativeWidth(); +} + +bool StyleCachedImage::imageHasRelativeHeight() const +{ + return m_image->imageHasRelativeHeight(); +} + +bool StyleCachedImage::usesImageContainerSize() const +{ + return m_image->usesImageContainerSize(); +} + +void StyleCachedImage::setImageContainerSize(const IntSize& size) +{ + return m_image->setImageContainerSize(size); +} + +void StyleCachedImage::addClient(RenderObject* renderer) +{ + return m_image->addClient(renderer); +} + +void StyleCachedImage::removeClient(RenderObject* renderer) +{ + return m_image->removeClient(renderer); +} + +Image* StyleCachedImage::image(RenderObject*, const IntSize&) const +{ + return m_image->image(); +} + +} diff --git a/src/3rdparty/webkit/WebCore/rendering/style/StyleCachedImage.h b/src/3rdparty/webkit/WebCore/rendering/style/StyleCachedImage.h new file mode 100644 index 0000000..3d22868 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/StyleCachedImage.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef StyleCachedImage_h +#define StyleCachedImage_h + +#include "CachedResourceHandle.h" +#include "StyleImage.h" + +namespace WebCore { + +class CachedImage; + +class StyleCachedImage : public StyleImage { +public: + static PassRefPtr<StyleCachedImage> create(CachedImage* image) { return adoptRef(new StyleCachedImage(image)); } + virtual WrappedImagePtr data() const { return m_image.get(); } + + virtual bool isCachedImage() const { return true; } + + virtual PassRefPtr<CSSValue> cssValue(); + + CachedImage* cachedImage() const { return m_image.get(); } + + virtual bool canRender(float multiplier) const; + virtual bool isLoaded() const; + virtual bool errorOccurred() const; + virtual IntSize imageSize(const RenderObject*, float multiplier) const; + virtual bool imageHasRelativeWidth() const; + virtual bool imageHasRelativeHeight() const; + virtual bool usesImageContainerSize() const; + virtual void setImageContainerSize(const IntSize&); + virtual void addClient(RenderObject*); + virtual void removeClient(RenderObject*); + virtual Image* image(RenderObject*, const IntSize&) const; + +private: + StyleCachedImage(CachedImage* image) + : m_image(image) + { + } + + CachedResourceHandle<CachedImage> m_image; +}; + +} +#endif diff --git a/src/3rdparty/webkit/WebCore/rendering/style/StyleDashboardRegion.h b/src/3rdparty/webkit/WebCore/rendering/style/StyleDashboardRegion.h new file mode 100644 index 0000000..bbb0cda --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/StyleDashboardRegion.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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. + * + */ + +#ifndef StyleDashboardRegion_h +#define StyleDashboardRegion_h +#if ENABLE(DASHBOARD_SUPPORT) + +#include "LengthBox.h" +#include "PlatformString.h" + +namespace WebCore { + +// Dashboard region attributes. Not inherited. + +struct StyleDashboardRegion { + String label; + LengthBox offset; + int type; + + enum { + None, + Circle, + Rectangle + }; + + bool operator==(const StyleDashboardRegion& o) const + { + return type == o.type && offset == o.offset && label == o.label; + } + + bool operator!=(const StyleDashboardRegion& o) const + { + return !(*this == o); + } +}; + +} // namespace WebCore + +#endif // ENABLE(DASHBOARD_SUPPORT) +#endif // StyleDashboardRegion_h diff --git a/src/3rdparty/webkit/WebCore/rendering/style/StyleFlexibleBoxData.cpp b/src/3rdparty/webkit/WebCore/rendering/style/StyleFlexibleBoxData.cpp new file mode 100644 index 0000000..7c00080 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/StyleFlexibleBoxData.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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" +#include "StyleFlexibleBoxData.h" + +#include "RenderStyle.h" + +namespace WebCore { + +StyleFlexibleBoxData::StyleFlexibleBoxData() + : flex(RenderStyle::initialBoxFlex()) + , flex_group(RenderStyle::initialBoxFlexGroup()) + , ordinal_group(RenderStyle::initialBoxOrdinalGroup()) + , align(RenderStyle::initialBoxAlign()) + , pack(RenderStyle::initialBoxPack()) + , orient(RenderStyle::initialBoxOrient()) + , lines(RenderStyle::initialBoxLines()) +{ +} + +StyleFlexibleBoxData::StyleFlexibleBoxData(const StyleFlexibleBoxData& o) + : RefCounted<StyleFlexibleBoxData>() + , flex(o.flex) + , flex_group(o.flex_group) + , ordinal_group(o.ordinal_group) + , align(o.align) + , pack(o.pack) + , orient(o.orient) + , lines(o.lines) +{ +} + +bool StyleFlexibleBoxData::operator==(const StyleFlexibleBoxData& o) const +{ + return flex == o.flex && flex_group == o.flex_group && + ordinal_group == o.ordinal_group && align == o.align && + pack == o.pack && orient == o.orient && lines == o.lines; +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/style/StyleFlexibleBoxData.h b/src/3rdparty/webkit/WebCore/rendering/style/StyleFlexibleBoxData.h new file mode 100644 index 0000000..f5d5e74 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/StyleFlexibleBoxData.h @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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. + * + */ + +#ifndef StyleFlexibleBoxData_h +#define StyleFlexibleBoxData_h + +#include <wtf/RefCounted.h> +#include <wtf/PassRefPtr.h> + +namespace WebCore { + +class StyleFlexibleBoxData : public RefCounted<StyleFlexibleBoxData> { +public: + static PassRefPtr<StyleFlexibleBoxData> create() { return adoptRef(new StyleFlexibleBoxData); } + PassRefPtr<StyleFlexibleBoxData> copy() const { return adoptRef(new StyleFlexibleBoxData(*this)); } + + bool operator==(const StyleFlexibleBoxData& o) const; + bool operator!=(const StyleFlexibleBoxData& o) const + { + return !(*this == o); + } + + float flex; + unsigned int flex_group; + unsigned int ordinal_group; + + unsigned align : 3; // EBoxAlignment + unsigned pack: 3; // EBoxAlignment + unsigned orient: 1; // EBoxOrient + unsigned lines : 1; // EBoxLines + +private: + StyleFlexibleBoxData(); + StyleFlexibleBoxData(const StyleFlexibleBoxData&); +}; + +} // namespace WebCore + +#endif // StyleFlexibleBoxData_h diff --git a/src/3rdparty/webkit/WebCore/rendering/style/StyleGeneratedImage.cpp b/src/3rdparty/webkit/WebCore/rendering/style/StyleGeneratedImage.cpp new file mode 100644 index 0000000..fa361e8 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/StyleGeneratedImage.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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" +#include "StyleGeneratedImage.h" + +#include "CSSImageGeneratorValue.h" +#include "RenderObject.h" + +namespace WebCore { + +PassRefPtr<CSSValue> StyleGeneratedImage::cssValue() +{ + return m_generator; +} + +IntSize StyleGeneratedImage::imageSize(const RenderObject* renderer, float /* multiplier */) const +{ + // We can ignore the multiplier, since we always store a raw zoomed size. + if (m_fixedSize) + return m_generator->fixedSize(renderer); + return m_containerSize; +} + +void StyleGeneratedImage::setImageContainerSize(const IntSize& size) +{ + m_containerSize = size; +} + +void StyleGeneratedImage::addClient(RenderObject* renderer) +{ + m_generator->addClient(renderer, IntSize()); +} + +void StyleGeneratedImage::removeClient(RenderObject* renderer) +{ + m_generator->removeClient(renderer); +} + +Image* StyleGeneratedImage::image(RenderObject* renderer, const IntSize& size) const +{ + return m_generator->image(renderer, size); +} + +} diff --git a/src/3rdparty/webkit/WebCore/rendering/style/StyleGeneratedImage.h b/src/3rdparty/webkit/WebCore/rendering/style/StyleGeneratedImage.h new file mode 100644 index 0000000..532e383 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/StyleGeneratedImage.h @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef StyleGeneratedImage_h +#define StyleGeneratedImage_h + +#include "StyleImage.h" + +namespace WebCore { + +class CSSValue; +class CSSImageGeneratorValue; + +class StyleGeneratedImage : public StyleImage { +public: + static PassRefPtr<StyleGeneratedImage> create(CSSImageGeneratorValue* val, bool fixedSize) + { + return adoptRef(new StyleGeneratedImage(val, fixedSize)); + } + + virtual WrappedImagePtr data() const { return m_generator; } + + virtual bool isGeneratedImage() const { return true; } + + virtual PassRefPtr<CSSValue> cssValue(); + + virtual IntSize imageSize(const RenderObject*, float multiplier) const; + virtual bool imageHasRelativeWidth() const { return !m_fixedSize; } + virtual bool imageHasRelativeHeight() const { return !m_fixedSize; } + virtual bool usesImageContainerSize() const { return !m_fixedSize; } + virtual void setImageContainerSize(const IntSize&); + virtual void addClient(RenderObject*); + virtual void removeClient(RenderObject*); + virtual Image* image(RenderObject*, const IntSize&) const; + +private: + StyleGeneratedImage(CSSImageGeneratorValue* val, bool fixedSize) + : m_generator(val) + , m_fixedSize(fixedSize) + { + } + + CSSImageGeneratorValue* m_generator; // The generator holds a reference to us. + IntSize m_containerSize; + bool m_fixedSize; +}; + +} +#endif diff --git a/src/3rdparty/webkit/WebCore/rendering/style/StyleImage.h b/src/3rdparty/webkit/WebCore/rendering/style/StyleImage.h new file mode 100644 index 0000000..cb90288 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/StyleImage.h @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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. + * + */ + +#ifndef StyleImage_h +#define StyleImage_h + +#include "IntSize.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class CSSValue; +class Image; +class RenderObject; + +typedef void* WrappedImagePtr; + +class StyleImage : public RefCounted<StyleImage> { +public: + virtual ~StyleImage() { } + + bool operator==(const StyleImage& other) + { + return data() == other.data(); + } + + virtual PassRefPtr<CSSValue> cssValue() = 0; + + virtual bool canRender(float /*multiplier*/) const { return true; } + virtual bool isLoaded() const { return true; } + virtual bool errorOccurred() const { return false; } + virtual IntSize imageSize(const RenderObject*, float multiplier) const = 0; + virtual bool imageHasRelativeWidth() const = 0; + virtual bool imageHasRelativeHeight() const = 0; + virtual bool usesImageContainerSize() const = 0; + virtual void setImageContainerSize(const IntSize&) = 0; + virtual void addClient(RenderObject*) = 0; + virtual void removeClient(RenderObject*) = 0; + virtual Image* image(RenderObject*, const IntSize&) const = 0; + virtual WrappedImagePtr data() const = 0; + virtual bool isCachedImage() const { return false; } + virtual bool isGeneratedImage() const { return false; } + + static bool imagesEquivalent(StyleImage* image1, StyleImage* image2) + { + if (image1 != image2) { + if (!image1 || !image2) + return false; + return *image1 == *image2; + } + return true; + } + +protected: + StyleImage() { } +}; + +} +#endif diff --git a/src/3rdparty/webkit/WebCore/rendering/style/StyleInheritedData.cpp b/src/3rdparty/webkit/WebCore/rendering/style/StyleInheritedData.cpp new file mode 100644 index 0000000..56d2686 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/StyleInheritedData.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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" +#include "StyleRareInheritedData.h" + +#include "RenderStyle.h" +#include "StyleImage.h" + +namespace WebCore { + +StyleInheritedData::StyleInheritedData() + : indent(RenderStyle::initialTextIndent()) + , line_height(RenderStyle::initialLineHeight()) + , list_style_image(RenderStyle::initialListStyleImage()) + , color(RenderStyle::initialColor()) + , m_effectiveZoom(RenderStyle::initialZoom()) + , horizontal_border_spacing(RenderStyle::initialHorizontalBorderSpacing()) + , vertical_border_spacing(RenderStyle::initialVerticalBorderSpacing()) + , widows(RenderStyle::initialWidows()) + , orphans(RenderStyle::initialOrphans()) + , page_break_inside(RenderStyle::initialPageBreak()) +{ +} + +StyleInheritedData::~StyleInheritedData() +{ +} + +StyleInheritedData::StyleInheritedData(const StyleInheritedData& o) + : RefCounted<StyleInheritedData>() + , indent(o.indent) + , line_height(o.line_height) + , list_style_image(o.list_style_image) + , cursorData(o.cursorData) + , font(o.font) + , color(o.color) + , m_effectiveZoom(o.m_effectiveZoom) + , horizontal_border_spacing(o.horizontal_border_spacing) + , vertical_border_spacing(o.vertical_border_spacing) + , widows(o.widows) + , orphans(o.orphans) + , page_break_inside(o.page_break_inside) +{ +} + +static bool cursorDataEquivalent(const CursorList* c1, const CursorList* c2) +{ + if (c1 == c2) + return true; + if (!c1 && c2 || c1 && !c2) + return false; + return (*c1 == *c2); +} + +bool StyleInheritedData::operator==(const StyleInheritedData& o) const +{ + return + indent == o.indent && + line_height == o.line_height && + StyleImage::imagesEquivalent(list_style_image.get(), o.list_style_image.get()) && + cursorDataEquivalent(cursorData.get(), o.cursorData.get()) && + font == o.font && + color == o.color && + m_effectiveZoom == o.m_effectiveZoom && + horizontal_border_spacing == o.horizontal_border_spacing && + vertical_border_spacing == o.vertical_border_spacing && + widows == o.widows && + orphans == o.orphans && + page_break_inside == o.page_break_inside; +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/style/StyleInheritedData.h b/src/3rdparty/webkit/WebCore/rendering/style/StyleInheritedData.h new file mode 100644 index 0000000..5f1077e --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/StyleInheritedData.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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. + * + */ + +#ifndef StyleInheritedData_h +#define StyleInheritedData_h + +#include "Color.h" +#include "Font.h" +#include "Length.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> +#include <wtf/RefPtr.h> + +namespace WebCore { + +class StyleImage; +class CursorList; + +class StyleInheritedData : public RefCounted<StyleInheritedData> { +public: + static PassRefPtr<StyleInheritedData> create() { return adoptRef(new StyleInheritedData); } + PassRefPtr<StyleInheritedData> copy() const { return adoptRef(new StyleInheritedData(*this)); } + ~StyleInheritedData(); + + bool operator==(const StyleInheritedData& o) const; + bool operator!=( const StyleInheritedData& o) const + { + return !(*this == o); + } + + Length indent; + // could be packed in a short but doesn't + // make a difference currently because of padding + Length line_height; + + RefPtr<StyleImage> list_style_image; + RefPtr<CursorList> cursorData; + + Font font; + Color color; + + float m_effectiveZoom; + + short horizontal_border_spacing; + short vertical_border_spacing; + + // Paged media properties. + short widows; + short orphans; + unsigned page_break_inside : 2; // EPageBreak + +private: + StyleInheritedData(); + StyleInheritedData(const StyleInheritedData&); +}; + +} // namespace WebCore + +#endif // StyleInheritedData_h diff --git a/src/3rdparty/webkit/WebCore/rendering/style/StyleMarqueeData.cpp b/src/3rdparty/webkit/WebCore/rendering/style/StyleMarqueeData.cpp new file mode 100644 index 0000000..f0e824d --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/StyleMarqueeData.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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" +#include "StyleBackgroundData.h" + +#include "RenderStyle.h" + +namespace WebCore { + +StyleMarqueeData::StyleMarqueeData() + : increment(RenderStyle::initialMarqueeIncrement()) + , speed(RenderStyle::initialMarqueeSpeed()) + , loops(RenderStyle::initialMarqueeLoopCount()) + , behavior(RenderStyle::initialMarqueeBehavior()) + , direction(RenderStyle::initialMarqueeDirection()) +{ +} + +StyleMarqueeData::StyleMarqueeData(const StyleMarqueeData& o) + : RefCounted<StyleMarqueeData>() + , increment(o.increment) + , speed(o.speed) + , loops(o.loops) + , behavior(o.behavior) + , direction(o.direction) +{ +} + +bool StyleMarqueeData::operator==(const StyleMarqueeData& o) const +{ + return increment == o.increment && speed == o.speed && direction == o.direction && + behavior == o.behavior && loops == o.loops; +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/style/StyleMarqueeData.h b/src/3rdparty/webkit/WebCore/rendering/style/StyleMarqueeData.h new file mode 100644 index 0000000..5765f5d --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/StyleMarqueeData.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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. + * + */ + +#ifndef StyleMarqueeData_h +#define StyleMarqueeData_h + +#include "Length.h" +#include "RenderStyleConstants.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class StyleMarqueeData : public RefCounted<StyleMarqueeData> { +public: + static PassRefPtr<StyleMarqueeData> create() { return adoptRef(new StyleMarqueeData); } + PassRefPtr<StyleMarqueeData> copy() const { return adoptRef(new StyleMarqueeData(*this)); } + + bool operator==(const StyleMarqueeData& o) const; + bool operator!=(const StyleMarqueeData& o) const + { + return !(*this == o); + } + + Length increment; + int speed; + + int loops; // -1 means infinite. + + unsigned behavior : 2; // EMarqueeBehavior + EMarqueeDirection direction : 3; // not unsigned because EMarqueeDirection has negative values + +private: + StyleMarqueeData(); + StyleMarqueeData(const StyleMarqueeData&); +}; + +} // namespace WebCore + +#endif // StyleMarqueeData_h diff --git a/src/3rdparty/webkit/WebCore/rendering/style/StyleMultiColData.cpp b/src/3rdparty/webkit/WebCore/rendering/style/StyleMultiColData.cpp new file mode 100644 index 0000000..bff4fb3 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/StyleMultiColData.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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" +#include "StyleMultiColData.h" + +#include "RenderStyle.h" + +namespace WebCore { + +StyleMultiColData::StyleMultiColData() + : m_width(0) + , m_count(RenderStyle::initialColumnCount()) + , m_gap(0) + , m_autoWidth(true) + , m_autoCount(true) + , m_normalGap(true) + , m_breakBefore(RenderStyle::initialPageBreak()) + , m_breakAfter(RenderStyle::initialPageBreak()) + , m_breakInside(RenderStyle::initialPageBreak()) +{ +} + +StyleMultiColData::StyleMultiColData(const StyleMultiColData& o) + : RefCounted<StyleMultiColData>() + , m_width(o.m_width) + , m_count(o.m_count) + , m_gap(o.m_gap) + , m_rule(o.m_rule) + , m_autoWidth(o.m_autoWidth) + , m_autoCount(o.m_autoCount) + , m_normalGap(o.m_normalGap) + , m_breakBefore(o.m_breakBefore) + , m_breakAfter(o.m_breakAfter) + , m_breakInside(o.m_breakInside) +{ +} + +bool StyleMultiColData::operator==(const StyleMultiColData& o) const +{ + return m_width == o.m_width && m_count == o.m_count && m_gap == o.m_gap && + m_rule == o.m_rule && m_breakBefore == o.m_breakBefore && + m_autoWidth == o.m_autoWidth && m_autoCount == o.m_autoCount && m_normalGap == o.m_normalGap && + m_breakAfter == o.m_breakAfter && m_breakInside == o.m_breakInside; +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/style/StyleMultiColData.h b/src/3rdparty/webkit/WebCore/rendering/style/StyleMultiColData.h new file mode 100644 index 0000000..dec0a55 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/StyleMultiColData.h @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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. + * + */ + +#ifndef StyleMultiColData_h +#define StyleMultiColData_h + +#include "BorderValue.h" +#include "Length.h" +#include "RenderStyleConstants.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +// CSS3 Multi Column Layout + +class StyleMultiColData : public RefCounted<StyleMultiColData> { +public: + static PassRefPtr<StyleMultiColData> create() { return adoptRef(new StyleMultiColData); } + PassRefPtr<StyleMultiColData> copy() const { return adoptRef(new StyleMultiColData(*this)); } + + bool operator==(const StyleMultiColData& o) const; + bool operator!=(const StyleMultiColData &o) const + { + return !(*this == o); + } + + unsigned short ruleWidth() const + { + if (m_rule.style() == BNONE || m_rule.style() == BHIDDEN) + return 0; + return m_rule.width; + } + + float m_width; + unsigned short m_count; + float m_gap; + BorderValue m_rule; + + bool m_autoWidth : 1; + bool m_autoCount : 1; + bool m_normalGap : 1; + unsigned m_breakBefore : 2; // EPageBreak + unsigned m_breakAfter : 2; // EPageBreak + unsigned m_breakInside : 2; // EPageBreak + +private: + StyleMultiColData(); + StyleMultiColData(const StyleMultiColData&); +}; + +} // namespace WebCore + +#endif // StyleMultiColData_h diff --git a/src/3rdparty/webkit/WebCore/rendering/style/StyleRareInheritedData.cpp b/src/3rdparty/webkit/WebCore/rendering/style/StyleRareInheritedData.cpp new file mode 100644 index 0000000..b8fb2dd --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/StyleRareInheritedData.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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" +#include "StyleRareInheritedData.h" + +#include "RenderStyle.h" +#include "RenderStyleConstants.h" + +namespace WebCore { + +StyleRareInheritedData::StyleRareInheritedData() + : textStrokeWidth(RenderStyle::initialTextStrokeWidth()) + , textShadow(0) + , textSecurity(RenderStyle::initialTextSecurity()) + , userModify(READ_ONLY) + , wordBreak(RenderStyle::initialWordBreak()) + , wordWrap(RenderStyle::initialWordWrap()) + , nbspMode(NBNORMAL) + , khtmlLineBreak(LBNORMAL) + , textSizeAdjust(RenderStyle::initialTextSizeAdjust()) + , resize(RenderStyle::initialResize()) + , userSelect(RenderStyle::initialUserSelect()) +{ +} + +StyleRareInheritedData::StyleRareInheritedData(const StyleRareInheritedData& o) + : RefCounted<StyleRareInheritedData>() + , textStrokeColor(o.textStrokeColor) + , textStrokeWidth(o.textStrokeWidth) + , textFillColor(o.textFillColor) + , textShadow(o.textShadow ? new ShadowData(*o.textShadow) : 0) + , highlight(o.highlight) + , textSecurity(o.textSecurity) + , userModify(o.userModify) + , wordBreak(o.wordBreak) + , wordWrap(o.wordWrap) + , nbspMode(o.nbspMode) + , khtmlLineBreak(o.khtmlLineBreak) + , textSizeAdjust(o.textSizeAdjust) + , resize(o.resize) + , userSelect(o.userSelect) +{ +} + +StyleRareInheritedData::~StyleRareInheritedData() +{ + delete textShadow; +} + +bool StyleRareInheritedData::operator==(const StyleRareInheritedData& o) const +{ + return textStrokeColor == o.textStrokeColor + && textStrokeWidth == o.textStrokeWidth + && textFillColor == o.textFillColor + && shadowDataEquivalent(o) + && highlight == o.highlight + && textSecurity == o.textSecurity + && userModify == o.userModify + && wordBreak == o.wordBreak + && wordWrap == o.wordWrap + && nbspMode == o.nbspMode + && khtmlLineBreak == o.khtmlLineBreak + && textSizeAdjust == o.textSizeAdjust + && userSelect == o.userSelect; +} + +bool StyleRareInheritedData::shadowDataEquivalent(const StyleRareInheritedData& o) const +{ + if (!textShadow && o.textShadow || textShadow && !o.textShadow) + return false; + if (textShadow && o.textShadow && (*textShadow != *o.textShadow)) + return false; + return true; +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/style/StyleRareInheritedData.h b/src/3rdparty/webkit/WebCore/rendering/style/StyleRareInheritedData.h new file mode 100644 index 0000000..06ad400 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/StyleRareInheritedData.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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. + * + */ + +#ifndef StyleRareInheritedData_h +#define StyleRareInheritedData_h + +#include "AtomicString.h" +#include "Color.h" +#include <wtf/RefCounted.h> +#include <wtf/PassRefPtr.h> + +namespace WebCore { + +struct ShadowData; + +// This struct is for rarely used inherited CSS3, CSS2, and WebKit-specific properties. +// By grouping them together, we save space, and only allocate this object when someone +// actually uses one of these properties. +class StyleRareInheritedData : public RefCounted<StyleRareInheritedData> { +public: + static PassRefPtr<StyleRareInheritedData> create() { return adoptRef(new StyleRareInheritedData); } + PassRefPtr<StyleRareInheritedData> copy() const { return adoptRef(new StyleRareInheritedData(*this)); } + ~StyleRareInheritedData(); + + bool operator==(const StyleRareInheritedData& o) const; + bool operator!=(const StyleRareInheritedData& o) const + { + return !(*this == o); + } + bool shadowDataEquivalent(const StyleRareInheritedData&) const; + + Color textStrokeColor; + float textStrokeWidth; + Color textFillColor; + + ShadowData* textShadow; // Our text shadow information for shadowed text drawing. + AtomicString highlight; // Apple-specific extension for custom highlight rendering. + unsigned textSecurity : 2; // ETextSecurity + unsigned userModify : 2; // EUserModify (editing) + unsigned wordBreak : 2; // EWordBreak + unsigned wordWrap : 1; // EWordWrap + unsigned nbspMode : 1; // ENBSPMode + unsigned khtmlLineBreak : 1; // EKHTMLLineBreak + bool textSizeAdjust : 1; // An Apple extension. + unsigned resize : 2; // EResize + unsigned userSelect : 1; // EUserSelect + +private: + StyleRareInheritedData(); + StyleRareInheritedData(const StyleRareInheritedData&); +}; + +} // namespace WebCore + +#endif // StyleRareInheritedData_h diff --git a/src/3rdparty/webkit/WebCore/rendering/style/StyleRareNonInheritedData.cpp b/src/3rdparty/webkit/WebCore/rendering/style/StyleRareNonInheritedData.cpp new file mode 100644 index 0000000..e8ceeeb --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/StyleRareNonInheritedData.cpp @@ -0,0 +1,167 @@ +/* + * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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" +#include "StyleRareNonInheritedData.h" + +#include "CSSStyleSelector.h" +#include "RenderStyle.h" + +namespace WebCore { + +StyleRareNonInheritedData::StyleRareNonInheritedData() + : lineClamp(RenderStyle::initialLineClamp()) + , opacity(RenderStyle::initialOpacity()) + , m_content(0) + , m_counterDirectives(0) + , userDrag(RenderStyle::initialUserDrag()) + , textOverflow(RenderStyle::initialTextOverflow()) + , marginTopCollapse(MCOLLAPSE) + , marginBottomCollapse(MCOLLAPSE) + , matchNearestMailBlockquoteColor(RenderStyle::initialMatchNearestMailBlockquoteColor()) + , m_appearance(RenderStyle::initialAppearance()) + , m_borderFit(RenderStyle::initialBorderFit()) + , m_boxShadow(0) + , m_animations(0) + , m_transitions(0) + , m_mask(FillLayer(MaskFillLayer)) +#if ENABLE(XBL) + , bindingURI(0) +#endif +{ +} + +StyleRareNonInheritedData::StyleRareNonInheritedData(const StyleRareNonInheritedData& o) + : RefCounted<StyleRareNonInheritedData>() + , lineClamp(o.lineClamp) + , opacity(o.opacity) + , flexibleBox(o.flexibleBox) + , marquee(o.marquee) + , m_multiCol(o.m_multiCol) + , m_transform(o.m_transform) + , m_content(0) + , m_counterDirectives(0) + , userDrag(o.userDrag) + , textOverflow(o.textOverflow) + , marginTopCollapse(o.marginTopCollapse) + , marginBottomCollapse(o.marginBottomCollapse) + , matchNearestMailBlockquoteColor(o.matchNearestMailBlockquoteColor) + , m_appearance(o.m_appearance) + , m_borderFit(o.m_borderFit) + , m_boxShadow(o.m_boxShadow ? new ShadowData(*o.m_boxShadow) : 0) + , m_boxReflect(o.m_boxReflect) + , m_animations(o.m_animations ? new AnimationList(*o.m_animations) : 0) + , m_transitions(o.m_transitions ? new AnimationList(*o.m_transitions) : 0) + , m_mask(o.m_mask) + , m_maskBoxImage(o.m_maskBoxImage) +#if ENABLE(XBL) + , bindingURI(o.bindingURI ? o.bindingURI->copy() : 0) +#endif +{ +} + +StyleRareNonInheritedData::~StyleRareNonInheritedData() +{ +} + +#if ENABLE(XBL) +bool StyleRareNonInheritedData::bindingsEquivalent(const StyleRareNonInheritedData& o) const +{ + if (this == &o) return true; + if (!bindingURI && o.bindingURI || bindingURI && !o.bindingURI) + return false; + if (bindingURI && o.bindingURI && (*bindingURI != *o.bindingURI)) + return false; + return true; +} +#endif + +bool StyleRareNonInheritedData::operator==(const StyleRareNonInheritedData& o) const +{ + return lineClamp == o.lineClamp +#if ENABLE(DASHBOARD_SUPPORT) + && m_dashboardRegions == o.m_dashboardRegions +#endif + && opacity == o.opacity + && flexibleBox == o.flexibleBox + && marquee == o.marquee + && m_multiCol == o.m_multiCol + && m_transform == o.m_transform + && m_content == o.m_content + && m_counterDirectives == o.m_counterDirectives + && userDrag == o.userDrag + && textOverflow == o.textOverflow + && marginTopCollapse == o.marginTopCollapse + && marginBottomCollapse == o.marginBottomCollapse + && matchNearestMailBlockquoteColor == o.matchNearestMailBlockquoteColor + && m_appearance == o.m_appearance + && m_borderFit == o.m_borderFit + && shadowDataEquivalent(o) + && reflectionDataEquivalent(o) + && animationDataEquivalent(o) + && transitionDataEquivalent(o) + && m_mask == o.m_mask + && m_maskBoxImage == o.m_maskBoxImage +#if ENABLE(XBL) + && bindingsEquivalent(o) +#endif + ; +} + +bool StyleRareNonInheritedData::shadowDataEquivalent(const StyleRareNonInheritedData& o) const +{ + if (!m_boxShadow && o.m_boxShadow || m_boxShadow && !o.m_boxShadow) + return false; + if (m_boxShadow && o.m_boxShadow && (*m_boxShadow != *o.m_boxShadow)) + return false; + return true; +} + +bool StyleRareNonInheritedData::reflectionDataEquivalent(const StyleRareNonInheritedData& o) const +{ + if (m_boxReflect != o.m_boxReflect) { + if (!m_boxReflect || !o.m_boxReflect) + return false; + return *m_boxReflect == *o.m_boxReflect; + } + return true; + +} + +bool StyleRareNonInheritedData::animationDataEquivalent(const StyleRareNonInheritedData& o) const +{ + if (!m_animations && o.m_animations || m_animations && !o.m_animations) + return false; + if (m_animations && o.m_animations && (*m_animations != *o.m_animations)) + return false; + return true; +} + +bool StyleRareNonInheritedData::transitionDataEquivalent(const StyleRareNonInheritedData& o) const +{ + if (!m_transitions && o.m_transitions || m_transitions && !o.m_transitions) + return false; + if (m_transitions && o.m_transitions && (*m_transitions != *o.m_transitions)) + return false; + return true; +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/style/StyleRareNonInheritedData.h b/src/3rdparty/webkit/WebCore/rendering/style/StyleRareNonInheritedData.h new file mode 100644 index 0000000..6ce6a33 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/StyleRareNonInheritedData.h @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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. + * + */ + +#ifndef StyleRareNonInheritedData_h +#define StyleRareNonInheritedData_h + +#include "CounterDirectives.h" +#include "CursorData.h" +#include "DataRef.h" +#include "FillLayer.h" +#include "NinePieceImage.h" +#include <wtf/OwnPtr.h> +#include <wtf/PassRefPtr.h> +#include <wtf/Vector.h> + +namespace WebCore { + +class AnimationList; +class CSSStyleSelector; +class StyleFlexibleBoxData; +class StyleMarqueeData; +class StyleMultiColData; +class StyleReflection; +class StyleTransformData; +struct ContentData; +struct ShadowData; + +#if ENABLE(DASHBOARD_SUPPORT) +class StyleDashboardRegion; +#endif + +#if ENABLE(XBL) +class BindingURI; +#endif + +// This struct is for rarely used non-inherited CSS3, CSS2, and WebKit-specific properties. +// By grouping them together, we save space, and only allocate this object when someone +// actually uses one of these properties. +class StyleRareNonInheritedData : public RefCounted<StyleRareNonInheritedData> { +public: + static PassRefPtr<StyleRareNonInheritedData> create() { return adoptRef(new StyleRareNonInheritedData); } + PassRefPtr<StyleRareNonInheritedData> copy() const { return adoptRef(new StyleRareNonInheritedData(*this)); } + ~StyleRareNonInheritedData(); + +#if ENABLE(XBL) + bool bindingsEquivalent(const StyleRareNonInheritedData&) const; +#endif + + bool operator==(const StyleRareNonInheritedData&) const; + bool operator!=(const StyleRareNonInheritedData& o) const { return !(*this == o); } + + bool shadowDataEquivalent(const StyleRareNonInheritedData& o) const; + bool reflectionDataEquivalent(const StyleRareNonInheritedData& o) const; + bool animationDataEquivalent(const StyleRareNonInheritedData&) const; + bool transitionDataEquivalent(const StyleRareNonInheritedData&) const; + + int lineClamp; // An Apple extension. +#if ENABLE(DASHBOARD_SUPPORT) + Vector<StyleDashboardRegion> m_dashboardRegions; +#endif + float opacity; // Whether or not we're transparent. + + DataRef<StyleFlexibleBoxData> flexibleBox; // Flexible box properties + DataRef<StyleMarqueeData> marquee; // Marquee properties + DataRef<StyleMultiColData> m_multiCol; // CSS3 multicol properties + DataRef<StyleTransformData> m_transform; // Transform properties (rotate, scale, skew, etc.) + + OwnPtr<ContentData> m_content; + OwnPtr<CounterDirectiveMap> m_counterDirectives; + + unsigned userDrag : 2; // EUserDrag + bool textOverflow : 1; // Whether or not lines that spill out should be truncated with "..." + unsigned marginTopCollapse : 2; // EMarginCollapse + unsigned marginBottomCollapse : 2; // EMarginCollapse + unsigned matchNearestMailBlockquoteColor : 1; // EMatchNearestMailBlockquoteColor, FIXME: This property needs to be eliminated. It should never have been added. + unsigned m_appearance : 6; // EAppearance + unsigned m_borderFit : 1; // EBorderFit + OwnPtr<ShadowData> m_boxShadow; // For box-shadow decorations. + + RefPtr<StyleReflection> m_boxReflect; + + OwnPtr<AnimationList> m_animations; + OwnPtr<AnimationList> m_transitions; + + FillLayer m_mask; + NinePieceImage m_maskBoxImage; + +#if ENABLE(XBL) + OwnPtr<BindingURI> bindingURI; // The XBL binding URI list. +#endif + +private: + StyleRareNonInheritedData(); + StyleRareNonInheritedData(const StyleRareNonInheritedData&); +}; + +} // namespace WebCore + +#endif // StyleRareNonInheritedData_h diff --git a/src/3rdparty/webkit/WebCore/rendering/style/StyleReflection.h b/src/3rdparty/webkit/WebCore/rendering/style/StyleReflection.h new file mode 100644 index 0000000..455d1b7 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/StyleReflection.h @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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. + * + */ + +#ifndef StyleReflection_h +#define StyleReflection_h + +#include "CSSReflectionDirection.h" +#include "Length.h" +#include "NinePieceImage.h" +#include <wtf/RefCounted.h> + +namespace WebCore { + +class StyleReflection : public RefCounted<StyleReflection> { +public: + static PassRefPtr<StyleReflection> create() + { + return adoptRef(new StyleReflection); + } + + bool operator==(const StyleReflection& o) const + { + return m_direction == o.m_direction && m_offset == o.m_offset && m_mask == o.m_mask; + } + bool operator!=(const StyleReflection& o) const { return !(*this == o); } + + CSSReflectionDirection direction() const { return m_direction; } + Length offset() const { return m_offset; } + const NinePieceImage& mask() const { return m_mask; } + + void setDirection(CSSReflectionDirection dir) { m_direction = dir; } + void setOffset(const Length& l) { m_offset = l; } + void setMask(const NinePieceImage& image) { m_mask = image; } + +private: + StyleReflection() + : m_direction(ReflectionBelow) + , m_offset(0, Fixed) + { + } + + CSSReflectionDirection m_direction; + Length m_offset; + NinePieceImage m_mask; +}; + +} // namespace WebCore + +#endif // StyleReflection_h diff --git a/src/3rdparty/webkit/WebCore/rendering/style/StyleSurroundData.cpp b/src/3rdparty/webkit/WebCore/rendering/style/StyleSurroundData.cpp new file mode 100644 index 0000000..8d5e79c --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/StyleSurroundData.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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" +#include "StyleSurroundData.h" + +namespace WebCore { + +StyleSurroundData::StyleSurroundData() + : margin(Fixed) + , padding(Fixed) +{ +} + +StyleSurroundData::StyleSurroundData(const StyleSurroundData& o) + : RefCounted<StyleSurroundData>() + , offset(o.offset) + , margin(o.margin) + , padding(o.padding) + , border(o.border) +{ +} + +bool StyleSurroundData::operator==(const StyleSurroundData& o) const +{ + return offset == o.offset && margin == o.margin && padding == o.padding && border == o.border; +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/style/StyleSurroundData.h b/src/3rdparty/webkit/WebCore/rendering/style/StyleSurroundData.h new file mode 100644 index 0000000..b8f21e4 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/StyleSurroundData.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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. + * + */ + +#ifndef StyleSurroundData_h +#define StyleSurroundData_h + +#include "BorderData.h" +#include "LengthBox.h" +#include <wtf/RefCounted.h> +#include <wtf/PassRefPtr.h> + +namespace WebCore { + +class StyleSurroundData : public RefCounted<StyleSurroundData> { +public: + static PassRefPtr<StyleSurroundData> create() { return adoptRef(new StyleSurroundData); } + PassRefPtr<StyleSurroundData> copy() const { return adoptRef(new StyleSurroundData(*this)); } + + bool operator==(const StyleSurroundData& o) const; + bool operator!=(const StyleSurroundData& o) const + { + return !(*this == o); + } + + LengthBox offset; + LengthBox margin; + LengthBox padding; + BorderData border; + +private: + StyleSurroundData(); + StyleSurroundData(const StyleSurroundData&); +}; + +} // namespace WebCore + +#endif // StyleSurroundData_h diff --git a/src/3rdparty/webkit/WebCore/rendering/style/StyleTransformData.cpp b/src/3rdparty/webkit/WebCore/rendering/style/StyleTransformData.cpp new file mode 100644 index 0000000..de20e77 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/StyleTransformData.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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" +#include "StyleTransformData.h" + +#include "RenderStyle.h" + +namespace WebCore { + +StyleTransformData::StyleTransformData() + : m_operations(RenderStyle::initialTransform()) + , m_x(RenderStyle::initialTransformOriginX()) + , m_y(RenderStyle::initialTransformOriginY()) +{ +} + +StyleTransformData::StyleTransformData(const StyleTransformData& o) + : RefCounted<StyleTransformData>() + , m_operations(o.m_operations) + , m_x(o.m_x) + , m_y(o.m_y) +{ +} + +bool StyleTransformData::operator==(const StyleTransformData& o) const +{ + return m_x == o.m_x && m_y == o.m_y && m_operations == o.m_operations; +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/style/StyleTransformData.h b/src/3rdparty/webkit/WebCore/rendering/style/StyleTransformData.h new file mode 100644 index 0000000..157e600 --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/StyleTransformData.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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. + * + */ + +#ifndef StyleTransformData_h +#define StyleTransformData_h + +#include "Length.h" +#include "TransformOperations.h" +#include <wtf/PassRefPtr.h> +#include <wtf/RefCounted.h> + +namespace WebCore { + +class StyleTransformData : public RefCounted<StyleTransformData> { +public: + static PassRefPtr<StyleTransformData> create() { return adoptRef(new StyleTransformData); } + PassRefPtr<StyleTransformData> copy() const { return adoptRef(new StyleTransformData(*this)); } + + bool operator==(const StyleTransformData& o) const; + bool operator!=(const StyleTransformData& o) const + { + return !(*this == o); + } + + TransformOperations m_operations; + Length m_x; + Length m_y; + +private: + StyleTransformData(); + StyleTransformData(const StyleTransformData&); +}; + +} // namespace WebCore + +#endif // StyleTransformData_h diff --git a/src/3rdparty/webkit/WebCore/rendering/style/StyleVisualData.cpp b/src/3rdparty/webkit/WebCore/rendering/style/StyleVisualData.cpp new file mode 100644 index 0000000..91690cf --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/StyleVisualData.cpp @@ -0,0 +1,53 @@ +/* + * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * 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" +#include "StyleVisualData.h" + +#include "RenderStyle.h" + +namespace WebCore { + +StyleVisualData::StyleVisualData() + : hasClip(false) + , textDecoration(RenderStyle::initialTextDecoration()) + , counterIncrement(0) + , counterReset(0) + , m_zoom(RenderStyle::initialZoom()) +{ +} + +StyleVisualData::~StyleVisualData() +{ +} + +StyleVisualData::StyleVisualData(const StyleVisualData& o) + : RefCounted<StyleVisualData>() + , clip(o.clip) + , hasClip(o.hasClip) + , textDecoration(o.textDecoration) + , counterIncrement(o.counterIncrement) + , counterReset(o.counterReset) + , m_zoom(RenderStyle::initialZoom()) +{ +} + +} // namespace WebCore diff --git a/src/3rdparty/webkit/WebCore/rendering/style/StyleVisualData.h b/src/3rdparty/webkit/WebCore/rendering/style/StyleVisualData.h new file mode 100644 index 0000000..613ef2f --- /dev/null +++ b/src/3rdparty/webkit/WebCore/rendering/style/StyleVisualData.h @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2000 Lars Knoll (knoll@kde.org) + * (C) 2000 Antti Koivisto (koivisto@kde.org) + * (C) 2000 Dirk Mueller (mueller@kde.org) + * Copyright (C) 2003, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2006 Graham Dennis (graham.dennis@gmail.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. + * + */ + +#ifndef StyleVisualData_h +#define StyleVisualData_h + +#include "LengthBox.h" +#include <wtf/RefCounted.h> +#include <wtf/PassRefPtr.h> + +namespace WebCore { + +class StyleVisualData : public RefCounted<StyleVisualData> { +public: + static PassRefPtr<StyleVisualData> create() { return adoptRef(new StyleVisualData); } + PassRefPtr<StyleVisualData> copy() const { return adoptRef(new StyleVisualData(*this)); } + ~StyleVisualData(); + + bool operator==(const StyleVisualData& o) const + { + return ( clip == o.clip && + hasClip == o.hasClip && + counterIncrement == o.counterIncrement && + counterReset == o.counterReset && + textDecoration == o.textDecoration && + m_zoom == o.m_zoom); + } + bool operator!=(const StyleVisualData& o) const { return !(*this == o); } + + LengthBox clip; + bool hasClip : 1; + unsigned textDecoration : 4; // Text decorations defined *only* by this element. + + short counterIncrement; // ok, so these are not visual mode specific + short counterReset; // can't go to inherited, since these are not inherited + + float m_zoom; + +private: + StyleVisualData(); + StyleVisualData(const StyleVisualData&); +}; + +} // namespace WebCore + +#endif // StyleVisualData_h |