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 "core/rendering/InlineBox.h" 22 23 #include "core/platform/Partitions.h" 24 #include "core/platform/graphics/FontMetrics.h" 25 #include "core/rendering/InlineFlowBox.h" 26 #include "core/rendering/PaintInfo.h" 27 #include "core/rendering/RenderBlock.h" 28 #include "core/rendering/RootInlineBox.h" 29 30 #ifndef NDEBUG 31 #include <stdio.h> 32 #endif 33 34 using namespace std; 35 36 namespace WebCore { 37 38 struct SameSizeAsInlineBox { 39 virtual ~SameSizeAsInlineBox() { } 40 void* a[4]; 41 FloatPoint b; 42 float c; 43 uint32_t d : 32; 44 #ifndef NDEBUG 45 bool f; 46 #endif 47 }; 48 49 COMPILE_ASSERT(sizeof(InlineBox) == sizeof(SameSizeAsInlineBox), InlineBox_size_guard); 50 51 #ifndef NDEBUG 52 static bool inInlineBoxDetach; 53 #endif 54 55 #ifndef NDEBUG 56 57 InlineBox::~InlineBox() 58 { 59 if (!m_hasBadParent && m_parent) 60 m_parent->setHasBadChildList(); 61 } 62 63 #endif 64 65 void InlineBox::remove() 66 { 67 if (parent()) 68 parent()->removeChild(this); 69 } 70 71 void* InlineBox::operator new(size_t sz) 72 { 73 return partitionAlloc(Partitions::getRenderingPartition(), sz); 74 } 75 76 void InlineBox::operator delete(void* ptr) 77 { 78 partitionFree(ptr); 79 } 80 81 #ifndef NDEBUG 82 const char* InlineBox::boxName() const 83 { 84 return "InlineBox"; 85 } 86 87 void InlineBox::showTreeForThis() const 88 { 89 if (m_renderer) 90 m_renderer->showTreeForThis(); 91 } 92 93 void InlineBox::showLineTreeForThis() const 94 { 95 if (m_renderer) 96 m_renderer->containingBlock()->showLineTreeAndMark(this, "*"); 97 } 98 99 void InlineBox::showLineTreeAndMark(const InlineBox* markedBox1, const char* markedLabel1, const InlineBox* markedBox2, const char* markedLabel2, const RenderObject* obj, int depth) const 100 { 101 int printedCharacters = 0; 102 if (this == markedBox1) 103 printedCharacters += fprintf(stderr, "%s", markedLabel1); 104 if (this == markedBox2) 105 printedCharacters += fprintf(stderr, "%s", markedLabel2); 106 if (renderer() == obj) 107 printedCharacters += fprintf(stderr, "*"); 108 for (; printedCharacters < depth * 2; printedCharacters++) 109 fputc(' ', stderr); 110 111 showBox(printedCharacters); 112 } 113 114 void InlineBox::showBox(int printedCharacters) const 115 { 116 printedCharacters += fprintf(stderr, "%s\t%p", boxName(), this); 117 for (; printedCharacters < showTreeCharacterOffset; printedCharacters++) 118 fputc(' ', stderr); 119 fprintf(stderr, "\t%s %p\n", renderer() ? renderer()->renderName() : "No Renderer", renderer()); 120 } 121 #endif 122 123 float InlineBox::logicalHeight() const 124 { 125 if (hasVirtualLogicalHeight()) 126 return virtualLogicalHeight(); 127 128 if (renderer()->isText()) 129 return m_bitfields.isText() ? renderer()->style(isFirstLineStyle())->fontMetrics().height() : 0; 130 if (renderer()->isBox() && parent()) 131 return isHorizontal() ? toRenderBox(m_renderer)->height() : toRenderBox(m_renderer)->width(); 132 133 ASSERT(isInlineFlowBox()); 134 RenderBoxModelObject* flowObject = boxModelObject(); 135 const FontMetrics& fontMetrics = renderer()->style(isFirstLineStyle())->fontMetrics(); 136 float result = fontMetrics.height(); 137 if (parent()) 138 result += flowObject->borderAndPaddingLogicalHeight(); 139 return result; 140 } 141 142 int InlineBox::baselinePosition(FontBaseline baselineType) const 143 { 144 return boxModelObject()->baselinePosition(baselineType, m_bitfields.firstLine(), isHorizontal() ? HorizontalLine : VerticalLine, PositionOnContainingLine); 145 } 146 147 LayoutUnit InlineBox::lineHeight() const 148 { 149 return boxModelObject()->lineHeight(m_bitfields.firstLine(), isHorizontal() ? HorizontalLine : VerticalLine, PositionOnContainingLine); 150 } 151 152 int InlineBox::caretMinOffset() const 153 { 154 return m_renderer->caretMinOffset(); 155 } 156 157 int InlineBox::caretMaxOffset() const 158 { 159 return m_renderer->caretMaxOffset(); 160 } 161 162 void InlineBox::dirtyLineBoxes() 163 { 164 markDirty(); 165 for (InlineFlowBox* curr = parent(); curr && !curr->isDirty(); curr = curr->parent()) 166 curr->markDirty(); 167 } 168 169 void InlineBox::deleteLine() 170 { 171 if (!m_bitfields.extracted() && m_renderer->isBox()) 172 toRenderBox(m_renderer)->setInlineBoxWrapper(0); 173 destroy(); 174 } 175 176 void InlineBox::extractLine() 177 { 178 m_bitfields.setExtracted(true); 179 if (m_renderer->isBox()) 180 toRenderBox(m_renderer)->setInlineBoxWrapper(0); 181 } 182 183 void InlineBox::attachLine() 184 { 185 m_bitfields.setExtracted(false); 186 if (m_renderer->isBox()) 187 toRenderBox(m_renderer)->setInlineBoxWrapper(this); 188 } 189 190 void InlineBox::adjustPosition(float dx, float dy) 191 { 192 m_topLeft.move(dx, dy); 193 194 if (m_renderer->isReplaced()) 195 toRenderBox(m_renderer)->move(dx, dy); 196 } 197 198 void InlineBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit /* lineTop */, LayoutUnit /*lineBottom*/) 199 { 200 if (!paintInfo.shouldPaintWithinRoot(renderer()) || (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection)) 201 return; 202 203 LayoutPoint childPoint = paintOffset; 204 if (parent()->renderer()->style()->isFlippedBlocksWritingMode()) // Faster than calling containingBlock(). 205 childPoint = renderer()->containingBlock()->flipForWritingModeForChild(toRenderBox(renderer()), childPoint); 206 207 // Paint all phases of replaced elements atomically, as though the replaced element established its 208 // own stacking context. (See Appendix E.2, section 6.4 on inline block/table elements in the CSS2.1 209 // specification.) 210 bool preservePhase = paintInfo.phase == PaintPhaseSelection || paintInfo.phase == PaintPhaseTextClip; 211 PaintInfo info(paintInfo); 212 info.phase = preservePhase ? paintInfo.phase : PaintPhaseBlockBackground; 213 renderer()->paint(info, childPoint); 214 if (!preservePhase) { 215 info.phase = PaintPhaseChildBlockBackgrounds; 216 renderer()->paint(info, childPoint); 217 info.phase = PaintPhaseFloat; 218 renderer()->paint(info, childPoint); 219 info.phase = PaintPhaseForeground; 220 renderer()->paint(info, childPoint); 221 info.phase = PaintPhaseOutline; 222 renderer()->paint(info, childPoint); 223 } 224 } 225 226 bool InlineBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit /* lineTop */, LayoutUnit /*lineBottom*/) 227 { 228 // Hit test all phases of replaced elements atomically, as though the replaced element established its 229 // own stacking context. (See Appendix E.2, section 6.4 on inline block/table elements in the CSS2.1 230 // specification.) 231 LayoutPoint childPoint = accumulatedOffset; 232 if (parent()->renderer()->style()->isFlippedBlocksWritingMode()) // Faster than calling containingBlock(). 233 childPoint = renderer()->containingBlock()->flipForWritingModeForChild(toRenderBox(renderer()), childPoint); 234 235 return renderer()->hitTest(request, result, locationInContainer, childPoint); 236 } 237 238 const RootInlineBox* InlineBox::root() const 239 { 240 if (m_parent) 241 return m_parent->root(); 242 ASSERT(isRootInlineBox()); 243 return static_cast<const RootInlineBox*>(this); 244 } 245 246 RootInlineBox* InlineBox::root() 247 { 248 if (m_parent) 249 return m_parent->root(); 250 ASSERT(isRootInlineBox()); 251 return static_cast<RootInlineBox*>(this); 252 } 253 254 bool InlineBox::nextOnLineExists() const 255 { 256 if (!m_bitfields.determinedIfNextOnLineExists()) { 257 m_bitfields.setDeterminedIfNextOnLineExists(true); 258 259 if (!parent()) 260 m_bitfields.setNextOnLineExists(false); 261 else if (nextOnLine()) 262 m_bitfields.setNextOnLineExists(true); 263 else 264 m_bitfields.setNextOnLineExists(parent()->nextOnLineExists()); 265 } 266 return m_bitfields.nextOnLineExists(); 267 } 268 269 InlineBox* InlineBox::nextLeafChild() const 270 { 271 InlineBox* leaf = 0; 272 for (InlineBox* box = nextOnLine(); box && !leaf; box = box->nextOnLine()) 273 leaf = box->isLeaf() ? box : toInlineFlowBox(box)->firstLeafChild(); 274 if (!leaf && parent()) 275 leaf = parent()->nextLeafChild(); 276 return leaf; 277 } 278 279 InlineBox* InlineBox::prevLeafChild() const 280 { 281 InlineBox* leaf = 0; 282 for (InlineBox* box = prevOnLine(); box && !leaf; box = box->prevOnLine()) 283 leaf = box->isLeaf() ? box : toInlineFlowBox(box)->lastLeafChild(); 284 if (!leaf && parent()) 285 leaf = parent()->prevLeafChild(); 286 return leaf; 287 } 288 289 InlineBox* InlineBox::nextLeafChildIgnoringLineBreak() const 290 { 291 InlineBox* leaf = nextLeafChild(); 292 if (leaf && leaf->isLineBreak()) 293 return 0; 294 return leaf; 295 } 296 297 InlineBox* InlineBox::prevLeafChildIgnoringLineBreak() const 298 { 299 InlineBox* leaf = prevLeafChild(); 300 if (leaf && leaf->isLineBreak()) 301 return 0; 302 return leaf; 303 } 304 305 RenderObject::SelectionState InlineBox::selectionState() 306 { 307 return renderer()->selectionState(); 308 } 309 310 bool InlineBox::canAccommodateEllipsis(bool ltr, int blockEdge, int ellipsisWidth) const 311 { 312 // Non-replaced elements can always accommodate an ellipsis. 313 if (!m_renderer || !m_renderer->isReplaced()) 314 return true; 315 316 IntRect boxRect(left(), 0, m_logicalWidth, 10); 317 IntRect ellipsisRect(ltr ? blockEdge - ellipsisWidth : blockEdge, 0, ellipsisWidth, 10); 318 return !(boxRect.intersects(ellipsisRect)); 319 } 320 321 float InlineBox::placeEllipsisBox(bool, float, float, float, float& truncatedWidth, bool&) 322 { 323 // Use -1 to mean "we didn't set the position." 324 truncatedWidth += logicalWidth(); 325 return -1; 326 } 327 328 void InlineBox::clearKnownToHaveNoOverflow() 329 { 330 m_bitfields.setKnownToHaveNoOverflow(false); 331 if (parent() && parent()->knownToHaveNoOverflow()) 332 parent()->clearKnownToHaveNoOverflow(); 333 } 334 335 FloatPoint InlineBox::locationIncludingFlipping() 336 { 337 if (!renderer()->style()->isFlippedBlocksWritingMode()) 338 return FloatPoint(x(), y()); 339 RenderBlock* block = root()->block(); 340 if (block->style()->isHorizontalWritingMode()) 341 return FloatPoint(x(), block->height() - height() - y()); 342 else 343 return FloatPoint(block->width() - width() - x(), y()); 344 } 345 346 void InlineBox::flipForWritingMode(FloatRect& rect) 347 { 348 if (!renderer()->style()->isFlippedBlocksWritingMode()) 349 return; 350 root()->block()->flipForWritingMode(rect); 351 } 352 353 FloatPoint InlineBox::flipForWritingMode(const FloatPoint& point) 354 { 355 if (!renderer()->style()->isFlippedBlocksWritingMode()) 356 return point; 357 return root()->block()->flipForWritingMode(point); 358 } 359 360 void InlineBox::flipForWritingMode(LayoutRect& rect) 361 { 362 if (!renderer()->style()->isFlippedBlocksWritingMode()) 363 return; 364 root()->block()->flipForWritingMode(rect); 365 } 366 367 LayoutPoint InlineBox::flipForWritingMode(const LayoutPoint& point) 368 { 369 if (!renderer()->style()->isFlippedBlocksWritingMode()) 370 return point; 371 return root()->block()->flipForWritingMode(point); 372 } 373 374 } // namespace WebCore 375 376 #ifndef NDEBUG 377 378 void showTree(const WebCore::InlineBox* b) 379 { 380 if (b) 381 b->showTreeForThis(); 382 } 383 384 void showLineTree(const WebCore::InlineBox* b) 385 { 386 if (b) 387 b->showLineTreeForThis(); 388 } 389 390 #endif 391