| Index: Source/WebCore/rendering/RenderQuote.cpp
 | 
| ===================================================================
 | 
| --- Source/WebCore/rendering/RenderQuote.cpp	(revision 127609)
 | 
| +++ Source/WebCore/rendering/RenderQuote.cpp	(working copy)
 | 
| @@ -21,34 +21,119 @@
 | 
|  #include "config.h"
 | 
|  #include "RenderQuote.h"
 | 
|  
 | 
| +#include "Document.h"
 | 
| +#include "QuotesData.h"
 | 
| +#include "RenderStyle.h"
 | 
|  #include <wtf/text/AtomicString.h>
 | 
|  
 | 
| -#define U(x) ((const UChar*)L##x)
 | 
| +#define UNKNOWN_DEPTH -1
 | 
|  
 | 
|  namespace WebCore {
 | 
| +static inline void adjustDepth(int &depth, QuoteType type)
 | 
| +{
 | 
| +    switch (type) {
 | 
| +    case OPEN_QUOTE:
 | 
| +    case NO_OPEN_QUOTE:
 | 
| +        ++depth;
 | 
| +        break;
 | 
| +    case CLOSE_QUOTE:
 | 
| +    case NO_CLOSE_QUOTE:
 | 
| +        if (depth)
 | 
| +            --depth;
 | 
| +        break;
 | 
| +    default:
 | 
| +        ASSERT_NOT_REACHED();
 | 
| +    }
 | 
| +}
 | 
|  
 | 
|  RenderQuote::RenderQuote(Document* node, QuoteType quote)
 | 
|      : RenderText(node, StringImpl::empty())
 | 
|      , m_type(quote)
 | 
| -    , m_depth(0)
 | 
| +    , m_depth(UNKNOWN_DEPTH)
 | 
|      , m_next(0)
 | 
|      , m_previous(0)
 | 
| -    , m_attached(false)
 | 
|  {
 | 
| +    view()->addRenderQuote();
 | 
|  }
 | 
|  
 | 
|  RenderQuote::~RenderQuote()
 | 
|  {
 | 
| -    ASSERT(!m_attached);
 | 
| -    ASSERT(!m_next && !m_previous);
 | 
|  }
 | 
|  
 | 
|  void RenderQuote::willBeDestroyed()
 | 
|  {
 | 
| -    detachQuote();
 | 
| +    if (view())
 | 
| +        view()->removeRenderQuote();
 | 
|      RenderText::willBeDestroyed();
 | 
|  }
 | 
|  
 | 
| +const char* RenderQuote::renderName() const
 | 
| +{
 | 
| +    return "RenderQuote";
 | 
| +}
 | 
| +
 | 
| +// This function places a list of quote renderers starting at "this" in the list of quote renderers already
 | 
| +// in the document's renderer tree.
 | 
| +// The assumptions are made (for performance):
 | 
| +// 1. The list of quotes already in the renderers tree of the document is already in a consistent state
 | 
| +// (All quote renderers are linked and have the correct depth set)
 | 
| +// 2. The quote renderers of the inserted list are in a tree of renderers of their own which has been just
 | 
| +// inserted in the main renderer tree with its root as child of some renderer.
 | 
| +// 3. The quote renderers in the inserted list have depths consistent with their position in the list relative
 | 
| +// to "this", thus if "this" does not need to change its depth upon insertion, the other renderers in the list don't
 | 
| +// need to either.
 | 
| +void RenderQuote::placeQuote()
 | 
| +{
 | 
| +    RenderQuote* head = this;
 | 
| +    ASSERT(!head->m_previous);
 | 
| +    RenderQuote* tail = 0;
 | 
| +    for (RenderObject* predecessor = head->previousInPreOrder(); predecessor; predecessor = predecessor->previousInPreOrder()) {
 | 
| +        if (!predecessor->isQuote())
 | 
| +            continue;
 | 
| +        head->m_previous = toRenderQuote(predecessor);
 | 
| +        if (head->m_previous->m_next) {
 | 
| +            // We need to splice the list of quotes headed by head into the document's list of quotes.
 | 
| +            tail = head;
 | 
| +            while (tail->m_next)
 | 
| +                 tail = tail->m_next;
 | 
| +            tail->m_next = head->m_previous->m_next;
 | 
| +            ASSERT(tail->m_next->m_previous == head->m_previous);
 | 
| +            tail->m_next->m_previous =  tail;
 | 
| +            tail = tail->m_next; // This marks the splicing point here there may be a depth discontinuity
 | 
| +        }
 | 
| +        head->m_previous->m_next = head;
 | 
| +        ASSERT(head->m_previous->m_depth != UNKNOWN_DEPTH);
 | 
| +        break;
 | 
| +    }
 | 
| +    int newDepth;
 | 
| +    if (!head->m_previous) {
 | 
| +        newDepth = 0;
 | 
| +        goto skipNewDepthCalc;
 | 
| +    }
 | 
| +    newDepth = head->m_previous->m_depth;
 | 
| +    do {
 | 
| +        adjustDepth(newDepth, head->m_previous->m_type);
 | 
| +skipNewDepthCalc:
 | 
| +        if (head->m_depth == newDepth) { // All remaining depth should be correct except if splicing was done.
 | 
| +            if (!tail) // We've done the post splicing section already or there was no splicing.
 | 
| +                break;
 | 
| +            head = tail; // Continue after the splicing point
 | 
| +            tail = 0; // Mark the possible splicing point discontinuity fixed.
 | 
| +            newDepth = head->m_previous->m_depth;
 | 
| +            continue;
 | 
| +        }
 | 
| +        head->m_depth = newDepth;
 | 
| +        // FIXME: If the width and height of the quotation characters does not change we may only need to
 | 
| +        // Invalidate the renderer's area not a relayout.
 | 
| +        head->setNeedsLayoutAndPrefWidthsRecalc();
 | 
| +        head = head->m_next;
 | 
| +        if (head == tail) // We are at the splicing point
 | 
| +            tail = 0; // Mark the possible depth discontinuity fixed.
 | 
| +    } while (head);
 | 
| +}
 | 
| +
 | 
| +#define U(x) ((const UChar*)L##x)
 | 
| +
 | 
|  typedef HashMap<AtomicString, const QuotesData*, CaseFoldingHash> QuotesMap;
 | 
|  
 | 
|  static const QuotesMap& quotesDataLanguageMap()
 | 
| @@ -72,24 +157,27 @@
 | 
|  
 | 
|  PassRefPtr<StringImpl> RenderQuote::originalText() const
 | 
|  {
 | 
| +    if (!parent())
 | 
| +        return 0;
 | 
| +    ASSERT(m_depth != UNKNOWN_DEPTH);
 | 
| +    const QuotesData* quotes = quotesData();
 | 
|      switch (m_type) {
 | 
|      case NO_OPEN_QUOTE:
 | 
|      case NO_CLOSE_QUOTE:
 | 
|          return StringImpl::empty();
 | 
|      case CLOSE_QUOTE:
 | 
|          // FIXME: When m_depth is 0 we should return empty string.
 | 
| -        return quotesData()->getCloseQuote(std::max(m_depth - 1, 0)).impl();
 | 
| +        return quotes->getCloseQuote(std::max(m_depth - 1, 0)).impl();
 | 
|      case OPEN_QUOTE:
 | 
| -        return quotesData()->getOpenQuote(m_depth).impl();
 | 
| +        return quotes->getOpenQuote(m_depth).impl();
 | 
| +    default:
 | 
| +        ASSERT_NOT_REACHED();
 | 
| +        return StringImpl::empty();
 | 
|      }
 | 
| -    ASSERT_NOT_REACHED();
 | 
| -    return StringImpl::empty();
 | 
|  }
 | 
|  
 | 
|  void RenderQuote::computePreferredLogicalWidths(float lead)
 | 
|  {
 | 
| -    if (!m_attached)
 | 
| -        attachQuote();
 | 
|      setTextInternal(originalText());
 | 
|      RenderText::computePreferredLogicalWidths(lead);
 | 
|  }
 | 
| @@ -108,95 +196,58 @@
 | 
|      return quotes;
 | 
|  }
 | 
