/*
 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
 *           (C) 1999 Antti Koivisto (koivisto@kde.org)
 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 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 "RenderInline.h"

#include "Chrome.h"
#include "FloatQuad.h"
#include "FrameSelection.h"
#include "GraphicsContext.h"
#include "HitTestResult.h"
#include "InlineElementBox.h"
#include "InlineTextBox.h"
#include "Page.h"
#include "RenderBR.h"
#include "RenderBlock.h"
#include "RenderBox.h"
#include "RenderChildIterator.h"
#include "RenderFragmentContainer.h"
#include "RenderFullScreen.h"
#include "RenderGeometryMap.h"
#include "RenderLayer.h"
#include "RenderTheme.h"
#include "RenderTreeBuilder.h"
#include "RenderView.h"
#include "Settings.h"
#include "TransformState.h"
#include "VisiblePosition.h"
#include <wtf/SetForScope.h>

#if ENABLE(DASHBOARD_SUPPORT)
#include "Frame.h"
#endif

namespace WebCore {

RenderInline::RenderInline(Element& element, PassOwnPtr<RenderStyle> style)
    : RenderBoxModelObject(element, style, RenderInlineFlag)
{
    setChildrenInline(true);
}

RenderInline::RenderInline(Document& document, PassOwnPtr<RenderStyle> style)
    : RenderBoxModelObject(document, style, RenderInlineFlag)
{
    setChildrenInline(true);
}

void RenderInline::willBeDestroyed()
{
#if !ASSERT_DISABLED
    // Make sure we do not retain "this" in the continuation outline table map of our containing blocks.
    if (parent() && style()->visibility() == VISIBLE && hasOutline()) {
        bool containingBlockPaintsContinuationOutline = continuation() || isContinuation();
        if (containingBlockPaintsContinuationOutline) {
            if (RenderBlock* cb = containingBlock()) {
                if (RenderBlock* cbCb = cb->containingBlock())
                    ASSERT(!cbCb->paintsContinuationOutline(this));
            }
        }
    }
#endif
    
    if (!renderTreeBeingDestroyed()) {
        if (firstLineBox()) {
            // We can't wait for RenderBoxModelObject::destroy to clear the selection,
            // because by then we will have nuked the line boxes.
            if (isSelectionBorder())
            	document().selection().setNeedsSelectionUpdate();

            // 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 (firstLineBox()->parent()) {
                for (auto* box = firstLineBox(); box; box = box->nextLineBox())
                    box->removeFromParent();
            }
        } else if (parent()) 
            parent()->dirtyLinesFromChangedChild(this);
    }

    m_lineBoxes.deleteLineBoxes();

    RenderBoxModelObject::willBeDestroyed();
}

void RenderInline::updateFromStyle()
{
    RenderBoxModelObject::updateFromStyle();

    // FIXME: Support transforms and reflections on inline flows someday.
    setHasTransformRelatedProperty(false);
    setHasReflection(false);    
}

static RenderObject* inFlowPositionedInlineAncestor(RenderObject* p)
{
    while (p && p->isRenderInline()) {
        if (p->isInFlowPositioned())
            return p;
        p = p->parent();
    }
    return 0;
}

static void updateStyleOfAnonymousBlockContinuations(const RenderBlock& block, const RenderStyle* newStyle, const RenderStyle* oldStyle)
{
    // If any descendant blocks exist then they will be in the next anonymous block and its siblings.
    for (RenderBox* box = block.nextSiblingBox(); box && box->isAnonymousBlock(); box = box->nextSiblingBox()) {
        if(box->style()->position() == newStyle->position())
    		continue;

        if (!box->isRenderBlock())
            continue;

        RenderBlock* block = toRenderBlock(box);
        if (!block->isContinuation())
            continue;

        // If we are no longer in-flow positioned but our descendant block(s) still have an in-flow positioned ancestor then
        // their containing anonymous block should keep its in-flow positioning.
        RenderInline* cont = block->inlineContinuation();
        if (oldStyle->hasInFlowPosition() && inFlowPositionedInlineAncestor(cont))
            continue;
        OwnPtr<RenderStyle> blockStyle = RenderStyle::createAnonymousStyleWithDisplay(*block->style(), DisplayType::Block);
        blockStyle->setPosition(newStyle->position());
        block->setStyle(blockStyle.release());
    }
}

void RenderInline::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
{
    RenderBoxModelObject::styleWillChange(diff, newStyle);
    // RenderInlines forward their absolute positioned descendants to their (non-anonymous) containing block.
	// Check if this non-anonymous containing block can hold the absolute positioned elements when the inline is no longer positioned.
    if (canContainAbsolutelyPositionedObjects() && newStyle->position() == PositionType::Static) {
    	auto* container = RenderObject::containingBlockForPositionType(PositionType::Absolute, *this);
		if (container && !container->canContainAbsolutelyPositionedObjects())
            container->removePositionedObjects(nullptr, NewContainingBlock);
    }
}

void RenderInline::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
{
    RenderBoxModelObject::styleDidChange(diff, oldStyle);

    // 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.
    auto* newStyle = style();
    RenderInline* continuation = inlineContinuation();
    if (continuation && !isContinuation()) {
		for (RenderInline* currCont = continuation; currCont; currCont = currCont->inlineContinuation())
			currCont->setStyle(RenderStyle::clone(*newStyle));

		// If an inline's in-flow positioning has changed and it is part of an active continuation as a descendant of an anonymous containing block,
		// then any descendant blocks will need to change their in-flow positioning accordingly.
		// Do this by updating the position of the descendant blocks' containing anonymous blocks - there may be more than one.
		if (containingBlock()->isAnonymousBlock() && oldStyle && newStyle->position() != oldStyle->position() && (newStyle->hasInFlowPosition() || oldStyle->hasInFlowPosition()))
            updateStyleOfAnonymousBlockContinuations(*containingBlock(), newStyle, oldStyle);
	}
}

bool RenderInline::mayAffectLayout() const
{
    auto* parentStyle = parent()->style();
    RenderInline* parentRenderInline = (*parent()).isRenderInline() ? toRenderInline(parent()) : nullptr;
    auto hasHardLineBreakChildOnly = firstChild() && firstChild() == lastChild() && firstChild()->isBR();
    bool checkFonts = document().inNoQuirksMode();
    auto mayAffectLayout = (parentRenderInline && parentRenderInline->mayAffectLayout())
        || (parentRenderInline && parentStyle->verticalAlign() != EVerticalAlign::BASELINE)
        || style()->verticalAlign() != EVerticalAlign::BASELINE
        || style()->textEmphasisMark() != TextEmphasisMarkNone
        || (checkFonts && (!parentStyle->fontCascade().fontMetrics().hasIdenticalAscentDescentAndLineGap(style()->fontCascade().fontMetrics())
        || parentStyle->lineHeight() != style()->lineHeight()))
        || hasHardLineBreakChildOnly;

    if (!mayAffectLayout && checkFonts) {
        // Have to check the first line style as well.
        parentStyle = parent()->firstLineStyle();
        auto& childStyle = *firstLineStyle();
        mayAffectLayout = !parentStyle->fontCascade().fontMetrics().hasIdenticalAscentDescentAndLineGap(childStyle.fontCascade().fontMetrics())
            || childStyle.verticalAlign() != EVerticalAlign::BASELINE
            || parentStyle->lineHeight() != childStyle.lineHeight();
    }
    return mayAffectLayout;
}

void RenderInline::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
    m_lineBoxes.paint(this, paintInfo, paintOffset);
}

template<typename GeneratorContext>
void RenderInline::generateLineBoxRects(GeneratorContext& yield) const
{
    if (InlineFlowBox* curr = firstLineBox()) {
        for (; curr; curr = curr->nextLineBox())
            yield(FloatRect(curr->topLeft(), curr->size()));
    } else
        yield(FloatRect());
}

namespace {

class AbsoluteRectsGeneratorContext {
public:
    AbsoluteRectsGeneratorContext(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset)
        : m_rects(rects)
        , m_accumulatedOffset(accumulatedOffset) { }

    void operator()(const FloatRect& rect)
    {
        IntRect intRect = enclosingIntRect(rect);
        intRect.move(m_accumulatedOffset.x(), m_accumulatedOffset.y());
        m_rects.append(intRect);
    }
private:
    Vector<IntRect>& m_rects;
    const LayoutPoint& m_accumulatedOffset;
};

} // unnamed namespace

void RenderInline::absoluteRects(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset) const
{
    AbsoluteRectsGeneratorContext context(rects, accumulatedOffset);
    generateLineBoxRects(context);

    if (RenderBoxModelObject* continuation = this->continuation()) {
        if (continuation->isBox()) {
            RenderBox* box = toRenderBox(continuation);
            continuation->absoluteRects(rects, toLayoutPoint(accumulatedOffset - containingBlock()->location() + box->locationOffset()));
        } else
            continuation->absoluteRects(rects, toLayoutPoint(accumulatedOffset - containingBlock()->location()));
    }
}


namespace {

class AbsoluteQuadsGeneratorContext {
public:
    AbsoluteQuadsGeneratorContext(const RenderInline* renderer, Vector<FloatQuad>& quads)
        : m_quads(quads)
        , m_geometryMap()
    {
        m_geometryMap.pushMappingsToAncestor(renderer, 0);
    }

    void operator()(const FloatRect& rect)
    {
        m_quads.append(m_geometryMap.absoluteRect(rect));
    }
private:
    Vector<FloatQuad>& m_quads;
    RenderGeometryMap m_geometryMap;
};

} // unnamed namespace

void RenderInline::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const
{
    AbsoluteQuadsGeneratorContext context(this, quads);
    generateLineBoxRects(context);

    if (continuation())
        continuation()->absoluteQuads(quads, wasFixed);
}

#if PLATFORM(IOS)
void RenderInline::absoluteQuadsForSelection(Vector<FloatQuad>& quads) const
{
    AbsoluteQuadsGeneratorContext context(this, quads);
    generateLineBoxRects(context);
}
#endif

LayoutUnit RenderInline::offsetLeft() const
{
    return adjustedPositionRelativeToOffsetParent(firstInlineBoxTopLeft()).x();
}

LayoutUnit RenderInline::offsetTop() const
{
    return adjustedPositionRelativeToOffsetParent(firstInlineBoxTopLeft()).y();
}

LayoutPoint RenderInline::firstInlineBoxTopLeft() const
{
#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
    if (auto* lineLayout = LayoutIntegration::LineLayout::containing(*this))
        return lineLayout->firstInlineBoxRect(*this).location();
#endif
    if (InlineBox* firstBox = firstLineBox())
        return flooredLayoutPoint(firstBox->locationIncludingFlipping());
    return LayoutPoint();
}

static LayoutUnit computeMargin(const RenderInline* renderer, const Length& margin)
{
    if (margin.isAuto())
        return 0;
    if (margin.isFixed())
        return margin.value();
    if (margin.isPercentOrCalculated())
        return minimumValueForLength(margin, max<LayoutUnit>(0, renderer->containingBlock()->availableLogicalWidth()));
    return 0;
}

LayoutUnit RenderInline::marginLeft() const
{
    return computeMargin(this, style()->marginLeft());
}

LayoutUnit RenderInline::marginRight() const
{
    return computeMargin(this, style()->marginRight());
}

LayoutUnit RenderInline::marginTop() const
{
    return computeMargin(this, style()->marginTop());
}

LayoutUnit RenderInline::marginBottom() const
{
    return computeMargin(this, style()->marginBottom());
}

LayoutUnit RenderInline::marginStart(const RenderStyle* otherStyle) const
{
    return computeMargin(this, style()->marginStartUsing(otherStyle ? otherStyle : style()));
}

LayoutUnit RenderInline::marginEnd(const RenderStyle* otherStyle) const
{
    return computeMargin(this, style()->marginEndUsing(otherStyle ? otherStyle : style()));
}

LayoutUnit RenderInline::marginBefore(const RenderStyle* otherStyle) const
{
    return computeMargin(this, style()->marginBeforeUsing(otherStyle ? otherStyle : style()));
}

LayoutUnit RenderInline::marginAfter(const RenderStyle* otherStyle) const
{
    return computeMargin(this, style()->marginAfterUsing(otherStyle ? otherStyle : style()));
}

const char* RenderInline::renderName() const
{
    if (isRelativelyPositioned())
        return "RenderInline (relative positioned)";
    if (isStickilyPositioned())
        return "RenderInline (sticky positioned)";
    // FIXME: Temporary hack while the new generated content system is being implemented.
    if (isPseudoElement())
        return "RenderInline (generated)";
    if (isAnonymous())
        return "RenderInline (generated)";
    return "RenderInline";
}

bool RenderInline::nodeAtPoint(const HitTestRequest& request, HitTestResult& result,
                                const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, HitTestAction hitTestAction)
{
    return m_lineBoxes.hitTest(this, request, result, locationInContainer, accumulatedOffset, hitTestAction);
}

VisiblePosition RenderInline::positionForPoint(const LayoutPoint& point, const RenderFragmentContainer* fragment)
{
    auto& containingBlock = *this->containingBlock();

    if (auto* continuation = this->continuation()) {
		// Translate the coords from the pre-anonymous block to the post-anonymous block.
		LayoutPoint parentBlockPoint = containingBlock.location() + point;
		while (continuation) {
			RenderBox* contBlock = continuation->isInline() ? continuation->containingBlock() : toRenderBlock(continuation);
			if (continuation->isInline() || continuation->firstChild())
				return continuation->positionForPoint(parentBlockPoint - contBlock->locationOffset(), fragment);
			continuation = continuation->inlineContinuation();
		}
		return RenderBoxModelObject::positionForPoint(point, fragment);
    }

    return containingBlock.positionForPoint(point, fragment);
}

namespace {

class LinesBoundingBoxGeneratorContext {
public:
    LinesBoundingBoxGeneratorContext(FloatRect& rect) : m_rect(rect) { }
    void operator()(const FloatRect& rect)
    {
        m_rect.uniteIfNonZero(rect);
    }
private:
    FloatRect& m_rect;
};

} // unnamed namespace

IntRect RenderInline::linesBoundingBox() const
{
    IntRect result;
    
    // 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 the width of the minimal left side and the maximal right side.
        float logicalLeftSide = 0;
        float logicalRightSide = 0;
        for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
            if (curr == firstLineBox() || curr->logicalLeft() < logicalLeftSide)
                logicalLeftSide = curr->logicalLeft();
            if (curr == firstLineBox() || curr->logicalRight() > logicalRightSide)
                logicalRightSide = curr->logicalRight();
        }
        
        bool isHorizontal = style()->isHorizontalWritingMode();
        
        float x = isHorizontal ? logicalLeftSide : firstLineBox()->x();
        float y = isHorizontal ? firstLineBox()->y() : logicalLeftSide;
        float width = isHorizontal ? logicalRightSide - logicalLeftSide : lastLineBox()->logicalBottom() - x;
        float height = isHorizontal ? lastLineBox()->logicalBottom() - y : logicalRightSide - logicalLeftSide;
        result = enclosingIntRect(FloatRect(x, y, width, height));
    }

    return result;
}

LayoutRect RenderInline::linesVisualOverflowBoundingBox() const
{
    if (!firstLineBox() || !lastLineBox())
        return LayoutRect();

    // Return the width of the minimal left side and the maximal right side.
    LayoutUnit logicalLeftSide = LayoutUnit::max();
    LayoutUnit logicalRightSide = LayoutUnit::min();
    for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
        logicalLeftSide = min(logicalLeftSide, curr->logicalLeftVisualOverflow());
        logicalRightSide = max(logicalRightSide, curr->logicalRightVisualOverflow());
    }

    RootInlineBox* firstRootBox = firstLineBox()->root();
    RootInlineBox* lastRootBox = lastLineBox()->root();
    
    LayoutUnit logicalTop = firstLineBox()->logicalTopVisualOverflow(firstRootBox->lineTop());
    LayoutUnit logicalWidth = logicalRightSide - logicalLeftSide;
    LayoutUnit logicalHeight = lastLineBox()->logicalBottomVisualOverflow(lastRootBox->lineBottom()) - logicalTop;
    
    LayoutRect rect(logicalLeftSide, logicalTop, logicalWidth, logicalHeight);
    if (!style()->isHorizontalWritingMode())
        rect = rect.transposedRect();
    return rect;
}

LayoutRect RenderInline::linesVisualOverflowBoundingBoxInFragment(const RenderFragmentContainer* fragment) const
{
    ASSERT(fragment);

    if (!firstLineBox() || !lastLineBox())
        return LayoutRect();

    // Return the width of the minimal left side and the maximal right side.
    LayoutUnit logicalLeftSide = LayoutUnit::max();
    LayoutUnit logicalRightSide = LayoutUnit::min();
    LayoutUnit logicalTop;
    LayoutUnit logicalHeight;
    InlineFlowBox* lastInlineInFragment = nullptr;
    for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
        const auto& root = *curr->root();
        if (root.containingFragment() != fragment) {
            if (lastInlineInFragment)
                break;
            continue;
        }

        if (!lastInlineInFragment)
            logicalTop = curr->logicalTopVisualOverflow(root.lineTop());

        lastInlineInFragment = curr;

        logicalLeftSide = std::min(logicalLeftSide, curr->logicalLeftVisualOverflow());
        logicalRightSide = std::max(logicalRightSide, curr->logicalRightVisualOverflow());
    }

    if (!lastInlineInFragment)
        return LayoutRect();

    logicalHeight = lastInlineInFragment->logicalBottomVisualOverflow(lastInlineInFragment->root()->lineBottom()) - logicalTop;

    LayoutUnit logicalWidth = logicalRightSide - logicalLeftSide;

    LayoutRect rect(logicalLeftSide, logicalTop, logicalWidth, logicalHeight);
    if (!style()->isHorizontalWritingMode())
        rect = rect.transposedRect();
    return rect;
}

LayoutRect RenderInline::clippedOverflowRectForRepaint(RenderLayerModelObject* repaintContainer) const
{
	// Only first-letter renderers are allowed in here during layout. They mutate the tree triggering repaints.
#ifndef NDEBUG
    auto insideSelfPaintingInlineBox = [&] {
        if (hasSelfPaintingLayer())
            return true;
        auto* containingBlock = this->containingBlock();
        for (auto* ancestor = this->parent(); ancestor && ancestor != containingBlock; ancestor = ancestor->parent()) {
            if (ancestor->hasSelfPaintingLayer())
                return true;
        }
        return false;
    };
    ASSERT(!view().frameView().layoutContext().isPaintOffsetCacheEnabled() || style().styleType() == PseudoId::FirstLetter || insideSelfPaintingInlineBox());
#endif

    auto knownEmpty = [&] {
        if (firstLineBox())
            return false;
        if (continuation())
            return false;
#if ENABLE(LAYOUT_FORMATTING_CONTEXT)
        if (LayoutIntegration::LineLayout::containing(*this))
            return false;
#endif
        return true;
    };

    if (knownEmpty())
        return LayoutRect();

    LayoutRect repaintRect(linesVisualOverflowBoundingBox());
    bool hitRepaintContainer = false;

    // We need to add in the in-flow position offsets of any inlines (including us) up to our
    // containing block.
    RenderBlock* containingBlock = this->containingBlock();
    for (const RenderObject* inlineFlow = this; inlineFlow && inlineFlow->isRenderInline() && inlineFlow != containingBlock;
         inlineFlow = inlineFlow->parent()) {
         if (inlineFlow == repaintContainer) {
            hitRepaintContainer = true;
            break;
        }
        if (inlineFlow->style()->hasInFlowPosition() && inlineFlow->hasLayer())
            repaintRect.move(toRenderInline(inlineFlow)->layer()->offsetForInFlowPosition());
    }

    LayoutUnit outlineSize = style()->outlineSize();
    repaintRect.inflate(outlineSize);

    if (hitRepaintContainer || !containingBlock)
        return repaintRect;

    if (containingBlock->hasNonVisibleOverflow())
        containingBlock->applyCachedClipAndScrollPosition(repaintRect, repaintContainer, visibleRectContextForRepaint());

    repaintRect = containingBlock->computeRectForRepaint(repaintRect, repaintContainer);

    if (outlineSize) {
        for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
            if (!curr->isText())
                repaintRect.unite(curr->rectWithOutlineForRepaint(repaintContainer, outlineSize));
        }

        if (continuation() && !continuation()->isInline() && continuation()->parent())
            repaintRect.unite(continuation()->rectWithOutlineForRepaint(repaintContainer, outlineSize));
    }

    return repaintRect;
}

LayoutRect RenderInline::rectWithOutlineForRepaint(RenderLayerModelObject* repaintContainer, LayoutUnit outlineWidth) const
{
    LayoutRect r(RenderBoxModelObject::rectWithOutlineForRepaint(repaintContainer, outlineWidth));
    for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
        if (!curr->isText())
            r.unite(curr->rectWithOutlineForRepaint(repaintContainer, outlineWidth));
    }
    return r;
}

LayoutRect RenderInline::computeVisibleRectUsingPaintOffset(const LayoutRect& rect) const
{
    LayoutRect adjustedRect = rect;
	LayoutState* layoutState = view().layoutState();
	if (style()->hasInFlowPosition() && layer())
		adjustedRect.move(layer()->offsetForInFlowPosition());
	adjustedRect.move(layoutState->m_paintOffset);
	if (layoutState->m_clipped)
		adjustedRect.intersect(layoutState->m_clipRect);
	return adjustedRect;
}

Optional<LayoutRect> RenderInline::computeVisibleRectInContainer(const LayoutRect& rect, const RenderLayerModelObject* container, VisibleRectContext context) const
{
	// LayoutState is only valid for root-relative repainting
    if (view().layoutStateEnabled() && !container && !context.m_options.contains(VisibleRectContextOption::UseEdgeInclusiveIntersection))
        return computeVisibleRectUsingPaintOffset(rect);

    if (container == this)
        return rect;

    bool containerSkipped;
    RenderElement* localContainer = this->container(container, containerSkipped);
    if (!localContainer)
        return rect;

    LayoutRect adjustedRect = rect;
    LayoutPoint topLeft = adjustedRect.location();

    if (style()->hasInFlowPosition() && layer()) {
        // Apply the in-flow 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 or sticky position
        // flag on the RenderObject has been cleared, so use the one on the style().
        topLeft += layer()->offsetForInFlowPosition();
    }
    
    // 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.
    adjustedRect.setLocation(topLeft);
    if (localContainer->hasNonVisibleOverflow()) {
        // FIXME: Respect the value of context.m_options.
        SetForScope<OptionSet<VisibleRectContextOption>> change(context.m_options, context.m_options | VisibleRectContextOption::ApplyCompositedContainerScrolls);
        bool isEmpty = !toRenderBox(*localContainer).applyCachedClipAndScrollPosition(adjustedRect, container, context);
        if (isEmpty) {
            if (context.m_options.contains(VisibleRectContextOption::UseEdgeInclusiveIntersection))
                return Nullopt;
            return adjustedRect;
        }
    }

    if (containerSkipped) {
        // If the repaintContainer is below o, then we need to map the rect into repaintContainer's coordinates.
        LayoutSize containerOffset = container->offsetFromAncestorContainer(*localContainer);
        adjustedRect.move(-containerOffset);
        return adjustedRect;
    }
    return localContainer->computeVisibleRectInContainer(adjustedRect, container, context);
}

LayoutSize RenderInline::offsetFromContainer(RenderElement& container, const LayoutPoint&, bool* offsetDependsOnPoint) const
{
    ASSERT(&container == this->container());
    
    LayoutSize offset;
    if (isInFlowPositioned())
        offset += offsetForInFlowPosition();

    if (container.isBox())
        offset -= toLayoutSize(toRenderBox(container).scrollPosition());

    if (offsetDependsOnPoint)
        *offsetDependsOnPoint = (container.isBox() && container.style()->isFlippedBlocksWritingMode()) || container.isRenderFragmentedFlow();

    return offset;
}

void RenderInline::mapLocalToContainer(RenderLayerModelObject* repaintContainer, TransformState& transformState, OptionSet<MapCoordinatesMode> mode, bool* wasFixed) const
{
    if (repaintContainer == this)
        return;

    RenderView& v = view();
	if (v.layoutStateEnabled() && !repaintContainer) {
		LayoutState* layoutState = v.layoutState();
		LayoutSize offset = layoutState->m_paintOffset;
		if (style()->hasInFlowPosition() && layer())
			offset += layer()->offsetForInFlowPosition();
		transformState.move(offset);
		return;
	}

    bool containerSkipped;
    auto* o = container(repaintContainer, containerSkipped);
    if (!o)
        return;

    if (mode.contains(ApplyContainerFlip) && o->isBox()) {
        if (o->style()->isFlippedBlocksWritingMode()) {
        	LayoutPoint centerPoint = LayoutPoint(transformState.mappedPoint());
            transformState.move(toRenderBox(o)->flipForWritingMode(centerPoint) - centerPoint);
        }
        mode.remove(ApplyContainerFlip);
    }

    LayoutSize containerOffset = offsetFromContainer(*o, LayoutPoint(transformState.mappedPoint()));

    bool preserve3D = mode.contains(UseTransforms) && (o->style()->preserves3D() || style()->preserves3D());
    if (mode.contains(UseTransforms) && shouldUseTransformFromContainer(o)) {
        TransformationMatrix t;
        getTransformFromContainer(o, containerOffset, t);
        transformState.applyTransform(t, preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
    } else
        transformState.move(containerOffset.width(), containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);

    if (containerSkipped) {
        // There can't be a transform between repaintContainer and o, because transforms create containers, so it should be safe
        // to just subtract the delta between the repaintContainer and o.
        LayoutSize containerOffset = repaintContainer->offsetFromAncestorContainer(*o);
        transformState.move(-containerOffset.width(), -containerOffset.height(), preserve3D ? TransformState::AccumulateTransform : TransformState::FlattenTransform);
        return;
    }

    o->mapLocalToContainer(repaintContainer, transformState, mode, wasFixed);
}

const RenderElement* RenderInline::pushMappingToContainer(const RenderLayerModelObject* ancestorToStopAt, RenderGeometryMap& geometryMap) const
{
    ASSERT(ancestorToStopAt != this);

    bool ancestorSkipped;
    auto* container = this->container(ancestorToStopAt, ancestorSkipped);
    if (!container)
        return nullptr;

    LayoutSize adjustmentForSkippedAncestor;
    if (ancestorSkipped) {
        // There can't be a transform between repaintContainer and o, because transforms create containers, so it should be safe
        // to just subtract the delta between the ancestor and o.
        adjustmentForSkippedAncestor = -ancestorToStopAt->offsetFromAncestorContainer(*container);
    }

    bool offsetDependsOnPoint = false;
    LayoutSize containerOffset = offsetFromContainer(*container, LayoutPoint(), &offsetDependsOnPoint);

    bool preserve3D = container->style()->preserves3D() || style()->preserves3D();
    if (shouldUseTransformFromContainer(container)) {
        TransformationMatrix t;
        getTransformFromContainer(container, containerOffset, t);
        t.translateRight(adjustmentForSkippedAncestor.width(), adjustmentForSkippedAncestor.height()); // FIXME: right?
        geometryMap.push(this, t, preserve3D, offsetDependsOnPoint);
    } else {
        containerOffset += adjustmentForSkippedAncestor;
        geometryMap.push(this, containerOffset, preserve3D, offsetDependsOnPoint);
    }
    
    return ancestorSkipped ? ancestorToStopAt : container;
}

void RenderInline::updateHitTestResult(HitTestResult& result, const LayoutPoint& point)
{
    if (result.innerNode())
        return;

    LayoutPoint localPoint(point);
    if (auto* node = nodeForHitTest()) {
        if (isContinuation()) {
            // 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.
        	auto* firstBlock = node->renderer()->containingBlock();
			localPoint.moveBy(containingBlock()->location() - firstBlock->locationOffset());
		}

        result.setInnerNode(node);
        if (!result.innerNonSharedNode())
            result.setInnerNonSharedNode(node);
        result.setLocalPoint(localPoint);
    }
}

void RenderInline::dirtyLineBoxes(bool fullLayout)
{
    if (fullLayout) {
        m_lineBoxes.deleteLineBoxes();
        return;
    }

    m_lineBoxes.dirtyLineBoxes();
}

void RenderInline::deleteLines()
{
    m_lineBoxes.deleteLineBoxTree();
}

PassOwnPtr<InlineFlowBox> RenderInline::createInlineFlowBox()
{
    return adoptPtr(new InlineFlowBox(*this));
}

InlineFlowBox* RenderInline::createAndAppendInlineFlowBox()
{
    OwnPtr<InlineFlowBox> newFlowBox = createInlineFlowBox();
    auto flowBox = newFlowBox.get();
    m_lineBoxes.appendLineBox(newFlowBox.release());
    return flowBox;
}

LayoutUnit RenderInline::lineHeight(bool firstLine, LineDirectionMode /*direction*/, LinePositionMode /*linePositionMode*/) const
{
    auto* lineStyle = firstLine ? firstLineStyle() : style();
    return lineStyle->computedLineHeight();
}

