Home | History | Annotate | Download | only in rendering
      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