|  
 | 
| -void RenderQuote::attachQuote()
 | 
| +void RenderQuote::rendererSubtreeAttached(RenderObject* renderer)
 | 
|  {
 | 
| -    ASSERT(view());
 | 
| -    ASSERT(!m_attached);
 | 
| -    ASSERT(!m_next && !m_previous);
 | 
| -
 | 
| -    // FIXME: Don't set pref widths dirty during layout. See updateDepth() for
 | 
| -    // more detail.
 | 
| -    if (!isRooted()) {
 | 
| -        setNeedsLayoutAndPrefWidthsRecalc();
 | 
| +    ASSERT(renderer->view());
 | 
| +    if (!renderer->view()->hasRenderQuotes())
 | 
|          return;
 | 
| -    }
 | 
| -
 | 
| -    if (!view()->renderQuoteHead()) {
 | 
| -        view()->setRenderQuoteHead(this);
 | 
| -        m_attached = true;
 | 
| -        return;
 | 
| -    }
 | 
| -
 | 
| -    for (RenderObject* predecessor = previousInPreOrder(); predecessor; predecessor = predecessor->previousInPreOrder()) {
 | 
| -        if (!predecessor->isQuote())
 | 
| -            continue;
 | 
| -        m_previous = toRenderQuote(predecessor);
 | 
| -        m_next = m_previous->m_next;
 | 
| -        m_previous->m_next = this;
 | 
| -        if (m_next)
 | 
| -            m_next->m_previous = this;
 | 
| -        break;
 | 
| -    }
 | 
| -
 | 
| -    if (!m_previous) {
 | 
| -        m_next = view()->renderQuoteHead();
 | 
| -        view()->setRenderQuoteHead(this);
 | 
| -    }
 | 
| -    m_attached = true;
 | 
| -    for (RenderQuote* quote = this; quote; quote = quote->m_next)
 | 
| -        quote->updateDepth();
 | 
| -
 | 
| -    ASSERT(!m_next || m_next->m_attached);
 | 
| -    ASSERT(!m_previous || m_previous->m_attached);
 | 
| +    for (RenderObject* descendant = renderer; descendant; descendant = descendant->nextInPreOrder(renderer))
 | 
| +        if (descendant->isQuote()) {
 | 
| +            toRenderQuote(descendant)->placeQuote();
 | 
| +            break;
 | 
| +        }
 | 
|  }
 | 