LayoutUnit RenderInline::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
{
    const RenderStyle& style = firstLine ? *firstLineStyle() : *this->style();
    const FontMetrics& fontMetrics = style.fontMetrics();
    return LayoutUnit((fontMetrics.ascent(baselineType) + (lineHeight(firstLine, direction, linePositionMode) - fontMetrics.height()) / 2).toInt());
}

LayoutSize RenderInline::offsetForInFlowPositionedInline(const RenderBox& child) const
{
    // FIXME: This function isn't right with mixed writing modes.

    ASSERT(isInFlowPositioned());
    if (!isInFlowPositioned())
        return LayoutSize();

    // 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.

    LayoutSize logicalOffset;
    LayoutUnit inlinePosition;
    LayoutUnit blockPosition;
    if (firstLineBox()) {
        inlinePosition = LayoutUnit::fromFloatRound(firstLineBox()->logicalLeft());
        blockPosition = firstLineBox()->logicalTop();
    } else {
        inlinePosition = layer()->staticInlinePosition();
        blockPosition = layer()->staticBlockPosition();
    }

    // Per http://www.w3.org/TR/CSS2/visudet.html#abs-non-replaced-width an absolute positioned box with a static position
    // should locate itself as though it is a normal flow box in relation to its containing block.
    if (!child.style()->hasStaticInlinePosition(style()->isHorizontalWritingMode()))
        logicalOffset.setWidth(inlinePosition);

    if (!child.style()->hasStaticBlockPosition(style()->isHorizontalWritingMode()))
        logicalOffset.setHeight(blockPosition);

    return style()->isHorizontalWritingMode() ? logicalOffset : logicalOffset.transposedSize();
}

