2 * Copyright (C) 2011 Nokia Inc. All rights reserved.
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.
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.
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.
22 #include "RenderQuote.h"
26 #include "HTMLElement.h"
27 #include "QuotesData.h"
28 #include "RenderStyle.h"
30 #include <wtf/text/AtomicString.h>
31 #include <wtf/text/CString.h>
33 #define UNKNOWN_DEPTH -1
36 static inline void adjustDepth(int &depth, QuoteType type)
53 RenderQuote::RenderQuote(Document* node, QuoteType quote)
54 : RenderText(node, StringImpl::empty())
56 , m_depth(UNKNOWN_DEPTH)
62 RenderQuote::~RenderQuote()
66 const char* RenderQuote::renderName() const
71 // This function places a list of quote renderers starting at "this" in the list of quote renderers already
72 // in the document's renderer tree.
73 // The assumptions are made (for performance):
74 // 1. The list of quotes already in the renderers tree of the document is already in a consistent state
75 // (All quote renderers are linked and have the correct depth set)
76 // 2. The quote renderers of the inserted list are in a tree of renderers of their own which has been just
77 // inserted in the main renderer tree with its root as child of some renderer.
78 // 3. The quote renderers in the inserted list have depths consistent with their position in the list relative
79 // to "this", thus if "this" does not need to change its depth upon insertion, the other renderers in the list don't
81 void RenderQuote::placeQuote()
83 RenderQuote* head = this;
84 ASSERT(!head->m_previous);
85 RenderQuote* tail = 0;
86 for (RenderObject* predecessor = head->previousInPreOrder(); predecessor; predecessor = predecessor->previousInPreOrder()) {
87 if (!predecessor->isQuote())
89 head->m_previous = toRenderQuote(predecessor);
90 if (head->m_previous->m_next) {
91 // We need to splice the list of quotes headed by head into the document's list of quotes.
95 tail->m_next = head->m_previous->m_next;
96 ASSERT(tail->m_next->m_previous == head->m_previous);
97 tail->m_next->m_previous = tail;
98 tail = tail->m_next; // This marks the splicing point here there may be a depth discontinuity
100 head->m_previous->m_next = head;
101 ASSERT(head->m_previous->m_depth != UNKNOWN_DEPTH);
105 if (!head->m_previous) {
107 goto skipNewDepthCalc;
109 newDepth = head->m_previous->m_depth;
111 adjustDepth(newDepth, head->m_previous->m_type);
113 if (head->m_depth == newDepth) { // All remaining depth should be correct except if splicing was done.
114 if (!tail) // We've done the post splicing section already or there was no splicing.
116 head = tail; // Continue after the splicing point
117 tail = 0; // Mark the possible splicing point discontinuity fixed.
118 newDepth = head->m_previous->m_depth;
121 head->m_depth = newDepth;
122 // FIXME: If the width and height of the quotation characters does not change we may only need to
123 // Invalidate the renderer's area not a relayout.
124 head->setNeedsLayoutAndPrefWidthsRecalc();
126 if (head == tail) // We are at the splicing point
127 tail = 0; // Mark the possible depth discontinuity fixed.
131 #define ARRAY_SIZE(Carray) (sizeof(Carray) / sizeof(*Carray))
132 #define LANGUAGE_DATA(name, languageSourceArray) { name, languageSourceArray, ARRAY_SIZE(languageSourceArray) }
133 #define U(x) ((const UChar*)L##x)
135 static const UChar* simpleQuotes[] = {U("\""), U("\""), U("'"), U("'")};
137 static const UChar* englishQuotes[] = {U("\x201C"), U("\x201D"), U("\x2018"), U("\x2019")};
138 static const UChar* norwegianQuotes[] = { U("\x00AB"), U("\x00BB"), U("\x2039"), U("\x203A") };
139 static const UChar* romanianQuotes[] = { U("\x201E"), U("\x201D")};
140 static const UChar* russianQuotes[] = { U("\x00AB"), U("\x00BB"), U("\x201E"), U("\x201C") };
143 struct LanguageData {
145 const UChar* const* const array;
147 bool operator<(const LanguageData& compareTo) const
149 return strcmp(name, compareTo.name);
153 // Data mast be alphabetically sorted and in all lower case for fast comparison
154 LanguageData languageData[] = {
155 LANGUAGE_DATA("en", englishQuotes),
156 LANGUAGE_DATA("no", norwegianQuotes),
157 LANGUAGE_DATA("ro", romanianQuotes),
158 LANGUAGE_DATA("ru", russianQuotes)
161 const LanguageData* const languageDataEnd = languageData + ARRAY_SIZE(languageData);
163 #define defaultLanguageQuotesSource simpleQuotes
164 #define defaultLanguageQuotesCount ARRAY_SIZE(defaultLanguageQuotesSource)
166 static QuotesData* defaultLanguageQuotesValue = 0;
167 static const QuotesData* defaultLanguageQuotes()
169 if (!defaultLanguageQuotesValue) {
170 defaultLanguageQuotesValue = QuotesData::create(defaultLanguageQuotesCount);
171 if (!defaultLanguageQuotesValue)
173 String* data = defaultLanguageQuotesValue->data();
174 for (size_t i = 0; i < defaultLanguageQuotesCount; ++i)
175 data[i] = defaultLanguageQuotesSource[i];
177 return defaultLanguageQuotesValue;
179 #undef defaultLanguageQuotesSource
180 #undef defaultLanguageQuotesCount
182 typedef HashMap<RefPtr<AtomicStringImpl>, QuotesData* > QuotesMap;
184 static QuotesMap& quotesMap()
186 DEFINE_STATIC_LOCAL(QuotesMap, staticQuotesMap, ());
187 return staticQuotesMap;
190 static const QuotesData* quotesForLanguage(AtomicStringImpl* language)
192 QuotesData* returnValue;
193 AtomicString lower(language->lower());
194 returnValue = quotesMap().get(lower.impl());
197 CString s(static_cast<const String&>(lower).ascii());
198 LanguageData request = { s.buffer()->data(), 0, 0 };
199 const LanguageData* lowerBound = std::lower_bound<const LanguageData*, const LanguageData>(languageData, languageDataEnd, request);
200 if (lowerBound == languageDataEnd)
201 return defaultLanguageQuotes();
202 if (strncmp(lowerBound->name, request.name, strlen(lowerBound->name)))
203 return defaultLanguageQuotes();
204 returnValue = QuotesData::create(lowerBound->arraySize);
206 return defaultLanguageQuotes();
207 String* data = returnValue->data();
208 for (int i = 0; i < lowerBound->arraySize; ++i)
209 data[i] = lowerBound->array[i];
210 quotesMap().set(lower.impl(), returnValue);
215 static const QuotesData* defaultQuotes(const RenderObject* object)
217 DEFINE_STATIC_LOCAL(String, langString, ("lang"));
218 Node* node = object->generatingNode();
221 element = object->document()->body();
223 element = object->document()->documentElement();
224 } else if (!node->isElementNode()) {
225 element = node->parentElement();
227 return defaultLanguageQuotes();
229 element = toElement(node);
230 const AtomicString* language;
231 while ((language = &element->getAttribute(langString)) && language->isNull()) {
232 element = element->parentElement();
234 return defaultLanguageQuotes();
236 return quotesForLanguage(language->impl());
239 PassRefPtr<StringImpl> RenderQuote::originalText() const
243 ASSERT(m_depth != UNKNOWN_DEPTH);
244 const QuotesData* quotes = style()->quotes();
246 quotes = defaultQuotes(this);
248 return emptyAtom.impl();
249 int index = m_depth * 2;
253 return String("").impl();
263 ASSERT_NOT_REACHED();
264 return emptyAtom.impl();
266 if (index >= quotes->length)
267 index = (quotes->length-2) | (index & 1);
269 return emptyAtom.impl();
270 return quotes->data()[index].impl();
273 void RenderQuote::computePreferredLogicalWidths(float lead)
275 setTextInternal(originalText());
276 RenderText::computePreferredLogicalWidths(lead);
279 void RenderQuote::rendererSubtreeAttached(RenderObject* renderer)
281 if (renderer->documentBeingDestroyed())
283 for (RenderObject* descendant = renderer; descendant; descendant = descendant->nextInPreOrder(renderer))
284 if (descendant->isQuote()) {
285 toRenderQuote(descendant)->placeQuote();
290 void RenderQuote::rendererRemovedFromTree(RenderObject* subtreeRoot)
292 if (subtreeRoot->documentBeingDestroyed())
294 for (RenderObject* descendant = subtreeRoot; descendant; descendant = descendant->nextInPreOrder(subtreeRoot))
295 if (descendant->isQuote()) {
296 RenderQuote* removedQuote = toRenderQuote(descendant);
297 RenderQuote* lastQuoteBefore = removedQuote->m_previous;
298 removedQuote->m_previous = 0;
299 int depth = removedQuote->m_depth;
300 for (descendant = descendant->nextInPreOrder(subtreeRoot); descendant; descendant = descendant->nextInPreOrder(subtreeRoot))
301 if (descendant->isQuote())
302 removedQuote = toRenderQuote(descendant);
303 RenderQuote* quoteAfter = removedQuote->m_next;
304 removedQuote->m_next = 0;
306 lastQuoteBefore->m_next = quoteAfter;
308 quoteAfter->m_previous = lastQuoteBefore;
310 if (depth == quoteAfter->m_depth)
312 quoteAfter->m_depth = depth;
313 quoteAfter->setNeedsLayoutAndPrefWidthsRecalc();
314 adjustDepth(depth, quoteAfter->m_type);
315 quoteAfter = quoteAfter->m_next;
316 } while (quoteAfter);
322 void RenderQuote::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
324 const QuotesData* newQuotes = style()->quotes();
325 const QuotesData* oldQuotes = oldStyle ? oldStyle->quotes() : 0;
326 if (!QuotesData::equal(newQuotes, oldQuotes))
327 setNeedsLayoutAndPrefWidthsRecalc();
328 RenderText::styleDidChange(diff, oldStyle);
331 } // namespace WebCore