|  
 | 
| -void RenderQuote::detachQuote()
 | 
| +void RenderQuote::rendererRemovedFromTree(RenderObject* renderer)
 | 
|  {
 | 
| -    ASSERT(!m_next || m_next->m_attached);
 | 
| -    ASSERT(!m_previous || m_previous->m_attached);
 | 
| -    if (!m_attached)
 | 
| +    ASSERT(renderer->view());
 | 
| +    if (!renderer->view()->hasRenderQuotes())
 | 
|          return;
 | 
| -    if (m_previous)
 | 
| -        m_previous->m_next = m_next;
 | 
| -    else if (view())
 | 
| -        view()->setRenderQuoteHead(m_next);
 | 
| -    if (m_next)
 | 
| -        m_next->m_previous = m_previous;
 | 
| -    if (!documentBeingDestroyed()) {
 | 
| -        for (RenderQuote* quote = m_next; quote; quote = quote->m_next)
 | 
| -            quote->updateDepth();
 | 
| -    }
 | 
| -    m_attached = false;
 | 
| -    m_next = 0;
 | 
| -    m_previous = 0;
 | 
| -    m_depth = 0;
 | 
| +    for (RenderObject* descendant = renderer; descendant; descendant = descendant->nextInPreOrder(renderer))
 | 
| +        if (descendant->isQuote()) {
 | 
| +            RenderQuote* removedQuote = toRenderQuote(descendant);
 | 
| +            RenderQuote* lastQuoteBefore = removedQuote->m_previous;
 | 
| +            removedQuote->m_previous = 0;
 | 
| +            int depth = removedQuote->m_depth;
 | 
| +            for (descendant = descendant->nextInPreOrder(renderer); descendant; descendant = descendant->nextInPreOrder(renderer))
 | 
| +                if (descendant->isQuote())
 | 
| +                    removedQuote = toRenderQuote(descendant);
 | 
| +            RenderQuote* quoteAfter = removedQuote->m_next;
 | 
| +            removedQuote->m_next = 0;
 | 
| +            if (lastQuoteBefore)
 | 
| +                lastQuoteBefore->m_next = quoteAfter;
 | 
| +            if (quoteAfter) {
 | 
| +                quoteAfter->m_previous = lastQuoteBefore;
 | 
| +                do {
 | 
| +                    if (depth == quoteAfter->m_depth)
 | 
| +                        break;
 | 
| +                    quoteAfter->m_depth = depth;
 | 
| +                    quoteAfter->setNeedsLayoutAndPrefWidthsRecalc();
 | 
| +                    adjustDepth(depth, quoteAfter->m_type);
 | 
| +                    quoteAfter = quoteAfter->m_next;
 | 
| +                } while (quoteAfter);
 | 
| +            }
 | 
| +            break;
 | 
| +        }
 | 
|  }
 | 
|  
 | 
| -void RenderQuote::updateDepth()
 | 
| +void RenderQuote::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
 | 
|  {
 | 
| -    ASSERT(m_attached);
 | 
| -    int oldDepth = m_depth;
 | 
| -    m_depth = 0;
 | 
| -    if (m_previous) {
 | 
| -        m_depth = m_previous->m_depth;
 | 
| -        switch (m_previous->m_type) {
 | 
| -        case OPEN_QUOTE:
 | 
| -        case NO_OPEN_QUOTE:
 | 
| -            m_depth++;
 | 
| -            break;
 | 
| -        case CLOSE_QUOTE:
 | 
| -        case NO_CLOSE_QUOTE:
 | 
| -            if (m_depth)
 | 
| -                m_depth--;
 | 
| -            break;
 | 
| -        }
 | 
| -    }
 | 
| -    // FIXME: Don't call setNeedsLayout or dirty our preferred widths during layout.
 | 
| -    // This is likely to fail anyway as one of our ancestor will call setNeedsLayout(false),
 | 
| -    // preventing the future layout to occur on |this|. The solution is to move that to a
 | 
| -    // pre-layout phase.
 | 
| -    if (oldDepth != m_depth)
 | 
| +    const QuotesData* newQuotes = style()->quotes();
 | 
| +    const QuotesData* oldQuotes = oldStyle ? oldStyle->quotes() : 0;
 | 
| +    if (!QuotesData::equals(newQuotes, oldQuotes))
 | 
|          setNeedsLayoutAndPrefWidthsRecalc();
 | 
| +    RenderText::styleDidChange(diff, oldStyle);
 | 
|  }
 | 
|  
 | 
|  } // namespace WebCore
 | 
| 
 |