void RenderInline::imageChanged(WrappedImagePtr, const IntRect*)
{
    if (!parent())
        return;
        
    // FIXME: We can do better.
    repaint();
}

void RenderInline::addFocusRingRects(Vector<IntRect>& rects, const LayoutPoint& additionalOffset, const RenderLayerModelObject* paintContainer)
{
    AbsoluteRectsGeneratorContext context(rects, additionalOffset);
    generateLineBoxRects(context);

    for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
        if (!curr->isText() && !curr->isListMarker()) {
            FloatPoint pos(additionalOffset);
            // FIXME: This doesn't work correctly with transforms.
            if (curr->hasLayer()) 
                pos = curr->localToContainerPoint(FloatPoint(), const_cast<RenderLayerModelObject*>(paintContainer));
            else if (curr->isBox())
                pos.move(toRenderBox(curr)->locationOffset());
            curr->addFocusRingRects(rects, flooredIntPoint(pos), paintContainer);
        }
    }

    if (continuation()) {
        if (continuation()->isInline())
            continuation()->addFocusRingRects(rects, flooredLayoutPoint(LayoutPoint(additionalOffset + continuation()->containingBlock()->location() - containingBlock()->location())), paintContainer);
        else
            continuation()->addFocusRingRects(rects, flooredLayoutPoint(LayoutPoint(additionalOffset + toRenderBox(continuation())->location() - containingBlock()->location())), paintContainer);
    }
}

