Fix the issue that Web Audio test case fails on PR3.
[framework/web/webkit-efl.git] / Source / WebCore / rendering / RenderQuote.cpp
1 /**
2  * Copyright (C) 2011 Nokia 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
21 #include "config.h"
22 #include "RenderQuote.h"
23
24 #include "Document.h"
25 #include "Element.h"
26 #include "HTMLElement.h"
27 #include "QuotesData.h"
28 #include "RenderStyle.h"
29 #include <algorithm>
30 #include <wtf/text/AtomicString.h>
31 #include <wtf/text/CString.h>
32
33 #define UNKNOWN_DEPTH -1
34
35 namespace WebCore {
36 static inline void adjustDepth(int &depth, QuoteType type)
37 {
38     switch (type) {
39     case OPEN_QUOTE:
40     case NO_OPEN_QUOTE:
41         ++depth;
42         break;
43     case CLOSE_QUOTE:
44     case NO_CLOSE_QUOTE:
45         if (depth)
46             --depth;
47         break;
48     default:
49         ASSERT_NOT_REACHED();
50     }
51 }
52
53 RenderQuote::RenderQuote(Document* node, QuoteType quote)
54     : RenderText(node, StringImpl::empty())
55     , m_type(quote)
56     , m_depth(UNKNOWN_DEPTH)
57     , m_next(0)
58     , m_previous(0)
59 {
60     view()->addRenderQuote();
61 }
62
63 RenderQuote::~RenderQuote()
64 {
65 }
66
67 void RenderQuote::willBeDestroyed()
68 {
69     if (view())
70         view()->removeRenderQuote();
71     RenderText::willBeDestroyed();
72 }
73
74 const char* RenderQuote::renderName() const
75 {
76     return "RenderQuote";
77 }
78
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
88 // need to either.
89 void RenderQuote::placeQuote()
90 {
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())
96             continue;
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.
100             tail = head;
101             while (tail->m_next)
102                  tail = tail->m_next;
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
107         }
108         head->m_previous->m_next = head;
109         ASSERT(head->m_previous->m_depth != UNKNOWN_DEPTH);
110         break;
111     }
112     int newDepth;
113     if (!head->m_previous) {
114         newDepth = 0;
115         goto skipNewDepthCalc;
116     }
117     newDepth = head->m_previous->m_depth;
118     do {
119         adjustDepth(newDepth, head->m_previous->m_type);
120 skipNewDepthCalc:
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.
123                 break;
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;
127             continue;
128         }
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();
133         head = head->m_next;
134         if (head == tail) // We are at the splicing point
135             tail = 0; // Mark the possible depth discontinuity fixed.
136     } while (head);
137 }
138
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)
142
143 static const UChar* simpleQuotes[] = {U("\""), U("\""), U("'"), U("'")};
144
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") };
149 #undef U
150
151 struct LanguageData {
152     const char *name;
153     const UChar* const* const array;
154     const int arraySize;
155     bool operator<(const LanguageData& compareTo) const
156     {
157         return strcmp(name, compareTo.name);
158     }
159 };
160
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)
167 };
168 #undef LANGUAGE_DATA
169 const LanguageData* const languageDataEnd = languageData + ARRAY_SIZE(languageData);
170
171 #define defaultLanguageQuotesSource simpleQuotes
172 #define defaultLanguageQuotesCount ARRAY_SIZE(defaultLanguageQuotesSource)
173
174 static QuotesData* defaultLanguageQuotesValue = 0;
175 static const QuotesData* defaultLanguageQuotes()
176 {
177     if (!defaultLanguageQuotesValue) {
178         defaultLanguageQuotesValue = QuotesData::create(defaultLanguageQuotesCount);
179         if (!defaultLanguageQuotesValue)
180             return 0;
181         String* data = defaultLanguageQuotesValue->data();
182         for (size_t i = 0; i < defaultLanguageQuotesCount; ++i)
183             data[i] = defaultLanguageQuotesSource[i];
184     }
185     return defaultLanguageQuotesValue;
186 }
187 #undef defaultLanguageQuotesSource
188 #undef defaultLanguageQuotesCount
189
190 typedef HashMap<RefPtr<AtomicStringImpl>, QuotesData* > QuotesMap;
191
192 static QuotesMap& quotesMap()
193 {
194     DEFINE_STATIC_LOCAL(QuotesMap, staticQuotesMap, ());
195     return staticQuotesMap;
196 }
197
198 static const QuotesData* quotesForLanguage(AtomicStringImpl* language)
199 {
200     QuotesData* returnValue;
201     AtomicString lower(language->lower());
202     returnValue = quotesMap().get(lower.impl());
203     if (returnValue)
204         return returnValue;
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);
213     if (!returnValue)
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);
219     return returnValue;
220 }
221 #undef ARRAY_SIZE
222
223 static const QuotesData* defaultQuotes(const RenderObject* object)
224 {
225     DEFINE_STATIC_LOCAL(String, langString, ("lang"));
226     Node* node =  object->generatingNode();
227     Element* element;
228     if (!node) {
229         element = object->document()->body();
230         if (!element)
231             element = object->document()->documentElement();
232     } else if (!node->isElementNode()) {
233         element = node->parentElement();
234         if (!element)
235             return defaultLanguageQuotes();
236     } else
237       element = toElement(node);
238     const AtomicString* language;
239     while ((language = &element->getAttribute(langString)) && language->isNull()) {
240         element = element->parentElement();
241         if (!element)
242             return defaultLanguageQuotes();
243     }
244     return quotesForLanguage(language->impl());
245 }
246
247 PassRefPtr<StringImpl> RenderQuote::originalText() const
248 {
249     if (!parent())
250         return 0;
251     ASSERT(m_depth != UNKNOWN_DEPTH);
252     const QuotesData* quotes = style()->quotes();
253     if (!quotes)
254         quotes = defaultQuotes(this);
255     if (!quotes->length)
256         return emptyAtom.impl();
257     int index = m_depth * 2;
258     switch (m_type) {
259     case NO_OPEN_QUOTE:
260     case NO_CLOSE_QUOTE:
261         return emptyString().impl();
262     case CLOSE_QUOTE:
263         if (index)
264             --index;
265         else
266             ++index;
267         break;
268     case OPEN_QUOTE:
269         break;
270     default:
271         ASSERT_NOT_REACHED();
272         return emptyAtom.impl();
273     }
274     if (index >= quotes->length)
275         index = (quotes->length-2) | (index & 1);
276     if (index < 0)
277         return emptyAtom.impl();
278     return quotes->data()[index].impl();
279 }
280
281 void RenderQuote::computePreferredLogicalWidths(float lead)
282 {
283     setTextInternal(originalText());
284     RenderText::computePreferredLogicalWidths(lead);
285 }
286
287 void RenderQuote::rendererSubtreeAttached(RenderObject* renderer)
288 {
289     ASSERT(renderer->view());
290     if (!renderer->view()->hasRenderQuotes())
291         return;
292     for (RenderObject* descendant = renderer; descendant; descendant = descendant->nextInPreOrder(renderer))
293         if (descendant->isQuote()) {
294             toRenderQuote(descendant)->placeQuote();
295             break;
296         }
297 }
298
299 void RenderQuote::rendererRemovedFromTree(RenderObject* renderer)
300 {
301     ASSERT(renderer->view());
302     if (!renderer->view()->hasRenderQuotes())
303         return;
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;
315             if (lastQuoteBefore)
316                 lastQuoteBefore->m_next = quoteAfter;
317             if (quoteAfter) {
318                 quoteAfter->m_previous = lastQuoteBefore;
319                 do {
320                     if (depth == quoteAfter->m_depth)
321                         break;
322                     quoteAfter->m_depth = depth;
323                     quoteAfter->setNeedsLayoutAndPrefWidthsRecalc();
324                     adjustDepth(depth, quoteAfter->m_type);
325                     quoteAfter = quoteAfter->m_next;
326                 } while (quoteAfter);
327             }
328             break;
329         }
330 }
331
332 void RenderQuote::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
333 {
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);
339 }
340
341 } // namespace WebCore