#include "config.h"
#if ENABLE(VIDEO_TRACK)
#include "RenderVTTCue.h"
#include "RenderView.h"
#include "TextTrackCueGeneric.h"
#include "VTTCue.h"
#include <wtf/StackStats.h>
namespace WebCore {
RenderVTTCue::RenderVTTCue(VTTCueBox& element, PassRef<RenderStyle> style)
: RenderBlockFlow(element, WTF::move(style))
, m_cue(element.getCue())
{
}
void RenderVTTCue::layout()
{
StackStats::LayoutCheckPoint layoutCheckPoint;
RenderBlockFlow::layout();
#if ENABLE(WEBVTT_REGIONS)
if (!m_cue->regionId().isEmpty())
return;
#endif
LayoutStateMaintainer statePusher(view(), *this, locationOffset(), hasTransform() || hasReflection() || style().isFlippedBlocksWritingMode());
if (m_cue->cueType()== TextTrackCue::WebVTT) {
if (toVTTCue(m_cue)->snapToLines())
repositionCueSnapToLinesSet();
else
repositionCueSnapToLinesNotSet();
} else
repositionGenericCue();
statePusher.pop();
}
bool RenderVTTCue::initializeLayoutParameters(InlineFlowBox*& firstLineBox, LayoutUnit& step, LayoutUnit& position)
{
ASSERT(firstChild());
RenderBlock* parentBlock = containingBlock();
RenderObject* firstChild = this->firstChild();
RenderElement* backdropElement = toRenderElement(firstChild);
firstLineBox = toRenderInline(backdropElement->firstChild())->firstLineBox();
if (!firstLineBox)
firstLineBox = this->firstRootBox();
step = m_cue->getWritingDirection() == VTTCue::Horizontal ? firstLineBox->height() : firstLineBox->width();
if (!step)
return false;
int linePosition = m_cue->calculateComputedLinePosition();
if (m_cue->getWritingDirection() == VTTCue::VerticalGrowingLeft)
linePosition = -(linePosition + 1);
position = step * linePosition;
if (m_cue->getWritingDirection() == VTTCue::VerticalGrowingLeft) {
position -= width();
position += step;
}
if (linePosition < 0) {
position += m_cue->getWritingDirection() == VTTCue::Horizontal ? parentBlock->height() : parentBlock->width();
step = -step;
}
return true;
}
void RenderVTTCue::placeBoxInDefaultPosition(LayoutUnit position, bool& switched)
{
if (m_cue->getWritingDirection() == VTTCue::Horizontal)
setY(y() + position);
else
setX(x() + position);
m_fallbackPosition = FloatPoint(x(), y());
switched = false;
}
bool RenderVTTCue::isOutside() const
{
return !rectIsWithinContainer(absoluteContentBox());
}
bool RenderVTTCue::rectIsWithinContainer(const IntRect& rect) const
{
return containingBlock()->absoluteBoundingBoxRect().contains(rect);
}
bool RenderVTTCue::isOverlapping() const
{
return overlappingObject();
}
RenderObject* RenderVTTCue::overlappingObject() const
{
return overlappingObjectForRect(absoluteBoundingBoxRect());
}
RenderObject* RenderVTTCue::overlappingObjectForRect(const IntRect& rect) const
{
for (RenderObject* box = previousSibling(); box; box = box->previousSibling()) {
IntRect boxRect = box->absoluteBoundingBoxRect();
if (rect.intersects(boxRect))
return box;
}
return 0;
}
bool RenderVTTCue::shouldSwitchDirection(InlineFlowBox* firstLineBox, LayoutUnit step) const
{
LayoutUnit top = y();
LayoutUnit left = x();
LayoutUnit bottom = top + firstLineBox->height();
LayoutUnit right = left + firstLineBox->width();
LayoutUnit parentHeight = containingBlock()->height();
if (m_cue->getWritingDirection() == VTTCue::Horizontal && ((step < 0 && top < 0) || (step > 0 && bottom > parentHeight)))
return true;
LayoutUnit parentWidth = containingBlock()->width();
if (m_cue->getWritingDirection() != VTTCue::Horizontal && ((step < 0 && left < 0) || (step > 0 && right > parentWidth)))
return true;
return false;
}
void RenderVTTCue::moveBoxesByStep(LayoutUnit step)
{
if (m_cue->getWritingDirection() == VTTCue::Horizontal)
setY(y() + step);
else
setX(x() + step);
}
bool RenderVTTCue::switchDirection(bool& switched, LayoutUnit& step)
{
setX(m_fallbackPosition.x());
setY(m_fallbackPosition.y());
if (switched)
return false;
step = -step;
switched = true;
return true;
}
void RenderVTTCue::moveIfNecessaryToKeepWithinContainer()
{
IntRect containerRect = containingBlock()->absoluteBoundingBoxRect();
IntRect cueRect = absoluteBoundingBoxRect();
int topOverflow = cueRect.y() - containerRect.y();
int bottomOverflow = containerRect.maxY() - cueRect.maxY();
int verticalAdjustment = 0;
if (topOverflow < 0)
verticalAdjustment = -topOverflow;
else if (bottomOverflow < 0)
verticalAdjustment = bottomOverflow;
if (verticalAdjustment)
setY(y() + verticalAdjustment);
int leftOverflow = cueRect.x() - containerRect.x();
int rightOverflow = containerRect.maxX() - cueRect.maxX();
int horizontalAdjustment = 0;
if (leftOverflow < 0)
horizontalAdjustment = -leftOverflow;
else if (rightOverflow < 0)
horizontalAdjustment = rightOverflow;
if (horizontalAdjustment)
setX(x() + horizontalAdjustment);
}
bool RenderVTTCue::findNonOverlappingPosition(int& newX, int& newY) const
{
newX = x();
newY = y();
IntRect srcRect = absoluteBoundingBoxRect();
IntRect destRect = srcRect;
while (RenderObject* box = overlappingObjectForRect(destRect)) {
if (m_cue->getWritingDirection() == VTTCue::Horizontal)
destRect.setY(box->absoluteBoundingBoxRect().y() - destRect.height());
else
destRect.setX(box->absoluteBoundingBoxRect().x() - destRect.width());
}
if (rectIsWithinContainer(destRect)) {
newX += destRect.x() - srcRect.x();
newY += destRect.y() - srcRect.y();
return true;
}
destRect = srcRect;
while (RenderObject* box = overlappingObjectForRect(destRect)) {
if (m_cue->getWritingDirection() == VTTCue::Horizontal)
destRect.setY(box->absoluteBoundingBoxRect().maxY());
else
destRect.setX(box->absoluteBoundingBoxRect().maxX());
}
if (rectIsWithinContainer(destRect)) {
newX += destRect.x() - srcRect.x();
newY += destRect.y() - srcRect.y();
return true;
}
return false;
}
void RenderVTTCue::repositionCueSnapToLinesSet()
{
InlineFlowBox* firstLineBox;
LayoutUnit step;
LayoutUnit position;
if (!initializeLayoutParameters(firstLineBox, step, position))
return;
bool switched;
placeBoxInDefaultPosition(position, switched);
while (isOutside() || isOverlapping()) {
if (!shouldSwitchDirection(firstLineBox, step))
moveBoxesByStep(step);
else if (!switchDirection(switched, step))
break;
}
if (hasInlineDirectionBordersPaddingOrMargin())
moveIfNecessaryToKeepWithinContainer();
}
void RenderVTTCue::repositionGenericCue()
{
ASSERT(firstChild());
RenderObject* firstChild = this->firstChild();
RenderElement* backdropElement = toRenderElement(firstChild);
InlineFlowBox* firstLineBox = toRenderInline(backdropElement->firstChild())->firstLineBox();
if (static_cast<TextTrackCueGeneric*>(m_cue)->useDefaultPosition() && firstLineBox) {
LayoutUnit parentWidth = containingBlock()->logicalWidth();
LayoutUnit width = firstLineBox->width();
LayoutUnit right = (parentWidth / 2) - (width / 2);
setX(right);
}
repositionCueSnapToLinesNotSet();
}
void RenderVTTCue::repositionCueSnapToLinesNotSet()
{
if (!isOutside() && !isOverlapping())
return;
moveIfNecessaryToKeepWithinContainer();
int x = 0;
int y = 0;
if (!findNonOverlappingPosition(x, y))
return;
setX(x);
setY(y);
}
}
#endif