void RenderInline::paintOutline(PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
    if (!hasOutline())
        return;
    
    auto* styleToUse = style();
    if (styleToUse->outlineStyleIsAuto() || hasOutlineAnnotation()) {
        if (!theme()->supportsFocusRing(styleToUse)) {
            // Only paint the focus ring by hand if the theme isn't able to draw the focus ring.
            paintFocusRing(paintInfo, paintOffset, styleToUse);
        }
    }

    GraphicsContext* graphicsContext = paintInfo.context;
    if (graphicsContext->paintingDisabled())
        return;

    if (styleToUse->outlineStyleIsAuto() || !styleToUse->hasOutline())
        return;

    Vector<LayoutRect> rects;
    rects.append(LayoutRect());

    for (InlineFlowBox* curr = firstLineBox(); curr; curr = curr->nextLineBox()) {
        RootInlineBox* root = curr->root();
        LayoutUnit top = max<LayoutUnit>(root->lineTop(), curr->logicalTop());
        LayoutUnit bottom = min<LayoutUnit>(root->lineBottom(), curr->logicalBottom());
        rects.append(LayoutRect(curr->x(), top, curr->logicalWidth(), bottom - top));
    }

    for(RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
        if (curr->isText())
        	continue;

        if(curr->isBox()){
        	RenderBox* renderBox = static_cast<RenderBox*>(curr);
        	LayoutRect boxRect = renderBox->contentBoxRect();

        	boxRect.move(renderBox->x(), renderBox->y());

        	bool shouldAppend = true;

            for (unsigned i = 1; i < rects.size(); i++){
            	if(rects[i].intersects(boxRect)){
            		rects[i].unite(boxRect);
            		shouldAppend = false;
            	}
            }

            if(shouldAppend)
            	rects.append(boxRect);
        }
    }

    rects.append(LayoutRect());

    Color outlineColor = styleToUse->visitedDependentColorWithColorFilter(CSSPropertyOutlineColor);
    bool useTransparencyLayer = outlineColor.hasAlpha();
    if (useTransparencyLayer) {
        graphicsContext->beginTransparencyLayer(static_cast<float>(outlineColor.alpha()) / 255);
        outlineColor = Color(outlineColor.red(), outlineColor.green(), outlineColor.blue());
    }

    for (unsigned i = 1; i < rects.size() - 1; i++)
        paintOutlineForLine(graphicsContext, paintOffset, rects.at(i - 1), rects.at(i), rects.at(i + 1), outlineColor);

    if (useTransparencyLayer)
        graphicsContext->endTransparencyLayer();
}

