Make QuotesData use a Vector of pairs
[WebKit-https.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 <wtf/text/AtomicString.h>
30
31 #define UNKNOWN_DEPTH -1
32
33 namespace WebCore {
34 static inline void adjustDepth(int &depth, QuoteType type)
35 {
36     switch (type) {
37     case OPEN_QUOTE:
38     case NO_OPEN_QUOTE:
39         ++depth;
40         break;
41     case CLOSE_QUOTE:
42     case NO_CLOSE_QUOTE:
43         if (depth)
44             --depth;
45         break;
46     default:
47         ASSERT_NOT_REACHED();
48     }
49 }
50
51 RenderQuote::RenderQuote(Document* node, QuoteType quote)
52     : RenderText(node, StringImpl::empty())
53     , m_type(quote)
54     , m_depth(UNKNOWN_DEPTH)
55     , m_next(0)
56     , m_previous(0)
57 {
58     view()->addRenderQuote();
59 }
60
61 RenderQuote::~RenderQuote()
62 {
63 }
64
65 void RenderQuote::willBeDestroyed()
66 {
67     if (view())
68         view()->removeRenderQuote();
69     RenderText::willBeDestroyed();
70 }
71
72 const char* RenderQuote::renderName() const
73 {
74     return "RenderQuote";
75 }
76
77 // This function places a list of quote renderers starting at "this" in the list of quote renderers already
78 // in the document's renderer tree.
79 // The assumptions are made (for performance):
80 // 1. The list of quotes already in the renderers tree of the document is already in a consistent state
81 // (All quote renderers are linked and have the correct depth set)
82 // 2. The quote renderers of the inserted list are in a tree of renderers of their own which has been just
83 // inserted in the main renderer tree with its root as child of some renderer.
84 // 3. The quote renderers in the inserted list have depths consistent with their position in the list relative
85 // to "this", thus if "this" does not need to change its depth upon insertion, the other renderers in the list don't
86 // need to either.
87 void RenderQuote::placeQuote()
88 {
89     RenderQuote* head = this;
90     ASSERT(!head->m_previous);
91     RenderQuote* tail = 0;
92     for (RenderObject* predecessor = head->previousInPreOrder(); predecessor; predecessor = predecessor->previousInPreOrder()) {
93         if (!predecessor->isQuote())
94             continue;
95         head->m_previous = toRenderQuote(predecessor);
96         if (head->m_previous->m_next) {
97             // We need to splice the list of quotes headed by head into the document's list of quotes.
98             tail = head;
99             while (tail->m_next)
100                  tail = tail->m_next;
101             tail->m_next = head->m_previous->m_next;
102             ASSERT(tail->m_next->m_previous == head->m_previous);
103             tail->m_next->m_previous =  tail;
104             tail = tail->m_next; // This marks the splicing point here there may be a depth discontinuity
105         }
106         head->m_previous->m_next = head;
107         ASSERT(head->m_previous->m_depth != UNKNOWN_DEPTH);
108         break;
109     }
110     int newDepth;
111     if (!head->m_previous) {
112         newDepth = 0;
113         goto skipNewDepthCalc;
114     }
115     newDepth = head->m_previous->m_depth;
116     do {
117         adjustDepth(newDepth, head->m_previous->m_type);
118 skipNewDepthCalc:
119         if (head->m_depth == newDepth) { // All remaining depth should be correct except if splicing was done.
120             if (!tail) // We've done the post splicing section already or there was no splicing.
121                 break;
122             head = tail; // Continue after the splicing point
123             tail = 0; // Mark the possible splicing point discontinuity fixed.
124             newDepth = head->m_previous->m_depth;
125             continue;
126         }
127         head->m_depth = newDepth;
128         // FIXME: If the width and height of the quotation characters does not change we may only need to
129         // Invalidate the renderer's area not a relayout.
130         head->setNeedsLayoutAndPrefWidthsRecalc();
131         head = head->m_next;
132         if (head == tail) // We are at the splicing point
133             tail = 0; // Mark the possible depth discontinuity fixed.
134     } while (head);
135 }
136
137 #define U(x) ((const UChar*)L##x)
138
139 typedef HashMap<AtomicString, const QuotesData*, CaseFoldingHash> QuotesMap;
140
141 static const QuotesMap& quotesDataLanguageMap()
142 {
143     DEFINE_STATIC_LOCAL(QuotesMap, staticQuotesMap, ());
144     if (staticQuotesMap.size())
145         return staticQuotesMap;
146     // FIXME: Expand this table to include all the languages in https://bug-3234-attachments.webkit.org/attachment.cgi?id=2135
147     staticQuotesMap.set("en", QuotesData::create(U("\x201C"), U("\x201D"), U("\x2018"), U("\x2019")).leakRef());
148     staticQuotesMap.set("no", QuotesData::create(U("\x00AB"), U("\x00BB"), U("\x2039"), U("\x203A")).leakRef());
149     staticQuotesMap.set("ro", QuotesData::create(U("\x201E"), U("\x201D")).leakRef());
150     staticQuotesMap.set("ru", QuotesData::create(U("\x00AB"), U("\x00BB"), U("\x201E"), U("\x201C")).leakRef());
151     return staticQuotesMap;
152 }
153
154 static const QuotesData* basicQuotesData()
155 {
156     static const QuotesData* staticBasicQuotes = QuotesData::create(U("\""), U("\""), U("'"), U("'")).leakRef();
157     return staticBasicQuotes;
158 }
159
160 static const QuotesData* defaultQuotes(const RenderObject* object)
161 {
162     DEFINE_STATIC_LOCAL(String, langString, ("lang"));
163     Node* node =  object->generatingNode();
164     Element* element;
165     if (!node) {
166         element = object->document()->body();
167         if (!element)
168             element = object->document()->documentElement();
169     } else if (!node->isElementNode()) {
170         element = node->parentElement();
171         if (!element)
172             return basicQuotesData();
173     } else
174       element = toElement(node);
175     AtomicString language = element->getAttribute(langString);
176     while (language.isNull()) {
177         element = element->parentElement();
178         if (!element)
179             return basicQuotesData();
180         language = element->getAttribute(langString);
181     }
182     const QuotesData* quotesData = quotesDataLanguageMap().get(language);
183     if (!quotesData)
184         return basicQuotesData();
185     return quotesData;
186 }
187
188 PassRefPtr<StringImpl> RenderQuote::originalText() const
189 {
190     if (!parent())
191         return 0;
192     ASSERT(m_depth != UNKNOWN_DEPTH);
193     const QuotesData* quotes = style()->quotes();
194     if (!quotes)
195         quotes = defaultQuotes(this);
196     switch (m_type) {
197     case NO_OPEN_QUOTE:
198     case NO_CLOSE_QUOTE:
199         return StringImpl::empty();
200     case CLOSE_QUOTE:
201         // FIXME: When m_depth is 0 we should return empty string.
202         return quotes->getCloseQuote(std::max(m_depth - 1, 0)).impl();
203     case OPEN_QUOTE:
204         return quotes->getOpenQuote(m_depth).impl();
205     default:
206         ASSERT_NOT_REACHED();
207         return StringImpl::empty();
208     }
209 }
210
211 void RenderQuote::computePreferredLogicalWidths(float lead)
212 {
213     setTextInternal(originalText());
214     RenderText::computePreferredLogicalWidths(lead);
215 }
216
217 void RenderQuote::rendererSubtreeAttached(RenderObject* renderer)
218 {
219     ASSERT(renderer->view());
220     if (!renderer->view()->hasRenderQuotes())
221         return;
222     for (RenderObject* descendant = renderer; descendant; descendant = descendant->nextInPreOrder(renderer))
223         if (descendant->isQuote()) {
224             toRenderQuote(descendant)->placeQuote();
225             break;
226         }
227 }
228
229 void RenderQuote::rendererRemovedFromTree(RenderObject* renderer)
230 {
231     ASSERT(renderer->view());
232     if (!renderer->view()->hasRenderQuotes())
233         return;
234     for (RenderObject* descendant = renderer; descendant; descendant = descendant->nextInPreOrder(renderer))
235         if (descendant->isQuote()) {
236             RenderQuote* removedQuote = toRenderQuote(descendant);
237             RenderQuote* lastQuoteBefore = removedQuote->m_previous;
238             removedQuote->m_previous = 0;
239             int depth = removedQuote->m_depth;
240             for (descendant = descendant->nextInPreOrder(renderer); descendant; descendant = descendant->nextInPreOrder(renderer))
241                 if (descendant->isQuote())
242                     removedQuote = toRenderQuote(descendant);
243             RenderQuote* quoteAfter = removedQuote->m_next;
244             removedQuote->m_next = 0;
245             if (lastQuoteBefore)
246                 lastQuoteBefore->m_next = quoteAfter;
247             if (quoteAfter) {
248                 quoteAfter->m_previous = lastQuoteBefore;
249                 do {
250                     if (depth == quoteAfter->m_depth)
251                         break;
252                     quoteAfter->m_depth = depth;
253                     quoteAfter->setNeedsLayoutAndPrefWidthsRecalc();
254                     adjustDepth(depth, quoteAfter->m_type);
255                     quoteAfter = quoteAfter->m_next;
256                 } while (quoteAfter);
257             }
258             break;
259         }
260 }
261
262 void RenderQuote::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
263 {
264     const QuotesData* newQuotes = style()->quotes();
265     const QuotesData* oldQuotes = oldStyle ? oldStyle->quotes() : 0;
266     if (!QuotesData::equals(newQuotes, oldQuotes))
267         setNeedsLayoutAndPrefWidthsRecalc();
268     RenderText::styleDidChange(diff, oldStyle);
269 }
270
271 } // namespace WebCore