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(RenderObject* object)
29{
30 return object->style()->position() != StaticPosition
31 || (object->hasTransform() && object->isRenderBlock())
32#if ENABLE(SVG)
33 || object->isSVGForeignObject()
34#endif
35 || object->isRenderView();
36}
37
38static inline bool isNonRenderBlockInline(RenderObject* object)
39{
40 return (object->isInline() && !object->isReplaced()) || !object->isRenderBlock();
41}
42
43static inline RenderObject* containingBlockForFixedPosition(RenderObject* parent)
44{
45 RenderObject* object = parent;
46 while (object && !object->canContainFixedPositionObjects())
47 object = object->parent();
48 ASSERT(!object || !object->isAnonymousBlock());
49 return object;
50}
51
52static inline RenderObject* containingBlockForAbsolutePosition(RenderObject* parent)
53{
54 RenderObject* object = parent;
55 while (object && !isContainingBlockCandidateForAbsolutelyPositionedObject(object))
56 object = object->parent();
57
58 // For a relatively positioned inline, return its nearest non-anonymous containing block,
59 // not the inline itself, to avoid having a positioned objects list in all RenderInlines
60 // and use RenderBlock* as RenderObject::containingBlock's return type.
61 // Use RenderBlock::container() to obtain the inline.
62 if (object->isRenderInline())
63 object = object->containingBlock();
64
65 while (object && object->isAnonymousBlock())
66 object = object->containingBlock();
67
68 return object;
69}
70
71static inline RenderObject* containingBlockForObjectInFlow(RenderObject* parent)
72{
73 RenderObject* object = parent;
74 while (object && isNonRenderBlockInline(object))
75 object = object->parent();
76 return object;
77}
78
79class LogicalSelectionOffsetCaches {
80public:
81 class ContainingBlockInfo {
82 public:
83 ContainingBlockInfo()
84 : m_block(0)
85 , m_cache(0)
86 , m_hasFloatsOrFlowThreads(false)
87 , m_cachedLogicalLeftSelectionOffset(false)
88 , m_cachedLogicalRightSelectionOffset(false)
89 { }
90
91 void setBlock(RenderBlock* block, const LogicalSelectionOffsetCaches* cache)
92 {
93 m_block = block;
94 m_hasFloatsOrFlowThreads = m_hasFloatsOrFlowThreads || m_block->containsFloats() || m_block->flowThreadContainingBlock();
95 m_cache = cache;
96 m_cachedLogicalLeftSelectionOffset = false;
97 m_cachedLogicalRightSelectionOffset = false;
98 }
99
100 RenderBlock* block() const { return m_block; }
101 const LogicalSelectionOffsetCaches* cache() const { return m_cache; }
102
103 LayoutUnit logicalLeftSelectionOffset(RenderBlock* rootBlock, LayoutUnit position) const
104 {
105 ASSERT(m_cache);
106 if (m_hasFloatsOrFlowThreads || !m_cachedLogicalLeftSelectionOffset) {
107 m_cachedLogicalLeftSelectionOffset = true;
108 m_logicalLeftSelectionOffset = m_block->logicalLeftSelectionOffset(rootBlock, position, *m_cache);
109 } else
110 ASSERT(m_logicalLeftSelectionOffset == m_block->logicalLeftSelectionOffset(rootBlock, position, *m_cache));
111 return m_logicalLeftSelectionOffset;
112 }
113
114 LayoutUnit logicalRightSelectionOffset(RenderBlock* rootBlock, LayoutUnit position) const
115 {
116 ASSERT(m_cache);
117 if (m_hasFloatsOrFlowThreads || !m_cachedLogicalRightSelectionOffset) {
118 m_cachedLogicalRightSelectionOffset = true;
119 m_logicalRightSelectionOffset = m_block->logicalRightSelectionOffset(rootBlock, position, *m_cache);
120 } else
121 ASSERT(m_logicalRightSelectionOffset == m_block->logicalRightSelectionOffset(rootBlock, position, *m_cache));
122 return m_logicalRightSelectionOffset;
123 }
124
125 private:
126 RenderBlock* m_block;
127 const LogicalSelectionOffsetCaches* m_cache;
128 bool m_hasFloatsOrFlowThreads : 1;
129 mutable bool m_cachedLogicalLeftSelectionOffset : 1;
130 mutable bool m_cachedLogicalRightSelectionOffset : 1;
131 mutable LayoutUnit m_logicalLeftSelectionOffset;
132 mutable LayoutUnit m_logicalRightSelectionOffset;
133
134 };
135
136 LogicalSelectionOffsetCaches(RenderBlock* rootBlock)
137 {
138 ASSERT(rootBlock->isSelectionRoot());
139 RenderObject* parent = rootBlock->parent();
140
141 // LogicalSelectionOffsetCaches should not be used on an orphaned tree.
142 m_containingBlockForFixedPosition.setBlock(toRenderBlock(containingBlockForFixedPosition(parent)), 0);
143 m_containingBlockForAbsolutePosition.setBlock(toRenderBlock(containingBlockForAbsolutePosition(parent)), 0);
144 m_containingBlockForInflowPosition.setBlock(toRenderBlock(containingBlockForObjectInFlow(parent)), 0);
145 }
146
147 LogicalSelectionOffsetCaches(RenderBlock* block, const LogicalSelectionOffsetCaches& cache)
148 : m_containingBlockForFixedPosition(cache.m_containingBlockForFixedPosition)
149 , m_containingBlockForAbsolutePosition(cache.m_containingBlockForAbsolutePosition)
150 , m_parentCache(&cache)
151 {
152 if (block->canContainFixedPositionObjects())
153 m_containingBlockForFixedPosition.setBlock(block, &cache);
154
155 if (isContainingBlockCandidateForAbsolutelyPositionedObject(block) && !block->isRenderInline() && !block->isAnonymousBlock())
156 m_containingBlockForFixedPosition.setBlock(block, &cache);
157
158 m_containingBlockForInflowPosition.setBlock(block, &cache);
159 }
160
161 const ContainingBlockInfo& containingBlockInfo(RenderBlock* block) const
162 {
163 EPosition position = block->style()->position();
164 if (position == FixedPosition) {
165 ASSERT(block->containingBlock() == m_containingBlockForFixedPosition.block());
166 return m_containingBlockForFixedPosition;
167 }
168 if (position == AbsolutePosition) {
169 ASSERT(block->containingBlock() == m_containingBlockForAbsolutePosition.block());
170 return m_containingBlockForAbsolutePosition;
171 }
172 ASSERT(block->containingBlock() == m_containingBlockForInflowPosition.block());
173 return m_containingBlockForInflowPosition;
174 }
175
176private:
177 ContainingBlockInfo m_containingBlockForFixedPosition;
178 ContainingBlockInfo m_containingBlockForAbsolutePosition;
179 ContainingBlockInfo m_containingBlockForInflowPosition;
180 const LogicalSelectionOffsetCaches* m_parentCache;
181};
182
183} // namespace WebCore
184
185#endif // LogicalSelectionOffsetCaches_h