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)
60 view()->addRenderQuote();
63 RenderQuote::~RenderQuote()
67 void RenderQuote::willBeDestroyed()
70 view()->removeRenderQuote();
71 RenderText::willBeDestroyed();
74 const char* RenderQuote::renderName() const
79 // This function places a list of quote renderers starting at "this" in the list of quote renderers already
80 // in the document's renderer tree.
81 // The assumptions are made (for performance):
82 // 1. The list of quotes already in the renderers tree of the document is already in a consistent state
83 // (All quote renderers are linked and have the correct depth set)
84 // 2. The quote renderers of the inserted list are in a tree of renderers of their own which has been just
85 // inserted in the main renderer tree with its root as child of some renderer.
86 // 3. The quote renderers in the inserted list have depths consistent with their position in the list relative
87 // to "this", thus if "this" does not need to change its depth upon insertion, the other renderers in the list don't
89 void RenderQuote::placeQuote()
91 RenderQuote* head = this;
92 ASSERT(!head->m_previous);
93 RenderQuote* tail = 0;
94 for (RenderObject* predecessor = head->previousInPreOrder(); predecessor; predecessor = predecessor->previousInPreOrder()) {
95 if (!predecessor->isQuote())
97 head->m_previous = toRenderQuote(predecessor);
98 if (head->m_previous->m_next) {
99 // We need to splice the list of quotes headed by head into the document's list of quotes.
103 tail->m_next = head->m_previous->m_next;
104 ASSERT(tail->m_next->m_previous == head->m_previous);
105 tail->m_next->m_previous = tail;
106 tail = tail->m_next; // This marks the splicing point here there may be a depth discontinuity
108 head->m_previous->m_next = head;
109 ASSERT(head->m_previous->m_depth != UNKNOWN_DEPTH);
113 if (!head->m_previous) {
115 goto skipNewDepthCalc;
117 newDepth = head->m_previous->m_depth;
119 adjustDepth(newDepth, head->m_previous->m_type);
121 if (head->m_depth == newDepth) { // All remaining depth should be correct except if splicing was done.
122 if (!tail) // We've done the post splicing section already or there was no splicing.
124 head = tail; // Continue after the splicing point
125 tail = 0; // Mark the possible splicing point discontinuity fixed.
126 newDepth = head->m_previous->m_depth;
129 head->m_depth = newDepth;
130 // FIXME: If the width and height of the quotation characters does not change we may only need to
131 // Invalidate the renderer's area not a relayout.
132 head->setNeedsLayoutAndPrefWidthsRecalc();
134 if (head == tail) // We are at the splicing point
135 tail = 0; // Mark the possible depth discontinuity fixed.
139 #define ARRAY_SIZE(Carray) (sizeof(Carray) / sizeof(*Carray))
140 #define LANGUAGE_DATA(name, languageSourceArray) { name, languageSourceArray, ARRAY_SIZE(languageSourceArray) }
141 #define U(x) ((const UChar*)L##x)
143 static const UChar* simpleQuotes[] = {U("\""), U("\""), U("'"), U("'")};
145 static const UChar* englishQuotes[] = {U("\x201C"), U("\x201D"), U("\x2018"), U("\x2019")};
146 static const UChar* norwegianQuotes[] = { U("\x00AB"), U("\x00BB"), U("\x2039"), U("\x203A") };
147 static const UChar* romanianQuotes[] = { U("\x201E"), U("\x201D")};
148 static const UChar* russianQuotes[] = { U("\x00AB"), U("\x00BB"), U("\x201E"), U("\x201C") };
151 struct LanguageData {
153 const UChar* const* const array;
155 bool operator<(const LanguageData& compareTo) const
157 return strcmp(name, compareTo.name);
161 // Data mast be alphabetically sorted and in all lower case for fast comparison
162 LanguageData languageData[] = {
163 LANGUAGE_DATA("en", englishQuotes),
164 LANGUAGE_DATA("no", norwegianQuotes),
165 LANGUAGE_DATA("ro", romanianQuotes),
166 LANGUAGE_DATA("ru", russianQuotes)
169 const LanguageData* const languageDataEnd = languageData + ARRAY_SIZE(languageData);
171 #define defaultLanguageQuotesSource simpleQuotes
172 #define defaultLanguageQuotesCount ARRAY_SIZE(defaultLanguageQuotesSource)
174 static QuotesData* defaultLanguageQuotesValue = 0;
175 static const QuotesData* defaultLanguageQuotes()
177 if (!defaultLanguageQuotesValue) {
178 defaultLanguageQuotesValue = QuotesData::create(defaultLanguageQuotesCount);
179 if (!defaultLanguageQuotesValue)
181 String* data = defaultLanguageQuotesValue->data();
182 for (size_t i = 0; i < defaultLanguageQuotesCount; ++i)
183 data[i] = defaultLanguageQuotesSource[i];
185 return defaultLanguageQuotesValue;
187 #undef defaultLanguageQuotesSource
188 #undef defaultLanguageQuotesCount
190 typedef HashMap<RefPtr<AtomicStringImpl>, QuotesData* > QuotesMap;
192 static QuotesMap& quotesMap()
194 DEFINE_STATIC_LOCAL(QuotesMap, staticQuotesMap, ());
195 return staticQuotesMap;
198 static const QuotesData* quotesForLanguage(AtomicStringImpl* language)
200 QuotesData* returnValue;
201 AtomicString lower(language->lower());
202 returnValue = quotesMap().get(lower.impl());
205 CString s(static_cast<const String&>(lower).ascii());
206 LanguageData request = { s.buffer()->data(), 0, 0 };
207 const LanguageData* lowerBound = std::lower_bound<const LanguageData*, const LanguageData>(languageData, languageDataEnd, request);
208 if (lowerBound == languageDataEnd)
209 return defaultLanguageQuotes();
210 if (strncmp(lowerBound->name, request.name, strlen(lowerBound->name)))
211 return defaultLanguageQuotes();
212 returnValue = QuotesData::create(lowerBound->arraySize);
214 return defaultLanguageQuotes();
215 String* data = returnValue->data();
216 for (int i = 0; i < lowerBound->arraySize; ++i)
217 data[i] = lowerBound->array[i];
218 quotesMap().set(lower.impl(), returnValue);
223 static const QuotesData* defaultQuotes(const RenderObject* object)
225 DEFINE_STATIC_LOCAL(String, langString, ("lang"));
226 Node* node = object->generatingNode();
229 element = object->document()->body();
231 element = object->document()->documentElement();
232 } else if (!node->isElementNode()) {
233 element = node->parentElement();
235 return defaultLanguageQuotes();
237 element = toElement(node);
238 const AtomicString* language;
239 while ((language = &element->getAttribute(langString)) && language->isNull()) {
240 element = element->parentElement();
242 return defaultLanguageQuotes();
244 return quotesForLanguage(language->impl());
247 PassRefPtr<StringImpl> RenderQuote::originalText() const
251 ASSERT(m_depth != UNKNOWN_DEPTH);
252 const QuotesData* quotes = style()->quotes();
254 quotes = defaultQuotes(this);
256 return emptyAtom.impl();
257 int index = m_depth * 2;
261 return emptyString().impl();
271 ASSERT_NOT_REACHED();
272 return emptyAtom.impl();
274 if (index >= quotes->length)
275 index = (quotes->length-2) | (index & 1);
277 return emptyAtom.impl();
278 return quotes->data()[index].impl();
281 void RenderQuote::computePreferredLogicalWidths(float lead)
283 setTextInternal(originalText());
284 RenderText::computePreferredLogicalWidths(lead);
287 void RenderQuote::rendererSubtreeAttached(RenderObject* renderer)
289 ASSERT(renderer->view());
290 if (!renderer->view()->hasRenderQuotes())
292 for (RenderObject* descendant = renderer; descendant; descendant = descendant->nextInPreOrder(renderer))
293 if (descendant->isQuote()) {
294 toRenderQuote(descendant)->placeQuote();
299 void RenderQuote::rendererRemovedFromTree(RenderObject* renderer)
301 ASSERT(renderer->view());
302 if (!renderer->view()->hasRenderQuotes())
304 for (RenderObject* descendant = renderer; descendant; descendant = descendant->nextInPreOrder(renderer))
305 if (descendant->isQuote()) {
306 RenderQuote* removedQuote = toRenderQuote(descendant);
307 RenderQuote* lastQuoteBefore = removedQuote->m_previous;
308 removedQuote->m_previous = 0;
309 int depth = removedQuote->m_depth;
310 for (descendant = descendant->nextInPreOrder(renderer); descendant; descendant = descendant->nextInPreOrder(renderer))
311 if (descendant->isQuote())
312 removedQuote = toRenderQuote(descendant);
313 RenderQuote* quoteAfter = removedQuote->m_next;
314 removedQuote->m_next = 0;
316 lastQuoteBefore->m_next = quoteAfter;
318 quoteAfter->m_previous = lastQuoteBefore;
320 if (depth == quoteAfter->m_depth)
322 quoteAfter->m_depth = depth;
323 quoteAfter->setNeedsLayoutAndPrefWidthsRecalc();
324 adjustDepth(depth, quoteAfter->m_type);
325 quoteAfter = quoteAfter->m_next;
326 } while (quoteAfter);
332 void RenderQuote::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
334 const QuotesData* newQuotes = style()->quotes();
335 const QuotesData* oldQuotes = oldStyle ? oldStyle->quotes() : 0;
336 if (!QuotesData::equal(newQuotes, oldQuotes))
337 setNeedsLayoutAndPrefWidthsRecalc();
338 RenderText::styleDidChange(diff, oldStyle);
341 } // namespace WebCore