1/*
2 * Copyright (C) 2013 Apple Inc. All rights reserved.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB.  If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 *
19 */
20
21#ifndef LogicalSelectionOffsetCaches_h
22#define LogicalSelectionOffsetCaches_h
23
24#include "RenderBlock.h"
25
26namespace WebCore {
27
28static inline bool isContainingBlockCandidateForAbsolutelyPositionedObject(RenderElement& object)
29{
30    return object.style().position() != StaticPosition
31        || (object.hasTransform() && object.isRenderBlock())
32        || object.isSVGForeignObject()
33        || object.isRenderView();
34}
35
36static inline bool isNonRenderBlockInline(RenderElement& object)
37{
38    return (object.isInline() && !object.isReplaced()) || !object.isRenderBlock();
39}
40
41static inline RenderBlock* containingBlockForFixedPosition(RenderElement* parent)
42{
43    RenderElement* object = parent;
44    while (object && !object->canContainFixedPositionObjects())
45        object = object->parent();
46    ASSERT(!object || !object->isAnonymousBlock());
47    return toRenderBlock(object);
48}
49
50static inline RenderBlock* containingBlockForAbsolutePosition(RenderElement* parent)
51{
52    RenderElement* object = parent;
53    while (object && !isContainingBlockCandidateForAbsolutelyPositionedObject(*object))
54        object = object->parent();
55
56    // For a relatively positioned inline, return its nearest non-anonymous containing block,
57    // not the inline itself, to avoid having a positioned objects list in all RenderInlines
58    // and use RenderBlock* as RenderElement::containingBlock's return type.
59    // Use RenderBlock::container() to obtain the inline.
60    if (object && !object->isRenderBlock())
61        object = object->containingBlock();
62
63    while (object && object->isAnonymousBlock())
64        object = object->containingBlock();
65
66    return toRenderBlock(object);
67}
68
69static inline RenderBlock* containingBlockForObjectInFlow(RenderElement* parent)
70{
71    RenderElement* object = parent;
72    while (object && isNonRenderBlockInline(*object))
73        object = object->parent();
74    return toRenderBlock(object);
75}
76
77class LogicalSelectionOffsetCaches {
78public:
79    class ContainingBlockInfo {
80    public:
81        ContainingBlockInfo()
82            : m_block(0)
83            , m_cache(0)
84            , m_hasFloatsOrFlowThreads(false)
85            , m_cachedLogicalLeftSelectionOffset(false)
86            , m_cachedLogicalRightSelectionOffset(false)
87        { }
88
89        void setBlock(RenderBlock* block, const LogicalSelectionOffsetCaches* cache)
90        {
91            m_block = block;
92            m_hasFloatsOrFlowThreads = m_hasFloatsOrFlowThreads || m_block->containsFloats() || m_block->flowThreadContainingBlock();
93            m_cache = cache;
94            m_cachedLogicalLeftSelectionOffset = false;
95            m_cachedLogicalRightSelectionOffset = false;
96        }
97
98        RenderBlock* block() const { return m_block; }
99        const LogicalSelectionOffsetCaches* cache() const { return m_cache; }
100
101        LayoutUnit logicalLeftSelectionOffset(RenderBlock& rootBlock, LayoutUnit position) const
102        {
103            ASSERT(m_cache);
104            if (m_hasFloatsOrFlowThreads || !m_cachedLogicalLeftSelectionOffset) {
105                m_cachedLogicalLeftSelectionOffset = true;
106                m_logicalLeftSelectionOffset = m_block->logicalLeftSelectionOffset(rootBlock, position, *m_cache);
107            } else
108                ASSERT(m_logicalLeftSelectionOffset == m_block->logicalLeftSelectionOffset(rootBlock, position, *m_cache));
109            return m_logicalLeftSelectionOffset;
110        }
111
112        LayoutUnit logicalRightSelectionOffset(RenderBlock& rootBlock, LayoutUnit position) const
113        {
114            ASSERT(m_cache);
115            if (m_hasFloatsOrFlowThreads || !m_cachedLogicalRightSelectionOffset) {
116                m_cachedLogicalRightSelectionOffset = true;
117                m_logicalRightSelectionOffset = m_block->logicalRightSelectionOffset(rootBlock, position, *m_cache);
118            } else
119                ASSERT(m_logicalRightSelectionOffset == m_block->logicalRightSelectionOffset(rootBlock, position, *m_cache));
120            return m_logicalRightSelectionOffset;
121        }
122
123    private:
124        RenderBlock* m_block;
125        const LogicalSelectionOffsetCaches* m_cache;
126        bool m_hasFloatsOrFlowThreads : 1;
127        mutable bool m_cachedLogicalLeftSelectionOffset : 1;
128        mutable bool m_cachedLogicalRightSelectionOffset : 1;
129        mutable LayoutUnit m_logicalLeftSelectionOffset;
130        mutable LayoutUnit m_logicalRightSelectionOffset;
131
132    };
133
134    explicit LogicalSelectionOffsetCaches(RenderBlock& rootBlock)
135    {
136#if ENABLE(TEXT_SELECTION)
137        // FIXME: We should either move this assertion to the caller (if applicable) or structure the code
138        // such that we can remove this assertion.
139        ASSERT(rootBlock.isSelectionRoot());
140#endif
141        auto parent = rootBlock.parent();
142
143        // LogicalSelectionOffsetCaches should not be used on an orphaned tree.
144        m_containingBlockForFixedPosition.setBlock(containingBlockForFixedPosition(parent), 0);
145        m_containingBlockForAbsolutePosition.setBlock(containingBlockForAbsolutePosition(parent), 0);
146        m_containingBlockForInflowPosition.setBlock(containingBlockForObjectInFlow(parent), 0);
147    }
148
149    LogicalSelectionOffsetCaches(RenderBlock& block, const LogicalSelectionOffsetCaches& cache)
150        : m_containingBlockForFixedPosition(cache.m_containingBlockForFixedPosition)
151        , m_containingBlockForAbsolutePosition(cache.m_containingBlockForAbsolutePosition)
152    {
153        if (block.canContainFixedPositionObjects())
154            m_containingBlockForFixedPosition.setBlock(&block, &cache);
155
156        if (isContainingBlockCandidateForAbsolutelyPositionedObject(block) && !block.isRenderInline() && !block.isAnonymousBlock())
157            m_containingBlockForFixedPosition.setBlock(&block, &cache);
158
159        m_containingBlockForInflowPosition.setBlock(&block, &cache);
160    }
161
162    const ContainingBlockInfo& containingBlockInfo(RenderBlock& block) const
163    {
164        EPosition position = block.style().position();
165        if (position == FixedPosition) {
166            ASSERT(block.containingBlock() == m_containingBlockForFixedPosition.block());
167            return m_containingBlockForFixedPosition;
168        }
169        if (position == AbsolutePosition) {
170            ASSERT(block.containingBlock() == m_containingBlockForAbsolutePosition.block());
171            return m_containingBlockForAbsolutePosition;
172        }
173        ASSERT(block.containingBlock() == m_containingBlockForInflowPosition.block());
174        return m_containingBlockForInflowPosition;
175    }
176
177private:
178    ContainingBlockInfo m_containingBlockForFixedPosition;
179    ContainingBlockInfo m_containingBlockForAbsolutePosition;
180    ContainingBlockInfo m_containingBlockForInflowPosition;
181};
182
183} // namespace WebCore
184
185#endif // LogicalSelectionOffsetCaches_h
186