void RenderInline::paintOutlineForLine(GraphicsContext* graphicsContext, const LayoutPoint& paintOffset,
    const LayoutRect& previousLine, const LayoutRect& thisLine, const LayoutRect& nextLine, const Color outlineColor)
{
    const auto& styleToUse = *style();
    float outlineOffset = styleToUse.outlineOffset();
    LayoutRect outlineBoxRect = thisLine;
    outlineBoxRect.inflate(outlineOffset);
    outlineBoxRect.moveBy(paintOffset);
    if (outlineBoxRect.isEmpty())
        return;

    float outlineWidth = styleToUse.outlineWidth();
    EBorderStyle outlineStyle = styleToUse.outlineStyle();
    bool antialias = shouldAntialiasLines(graphicsContext);

    auto adjustedPreviousLine = previousLine;
    adjustedPreviousLine.moveBy(paintOffset);
    auto adjustedNextLine = nextLine;
    adjustedNextLine.moveBy(paintOffset);
    
    float adjacentWidth1 = 0;
    float adjacentWidth2 = 0;
    // left edge
    auto topLeft = outlineBoxRect.minXMinYCorner();
    if (previousLine.isEmpty() || thisLine.x() < previousLine.x() || (previousLine.maxX()) <= thisLine.x()) {
        topLeft.move(-outlineWidth, -outlineWidth);
        adjacentWidth1 = outlineWidth;
    } else {
        topLeft.move(-outlineWidth, 2 * outlineOffset);
        adjacentWidth1 = -outlineWidth;
    }
    auto bottomRight = outlineBoxRect.minXMaxYCorner();
    if (nextLine.isEmpty() || thisLine.x() <= nextLine.x() || (nextLine.maxX()) <= thisLine.x()) {
        bottomRight.move(0, outlineWidth);
        adjacentWidth2 = outlineWidth;
    } else {
        bottomRight.move(0, -2 * outlineOffset);
        adjacentWidth2 = -outlineWidth;
    }
    drawLineForBoxSide(graphicsContext, FloatRect(topLeft, bottomRight), BSLeft, outlineColor, outlineStyle, adjacentWidth1, adjacentWidth2, antialias);
    
    // right edge
    topLeft = outlineBoxRect.maxXMinYCorner();
    if (previousLine.isEmpty() || previousLine.maxX() < thisLine.maxX() || thisLine.maxX() <= previousLine.x()) {
        topLeft.move(0, -outlineWidth);
        adjacentWidth1 = outlineWidth;
    } else {
        topLeft.move(0, 2 * outlineOffset);
        adjacentWidth1 = -outlineWidth;
    }
    bottomRight = outlineBoxRect.maxXMaxYCorner();
    if (nextLine.isEmpty() || nextLine.maxX() <= thisLine.maxX() || thisLine.maxX() <= nextLine.x()) {
        bottomRight.move(outlineWidth, outlineWidth);
        adjacentWidth2 = outlineWidth;
    } else {
        bottomRight.move(outlineWidth, -2 * outlineOffset);
        adjacentWidth2 = -outlineWidth;
    }
    drawLineForBoxSide(graphicsContext, FloatRect(topLeft, bottomRight), BSRight, outlineColor, outlineStyle, adjacentWidth1, adjacentWidth2, antialias);

    // upper edge
    if (thisLine.x() < previousLine.x()) {
        topLeft = outlineBoxRect.minXMinYCorner();
        topLeft.move(-outlineWidth, -outlineWidth);
        adjacentWidth1 = outlineWidth;
        bottomRight = outlineBoxRect.maxXMinYCorner();
        bottomRight.move(outlineWidth, 0);
        if (!previousLine.isEmpty() && adjustedPreviousLine.x() < bottomRight.x()) {
            bottomRight.setX(adjustedPreviousLine.x() - outlineOffset);
            adjacentWidth2 = -outlineWidth;
        } else
            adjacentWidth2 = outlineWidth;
        drawLineForBoxSide(graphicsContext, FloatRect(topLeft, bottomRight), BSTop, outlineColor, outlineStyle, adjacentWidth1, adjacentWidth2, antialias);
    }
    
    if (previousLine.maxX() < thisLine.maxX()) {
        topLeft = outlineBoxRect.minXMinYCorner();
        topLeft.move(-outlineWidth, -outlineWidth);
        if (!previousLine.isEmpty() && adjustedPreviousLine.maxX() > topLeft.x()) {
            topLeft.setX(adjustedPreviousLine.maxX() + outlineOffset);
            adjacentWidth1 = -outlineWidth;
        } else
            adjacentWidth1 = outlineWidth;
        bottomRight = outlineBoxRect.maxXMinYCorner();
        bottomRight.move(outlineWidth, 0);
        adjacentWidth2 = outlineWidth;
        drawLineForBoxSide(graphicsContext, FloatRect(topLeft, bottomRight), BSTop, outlineColor, outlineStyle, adjacentWidth1, adjacentWidth2, antialias);
    }

    if (thisLine.x() == thisLine.maxX()) {
        topLeft = outlineBoxRect.minXMinYCorner();
        topLeft.move(-outlineWidth, -outlineWidth);
        adjacentWidth1 = outlineWidth;
        bottomRight = outlineBoxRect.maxXMinYCorner();
        bottomRight.move(outlineWidth, 0);
        adjacentWidth2 = outlineWidth;
        drawLineForBoxSide(graphicsContext, FloatRect(topLeft, bottomRight), BSTop, outlineColor, outlineStyle, adjacentWidth1, adjacentWidth2, antialias);
    }

    // lower edge
    if (thisLine.x() < nextLine.x()) {
        topLeft = outlineBoxRect.minXMaxYCorner();
        topLeft.move(-outlineWidth, 0);
        adjacentWidth1 = outlineWidth;
        bottomRight = outlineBoxRect.maxXMaxYCorner();
        bottomRight.move(outlineWidth, outlineWidth);
        if (!nextLine.isEmpty() && (adjustedNextLine.x() < bottomRight.x())) {
            bottomRight.setX(adjustedNextLine.x() - outlineOffset);
            adjacentWidth2 = -outlineWidth;
        } else
            adjacentWidth2 = outlineWidth;
        drawLineForBoxSide(graphicsContext, FloatRect(topLeft, bottomRight), BSBottom, outlineColor, outlineStyle, adjacentWidth1, adjacentWidth2, antialias);
    }
    
    if (nextLine.maxX() < thisLine.maxX()) {
        topLeft = outlineBoxRect.minXMaxYCorner();
        topLeft.move(-outlineWidth, 0);
        if (!nextLine.isEmpty() && adjustedNextLine.maxX() > topLeft.x()) {
            topLeft.setX(adjustedNextLine.maxX() + outlineOffset);
            adjacentWidth1 = -outlineWidth;
        } else
            adjacentWidth1 = outlineWidth;
        bottomRight = outlineBoxRect.maxXMaxYCorner();
        bottomRight.move(outlineWidth, outlineWidth);
        adjacentWidth2 = outlineWidth;
        drawLineForBoxSide(graphicsContext, FloatRect(topLeft, bottomRight), BSBottom, outlineColor, outlineStyle, adjacentWidth1, adjacentWidth2, antialias);
    }

    if (thisLine.x() == thisLine.maxX()) {
        topLeft = outlineBoxRect.minXMaxYCorner();
        topLeft.move(-outlineWidth, 0);
        adjacentWidth1 = outlineWidth;
        bottomRight = outlineBoxRect.maxXMaxYCorner();
        bottomRight.move(outlineWidth, outlineWidth);
        adjacentWidth2 = outlineWidth;
        drawLineForBoxSide(graphicsContext, FloatRect(topLeft, bottomRight), BSBottom, outlineColor, outlineStyle, adjacentWidth1, adjacentWidth2, antialias);
    }
}

#if ENABLE(DASHBOARD_SUPPORT)
void RenderInline::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];

        LayoutRect linesBoundingBox = this->linesBoundingBox();
        LayoutUnit w = linesBoundingBox.width();
        LayoutUnit h = linesBoundingBox.height();

        DashboardRegionValue region;
        region.label = styleRegion.label;
        region.bounds = LayoutRect(linesBoundingBox.x() + styleRegion.offset.left().value(),
                                linesBoundingBox.y() + 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;

        RenderObject* container = containingBlock();
        if (!container)
            container = this;

        region.clip = region.bounds;
        container->computeAbsoluteRepaintRect(region.clip);
        if (region.clip.height() < 0) {
            region.clip.setHeight(0);
            region.clip.setWidth(0);
        }

        FloatPoint absPos = container->localToAbsolute();
        region.bounds.setX(absPos.x() + region.bounds.x());
        region.bounds.setY(absPos.y() + region.bounds.y());

        regions.append(region);
    }
}
#endif

} // namespace WebCore
