OLD | NEW |
| (Empty) |
1 /** | |
2 * Copyright (C) 2011 Nokia Inc. All rights reserved. | |
3 * Copyright (C) 2012 Google Inc. All rights reserved. | |
4 * | |
5 * This library is free software; you can redistribute it and/or | |
6 * modify it under the terms of the GNU Library General Public | |
7 * License as published by the Free Software Foundation; either | |
8 * version 2 of the License, or (at your option) any later version. | |
9 * | |
10 * This library is distributed in the hope that it will be useful, | |
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 * Library General Public License for more details. | |
14 * | |
15 * You should have received a copy of the GNU Library General Public License | |
16 * along with this library; see the file COPYING.LIB. If not, write to | |
17 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | |
18 * Boston, MA 02110-1301, USA. | |
19 * | |
20 */ | |
21 | |
22 #include "config.h" | |
23 #include "core/rendering/RenderQuote.h" | |
24 | |
25 #include "core/rendering/RenderTextFragment.h" | |
26 #include "core/rendering/RenderView.h" | |
27 #include "wtf/StdLibExtras.h" | |
28 #include "wtf/text/AtomicString.h" | |
29 | |
30 #include <algorithm> | |
31 | |
32 namespace blink { | |
33 | |
34 RenderQuote::RenderQuote(Document* node, QuoteType quote) | |
35 : RenderInline(0) | |
36 , m_type(quote) | |
37 , m_depth(0) | |
38 , m_next(nullptr) | |
39 , m_previous(nullptr) | |
40 , m_attached(false) | |
41 { | |
42 setDocumentForAnonymous(node); | |
43 } | |
44 | |
45 RenderQuote::~RenderQuote() | |
46 { | |
47 ASSERT(!m_attached); | |
48 ASSERT(!m_next && !m_previous); | |
49 } | |
50 | |
51 void RenderQuote::willBeDestroyed() | |
52 { | |
53 detachQuote(); | |
54 RenderInline::willBeDestroyed(); | |
55 } | |
56 | |
57 void RenderQuote::willBeRemovedFromTree() | |
58 { | |
59 RenderInline::willBeRemovedFromTree(); | |
60 detachQuote(); | |
61 } | |
62 | |
63 void RenderQuote::styleDidChange(StyleDifference diff, const LayoutStyle* oldSty
le) | |
64 { | |
65 RenderInline::styleDidChange(diff, oldStyle); | |
66 updateText(); | |
67 } | |
68 | |
69 struct Language { | |
70 const char* lang; | |
71 UChar open1; | |
72 UChar close1; | |
73 UChar open2; | |
74 UChar close2; | |
75 QuotesData* data; | |
76 | |
77 bool operator<(const Language& b) const { return strcmp(lang, b.lang) < 0; } | |
78 }; | |
79 | |
80 // Table of quotes from http://www.whatwg.org/specs/web-apps/current-work/multip
age/rendering.html#quote | |
81 Language languages[] = { | |
82 { "af", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
83 { "agq", 0x201e, 0x201d, 0x201a, 0x2019, 0 }, | |
84 { "ak", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
85 { "am", 0x00ab, 0x00bb, 0x2039, 0x203a, 0 }, | |
86 { "ar", 0x201d, 0x201c, 0x2019, 0x2018, 0 }, | |
87 { "asa", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
88 { "az-cyrl", 0x00ab, 0x00bb, 0x2039, 0x203a, 0 }, | |
89 { "bas", 0x00ab, 0x00bb, 0x201e, 0x201c, 0 }, | |
90 { "bem", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
91 { "bez", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
92 { "bg", 0x201e, 0x201c, 0x201a, 0x2018, 0 }, | |
93 { "bm", 0x00ab, 0x00bb, 0x201c, 0x201d, 0 }, | |
94 { "bn", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
95 { "br", 0x00ab, 0x00bb, 0x2039, 0x203a, 0 }, | |
96 { "brx", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
97 { "bs-cyrl" , 0x201e, 0x201c, 0x201a, 0x2018, 0 }, | |
98 { "ca", 0x201c, 0x201d, 0x00ab, 0x00bb, 0 }, | |
99 { "cgg", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
100 { "chr", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
101 { "cs", 0x201e, 0x201c, 0x201a, 0x2018, 0 }, | |
102 { "da", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
103 { "dav", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
104 { "de", 0x201e, 0x201c, 0x201a, 0x2018, 0 }, | |
105 { "de-ch", 0x00ab, 0x00bb, 0x2039, 0x203a, 0 }, | |
106 { "dje", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
107 { "dua", 0x00ab, 0x00bb, 0x2018, 0x2019, 0 }, | |
108 { "dyo", 0x00ab, 0x00bb, 0x201c, 0x201d, 0 }, | |
109 { "dz", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
110 { "ebu", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
111 { "ee", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
112 { "el", 0x00ab, 0x00bb, 0x201c, 0x201d, 0 }, | |
113 { "en", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
114 { "en-gb", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
115 { "es", 0x201c, 0x201d, 0x00ab, 0x00bb, 0 }, | |
116 { "et", 0x201e, 0x201c, 0x201a, 0x2018, 0 }, | |
117 { "eu", 0x201c, 0x201d, 0x00ab, 0x00bb, 0 }, | |
118 { "ewo", 0x00ab, 0x00bb, 0x201c, 0x201d, 0 }, | |
119 { "fa", 0x00ab, 0x00bb, 0x2039, 0x203a, 0 }, | |
120 { "ff", 0x201e, 0x201d, 0x201a, 0x2019, 0 }, | |
121 { "fi", 0x201d, 0x201d, 0x2019, 0x2019, 0 }, | |
122 { "fr", 0x00ab, 0x00bb, 0x00ab, 0x00bb, 0 }, | |
123 { "fr-ca", 0x00ab, 0x00bb, 0x2039, 0x203a, 0 }, | |
124 { "fr-ch", 0x00ab, 0x00bb, 0x2039, 0x203a, 0 }, | |
125 { "gsw", 0x00ab, 0x00bb, 0x2039, 0x203a, 0 }, | |
126 { "gu", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
127 { "guz", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
128 { "ha", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
129 { "he", 0x0022, 0x0022, 0x0027, 0x0027, 0 }, | |
130 { "hi", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
131 { "hr", 0x201e, 0x201c, 0x201a, 0x2018, 0 }, | |
132 { "hu", 0x201e, 0x201d, 0x00bb, 0x00ab, 0 }, | |
133 { "id", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
134 { "ig", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
135 { "it", 0x00ab, 0x00bb, 0x201c, 0x201d, 0 }, | |
136 { "ja", 0x300c, 0x300d, 0x300e, 0x300f, 0 }, | |
137 { "jgo", 0x00ab, 0x00bb, 0x2039, 0x203a, 0 }, | |
138 { "jmc", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
139 { "kab", 0x00ab, 0x00bb, 0x201c, 0x201d, 0 }, | |
140 { "kam", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
141 { "kde", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
142 { "kea", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
143 { "khq", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
144 { "ki", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
145 { "kkj", 0x00ab, 0x00bb, 0x2039, 0x203a, 0 }, | |
146 { "kln", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
147 { "km", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
148 { "kn", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
149 { "ko", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
150 { "ksb", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
151 { "ksf", 0x00ab, 0x00bb, 0x2018, 0x2019, 0 }, | |
152 { "lag", 0x201d, 0x201d, 0x2019, 0x2019, 0 }, | |
153 { "lg", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
154 { "ln", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
155 { "lo", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
156 { "lt", 0x201e, 0x201c, 0x201e, 0x201c, 0 }, | |
157 { "lu", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
158 { "luo", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
159 { "luy", 0x201e, 0x201c, 0x201a, 0x2018, 0 }, | |
160 { "lv", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
161 { "mas", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
162 { "mer", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
163 { "mfe", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
164 { "mg", 0x00ab, 0x00bb, 0x201c, 0x201d, 0 }, | |
165 { "mgo", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
166 { "mk", 0x201e, 0x201c, 0x201a, 0x2018, 0 }, | |
167 { "ml", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
168 { "mr", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
169 { "ms", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
170 { "mua", 0x00ab, 0x00bb, 0x201c, 0x201d, 0 }, | |
171 { "my", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
172 { "naq", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
173 { "nb", 0x00ab, 0x00bb, 0x2018, 0x2019, 0 }, | |
174 { "nd", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
175 { "nl", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
176 { "nmg", 0x201e, 0x201d, 0x00ab, 0x00bb, 0 }, | |
177 { "nn", 0x00ab, 0x00bb, 0x2018, 0x2019, 0 }, | |
178 { "nnh", 0x00ab, 0x00bb, 0x201c, 0x201d, 0 }, | |
179 { "nus", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
180 { "nyn", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
181 { "pl", 0x201e, 0x201d, 0x00ab, 0x00bb, 0 }, | |
182 { "pt", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
183 { "pt-pt", 0x00ab, 0x00bb, 0x201c, 0x201d, 0 }, | |
184 { "rn", 0x201d, 0x201d, 0x2019, 0x2019, 0 }, | |
185 { "ro", 0x201e, 0x201d, 0x00ab, 0x00bb, 0 }, | |
186 { "rof", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
187 { "ru", 0x00ab, 0x00bb, 0x201e, 0x201c, 0 }, | |
188 { "rw", 0x00ab, 0x00bb, 0x2018, 0x2019, 0 }, | |
189 { "rwk", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
190 { "saq", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
191 { "sbp", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
192 { "seh", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
193 { "ses", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
194 { "sg", 0x00ab, 0x00bb, 0x201c, 0x201d, 0 }, | |
195 { "shi", 0x00ab, 0x00bb, 0x201e, 0x201d, 0 }, | |
196 { "shi-tfng", 0x00ab, 0x00bb, 0x201e, 0x201d, 0 }, | |
197 { "si", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
198 { "sk", 0x201e, 0x201c, 0x201a, 0x2018, 0 }, | |
199 { "sl", 0x201e, 0x201c, 0x201a, 0x2018, 0 }, | |
200 { "sn", 0x201d, 0x201d, 0x2019, 0x2019, 0 }, | |
201 { "so", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
202 { "sq", 0x201e, 0x201c, 0x201a, 0x2018, 0 }, | |
203 { "sr", 0x201e, 0x201c, 0x201a, 0x2018, 0 }, | |
204 { "sr-latn", 0x201e, 0x201c, 0x201a, 0x2018, 0 }, | |
205 { "sv", 0x201d, 0x201d, 0x2019, 0x2019, 0 }, | |
206 { "sw", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
207 { "swc", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
208 { "ta", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
209 { "te", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
210 { "teo", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
211 { "th", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
212 { "ti-er", 0x2018, 0x2019, 0x201c, 0x201d, 0 }, | |
213 { "to", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
214 { "tr", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
215 { "twq", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
216 { "tzm", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
217 { "uk", 0x00ab, 0x00bb, 0x201e, 0x201c, 0 }, | |
218 { "ur", 0x201d, 0x201c, 0x2019, 0x2018, 0 }, | |
219 { "vai", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
220 { "vai-latn", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
221 { "vi", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
222 { "vun", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
223 { "xh", 0x2018, 0x2019, 0x201c, 0x201d, 0 }, | |
224 { "xog", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
225 { "yav", 0x00ab, 0x00bb, 0x00ab, 0x00bb, 0 }, | |
226 { "yo", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
227 { "zh", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
228 { "zh-hant", 0x300c, 0x300d, 0x300e, 0x300f, 0 }, | |
229 { "zu", 0x201c, 0x201d, 0x2018, 0x2019, 0 }, | |
230 }; | |
231 | |
232 const QuotesData* quotesDataForLanguage(const AtomicString& lang) | |
233 { | |
234 if (lang.isNull()) | |
235 return 0; | |
236 | |
237 // This could be just a hash table, but doing that adds 200k to RenderQuote.
o | |
238 Language* languagesEnd = languages + WTF_ARRAY_LENGTH(languages); | |
239 CString lowercaseLang = lang.lower().utf8(); | |
240 Language key = { lowercaseLang.data(), 0, 0, 0, 0, 0 }; | |
241 Language* match = std::lower_bound(languages, languagesEnd, key); | |
242 if (match == languagesEnd || strcmp(match->lang, key.lang)) | |
243 return 0; | |
244 | |
245 if (!match->data) | |
246 match->data = QuotesData::create(match->open1, match->close1, match->ope
n2, match->close2).leakRef(); | |
247 | |
248 return match->data; | |
249 } | |
250 | |
251 static const QuotesData* basicQuotesData() | |
252 { | |
253 // FIXME: The default quotes should be the fancy quotes for "en". | |
254 DEFINE_STATIC_REF(QuotesData, staticBasicQuotes, (QuotesData::create('"', '"
', '\'', '\''))); | |
255 return staticBasicQuotes; | |
256 } | |
257 | |
258 void RenderQuote::updateText() | |
259 { | |
260 String text = computeText(); | |
261 if (m_text == text) | |
262 return; | |
263 | |
264 m_text = text; | |
265 | |
266 RenderTextFragment* fragment = findFragmentChild(); | |
267 if (fragment) { | |
268 fragment->setStyle(style()); | |
269 fragment->setContentString(m_text.impl()); | |
270 } else { | |
271 fragment = new RenderTextFragment(&document(), m_text.impl()); | |
272 fragment->setStyle(style()); | |
273 addChild(fragment); | |
274 } | |
275 } | |
276 | |
277 RenderTextFragment* RenderQuote::findFragmentChild() const | |
278 { | |
279 // We walk from the end of the child list because, if we've had a first-lett
er | |
280 // renderer inserted then the remaining text will be at the end. | |
281 while (LayoutObject* child = lastChild()) { | |
282 if (child->isText() && toRenderText(child)->isTextFragment()) | |
283 return toRenderTextFragment(child); | |
284 } | |
285 | |
286 return nullptr; | |
287 } | |
288 | |
289 String RenderQuote::computeText() const | |
290 { | |
291 switch (m_type) { | |
292 case NO_OPEN_QUOTE: | |
293 case NO_CLOSE_QUOTE: | |
294 return emptyString(); | |
295 case CLOSE_QUOTE: | |
296 return quotesData()->getCloseQuote(m_depth - 1).impl(); | |
297 case OPEN_QUOTE: | |
298 return quotesData()->getOpenQuote(m_depth).impl(); | |
299 } | |
300 ASSERT_NOT_REACHED(); | |
301 return emptyString(); | |
302 } | |
303 | |
304 const QuotesData* RenderQuote::quotesData() const | |
305 { | |
306 if (const QuotesData* customQuotes = style()->quotes()) | |
307 return customQuotes; | |
308 | |
309 if (const QuotesData* quotes = quotesDataForLanguage(style()->locale())) | |
310 return quotes; | |
311 | |
312 return basicQuotesData(); | |
313 } | |
314 | |
315 void RenderQuote::attachQuote() | |
316 { | |
317 ASSERT(view()); | |
318 ASSERT(!m_attached); | |
319 ASSERT(!m_next && !m_previous); | |
320 ASSERT(isRooted()); | |
321 | |
322 if (!view()->renderQuoteHead()) { | |
323 view()->setRenderQuoteHead(this); | |
324 m_attached = true; | |
325 return; | |
326 } | |
327 | |
328 for (LayoutObject* predecessor = previousInPreOrder(); predecessor; predeces
sor = predecessor->previousInPreOrder()) { | |
329 // Skip unattached predecessors to avoid having stale m_previous pointer
s | |
330 // if the previous node is never attached and is then destroyed. | |
331 if (!predecessor->isQuote() || !toRenderQuote(predecessor)->isAttached()
) | |
332 continue; | |
333 m_previous = toRenderQuote(predecessor); | |
334 m_next = m_previous->m_next; | |
335 m_previous->m_next = this; | |
336 if (m_next) | |
337 m_next->m_previous = this; | |
338 break; | |
339 } | |
340 | |
341 if (!m_previous) { | |
342 m_next = view()->renderQuoteHead(); | |
343 view()->setRenderQuoteHead(this); | |
344 if (m_next) | |
345 m_next->m_previous = this; | |
346 } | |
347 m_attached = true; | |
348 | |
349 for (RenderQuote* quote = this; quote; quote = quote->m_next) | |
350 quote->updateDepth(); | |
351 | |
352 ASSERT(!m_next || m_next->m_attached); | |
353 ASSERT(!m_next || m_next->m_previous == this); | |
354 ASSERT(!m_previous || m_previous->m_attached); | |
355 ASSERT(!m_previous || m_previous->m_next == this); | |
356 } | |
357 | |
358 void RenderQuote::detachQuote() | |
359 { | |
360 ASSERT(!m_next || m_next->m_attached); | |
361 ASSERT(!m_previous || m_previous->m_attached); | |
362 if (!m_attached) | |
363 return; | |
364 | |
365 // Reset our attached status at this point because it's possible for | |
366 // updateDepth() to call into attachQuote(). Attach quote walks the render | |
367 // tree looking for quotes that are attached and does work on them. | |
368 m_attached = false; | |
369 | |
370 if (m_previous) | |
371 m_previous->m_next = m_next; | |
372 else if (view()) | |
373 view()->setRenderQuoteHead(m_next); | |
374 if (m_next) | |
375 m_next->m_previous = m_previous; | |
376 if (!documentBeingDestroyed()) { | |
377 for (RenderQuote* quote = m_next; quote; quote = quote->m_next) | |
378 quote->updateDepth(); | |
379 } | |
380 m_next = nullptr; | |
381 m_previous = nullptr; | |
382 m_depth = 0; | |
383 } | |
384 | |
385 void RenderQuote::updateDepth() | |
386 { | |
387 ASSERT(m_attached); | |
388 int oldDepth = m_depth; | |
389 m_depth = 0; | |
390 if (m_previous) { | |
391 m_depth = m_previous->m_depth; | |
392 switch (m_previous->m_type) { | |
393 case OPEN_QUOTE: | |
394 case NO_OPEN_QUOTE: | |
395 m_depth++; | |
396 break; | |
397 case CLOSE_QUOTE: | |
398 case NO_CLOSE_QUOTE: | |
399 if (m_depth) | |
400 m_depth--; | |
401 break; | |
402 } | |
403 } | |
404 if (oldDepth != m_depth) | |
405 updateText(); | |
406 } | |
407 | |
408 } // namespace blink | |
OLD | NEW |