2 * Copyright (C) 2011 Nokia Inc. All rights reserved.
3 * Copyright (C) 2012 Google Inc. All rights reserved.
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.
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.
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.
23 #include "core/rendering/RenderQuote.h"
25 #include "core/rendering/RenderTextFragment.h"
26 #include "core/rendering/RenderView.h"
27 #include "wtf/StdLibExtras.h"
28 #include "wtf/text/AtomicString.h"
34 RenderQuote::RenderQuote(Document* node, QuoteType quote)
42 setDocumentForAnonymous(node);
45 RenderQuote::~RenderQuote()
48 ASSERT(!m_next && !m_previous);
51 void RenderQuote::trace(Visitor* visitor)
53 visitor->trace(m_next);
54 visitor->trace(m_previous);
55 RenderInline::trace(visitor);
58 void RenderQuote::willBeDestroyed()
61 RenderInline::willBeDestroyed();
64 void RenderQuote::willBeRemovedFromTree()
66 RenderInline::willBeRemovedFromTree();
70 void RenderQuote::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
72 RenderInline::styleDidChange(diff, oldStyle);
84 bool operator<(const Language& b) const { return strcmp(lang, b.lang) < 0; }
87 // Table of quotes from http://www.whatwg.org/specs/web-apps/current-work/multipage/rendering.html#quote
88 Language languages[] = {
89 { "af", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
90 { "agq", 0x201e, 0x201d, 0x201a, 0x2019, 0 },
91 { "ak", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
92 { "am", 0x00ab, 0x00bb, 0x2039, 0x203a, 0 },
93 { "ar", 0x201d, 0x201c, 0x2019, 0x2018, 0 },
94 { "asa", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
95 { "az-cyrl", 0x00ab, 0x00bb, 0x2039, 0x203a, 0 },
96 { "bas", 0x00ab, 0x00bb, 0x201e, 0x201c, 0 },
97 { "bem", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
98 { "bez", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
99 { "bg", 0x201e, 0x201c, 0x201a, 0x2018, 0 },
100 { "bm", 0x00ab, 0x00bb, 0x201c, 0x201d, 0 },
101 { "bn", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
102 { "br", 0x00ab, 0x00bb, 0x2039, 0x203a, 0 },
103 { "brx", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
104 { "bs-cyrl" , 0x201e, 0x201c, 0x201a, 0x2018, 0 },
105 { "ca", 0x201c, 0x201d, 0x00ab, 0x00bb, 0 },
106 { "cgg", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
107 { "chr", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
108 { "cs", 0x201e, 0x201c, 0x201a, 0x2018, 0 },
109 { "da", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
110 { "dav", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
111 { "de", 0x201e, 0x201c, 0x201a, 0x2018, 0 },
112 { "de-ch", 0x00ab, 0x00bb, 0x2039, 0x203a, 0 },
113 { "dje", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
114 { "dua", 0x00ab, 0x00bb, 0x2018, 0x2019, 0 },
115 { "dyo", 0x00ab, 0x00bb, 0x201c, 0x201d, 0 },
116 { "dz", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
117 { "ebu", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
118 { "ee", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
119 { "el", 0x00ab, 0x00bb, 0x201c, 0x201d, 0 },
120 { "en", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
121 { "en-gb", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
122 { "es", 0x201c, 0x201d, 0x00ab, 0x00bb, 0 },
123 { "et", 0x201e, 0x201c, 0x201a, 0x2018, 0 },
124 { "eu", 0x201c, 0x201d, 0x00ab, 0x00bb, 0 },
125 { "ewo", 0x00ab, 0x00bb, 0x201c, 0x201d, 0 },
126 { "fa", 0x00ab, 0x00bb, 0x2039, 0x203a, 0 },
127 { "ff", 0x201e, 0x201d, 0x201a, 0x2019, 0 },
128 { "fi", 0x201d, 0x201d, 0x2019, 0x2019, 0 },
129 { "fr", 0x00ab, 0x00bb, 0x00ab, 0x00bb, 0 },
130 { "fr-ca", 0x00ab, 0x00bb, 0x2039, 0x203a, 0 },
131 { "fr-ch", 0x00ab, 0x00bb, 0x2039, 0x203a, 0 },
132 { "gsw", 0x00ab, 0x00bb, 0x2039, 0x203a, 0 },
133 { "gu", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
134 { "guz", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
135 { "ha", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
136 { "he", 0x0022, 0x0022, 0x0027, 0x0027, 0 },
137 { "hi", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
138 { "hr", 0x201e, 0x201c, 0x201a, 0x2018, 0 },
139 { "hu", 0x201e, 0x201d, 0x00bb, 0x00ab, 0 },
140 { "id", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
141 { "ig", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
142 { "it", 0x00ab, 0x00bb, 0x201c, 0x201d, 0 },
143 { "ja", 0x300c, 0x300d, 0x300e, 0x300f, 0 },
144 { "jgo", 0x00ab, 0x00bb, 0x2039, 0x203a, 0 },
145 { "jmc", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
146 { "kab", 0x00ab, 0x00bb, 0x201c, 0x201d, 0 },
147 { "kam", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
148 { "kde", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
149 { "kea", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
150 { "khq", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
151 { "ki", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
152 { "kkj", 0x00ab, 0x00bb, 0x2039, 0x203a, 0 },
153 { "kln", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
154 { "km", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
155 { "kn", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
156 { "ko", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
157 { "ksb", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
158 { "ksf", 0x00ab, 0x00bb, 0x2018, 0x2019, 0 },
159 { "lag", 0x201d, 0x201d, 0x2019, 0x2019, 0 },
160 { "lg", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
161 { "ln", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
162 { "lo", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
163 { "lt", 0x201e, 0x201c, 0x201e, 0x201c, 0 },
164 { "lu", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
165 { "luo", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
166 { "luy", 0x201e, 0x201c, 0x201a, 0x2018, 0 },
167 { "lv", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
168 { "mas", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
169 { "mer", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
170 { "mfe", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
171 { "mg", 0x00ab, 0x00bb, 0x201c, 0x201d, 0 },
172 { "mgo", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
173 { "mk", 0x201e, 0x201c, 0x201a, 0x2018, 0 },
174 { "ml", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
175 { "mr", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
176 { "ms", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
177 { "mua", 0x00ab, 0x00bb, 0x201c, 0x201d, 0 },
178 { "my", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
179 { "naq", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
180 { "nb", 0x00ab, 0x00bb, 0x2018, 0x2019, 0 },
181 { "nd", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
182 { "nl", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
183 { "nmg", 0x201e, 0x201d, 0x00ab, 0x00bb, 0 },
184 { "nn", 0x00ab, 0x00bb, 0x2018, 0x2019, 0 },
185 { "nnh", 0x00ab, 0x00bb, 0x201c, 0x201d, 0 },
186 { "nus", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
187 { "nyn", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
188 { "pl", 0x201e, 0x201d, 0x00ab, 0x00bb, 0 },
189 { "pt", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
190 { "pt-pt", 0x00ab, 0x00bb, 0x201c, 0x201d, 0 },
191 { "rn", 0x201d, 0x201d, 0x2019, 0x2019, 0 },
192 { "ro", 0x201e, 0x201d, 0x00ab, 0x00bb, 0 },
193 { "rof", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
194 { "ru", 0x00ab, 0x00bb, 0x201e, 0x201c, 0 },
195 { "rw", 0x00ab, 0x00bb, 0x2018, 0x2019, 0 },
196 { "rwk", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
197 { "saq", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
198 { "sbp", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
199 { "seh", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
200 { "ses", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
201 { "sg", 0x00ab, 0x00bb, 0x201c, 0x201d, 0 },
202 { "shi", 0x00ab, 0x00bb, 0x201e, 0x201d, 0 },
203 { "shi-tfng", 0x00ab, 0x00bb, 0x201e, 0x201d, 0 },
204 { "si", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
205 { "sk", 0x201e, 0x201c, 0x201a, 0x2018, 0 },
206 { "sl", 0x201e, 0x201c, 0x201a, 0x2018, 0 },
207 { "sn", 0x201d, 0x201d, 0x2019, 0x2019, 0 },
208 { "so", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
209 { "sq", 0x201e, 0x201c, 0x201a, 0x2018, 0 },
210 { "sr", 0x201e, 0x201c, 0x201a, 0x2018, 0 },
211 { "sr-latn", 0x201e, 0x201c, 0x201a, 0x2018, 0 },
212 { "sv", 0x201d, 0x201d, 0x2019, 0x2019, 0 },
213 { "sw", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
214 { "swc", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
215 { "ta", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
216 { "te", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
217 { "teo", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
218 { "th", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
219 { "ti-er", 0x2018, 0x2019, 0x201c, 0x201d, 0 },
220 { "to", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
221 { "tr", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
222 { "twq", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
223 { "tzm", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
224 { "uk", 0x00ab, 0x00bb, 0x201e, 0x201c, 0 },
225 { "ur", 0x201d, 0x201c, 0x2019, 0x2018, 0 },
226 { "vai", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
227 { "vai-latn", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
228 { "vi", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
229 { "vun", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
230 { "xh", 0x2018, 0x2019, 0x201c, 0x201d, 0 },
231 { "xog", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
232 { "yav", 0x00ab, 0x00bb, 0x00ab, 0x00bb, 0 },
233 { "yo", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
234 { "zh", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
235 { "zh-hant", 0x300c, 0x300d, 0x300e, 0x300f, 0 },
236 { "zu", 0x201c, 0x201d, 0x2018, 0x2019, 0 },
239 const QuotesData* quotesDataForLanguage(const AtomicString& lang)
244 // This could be just a hash table, but doing that adds 200k to RenderQuote.o
245 Language* languagesEnd = languages + WTF_ARRAY_LENGTH(languages);
246 CString lowercaseLang = lang.string().lower().utf8();
247 Language key = { lowercaseLang.data(), 0, 0, 0, 0, 0 };
248 Language* match = std::lower_bound(languages, languagesEnd, key);
249 if (match == languagesEnd || strcmp(match->lang, key.lang))
253 match->data = QuotesData::create(match->open1, match->close1, match->open2, match->close2).leakRef();
258 static const QuotesData* basicQuotesData()
260 // FIXME: The default quotes should be the fancy quotes for "en".
261 DEFINE_STATIC_REF(QuotesData, staticBasicQuotes, (QuotesData::create('"', '"', '\'', '\'')));
262 return staticBasicQuotes;
265 void RenderQuote::updateText()
267 String text = computeText();
273 while (RenderObject* child = lastChild())
276 RenderTextFragment* fragment = new RenderTextFragment(&document(), m_text.impl());
277 fragment->setStyle(style());
281 String RenderQuote::computeText() const
286 return emptyString();
288 return quotesData()->getCloseQuote(m_depth - 1).impl();
290 return quotesData()->getOpenQuote(m_depth).impl();
292 ASSERT_NOT_REACHED();
293 return emptyString();
296 const QuotesData* RenderQuote::quotesData() const
298 if (const QuotesData* customQuotes = style()->quotes())
301 if (const QuotesData* quotes = quotesDataForLanguage(style()->locale()))
304 return basicQuotesData();
307 void RenderQuote::attachQuote()
311 ASSERT(!m_next && !m_previous);
314 if (!view()->renderQuoteHead()) {
315 view()->setRenderQuoteHead(this);
320 for (RenderObject* predecessor = previousInPreOrder(); predecessor; predecessor = predecessor->previousInPreOrder()) {
321 // Skip unattached predecessors to avoid having stale m_previous pointers
322 // if the previous node is never attached and is then destroyed.
323 if (!predecessor->isQuote() || !toRenderQuote(predecessor)->isAttached())
325 m_previous = toRenderQuote(predecessor);
326 m_next = m_previous->m_next;
327 m_previous->m_next = this;
329 m_next->m_previous = this;
334 m_next = view()->renderQuoteHead();
335 view()->setRenderQuoteHead(this);
337 m_next->m_previous = this;
341 for (RenderQuote* quote = this; quote; quote = quote->m_next)
342 quote->updateDepth();
344 ASSERT(!m_next || m_next->m_attached);
345 ASSERT(!m_next || m_next->m_previous == this);
346 ASSERT(!m_previous || m_previous->m_attached);
347 ASSERT(!m_previous || m_previous->m_next == this);
350 void RenderQuote::detachQuote()
352 ASSERT(!m_next || m_next->m_attached);
353 ASSERT(!m_previous || m_previous->m_attached);
357 m_previous->m_next = m_next;
359 view()->setRenderQuoteHead(m_next);
361 m_next->m_previous = m_previous;
362 if (!documentBeingDestroyed()) {
363 for (RenderQuote* quote = m_next; quote; quote = quote->m_next)
364 quote->updateDepth();
368 m_previous = nullptr;
372 void RenderQuote::updateDepth()
375 int oldDepth = m_depth;
378 m_depth = m_previous->m_depth;
379 switch (m_previous->m_type) {
391 if (oldDepth != m_depth)