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