From 81bc750723a18f21cd17d1b173cd2a4dda9cea6e Mon Sep 17 00:00:00 2001 From: Ben Murdoch Date: Tue, 24 May 2011 11:24:40 +0100 Subject: Merge WebKit at r80534: Intial merge by Git Change-Id: Ia7a83357124c9e1cdb1debf55d9661ec0bd09a61 --- Source/WebCore/rendering/RenderQuote.cpp | 331 +++++++++++++++++++++++++++++++ 1 file changed, 331 insertions(+) create mode 100644 Source/WebCore/rendering/RenderQuote.cpp (limited to 'Source/WebCore/rendering/RenderQuote.cpp') diff --git a/Source/WebCore/rendering/RenderQuote.cpp b/Source/WebCore/rendering/RenderQuote.cpp new file mode 100644 index 0000000..4523df8 --- /dev/null +++ b/Source/WebCore/rendering/RenderQuote.cpp @@ -0,0 +1,331 @@ +/** + * Copyright (C) 2011 Nokia Inc. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "RenderQuote.h" + +#include "Document.h" +#include "Element.h" +#include "HTMLElement.h" +#include "QuotesData.h" +#include "RenderStyle.h" +#include +#include +#include + +#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(UNKNOWN_DEPTH) + , m_next(0) + , m_previous(0) +{ +} + +RenderQuote::~RenderQuote() +{ +} + +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 ARRAY_SIZE(Carray) (sizeof(Carray) / sizeof(*Carray)) +#define LANGUAGE_DATA(name, languageSourceArray) { name, languageSourceArray, ARRAY_SIZE(languageSourceArray) } +#define U(x) ((const UChar*)L##x) + +static const UChar* simpleQuotes[] = {U("\""), U("\""), U("'"), U("'")}; + +static const UChar* englishQuotes[] = {U("\x201C"), U("\x201D"), U("\x2018"), U("\x2019")}; +static const UChar* norwegianQuotes[] = { U("\x00AB"), U("\x00BB"), U("\x2039"), U("\x203A") }; +static const UChar* romanianQuotes[] = { U("\x201E"), U("\x201D")}; +static const UChar* russianQuotes[] = { U("\x00AB"), U("\x00BB"), U("\x201E"), U("\x201C") }; +#undef U + +struct LanguageData { + const char *name; + const UChar* const* const array; + const int arraySize; + bool operator<(const LanguageData& compareTo) const + { + return strcmp(name, compareTo.name); + } +}; + +// Data mast be alphabetically sorted and in all lower case for fast comparison +LanguageData languageData[] = { + LANGUAGE_DATA("en", englishQuotes), + LANGUAGE_DATA("no", norwegianQuotes), + LANGUAGE_DATA("ro", romanianQuotes), + LANGUAGE_DATA("ru", russianQuotes) +}; +#undef LANGUAGE_DATA +const LanguageData* const languageDataEnd = languageData + ARRAY_SIZE(languageData); + +#define defaultLanguageQuotesSource simpleQuotes +#define defaultLanguageQuotesCount ARRAY_SIZE(defaultLanguageQuotesSource) + +static QuotesData* defaultLanguageQuotesValue = 0; +static const QuotesData* defaultLanguageQuotes() +{ + if (!defaultLanguageQuotesValue) { + defaultLanguageQuotesValue = QuotesData::create(defaultLanguageQuotesCount); + if (!defaultLanguageQuotesValue) + return 0; + String* data = defaultLanguageQuotesValue->data(); + for (size_t i = 0; i < defaultLanguageQuotesCount; ++i) + data[i] = defaultLanguageQuotesSource[i]; + } + return defaultLanguageQuotesValue; +} +#undef defaultLanguageQuotesSource +#undef defaultLanguageQuotesCount + +typedef HashMap, QuotesData* > QuotesMap; + +static QuotesMap& quotesMap() +{ + DEFINE_STATIC_LOCAL(QuotesMap, staticQuotesMap, ()); + return staticQuotesMap; +} + +static const QuotesData* quotesForLanguage(AtomicStringImpl* language) +{ + QuotesData* returnValue; + AtomicString lower(language->lower()); + returnValue = quotesMap().get(lower.impl()); + if (returnValue) + return returnValue; + CString s(static_cast(lower).ascii()); + LanguageData request = { s.buffer()->data(), 0, 0 }; + const LanguageData* lowerBound = std::lower_bound(languageData, languageDataEnd, request); + if (lowerBound == languageDataEnd) + return defaultLanguageQuotes(); + if (strncmp(lowerBound->name, request.name, strlen(lowerBound->name))) + return defaultLanguageQuotes(); + returnValue = QuotesData::create(lowerBound->arraySize); + if (!returnValue) + return defaultLanguageQuotes(); + String* data = returnValue->data(); + for (int i = 0; i < lowerBound->arraySize; ++i) + data[i] = lowerBound->array[i]; + quotesMap().set(lower.impl(), returnValue); + return returnValue; +} +#undef ARRAY_SIZE + +static const QuotesData* defaultQuotes(const RenderObject* object) +{ + DEFINE_STATIC_LOCAL(String, langString, ("lang")); + Node* node = object->generatingNode(); + Element* element; + if (!node) { + element = object->document()->body(); + if (!element) + element = object->document()->documentElement(); + } else if (!node->isElementNode()) { + element = node->parentElement(); + if (!element) + return defaultLanguageQuotes(); + } else + element = toElement(node); + const AtomicString* language; + while ((language = &element->getAttribute(langString)) && language->isNull()) { + element = element->parentElement(); + if (!element) + return defaultLanguageQuotes(); + } + return quotesForLanguage(language->impl()); +} + +PassRefPtr RenderQuote::originalText() const +{ + if (!parent()) + return 0; + ASSERT(m_depth != UNKNOWN_DEPTH); + const QuotesData* quotes = style()->quotes(); + if (!quotes) + quotes = defaultQuotes(this); + if (!quotes->length) + return emptyAtom.impl(); + int index = m_depth * 2; + switch (m_type) { + case NO_OPEN_QUOTE: + case NO_CLOSE_QUOTE: + return String("").impl(); + case CLOSE_QUOTE: + if (index) + --index; + else + ++index; + break; + case OPEN_QUOTE: + break; + default: + ASSERT_NOT_REACHED(); + return emptyAtom.impl(); + } + if (index >= quotes->length) + index = (quotes->length-2) | (index & 1); + if (index < 0) + return emptyAtom.impl(); + return quotes->data()[index].impl(); +} + +void RenderQuote::computePreferredLogicalWidths(float lead) +{ + setTextInternal(originalText()); + RenderText::computePreferredLogicalWidths(lead); +} + +void RenderQuote::rendererSubtreeAttached(RenderObject* renderer) +{ + if (renderer->documentBeingDestroyed()) + return; + for (RenderObject* descendant = renderer; descendant; descendant = descendant->nextInPreOrder(renderer)) + if (descendant->isQuote()) { + toRenderQuote(descendant)->placeQuote(); + break; + } +} + +void RenderQuote::rendererRemovedFromTree(RenderObject* subtreeRoot) +{ + if (subtreeRoot->documentBeingDestroyed()) + return; + for (RenderObject* descendant = subtreeRoot; descendant; descendant = descendant->nextInPreOrder(subtreeRoot)) + 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(subtreeRoot); descendant; descendant = descendant->nextInPreOrder(subtreeRoot)) + 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::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) +{ + const QuotesData* newQuotes = style()->quotes(); + const QuotesData* oldQuotes = oldStyle ? oldStyle->quotes() : 0; + if (!((newQuotes && oldQuotes && (*newQuotes == *oldQuotes)) || (!newQuotes && !oldQuotes))) + setNeedsLayoutAndPrefWidthsRecalc(); + RenderText::styleDidChange(diff, oldStyle); +} + +} // namespace WebCore -- cgit v1.1