1 /* 2 * Copyright (C) 2003, 2004, 2005, 2006, 2007 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 #include "config.h" 21 #include "InlineBox.h" 22 23 #include "HitTestResult.h" 24 #include "InlineFlowBox.h" 25 #include "PaintInfo.h" 26 #include "RenderArena.h" 27 #include "RenderBlock.h" 28 #include "RootInlineBox.h" 29 30 using namespace std; 31 32 namespace WebCore { 33 34 #ifndef NDEBUG 35 static bool inInlineBoxDetach; 36 #endif 37 38 #ifndef NDEBUG 39 40 InlineBox::~InlineBox() 41 { 42 if (!m_hasBadParent && m_parent) 43 m_parent->setHasBadChildList(); 44 } 45 46 #endif 47 48 void InlineBox::remove() 49 { 50 if (parent()) 51 parent()->removeChild(this); 52 } 53 54 void InlineBox::destroy(RenderArena* renderArena) 55 { 56 #ifndef NDEBUG 57 inInlineBoxDetach = true; 58 #endif 59 delete this; 60 #ifndef NDEBUG 61 inInlineBoxDetach = false; 62 #endif 63 64 // Recover the size left there for us by operator delete and free the memory. 65 renderArena->free(*(size_t *)this, this); 66 } 67 68 void* InlineBox::operator new(size_t sz, RenderArena* renderArena) throw() 69 { 70 return renderArena->allocate(sz); 71 } 72 73 void InlineBox::operator delete(void* ptr, size_t sz) 74 { 75 ASSERT(inInlineBoxDetach); 76 77 // Stash size where destroy can find it. 78 *(size_t *)ptr = sz; 79 } 80 81 #ifndef NDEBUG 82 void InlineBox::showTreeForThis() const 83 { 84 if (m_renderer) 85 m_renderer->showTreeForThis(); 86 } 87 #endif 88 89 int InlineBox::logicalHeight() const 90 { 91 #if ENABLE(SVG) 92 if (hasVirtualLogicalHeight()) 93 return virtualLogicalHeight(); 94 #endif 95 96 if (renderer()->isText()) 97 return m_isText ? renderer()->style(m_firstLine)->fontMetrics().height() : 0; 98 if (renderer()->isBox() && parent()) 99 return isHorizontal() ? toRenderBox(m_renderer)->height() : toRenderBox(m_renderer)->width(); 100 101 ASSERT(isInlineFlowBox()); 102 RenderBoxModelObject* flowObject = boxModelObject(); 103 const FontMetrics& fontMetrics = renderer()->style(m_firstLine)->fontMetrics(); 104 int result = fontMetrics.height(); 105 if (parent()) 106 result += flowObject->borderAndPaddingLogicalHeight(); 107 return result; 108 } 109 110 int InlineBox::caretMinOffset() const 111 { 112 return m_renderer->caretMinOffset(); 113 } 114 115 int InlineBox::caretMaxOffset() const 116 { 117 return m_renderer->caretMaxOffset(); 118 } 119 120 unsigned InlineBox::caretMaxRenderedOffset() const 121 { 122 return 1; 123 } 124 125 void InlineBox::dirtyLineBoxes() 126 { 127 markDirty(); 128 for (InlineFlowBox* curr = parent(); curr && !curr->isDirty(); curr = curr->parent()) 129 curr->markDirty(); 130 } 131 132 void InlineBox::deleteLine(RenderArena* arena) 133 { 134 if (!m_extracted && m_renderer->isBox()) 135 toRenderBox(m_renderer)->setInlineBoxWrapper(0); 136 destroy(arena); 137 } 138 139 void InlineBox::extractLine() 140 { 141 m_extracted = true; 142 if (m_renderer->isBox()) 143 toRenderBox(m_renderer)->setInlineBoxWrapper(0); 144 } 145 146 void InlineBox::attachLine() 147 { 148 m_extracted = false; 149 if (m_renderer->isBox()) 150 toRenderBox(m_renderer)->setInlineBoxWrapper(this); 151 } 152 153 void InlineBox::adjustPosition(float dx, float dy) 154 { 155 m_x += dx; 156 m_y += dy; 157 158 if (m_renderer->isReplaced()) 159 toRenderBox(m_renderer)->move(dx, dy); 160 } 161 162 void InlineBox::paint(PaintInfo& paintInfo, int tx, int ty, int /* lineTop */, int /*lineBottom*/) 163 { 164 if (!paintInfo.shouldPaintWithinRoot(renderer()) || (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection)) 165 return; 166 167 IntPoint childPoint = IntPoint(tx, ty); 168 if (parent()->renderer()->style()->isFlippedBlocksWritingMode()) // Faster than calling containingBlock(). 169 childPoint = renderer()->containingBlock()->flipForWritingMode(toRenderBox(renderer()), childPoint, RenderBox::ParentToChildFlippingAdjustment); 170 171 // Paint all phases of replaced elements atomically, as though the replaced element established its 172 // own stacking context. (See Appendix E.2, section 6.4 on inline block/table elements in the CSS2.1 173 // specification.) 174 bool preservePhase = paintInfo.phase == PaintPhaseSelection || paintInfo.phase == PaintPhaseTextClip; 175 PaintInfo info(paintInfo); 176 info.phase = preservePhase ? paintInfo.phase : PaintPhaseBlockBackground; 177 renderer()->paint(info, childPoint.x(), childPoint.y()); 178 if (!preservePhase) { 179 info.phase = PaintPhaseChildBlockBackgrounds; 180 renderer()->paint(info, childPoint.x(), childPoint.y()); 181 info.phase = PaintPhaseFloat; 182 renderer()->paint(info, childPoint.x(), childPoint.y()); 183 info.phase = PaintPhaseForeground; 184 renderer()->paint(info, childPoint.x(), childPoint.y()); 185 info.phase = PaintPhaseOutline; 186 renderer()->paint(info, childPoint.x(), childPoint.y()); 187 } 188 } 189 190 bool InlineBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, int /* lineTop */, int /*lineBottom*/) 191 { 192 // Hit test all phases of replaced elements atomically, as though the replaced element established its 193 // own stacking context. (See Appendix E.2, section 6.4 on inline block/table elements in the CSS2.1 194 // specification.) 195 return renderer()->hitTest(request, result, IntPoint(x, y), tx, ty); 196 } 197 198 const RootInlineBox* InlineBox::root() const 199 { 200 if (m_parent) 201 return m_parent->root(); 202 ASSERT(isRootInlineBox()); 203 return static_cast<const RootInlineBox*>(this); 204 } 205 206 RootInlineBox* InlineBox::root() 207 { 208 if (m_parent) 209 return m_parent->root(); 210 ASSERT(isRootInlineBox()); 211 return static_cast<RootInlineBox*>(this); 212 } 213 214 bool InlineBox::nextOnLineExists() const 215 { 216 if (!m_determinedIfNextOnLineExists) { 217 m_determinedIfNextOnLineExists = true; 218 219 if (!parent()) 220 m_nextOnLineExists = false; 221 else if (nextOnLine()) 222 m_nextOnLineExists = true; 223 else 224 m_nextOnLineExists = parent()->nextOnLineExists(); 225 } 226 return m_nextOnLineExists; 227 } 228 229 bool InlineBox::prevOnLineExists() const 230 { 231 if (!m_determinedIfPrevOnLineExists) { 232 m_determinedIfPrevOnLineExists = true; 233 234 if (!parent()) 235 m_prevOnLineExists = false; 236 else if (prevOnLine()) 237 m_prevOnLineExists = true; 238 else 239 m_prevOnLineExists = parent()->prevOnLineExists(); 240 } 241 return m_prevOnLineExists; 242 } 243 244 InlineBox* InlineBox::nextLeafChild() const 245 { 246 InlineBox* leaf = 0; 247 for (InlineBox* box = nextOnLine(); box && !leaf; box = box->nextOnLine()) 248 leaf = box->isLeaf() ? box : static_cast<InlineFlowBox*>(box)->firstLeafChild(); 249 if (!leaf && parent()) 250 leaf = parent()->nextLeafChild(); 251 return leaf; 252 } 253 254 InlineBox* InlineBox::prevLeafChild() const 255 { 256 InlineBox* leaf = 0; 257 for (InlineBox* box = prevOnLine(); box && !leaf; box = box->prevOnLine()) 258 leaf = box->isLeaf() ? box : static_cast<InlineFlowBox*>(box)->lastLeafChild(); 259 if (!leaf && parent()) 260 leaf = parent()->prevLeafChild(); 261 return leaf; 262 } 263 264 RenderObject::SelectionState InlineBox::selectionState() 265 { 266 return renderer()->selectionState(); 267 } 268 269 bool InlineBox::canAccommodateEllipsis(bool ltr, int blockEdge, int ellipsisWidth) 270 { 271 // Non-replaced elements can always accommodate an ellipsis. 272 if (!m_renderer || !m_renderer->isReplaced()) 273 return true; 274 275 IntRect boxRect(m_x, 0, m_logicalWidth, 10); 276 IntRect ellipsisRect(ltr ? blockEdge - ellipsisWidth : blockEdge, 0, ellipsisWidth, 10); 277 return !(boxRect.intersects(ellipsisRect)); 278 } 279 280 float InlineBox::placeEllipsisBox(bool, float, float, float, bool&) 281 { 282 // Use -1 to mean "we didn't set the position." 283 return -1; 284 } 285 286 void InlineBox::clearKnownToHaveNoOverflow() 287 { 288 m_knownToHaveNoOverflow = false; 289 if (parent() && parent()->knownToHaveNoOverflow()) 290 parent()->clearKnownToHaveNoOverflow(); 291 } 292 293 FloatPoint InlineBox::locationIncludingFlipping() 294 { 295 if (!renderer()->style()->isFlippedBlocksWritingMode()) 296 return FloatPoint(x(), y()); 297 RenderBlock* block = root()->block(); 298 if (block->style()->isHorizontalWritingMode()) 299 return FloatPoint(x(), block->height() - height() - y()); 300 else 301 return FloatPoint(block->width() - width() - x(), y()); 302 } 303 304 void InlineBox::flipForWritingMode(FloatRect& rect) 305 { 306 if (!renderer()->style()->isFlippedBlocksWritingMode()) 307 return; 308 root()->block()->flipForWritingMode(rect); 309 } 310 311 FloatPoint InlineBox::flipForWritingMode(const FloatPoint& point) 312 { 313 if (!renderer()->style()->isFlippedBlocksWritingMode()) 314 return point; 315 return root()->block()->flipForWritingMode(point); 316 } 317 318 void InlineBox::flipForWritingMode(IntRect& rect) 319 { 320 if (!renderer()->style()->isFlippedBlocksWritingMode()) 321 return; 322 root()->block()->flipForWritingMode(rect); 323 } 324 325 IntPoint InlineBox::flipForWritingMode(const IntPoint& point) 326 { 327 if (!renderer()->style()->isFlippedBlocksWritingMode()) 328 return point; 329 return root()->block()->flipForWritingMode(point); 330 } 331 332 } // namespace WebCore 333 334 #ifndef NDEBUG 335 336 void showTree(const WebCore::InlineBox* b) 337 { 338 if (b) 339 b->showTreeForThis(); 340 } 341 342 #endif 343