/*
 * Copyright (C) 2011 Adobe Systems Incorporated. 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 THE COPYRIGHT HOLDER "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 THE COPYRIGHT HOLDER 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 "RenderFragmentedFlow.h"

#include "HitTestRequest.h"
#include "HitTestResult.h"
#include "InlineElementBox.h"
#include "Node.h"
#include "PODIntervalTree.h"
#include "PaintInfo.h"
#include "RenderBoxFragmentInfo.h"
#include "RenderInline.h"
#include "RenderLayer.h"
#include "RenderLayerCompositor.h"
#include "RenderFragmentContainer.h"
#include "RenderTableCell.h"
#include "RenderTableSection.h"
#include "RenderTheme.h"
#include "RenderView.h"
#include "TransformState.h"

namespace WebCore {

// FIXME: RenderFragmentedFlow should either be anonymous or be associated with some Element.
// It should not be constructed as a renderer for the Document.
RenderFragmentedFlow::RenderFragmentedFlow(Document& document, PassOwnPtr<RenderStyle> style)
	: RenderBlockFlow(document, style)
	, m_currentRegionMaintainer(nullptr)
	, m_regionsInvalidated(false)
	, m_regionsHaveUniformLogicalWidth(true)
	, m_regionsHaveUniformLogicalHeight(true)
	, m_pageLogicalSizeChanged(false)
{
	m_baseTypeFlags |= RenderFragmentedFlowFlag;

	view().setHasRenderFragmentedFlows(true);
}

void RenderFragmentedFlow::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
{
	RenderBlockFlow::styleDidChange(diff, oldStyle);

    if (oldStyle && oldStyle->writingMode() != style()->writingMode())
    	invalidateFragments();
}

void RenderFragmentedFlow::removeFlowChildInfo(RenderElement& child)
{
    if (child.isRenderBlockFlow())
        removeLineRegionInfo(toRenderBlockFlow(child));
    if (child.isBox())
        removeRenderBoxFragmentInfo(toRenderBox(child));
}

void RenderFragmentedFlow::removeRegionFromThread(RenderFragmentContainer* renderRegion)
{
    ASSERT(renderRegion);
    m_fragmentList.remove(renderRegion);
}

void RenderFragmentedFlow::invalidateFragments(MarkingBehavior markingParents)
{
    if (m_regionsInvalidated) {
        ASSERT(selfNeedsLayout());
        return;
    }

    m_fragmentRangeMap.clear();
    m_breakBeforeToRegionMap.clear();
    m_breakAfterToRegionMap.clear();
    if (m_lineToFragmentMap)
        m_lineToFragmentMap->clear();
    setNeedsLayout(markingParents);

    m_regionsInvalidated = true;
}

void RenderFragmentedFlow::validateRegions()
{
    if (m_regionsInvalidated) {
        m_regionsInvalidated = false;
        m_regionsHaveUniformLogicalWidth = true;
        m_regionsHaveUniformLogicalHeight = true;
        bool firstRegionVisited = false;

        if (hasFragments()) {
        	LayoutUnit previousRegionLogicalWidth;
        	LayoutUnit previousRegionLogicalHeight;

            for (auto& fragment : m_fragmentList) {
                ASSERT(!fragment->needsLayout() || fragment->isRenderFragmentContainerSet());
                
                fragment->deleteAllRenderBoxFragmentInfo();

                LayoutUnit regionLogicalWidth = fragment->pageLogicalWidth();
                LayoutUnit regionLogicalHeight = fragment->pageLogicalHeight();

                if (!firstRegionVisited)
                    firstRegionVisited = true;
                else {
                    if (m_regionsHaveUniformLogicalWidth && previousRegionLogicalWidth != regionLogicalWidth)
                        m_regionsHaveUniformLogicalWidth = false;
                    if (m_regionsHaveUniformLogicalHeight && previousRegionLogicalHeight != regionLogicalHeight)
                        m_regionsHaveUniformLogicalHeight = false;
                }

                previousRegionLogicalWidth = regionLogicalWidth;
            }

            setFragmentRangeForBox(*this, m_fragmentList.first(), m_fragmentList.last());
        }
    }

	updateLogicalWidth(); // Called to get the maximum logical width for the region.
	updateRegionsFlowThreadPortionRect();
}

void RenderFragmentedFlow::layout()
{
    StackStats::LayoutCheckPoint layoutCheckPoint;

    m_pageLogicalSizeChanged = m_regionsInvalidated && everHadLayout();

    validateRegions();

    RenderBlockFlow::layout();

    m_pageLogicalSizeChanged = false;
}

void RenderFragmentedFlow::updateLogicalWidth()
{
    LayoutUnit logicalWidth = initialLogicalWidth();
    for (auto& fragment : m_fragmentList) {
        ASSERT(!fragment->needsLayout() || fragment->isRenderFragmentContainerSet());
        logicalWidth = max(fragment->pageLogicalWidth(), logicalWidth);
    }
    setLogicalWidth(logicalWidth);

    // If the regions have non-uniform logical widths, then insert inset information for the RenderFragmentedFlow.
    for (auto& fragment : m_fragmentList) {
        LayoutUnit regionLogicalWidth = fragment->pageLogicalWidth();
		LayoutUnit logicalLeft = style()->direction() == LTR ? LayoutUnit() : logicalWidth - regionLogicalWidth;
		fragment->setRenderBoxFragmentInfo(this, logicalLeft, regionLogicalWidth, false);
    }
}

RenderBox::LogicalExtentComputedValues RenderFragmentedFlow::computeLogicalHeight(LayoutUnit, LayoutUnit logicalTop) const
{
	LogicalExtentComputedValues computedValues;
    computedValues.m_position = logicalTop;
    computedValues.m_extent = 0;

    const LayoutUnit maxFlowSize = RenderFragmentedFlow::maxLogicalHeight();
    for (auto& fragment : m_fragmentList) {
        ASSERT(!fragment->needsLayout() || fragment->isRenderFragmentContainerSet());

        LayoutUnit distanceToMaxSize = maxFlowSize - computedValues.m_extent;
        computedValues.m_extent += std::min(distanceToMaxSize, fragment->logicalHeightOfAllFragmentedFlowContent());

        // If we reached the maximum size there's no point in going further.
        if (computedValues.m_extent == maxFlowSize)
            return computedValues;
    }
    return computedValues;
}

bool RenderFragmentedFlow::shouldRepaint(const LayoutRect& r) const
{
    if (view().printing() || r.isEmpty())
        return false;

    return true;
}

void RenderFragmentedFlow::repaintRectangleInRegions(const LayoutRect& repaintRect)
{
    if (!shouldRepaint(repaintRect) || !hasValidFragmentInfo())
        return;

    LayoutStateDisabler layoutStateDisabler(view()); // We can't use layout state to repaint, since the regions are somewhere else.

    for (auto& fragment : m_fragmentList)
    	fragment->repaintFragmentedFlowContent(repaintRect);
}

bool RenderFragmentedFlow::absoluteQuadsForBox(Vector<FloatQuad>& quads, bool* wasFixed, const RenderBox* box) const
{
    if (!hasValidFragmentInfo())
        return false;

    auto boxRect = FloatRect(FloatPoint(), box->size());
    auto boxRectInFlowCoordinates = LayoutRect(box->localToContainerQuad(boxRect, const_cast<RenderFragmentedFlow*>(this)).boundingBox());

    RenderFragmentContainer* startFragment = nullptr;
    RenderFragmentContainer* endFragment = nullptr;
    if (!computedFragmentRangeForBox(box, startFragment, endFragment))
        return false;

    for (auto it = m_fragmentList.find(startFragment), end = m_fragmentList.end(); it != end; ++it) {
        auto* fragment = *it;
        auto rectsInFragment = fragment->fragmentRectsForFlowContentRect(boxRectInFlowCoordinates);
        for (auto rect : rectsInFragment) {
            auto absoluteQuad = fragment->localToAbsoluteQuad(FloatRect(rect), UseTransforms, wasFixed);
            quads.append(absoluteQuad);
        }

        if (fragment == endFragment)
            break;
    }

    return true;
}

RenderFragmentContainer* RenderFragmentedFlow::fragmentAtBlockOffset(const RenderBox* clampBox, LayoutUnit offset, bool extendLastRegion) const
{
    ASSERT(!m_regionsInvalidated);

    if (m_fragmentList.isEmpty())
        return nullptr;

    if (m_fragmentList.size() == 1 && extendLastRegion)
        return m_fragmentList.first();

    if (offset <= 0)
        return clampBox ? clampBox->clampToStartAndEndRegions(m_fragmentList.first()) : m_fragmentList.first();

    RegionSearchAdapter adapter(offset);
    m_regionIntervalTree.allOverlapsWithAdapter<RegionSearchAdapter>(adapter);

    // If no region was found, the offset is in the flow thread overflow.
    // The last region will contain the offset if extendLastRegion is set or if the last region is a set.
    if (!adapter.result() && (extendLastRegion || m_fragmentList.last()->isRenderFragmentContainerSet()))
		return clampBox ? clampBox->clampToStartAndEndRegions(m_fragmentList.last()) : m_fragmentList.last();

	RenderFragmentContainer* region = adapter.result();
	if (!clampBox)
		return region;
	return region ? clampBox->clampToStartAndEndRegions(region) : nullptr;
}

LayoutPoint RenderFragmentedFlow::adjustedPositionRelativeToOffsetParent(const RenderBoxModelObject& boxModelObject, const LayoutPoint& startPoint) const
{
    LayoutPoint referencePoint = startPoint;

    const RenderBlock* objContainingBlock = boxModelObject.containingBlock();
    // FIXME: This needs to be adapted for different writing modes inside the flow thread.
    RenderFragmentContainer* startRegion = fragmentAtBlockOffset(objContainingBlock, referencePoint.y());
    if (startRegion) {
        // Take into account the offset coordinates of the region.
    	RenderBoxModelObject* startRegionBox = startRegion;
        RenderBoxModelObject* currObject = startRegionBox;
        RenderBoxModelObject* currOffsetParent;
        while ((currOffsetParent = currObject->offsetParent())) {
            referencePoint.move(currObject->offsetLeft(), currObject->offsetTop());

            // Since we're looking for the offset relative to the body, we must also
            // take into consideration the borders of the region's offsetParent.
            if (currOffsetParent->isBox() && !currOffsetParent->isBody())
                referencePoint.move(toRenderBox(currOffsetParent)->borderLeft(), toRenderBox(currOffsetParent)->borderTop());

            currObject = currOffsetParent;
        }

        // We need to check if any of this box's containing blocks start in a different region
        // and if so, drop the object's top position (which was computed relative to its containing block
        // and is no longer valid) and recompute it using the region in which it flows as reference.
        bool wasComputedRelativeToOtherRegion = false;
        while (objContainingBlock && !objContainingBlock->isRenderView()) {
            // Check if this object is in a different region.
            RenderFragmentContainer* parentStartRegion = nullptr;
            RenderFragmentContainer* parentEndRegion = nullptr;
            if (getFragmentRangeForBox(*objContainingBlock, parentStartRegion, parentEndRegion)) {
                wasComputedRelativeToOtherRegion = true;
                break;
            }
            objContainingBlock = objContainingBlock->containingBlock();
        }

        if (wasComputedRelativeToOtherRegion) {
            if (boxModelObject.isBox()) {
                // Use borderBoxRectInRegion to account for variations such as percentage margins.
                LayoutRect borderBoxRect = toRenderBox(&boxModelObject)->borderBoxRectInRegion(startRegion, RenderBox::DoNotCacheRenderBoxRegionInfo);
                referencePoint.move(borderBoxRect.location().x(), 0);
            }

            // Get the logical top coordinate of the current object.
            LayoutUnit top;
            if (boxModelObject.isRenderBlock())
                top = toRenderBlock(&boxModelObject)->offsetFromLogicalTopOfFirstPage();
            else {
                if (boxModelObject.containingBlock())
                    top = boxModelObject.containingBlock()->offsetFromLogicalTopOfFirstPage();

                if (boxModelObject.isBox())
                    top += toRenderBox(&boxModelObject)->topLeftLocation().y();
                else if (boxModelObject.isRenderInline())
                    top -= toRenderInline(&boxModelObject)->borderTop();
            }

            // Get the logical top of the region this object starts in
            // and compute the object's top, relative to the region's top.
            LayoutUnit regionLogicalTop = startRegion->pageLogicalTopForOffset(top);
            LayoutUnit topRelativeToRegion = top - regionLogicalTop;
            referencePoint.setY(startRegionBox->offsetTop() + topRelativeToRegion);

            // Since the top has been overriden, check if the
            // relative/sticky positioning must be reconsidered.
            if (boxModelObject.isRelativelyPositioned())
                referencePoint.move(0, boxModelObject.relativePositionOffset().height());
            else if (boxModelObject.isStickilyPositioned())
                referencePoint.move(0, boxModelObject.stickyPositionOffset().height());
        }

        // Since we're looking for the offset relative to the body, we must also
        // take into consideration the borders of the region.
        referencePoint.move(startRegionBox->borderLeft(), startRegionBox->borderTop());
    }

    return referencePoint;
}

LayoutUnit RenderFragmentedFlow::pageLogicalTopForOffset(LayoutUnit offset) const
{
	RenderFragmentContainer* region = fragmentAtBlockOffset(0, offset, false);
	return region ? region->pageLogicalTopForOffset(offset) : LayoutUnit();
}

LayoutUnit RenderFragmentedFlow::pageLogicalWidthForOffset(LayoutUnit offset) const
{
	RenderFragmentContainer* region = fragmentAtBlockOffset(0, offset, true);
	return region ? region->pageLogicalWidth() : contentLogicalWidth();
}

LayoutUnit RenderFragmentedFlow::pageLogicalHeightForOffset(LayoutUnit offset) const
{
	RenderFragmentContainer* fragment = fragmentAtBlockOffset(0, offset, false);
	if (!fragment)
        return LayoutUnit();
    return fragment->pageLogicalHeight();
}

LayoutUnit RenderFragmentedFlow::pageRemainingLogicalHeightForOffset(LayoutUnit offset, PageBoundaryRule pageBoundaryRule) const
{
	RenderFragmentContainer* fragment = fragmentAtBlockOffset(0, offset, false);
	if (!fragment)
        return LayoutUnit();

    LayoutUnit pageLogicalTop = fragment->pageLogicalTopForOffset(offset);
    LayoutUnit pageLogicalHeight = fragment->pageLogicalHeight();
    LayoutUnit pageLogicalBottom = pageLogicalTop + pageLogicalHeight;
    LayoutUnit remainingHeight = pageLogicalBottom - offset;
    if (pageBoundaryRule == IncludePageBoundary) {
        // If IncludePageBoundary is set, the line exactly on the top edge of a
        // region will act as being part of the previous region.
        remainingHeight = intMod(remainingHeight, pageLogicalHeight);
    } else if (!remainingHeight) {
        // When pageBoundaryRule is IncludePageBoundary, we shouldn't just return 0 if there's no
        // space left, because in that case we're at a column boundary, in which case we should
        // return the amount of space remaining in the *next* column. Note that the page height
        // itself may be 0, though.
        remainingHeight = pageLogicalHeight;
    }
    return remainingHeight;
}

RenderFragmentContainer* RenderFragmentedFlow::mapFromFlowToFragment(TransformState& transformState) const
{
    if (!hasValidFragmentInfo())
        return nullptr;

    RenderFragmentContainer* renderRegion = currentRegion();
    if (!renderRegion) {
		LayoutRect boxRect = transformState.mappedQuad().enclosingBoundingBox();
		flipForWritingMode(boxRect);

		LayoutPoint center = boxRect.center();
		renderRegion = fragmentAtBlockOffset(this, isHorizontalWritingMode() ? center.y() : center.x(), true);
		if (!renderRegion)
			return nullptr;
    }

    LayoutRect flippedRegionRect(renderRegion->fragmentedFlowPortionRect());
    flipForWritingMode(flippedRegionRect);

    transformState.move(renderRegion->contentBoxRect().location() - flippedRegionRect.location());

    return renderRegion;
}

void RenderFragmentedFlow::removeRenderBoxFragmentInfo(RenderBox& box)
{
    if (!hasFragments())
        return;

    // If the region chain was invalidated the next layout will clear the box information from all the regions.
    if (m_regionsInvalidated) {
        ASSERT(selfNeedsLayout());
        return;
    }

    RenderFragmentContainer* startRegion = 0;
    RenderFragmentContainer* endRegion = 0;
    if (getFragmentRangeForBox(box, startRegion, endRegion)) {
        for (auto it = m_fragmentList.find(startRegion), end = m_fragmentList.end(); it != end; ++it) {
            RenderFragmentContainer* region = *it;
			region->removeRenderBoxFragmentInfo(box);
			if (region == endRegion)
				break;
		}
    }

#ifndef NDEBUG
    // We have to make sure we did not leave any RenderBoxFragmentInfo attached.
    for (auto& fragment : m_fragmentList)
        ASSERT(!fragment->renderBoxFragmentInfo(&box));
#endif

    m_fragmentRangeMap.remove(&box);
}

void RenderFragmentedFlow::removeLineRegionInfo(const RenderBlockFlow& blockFlow)
{
    if (!m_lineToFragmentMap)
        return;

    for (RootInlineBox* curr = blockFlow.firstRootBox(); curr; curr = curr->nextRootBox())
        m_lineToFragmentMap->remove(curr);

    ASSERT_WITH_SECURITY_IMPLICATION(checkLinesConsistency(blockFlow));
}

void RenderFragmentedFlow::logicalWidthChangedInRegionsForBlock(const RenderBlock* block, bool& relayoutChildren)
{
    if (!hasValidFragmentInfo())
        return;

    auto it = m_fragmentRangeMap.find(block);
    if (it == m_fragmentRangeMap.end())
        return;

    RenderRegionRange& range = it->second;
    bool rangeInvalidated = range.rangeInvalidated();
    range.clearRangeInvalidated();

    // If there will be a relayout anyway skip the next steps because they only verify
    // the state of the ranges.
    if (relayoutChildren)
        return;

    // Not necessary for the flow thread, since we already computed the correct info for it.
    // If the regions have changed invalidate the children.
    if (block == this) {
		relayoutChildren = m_pageLogicalSizeChanged;
		return;
	}

    RenderFragmentContainer* startRegion = 0;
    RenderFragmentContainer* endRegion = 0;
    if (!getFragmentRangeForBox(*block, startRegion, endRegion))
        return;

    for (auto it = m_fragmentList.find(startRegion), end = m_fragmentList.end(); it != end; ++it) {
        RenderFragmentContainer* region = *it;
        ASSERT(!region->needsLayout() || region->isRenderFragmentContainerSet());

        // We have no information computed for this region so we need to do it.
		OwnPtr<RenderBoxFragmentInfo> oldInfo = region->takeRenderBoxFragmentInfo(block);
		if (!oldInfo) {
			relayoutChildren = rangeInvalidated;
			return;
		}

        LayoutUnit oldLogicalWidth = oldInfo->logicalWidth();
        RenderBoxFragmentInfo* newInfo = block->renderBoxFragmentInfo(region);
        if (!newInfo || newInfo->logicalWidth() != oldLogicalWidth) {
            relayoutChildren = true;
            return;
        }

        if (region == endRegion)
            break;
    }
}

LayoutUnit RenderFragmentedFlow::contentLogicalWidthOfFirstFragment() const
{
    RenderFragmentContainer* firstValidFragmentInFlow = firstFragment();
    if (!firstValidFragmentInFlow)
        return 0;
    return isHorizontalWritingMode() ? firstValidFragmentInFlow->contentWidth() : firstValidFragmentInFlow->contentHeight();
}

LayoutUnit RenderFragmentedFlow::contentLogicalHeightOfFirstFragment() const
{
    RenderFragmentContainer* firstValidFragmentInFlow = firstFragment();
    if (!firstValidFragmentInFlow)
        return 0;
    return isHorizontalWritingMode() ? firstValidFragmentInFlow->contentHeight() : firstValidFragmentInFlow->contentWidth();
}

LayoutUnit RenderFragmentedFlow::contentLogicalLeftOfFirstFragment() const
{
    RenderFragmentContainer* firstValidFragmentInFlow = firstFragment();
    if (!firstValidFragmentInFlow)
        return 0;
    return isHorizontalWritingMode() ? firstValidFragmentInFlow->fragmentedFlowPortionRect().x() : firstValidFragmentInFlow->fragmentedFlowPortionRect().y();
}

RenderFragmentContainer* RenderFragmentedFlow::firstFragment() const
{
    if (!hasFragments())
        return nullptr;
    return m_fragmentList.first();
}

RenderFragmentContainer* RenderFragmentedFlow::lastFragment() const
{
    if (!hasFragments())
        return nullptr;
    return m_fragmentList.last();
}

void RenderFragmentedFlow::clearRenderBoxRegionInfoAndCustomStyle(const RenderBox& box,
    const RenderFragmentContainer* newStartRegion, const RenderFragmentContainer* newEndRegion,
    const RenderFragmentContainer* oldStartRegion, const RenderFragmentContainer* oldEndRegion)
{
    ASSERT(newStartRegion && newEndRegion && oldStartRegion && oldEndRegion);

    bool insideOldRegionRange = false;
    bool insideNewRegionRange = false;
    for (auto& fragment : m_fragmentList) {
        if (oldStartRegion == fragment)
            insideOldRegionRange = true;
        if (newStartRegion == fragment)
            insideNewRegionRange = true;

        if (!(insideOldRegionRange && insideNewRegionRange)){
            if (fragment->renderBoxFragmentInfo(box))
            	fragment->removeRenderBoxFragmentInfo(box);
        }

        if (oldEndRegion == fragment)
            insideOldRegionRange = false;
        if (newEndRegion == fragment)
            insideNewRegionRange = false;
    }
}

void RenderFragmentedFlow::setFragmentRangeForBox(const RenderBox& box, RenderFragmentContainer* startRegion, RenderFragmentContainer* endRegion)
{
    ASSERT(hasFragments());
    ASSERT(startRegion && endRegion && startRegion->fragmentedFlow() == this && endRegion->fragmentedFlow() == this);

    auto result = m_fragmentRangeMap.set(&box, RenderRegionRange(startRegion, endRegion));
	if (result.isNewEntry)
		return;

    // If nothing changed, just bail.
    RenderRegionRange& range = result.iterator->second;
    if (range.startRegion() == startRegion && range.endRegion() == endRegion)
        return;
    clearRenderBoxRegionInfoAndCustomStyle(box, startRegion, endRegion, range.startRegion(), range.endRegion());
}

bool RenderFragmentedFlow::hasCachedRegionRangeForBox(const RenderBox& box) const
{
    return m_fragmentRangeMap.contains(&box);
}

bool RenderFragmentedFlow::getFragmentRangeForBoxFromCachedInfo(const RenderBox& box, RenderFragmentContainer*& startRegion, RenderFragmentContainer*& endRegion) const
{
    ASSERT(box);
    ASSERT(hasValidFragmentInfo());
    ASSERT((startRegion == nullptr) && (endRegion == nullptr));

    RenderRegionRangeMap::const_iterator it = m_fragmentRangeMap.find(&box);
    if (it != m_fragmentRangeMap.end()) {
		const RenderRegionRange& range = it->second;
		startRegion = range.startRegion();
		endRegion = range.endRegion();
		ASSERT(m_fragmentList.contains(startRegion) && m_fragmentList.contains(endRegion));
		return true;
	}

    return false;
}

bool RenderFragmentedFlow::getFragmentRangeForBox(const RenderBox& box, RenderFragmentContainer*& startFragment, RenderFragmentContainer*& endFragment) const
{
    ASSERT(box);

    startFragment = endFragment = nullptr;
    if (!hasValidFragmentInfo()) // We clear the ranges when we invalidate the regions.
        return false;

    if (m_fragmentList.size() == 1) {
    	startFragment = endFragment = m_fragmentList.first();
        return true;
    }

    if (getFragmentRangeForBoxFromCachedInfo(box, startFragment, endFragment))
        return true;

    // Check if the box is contained in an
    return false;
}

bool RenderFragmentedFlow::computedFragmentRangeForBox(const RenderBox* box, RenderFragmentContainer*& startRegion, RenderFragmentContainer*& endRegion) const
{
    ASSERT(box);

    startRegion = endRegion = nullptr;
    if (!hasValidFragmentInfo()) // We clear the ranges when we invalidate the regions.
        return false;

    if (getFragmentRangeForBox(*box, startRegion, endRegion))
        return true;

    // Search the region range using the information provided by the containing block chain.
    auto* containingBlock = const_cast<RenderBox*>(box);
    while (!containingBlock->isRenderFragmentedFlow()) {
        InlineElementBox* boxWrapper = containingBlock->inlineBoxWrapper();
        if (boxWrapper && boxWrapper->root()->containingFragment()) {
            startRegion = endRegion = boxWrapper->root()->containingFragment();
            ASSERT(m_fragmentList.contains(startRegion));
            return true;
        }

        // FIXME: Use the containingBlock() value once we patch all the layout systems to be region range aware
        // (e.g. if we use containingBlock() the shadow controls of a video element won't get the range from the
        // video box because it's not a block; they need to be patched separately).
        ASSERT(containingBlock->parent());
		containingBlock = &containingBlock->parent()->enclosingBox();
        ASSERT(containingBlock);

        // If a box doesn't have a cached region range it usually means the box belongs to a line so startRegion should be equal with endRegion.
        // FIXME: Find the cases when this startRegion should not be equal with endRegion and make sure these boxes have cached region ranges.
		 if (hasCachedRegionRangeForBox(*containingBlock)) {
        	startRegion = endRegion = fragmentAtBlockOffset(containingBlock, containingBlock->offsetFromLogicalTopOfFirstPage(), true);
        	return true;
        }
    }
	ASSERT_NOT_REACHED();
    return false;
}

bool RenderFragmentedFlow::regionInRange(const RenderFragmentContainer* targetRegion, const RenderFragmentContainer* startRegion, const RenderFragmentContainer* endRegion) const
{
    ASSERT(targetRegion);

    for (auto it = m_fragmentList.find(const_cast<RenderFragmentContainer*>(startRegion)); it != m_fragmentList.end(); ++it) {
        const RenderFragmentContainer* currRegion = *it;
        if (targetRegion == currRegion)
            return true;
        if (currRegion == endRegion)
            break;
    }

    return false;
}

bool RenderFragmentedFlow::objectShouldFragmentInFlowRegion(const RenderObject* object, const RenderFragmentContainer* region) const
{
    ASSERT(object);
    ASSERT(region);

    RenderFragmentedFlow* flowThread = object->enclosingFragmentedFlow();
    if (flowThread != this)
        return false;

    if (!m_fragmentList.contains(const_cast<RenderFragmentContainer*>(region)))
        return false;

    RenderFragmentContainer* enclosingBoxStartRegion = 0;
    RenderFragmentContainer* enclosingBoxEndRegion = 0;
    // If the box has no range, do not check regionInRange. Boxes inside inlines do not get ranges.
    // Instead, the containing RootInlineBox will abort when trying to paint inside the wrong region.
    if (computedFragmentRangeForBox(&object->enclosingBox(), enclosingBoxStartRegion, enclosingBoxEndRegion)
		&& !regionInRange(region, enclosingBoxStartRegion, enclosingBoxEndRegion))
            return false;

    return object->isBox() || object->isRenderInline();
}

bool RenderFragmentedFlow::objectInFlowFragment(const RenderObject* object, const RenderFragmentContainer* region) const
{
    ASSERT(object);
    ASSERT(region);

    RenderFragmentedFlow* flowThread = object->enclosingFragmentedFlow();
    if (flowThread != this)
        return false;

    if (!m_fragmentList.contains(const_cast<RenderFragmentContainer*>(region)))
        return false;

    RenderFragmentContainer* enclosingBoxStartRegion = 0;
    RenderFragmentContainer* enclosingBoxEndRegion = 0;
    if(!getFragmentRangeForBox(object->enclosingBox(), enclosingBoxStartRegion, enclosingBoxEndRegion))
    	return false;

    if (!regionInRange(region, enclosingBoxStartRegion, enclosingBoxEndRegion))
       return false;

    if (object->isBox())
        return true;

    LayoutRect objectABBRect = object->absoluteBoundingBoxRect(true);
    if (!objectABBRect.width())
        objectABBRect.setWidth(1);
    if (!objectABBRect.height())
        objectABBRect.setHeight(1); 
    if (objectABBRect.intersects(region->absoluteBoundingBoxRect(true)))
        return true;

    if (region == lastFragment()) {
        // If the object does not intersect any of the enclosing box regions
        // then the object is in last region.
        for (auto it = m_fragmentList.find(enclosingBoxStartRegion); it != m_fragmentList.end(); ++it) {
            const RenderFragmentContainer* currRegion = *it;
            if (currRegion == region)
                break;
            if (objectABBRect.intersects(currRegion->absoluteBoundingBoxRect(true)))
                return false;
        }
        return true;
    }

    return false;
}

void RenderFragmentedFlow::collectLayerFragments(LayerFragments& layerFragments, const LayoutRect& layerBoundingBox, const LayoutRect& dirtyRect)
{
    ASSERT(!m_regionsInvalidated);

    for (auto& fragment : m_fragmentList)
    	fragment->collectLayerFragments(layerFragments, layerBoundingBox, dirtyRect);
}

LayoutRect RenderFragmentedFlow::fragmentsBoundingBox(const LayoutRect& layerBoundingBox)
{
    ASSERT(!m_regionsInvalidated);

    LayoutRect result;
    for (auto& fragment : m_fragmentList) {
        LayerFragments fragments;
        fragment->collectLayerFragments(fragments, layerBoundingBox, LayoutRect::infiniteRect());
        for (size_t i = 0; i < fragments.size(); ++i) {
            const auto& fragment = fragments.at(i);
            LayoutRect fragmentRect(layerBoundingBox);
            fragmentRect.intersect(fragment.paginationClip);
            fragmentRect.move(fragment.paginationOffset);
            result.unite(fragmentRect);
        }
    }

    return result;
}

void RenderFragmentedFlow::clearLinesToRegionMap()
{
    if (m_lineToFragmentMap)
        m_lineToFragmentMap->clear();
}

void RenderFragmentedFlow::deleteLines()
{
    clearLinesToRegionMap();
    RenderBlockFlow::deleteLines();
}

void RenderFragmentedFlow::willBeDestroyed()
{
    clearLinesToRegionMap();
    RenderBlockFlow::willBeDestroyed();
}

#if !ASSERT_WITH_SECURITY_IMPLICATION_DISABLED
bool RenderFragmentedFlow::checkLinesConsistency(const RenderBlockFlow* removedBlock) const
{
    if (!m_lineToFragmentMap)
        return true;

    for (auto& linePair : *m_lineToFragmentMap.get()) {
        const RootInlineBox* line = linePair.key;
        RenderFragmentContainer* region = linePair.value;
        if (&line->blockFlow() == removedBlock)
            return false;
        if (line->blockFlow().fragmentedFlowState() == NotInsideFragmentedFlow)
            return false;
        if (!m_fragmentList.contains(region))
            return false;
    }

    return true;
}
#endif

void RenderFragmentedFlow::markFragmentsForOverflowLayoutIfNeeded()
{
    if (!hasFragments())
        return;

    for (auto& fragment : m_fragmentList)
        fragment->setNeedsSimplifiedNormalFlowLayout();
}

void RenderFragmentedFlow::updateRegionsFlowThreadPortionRect()
{
    LayoutUnit logicalHeight;
    // FIXME: Optimize not to clear the interval all the time. This implies manually managing the tree nodes lifecycle.
    m_regionIntervalTree.clear();
    for (auto& region : m_fragmentList) {
        LayoutUnit regionLogicalWidth = region->pageLogicalWidth();
        LayoutUnit regionLogicalHeight = std::min<LayoutUnit>(RenderFragmentedFlow::maxLogicalHeight() - logicalHeight, region->logicalHeightOfAllFragmentedFlowContent());

        LayoutRect regionRect(style()->direction() == LTR ? LayoutUnit() : logicalWidth() - regionLogicalWidth, logicalHeight, regionLogicalWidth, regionLogicalHeight);

        region->setFragmentedFlowPortionRect(isHorizontalWritingMode() ? regionRect : regionRect.transposedRect());

        m_regionIntervalTree.add(RegionIntervalTree::createInterval(logicalHeight, logicalHeight + regionLogicalHeight, makeWeakPtr(region)));

        logicalHeight += regionLogicalHeight;
    }
}

// Even if we require the break to occur at offsetBreakInFlowThread, because regions may have min/max-height values,
// it is possible that the break will occur at a different offset than the original one required.
// offsetBreakAdjustment measures the different between the requested break offset and the current break offset.
bool RenderFragmentedFlow::addForcedFragmentBreak(const RenderBlock* block, LayoutUnit offsetBreakInFlowThread, RenderBox&, bool, LayoutUnit* offsetBreakAdjustment)
{
	// We need to update the regions flow thread portion rect because we are going to process
	// a break on these regions.
	updateRegionsFlowThreadPortionRect();

    // Simulate a region break at offsetBreakInFlowThread. If it points inside an auto logical height region,
    // then it determines the region computed auto height.
    RenderFragmentContainer* region = fragmentAtBlockOffset(block, offsetBreakInFlowThread);
    if (!region)
        return false;

    LayoutUnit currentRegionOffsetInFlowThread = isHorizontalWritingMode() ? region->fragmentedFlowPortionRect().y() : region->fragmentedFlowPortionRect().x();

    currentRegionOffsetInFlowThread += isHorizontalWritingMode() ? region->fragmentedFlowPortionRect().height() : region->fragmentedFlowPortionRect().width();

    if (offsetBreakAdjustment)
        *offsetBreakAdjustment = std::max<LayoutUnit>(LayoutUnit(), currentRegionOffsetInFlowThread - offsetBreakInFlowThread);

    return false;
}

LayoutUnit RenderFragmentedFlow::offsetFromLogicalTopOfFirstRegion(const RenderBlock* currentBlock) const
{
    // As a last resort, take the slow path.
    LayoutRect blockRect(0, 0, currentBlock->width(), currentBlock->height());
    while (currentBlock && !currentBlock->isRenderView() && !currentBlock->isRenderFragmentedFlow()) {
        RenderBlock* containerBlock = currentBlock->containingBlock();
        ASSERT(containerBlock);
        if (!containerBlock)
            return 0;
        LayoutPoint currentBlockLocation = currentBlock->location();
        if ((*currentBlock).isTableCell()) {
			if (auto* section = toRenderTableCell(*currentBlock).section())
				currentBlockLocation.moveBy(section->location());
		}

        if (containerBlock->style()->writingMode() != currentBlock->style()->writingMode()) {
            // We have to put the block rect in container coordinates
            // and we have to take into account both the container and current block flipping modes
            if (containerBlock->style()->isFlippedBlocksWritingMode()) {
                if (containerBlock->isHorizontalWritingMode())
                    blockRect.setY(currentBlock->height() - blockRect.maxY());
                else
                    blockRect.setX(currentBlock->width() - blockRect.maxX());
            }
            currentBlock->flipForWritingMode(blockRect);
        }
        blockRect.moveBy(currentBlockLocation);
        currentBlock = containerBlock;
    }

    return currentBlock->isHorizontalWritingMode() ? blockRect.y() : blockRect.x();
}

void RenderFragmentedFlow::RegionSearchAdapter::collectIfNeeded(const RegionInterval& interval)
{
    if (m_result)
        return;
    if (interval.low() <= m_offset && interval.high() > m_offset)
        m_result = interval.data();
}

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

    if (RenderFragmentContainer* region = mapFromFlowToFragment(transformState)) {
        // FIXME: The cast below is probably not the best solution, we may need to find a better way.
    	const RenderObject* regionObject = static_cast<const RenderObject*>(region);

		// If the repaint container is nullptr, we have to climb up to the RenderView, otherwise swap
		// it with the region's repaint container.
		repaintContainer = repaintContainer ? region->containerForRepaint() : nullptr;

		if (RenderFragmentedFlow* regionFlowThread = region->enclosingFragmentedFlow()) {
			RenderFragmentContainer* startRegion = nullptr;
			RenderFragmentContainer* endRegion = nullptr;
			if (regionFlowThread->getFragmentRangeForBox(*region, startRegion, endRegion)) {
				CurrentRenderFragmentContainerMaintainer regionMaintainer(*startRegion);
				regionObject->mapLocalToContainer(repaintContainer, transformState, mode, wasFixed);
				return;
			}
		}

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

// FIXME: Make this function faster. Walking the render tree is slow, better use a caching mechanism (e.g. |cachedOffsetFromLogicalTopOfFirstRegion|).
LayoutRect RenderFragmentedFlow::mapFromLocalToFragmentedFlow(const RenderBox* box, const LayoutRect& localRect) const
{
    LayoutRect boxRect = localRect;

    while (box && box != this) {
        RenderBlock* containerBlock = box->containingBlock();
        ASSERT(containerBlock);
        if (!containerBlock)
            return LayoutRect();
        LayoutPoint currentBoxLocation = box->location();

        if (containerBlock->style()->writingMode() != box->style()->writingMode())
            box->flipForWritingMode(boxRect);

        boxRect.moveBy(currentBoxLocation);
        box = containerBlock;
    }

    return boxRect;
}

// FIXME: Make this function faster. Walking the render tree is slow, better use a caching mechanism (e.g. |cachedOffsetFromLogicalTopOfFirstRegion|).
LayoutRect RenderFragmentedFlow::mapFromFragmentedFlowToLocal(const RenderBox* box, const LayoutRect& rect) const
{
    LayoutRect localRect = rect;
    if (box == this)
        return localRect;

    RenderBlock* containerBlock = box->containingBlock();
    ASSERT(containerBlock);
    if (!containerBlock)
        return LayoutRect();
    localRect = mapFromFragmentedFlowToLocal(containerBlock, localRect);

    LayoutPoint currentBoxLocation = box->location();
    localRect.moveBy(-currentBoxLocation);

    if (containerBlock->style()->writingMode() != box->style()->writingMode())
        box->flipForWritingMode(localRect);

    return localRect;
}

void RenderFragmentedFlow::flipForWritingModeLocalCoordinates(LayoutRect& rect) const
{
    if (!style()->isFlippedBlocksWritingMode())
        return;

    if (isHorizontalWritingMode())
        rect.setY(0 - rect.maxY());
    else
        rect.setX(0 - rect.maxX());
}

void RenderFragmentedFlow::addRegionsVisualEffectOverflow(const RenderBox& box)
{
    RenderFragmentContainer* startRegion = 0;
    RenderFragmentContainer* endRegion = 0;
    if (!getFragmentRangeForBox(box, startRegion, endRegion))
    	return;

    for (auto iter = m_fragmentList.find(startRegion); iter != m_fragmentList.end(); ++iter) {
        RenderFragmentContainer* region = *iter;

        LayoutRect borderBox = box.borderBoxRectInRegion(region);
        borderBox = box.applyVisualEffectOverflow(borderBox);
        borderBox = region->rectFlowPortionForBox(box, borderBox);

        region->addVisualOverflowForBox(box, borderBox);
        if (region == endRegion)
            break;
    }
}

void RenderFragmentedFlow::addRegionsVisualOverflowFromTheme(const RenderBlock& block)
{
    RenderFragmentContainer* startRegion = 0;
    RenderFragmentContainer* endRegion = 0;
    if (!getFragmentRangeForBox(block, startRegion, endRegion))
    	return;

    for (auto iter = m_fragmentList.find(startRegion); iter != m_fragmentList.end(); ++iter) {
        RenderFragmentContainer* region = *iter;

        LayoutRect borderBox = block.borderBoxRectInRegion(region);
        borderBox = region->rectFlowPortionForBox(block, borderBox);

        FloatRect inflatedRect = borderBox;
        block.theme()->adjustRepaintRect(&block, inflatedRect);

        region->addVisualOverflowForBox(block, snappedIntRect(LayoutRect(inflatedRect)));
        if (region == endRegion)
            break;
    }
}

void RenderFragmentedFlow::addRegionsOverflowFromChild(const RenderBox& box, const RenderBox& child, const LayoutSize& delta)
{
    RenderFragmentContainer* startRegion = nullptr;
    RenderFragmentContainer* endRegion = nullptr;
    if (!getFragmentRangeForBox(child, startRegion, endRegion))
    	return;

    RenderFragmentContainer* containerStartRegion = nullptr;
    RenderFragmentContainer* containerEndRegion = nullptr;
    if (!getFragmentRangeForBox(box, containerStartRegion, containerEndRegion))
    	return;

    for (auto iter = m_fragmentList.find(startRegion); iter != m_fragmentList.end(); ++iter) {
        RenderFragmentContainer* region = *iter;
        if (!regionInRange(region, containerStartRegion, containerEndRegion)) {
            if (region == endRegion)
                break;
            continue;
        }

        LayoutRect childLayoutOverflowRect = region->layoutOverflowRectForBoxForPropagation(child);
        childLayoutOverflowRect.move(delta);

        region->addLayoutOverflowForBox(box, childLayoutOverflowRect);

        if (child.hasSelfPaintingLayer() || box.hasNonVisibleOverflow()) {
            if (region == endRegion)
                break;
            continue;
        }
        LayoutRect childVisualOverflowRect = region->visualOverflowRectForBoxForPropagation(child);
        childVisualOverflowRect.move(delta);
        region->addVisualOverflowForBox(box, childVisualOverflowRect);

        if (region == endRegion)
            break;
    }
}

void RenderFragmentedFlow::addRegionsLayoutOverflow(const RenderBox& box, const LayoutRect& layoutOverflow)
{
    RenderFragmentContainer* startRegion = nullptr;
    RenderFragmentContainer* endRegion = nullptr;
    if (!getFragmentRangeForBox(box, startRegion, endRegion))
    	return;

    for (auto iter = m_fragmentList.find(startRegion); iter != m_fragmentList.end(); ++iter) {
        RenderFragmentContainer* region = *iter;
        LayoutRect layoutOverflowInRegion = region->rectFlowPortionForBox(box, layoutOverflow);

        region->addLayoutOverflowForBox(box, layoutOverflowInRegion);

        if (region == endRegion)
            break;
    }
}

void RenderFragmentedFlow::addFragmentsVisualOverflow(const RenderBox& box, const LayoutRect& visualOverflow)
{
    RenderFragmentContainer* startRegion = nullptr;
    RenderFragmentContainer* endRegion = nullptr;
    if (!getFragmentRangeForBox(box, startRegion, endRegion))
    	return;

    for (auto iter = m_fragmentList.find(startRegion); iter != m_fragmentList.end(); ++iter) {
        RenderFragmentContainer* region = *iter;
        LayoutRect visualOverflowInRegion = region->rectFlowPortionForBox(box, visualOverflow);

        region->addVisualOverflowForBox(box, visualOverflowInRegion);

        if (region == endRegion)
            break;
    }
}

void RenderFragmentedFlow::clearRegionsOverflow(const RenderBox& box)
{
    RenderFragmentContainer* startRegion = nullptr;
    RenderFragmentContainer* endRegion = nullptr;
    if (!getFragmentRangeForBox(box, startRegion, endRegion))
    	return;

    for (auto iter = m_fragmentList.find(startRegion); iter != m_fragmentList.end(); ++iter) {
        RenderFragmentContainer* region = *iter;
        RenderBoxFragmentInfo* boxInfo = region->renderBoxFragmentInfo(box);
        if (boxInfo && boxInfo->overflow())
            boxInfo->clearOverflow();

        if (region == endRegion)
            break;
    }
}

RenderFragmentContainer* RenderFragmentedFlow::currentRegion() const
{
    return m_currentRegionMaintainer ? &m_currentRegionMaintainer->fragment() : nullptr;
}

ContainingRegionMap& RenderFragmentedFlow::containingRegionMap()
{
    if (!m_lineToFragmentMap)
        m_lineToFragmentMap = adoptPtr(new ContainingRegionMap());

    return *m_lineToFragmentMap;
}

} // namespace WebCore
