#include "config.h"
#include "RootInlineBox.h"
#include "BidiResolver.h"
#include "Chrome.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::destroy(RenderArena* 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;
}
}
RenderLineBoxList* RootInlineBox::rendererLineBoxes() const
{
return block()->lineBoxes();
}
void RootInlineBox::clearTruncation()
{
if (m_hasEllipsisBox) {
detachEllipsisBox(renderer()->renderArena());
InlineFlowBox::clearTruncation();
}
}
bool RootInlineBox::canAccommodateEllipsis(bool ltr, int blockEdge, int lineBoxEdge, int ellipsisWidth)
{
int delta = ltr ? lineBoxEdge - blockEdge : blockEdge - lineBoxEdge;
if (width() - delta < ellipsisWidth)
return false;
return InlineFlowBox::canAccommodateEllipsis(ltr, blockEdge, ellipsisWidth);
}
void RootInlineBox::placeEllipsis(const AtomicString& ellipsisStr, bool ltr, int blockLeftEdge, int blockRightEdge, int ellipsisWidth,
InlineBox* markupBox)
{
EllipsisBox* ellipsisBox = new (renderer()->renderArena()) EllipsisBox(renderer(), ellipsisStr, this,
ellipsisWidth - (markupBox ? markupBox->width() : 0), height(),
y(), !prevRootBox(),
markupBox);
if (!gEllipsisBoxMap)
gEllipsisBoxMap = new EllipsisBoxMap();
gEllipsisBoxMap->add(this, ellipsisBox);
m_hasEllipsisBox = true;
if (ltr && (x() + width() + ellipsisWidth) <= blockRightEdge) {
ellipsisBox->m_x = x() + width();
return;
}
bool foundBox = false;
ellipsisBox->m_x = placeEllipsisBox(ltr, blockLeftEdge, blockRightEdge, ellipsisWidth, foundBox);
}
int RootInlineBox::placeEllipsisBox(bool ltr, int blockLeftEdge, int blockRightEdge, int ellipsisWidth, bool& foundBox)
{
int result = InlineFlowBox::placeEllipsisBox(ltr, blockLeftEdge, blockRightEdge, ellipsisWidth, foundBox);
if (result == -1)
result = ltr ? blockRightEdge - ellipsisWidth : blockLeftEdge;
return result;
}
void RootInlineBox::paintEllipsisBox(RenderObject::PaintInfo& paintInfo, int tx, int ty) const
{
if (m_hasEllipsisBox && renderer()->shouldPaintWithinRoot(paintInfo) && renderer()->style()->visibility() == VISIBLE &&
paintInfo.phase == PaintPhaseForeground)
ellipsisBox()->paint(paintInfo, tx, ty);
}
#if PLATFORM(MAC)
void RootInlineBox::addHighlightOverflow()
{
Frame* frame = renderer()->document()->frame();
if (!frame)
return;
Page* page = frame->page();
if (!page)
return;
FloatRect rootRect(0, selectionTop(), width(), selectionHeight());
IntRect inflatedRect = enclosingIntRect(page->chrome()->client()->customHighlightRect(renderer()->node(), renderer()->style()->highlight(), rootRect));
setHorizontalOverflowPositions(leftLayoutOverflow(), rightLayoutOverflow(), min(leftVisualOverflow(), inflatedRect.x()), max(rightVisualOverflow(), inflatedRect.right()));
setVerticalOverflowPositions(topLayoutOverflow(), bottomLayoutOverflow(), min(topVisualOverflow(), inflatedRect.y()), max(bottomVisualOverflow(), inflatedRect.bottom()), height());
}
void RootInlineBox::paintCustomHighlight(RenderObject::PaintInfo& paintInfo, int tx, int ty, const AtomicString& highlightType)
{
if (!renderer()->shouldPaintWithinRoot(paintInfo) || renderer()->style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseForeground)
return;
Frame* frame = renderer()->document()->frame();
if (!frame)
return;
Page* page = frame->page();
if (!page)
return;
FloatRect rootRect(tx + x(), ty + selectionTop(), width(), selectionHeight());
FloatRect inflatedRect = page->chrome()->client()->customHighlightRect(renderer()->node(), highlightType, rootRect);
if (inflatedRect.intersects(paintInfo.rect))
page->chrome()->client()->paintCustomHighlight(renderer()->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 = renderer()->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)) {
renderer()->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);
m_lineTop += dy;
m_lineBottom += dy;
m_blockHeight += dy;
}
void RootInlineBox::childRemoved(InlineBox* box)
{
if (box->renderer() == m_lineBreakObj)
setLineBreakInfo(0, 0, BidiStatus());
for (RootInlineBox* prev = prevRootBox(); prev && prev->lineBreakObj() == box->renderer(); prev = prev->prevRootBox()) {
prev->setLineBreakInfo(0, 0, BidiStatus());
prev->markDirty();
}
}
int RootInlineBox::verticallyAlignBoxes(int heightOfBlock)
{
int maxPositionTop = 0;
int maxPositionBottom = 0;
int maxAscent = 0;
int maxDescent = 0;
RenderObject* curr = renderer();
while (curr && !curr->node())
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 lineTop = heightOfBlock;
int lineBottom = heightOfBlock;
placeBoxesVertically(heightOfBlock, maxHeight, maxAscent, strictMode, lineTop, lineBottom);
computeVerticalOverflow(lineTop, lineBottom, strictMode);
setLineTopBottomPositions(lineTop, lineBottom);
if (heightOfBlock > numeric_limits<int>::max() - maxHeight)
return numeric_limits<int>::max();
heightOfBlock = heightOfBlock + maxHeight;
return heightOfBlock;
}
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()->renderer(),
firstBox->x(), selTop, selHeight,
rootBlock, blockX, blockY, tx, ty, paintInfo));
if (rightGap)
result.uniteRight(block()->fillRightSelectionGap(lastBox->parent()->renderer(),
lastBox->x() + lastBox->width(), selTop, selHeight,
rootBlock, blockX, blockY, tx, ty, paintInfo));
if (firstBox && firstBox != lastBox) {
int lastX = firstBox->x() + firstBox->width();
bool isPreviousBoxSelected = firstBox->selectionState() != RenderObject::SelectionNone;
for (InlineBox* box = firstBox->nextLeafChild(); box; box = box->nextLeafChild()) {
if (box->selectionState() != RenderObject::SelectionNone) {
if (isPreviousBoxSelected) result.uniteCenter(block()->fillHorizontalSelectionGap(box->parent()->renderer(),
lastX + tx, selTop + ty,
box->x() - lastX, selHeight, paintInfo));
lastX = box->x() + box->width();
}
if (box == lastBox)
break;
isPreviousBoxSelected = box->selectionState() != RenderObject::SelectionNone;
}
}
return result;
}
void RootInlineBox::setHasSelectedChildren(bool b)
{
if (m_hasSelectedChildren == b)
return;
m_hasSelectedChildren = b;
}
RenderObject::SelectionState RootInlineBox::selectionState()
{
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() const
{
int selectionTop = m_lineTop;
if (!prevRootBox())
return selectionTop;
int prevBottom = prevRootBox()->selectionBottom();
if (prevBottom < selectionTop && block()->containsFloats()) {
int prevLeft = block()->leftOffset(prevBottom, !prevRootBox());
int prevRight = block()->rightOffset(prevBottom, !prevRootBox());
int newLeft = block()->leftOffset(selectionTop, !prevRootBox());
int newRight = block()->rightOffset(selectionTop, !prevRootBox());
if (prevLeft > newLeft || prevRight < newRight)
return selectionTop;
}
return prevBottom;
}
RenderBlock* RootInlineBox::block() const
{
return toRenderBlock(renderer());
}
static bool isEditableLeaf(InlineBox* leaf)
{
return leaf && leaf->renderer() && leaf->renderer()->node() && leaf->renderer()->node()->isContentEditable();
}
InlineBox* RootInlineBox::closestLeafChildForXPos(int x, bool onlyEditableLeaves)
{
InlineBox* firstLeaf = firstLeafChild();
InlineBox* lastLeaf = lastLeafChild();
if (firstLeaf == lastLeaf && (!onlyEditableLeaves || isEditableLeaf(firstLeaf)))
return firstLeaf;
if (x <= firstLeaf->m_x && !firstLeaf->renderer()->isListMarker() && (!onlyEditableLeaves || isEditableLeaf(firstLeaf)))
return firstLeaf;
if (x >= lastLeaf->m_x + lastLeaf->m_width && !lastLeaf->renderer()->isListMarker() && (!onlyEditableLeaves || isEditableLeaf(lastLeaf)))
return lastLeaf;
InlineBox* closestLeaf = 0;
for (InlineBox* leaf = firstLeaf; leaf; leaf = leaf->nextLeafChild()) {
if (!leaf->renderer()->isListMarker() && (!onlyEditableLeaves || isEditableLeaf(leaf))) {
closestLeaf = leaf;
if (x < leaf->m_x + leaf->m_width)
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::removeLineBoxFromRenderObject()
{
block()->lineBoxes()->removeLineBox(this);
}
void RootInlineBox::extractLineBoxFromRenderObject()
{
block()->lineBoxes()->extractLineBox(this);
}
void RootInlineBox::attachLineBoxToRenderObject()
{
block()->lineBoxes()->attachLineBox(this);
}
}