1 | /* |
2 | * Copyright (C) 2011 Nokia Inc. All rights reserved. |
3 | * Copyright (C) 2012 Google Inc. All rights reserved. |
4 | * Copyright (C) 2013, 2017 Apple Inc. All rights reserved. |
5 | * |
6 | * This library is free software; you can redistribute it and/or |
7 | * modify it under the terms of the GNU Library General Public |
8 | * License as published by the Free Software Foundation; either |
9 | * version 2 of the License, or (at your option) any later version. |
10 | * |
11 | * This library is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
14 | * Library General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU Library General Public License |
17 | * along with this library; see the file COPYING.LIB. If not, write to |
18 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
19 | * Boston, MA 02110-1301, USA. |
20 | * |
21 | */ |
22 | |
23 | #include "config.h" |
24 | #include "RenderQuote.h" |
25 | |
26 | #include "QuotesData.h" |
27 | #include "RenderTextFragment.h" |
28 | #include "RenderTreeBuilder.h" |
29 | #include "RenderView.h" |
30 | #include <wtf/IsoMallocInlines.h> |
31 | #include <wtf/unicode/CharacterNames.h> |
32 | |
33 | namespace WebCore { |
34 | using namespace WTF::Unicode; |
35 | |
36 | WTF_MAKE_ISO_ALLOCATED_IMPL(RenderQuote); |
37 | |
38 | RenderQuote::RenderQuote(Document& document, RenderStyle&& style, QuoteType quote) |
39 | : RenderInline(document, WTFMove(style)) |
40 | , m_type(quote) |
41 | , m_text(emptyString()) |
42 | { |
43 | } |
44 | |
45 | RenderQuote::~RenderQuote() |
46 | { |
47 | // Do not add any code here. Add it to willBeDestroyed() instead. |
48 | } |
49 | |
50 | void RenderQuote::insertedIntoTree() |
51 | { |
52 | RenderInline::insertedIntoTree(); |
53 | view().setHasQuotesNeedingUpdate(true); |
54 | } |
55 | |
56 | void RenderQuote::willBeRemovedFromTree() |
57 | { |
58 | view().setHasQuotesNeedingUpdate(true); |
59 | RenderInline::willBeRemovedFromTree(); |
60 | } |
61 | |
62 | void RenderQuote::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle) |
63 | { |
64 | RenderInline::styleDidChange(diff, oldStyle); |
65 | if (diff >= StyleDifference::Layout) { |
66 | m_needsTextUpdate = true; |
67 | view().setHasQuotesNeedingUpdate(true); |
68 | } |
69 | } |
70 | |
71 | const unsigned maxDistinctQuoteCharacters = 16; |
72 | |
73 | #if !ASSERT_DISABLED |
74 | |
75 | static void checkNumberOfDistinctQuoteCharacters(UChar character) |
76 | { |
77 | ASSERT(character); |
78 | static UChar distinctQuoteCharacters[maxDistinctQuoteCharacters]; |
79 | for (unsigned i = 0; i < maxDistinctQuoteCharacters; ++i) { |
80 | if (distinctQuoteCharacters[i] == character) |
81 | return; |
82 | if (!distinctQuoteCharacters[i]) { |
83 | distinctQuoteCharacters[i] = character; |
84 | return; |
85 | } |
86 | } |
87 | ASSERT_NOT_REACHED(); |
88 | } |
89 | |
90 | #endif |
91 | |
92 | struct QuotesForLanguage { |
93 | const char* language; |
94 | UChar open1; |
95 | UChar close1; |
96 | UChar open2; |
97 | UChar close2; |
98 | }; |
99 | |
100 | static int quoteTableLanguageComparisonFunction(const void* a, const void* b) |
101 | { |
102 | return strcmp(static_cast<const QuotesForLanguage*>(a)->language, |
103 | static_cast<const QuotesForLanguage*>(b)->language); |
104 | } |
105 | |
106 | static const QuotesForLanguage* quotesForLanguage(const String& language) |
107 | { |
108 | // Table of quotes from http://www.whatwg.org/specs/web-apps/current-work/multipage/rendering.html#quotes |
109 | static const QuotesForLanguage quoteTable[] = { |
110 | { "af" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
111 | { "agq" , 0x201e, 0x201d, 0x201a, 0x2019 }, |
112 | { "ak" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
113 | { "am" , 0x00ab, 0x00bb, 0x2039, 0x203a }, |
114 | { "ar" , 0x201d, 0x201c, 0x2019, 0x2018 }, |
115 | { "asa" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
116 | { "az-cyrl" , 0x00ab, 0x00bb, 0x2039, 0x203a }, |
117 | { "bas" , 0x00ab, 0x00bb, 0x201e, 0x201c }, |
118 | { "bem" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
119 | { "bez" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
120 | { "bg" , 0x201e, 0x201c, 0x201a, 0x2018 }, |
121 | { "bm" , 0x00ab, 0x00bb, 0x201c, 0x201d }, |
122 | { "bn" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
123 | { "br" , 0x00ab, 0x00bb, 0x2039, 0x203a }, |
124 | { "brx" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
125 | { "bs-cyrl" , 0x201e, 0x201c, 0x201a, 0x2018 }, |
126 | { "ca" , 0x201c, 0x201d, 0x00ab, 0x00bb }, |
127 | { "cgg" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
128 | { "chr" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
129 | { "cs" , 0x201e, 0x201c, 0x201a, 0x2018 }, |
130 | { "da" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
131 | { "dav" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
132 | { "de" , 0x201e, 0x201c, 0x201a, 0x2018 }, |
133 | { "de-ch" , 0x00ab, 0x00bb, 0x2039, 0x203a }, |
134 | { "dje" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
135 | { "dua" , 0x00ab, 0x00bb, 0x2018, 0x2019 }, |
136 | { "dyo" , 0x00ab, 0x00bb, 0x201c, 0x201d }, |
137 | { "dz" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
138 | { "ebu" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
139 | { "ee" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
140 | { "el" , 0x00ab, 0x00bb, 0x201c, 0x201d }, |
141 | { "en" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
142 | { "en-gb" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
143 | { "es" , 0x201c, 0x201d, 0x00ab, 0x00bb }, |
144 | { "et" , 0x201e, 0x201c, 0x201a, 0x2018 }, |
145 | { "eu" , 0x201c, 0x201d, 0x00ab, 0x00bb }, |
146 | { "ewo" , 0x00ab, 0x00bb, 0x201c, 0x201d }, |
147 | { "fa" , 0x00ab, 0x00bb, 0x2039, 0x203a }, |
148 | { "ff" , 0x201e, 0x201d, 0x201a, 0x2019 }, |
149 | { "fi" , 0x201d, 0x201d, 0x2019, 0x2019 }, |
150 | { "fr" , 0x00ab, 0x00bb, 0x00ab, 0x00bb }, |
151 | { "fr-ca" , 0x00ab, 0x00bb, 0x2039, 0x203a }, |
152 | { "fr-ch" , 0x00ab, 0x00bb, 0x2039, 0x203a }, |
153 | { "gsw" , 0x00ab, 0x00bb, 0x2039, 0x203a }, |
154 | { "gu" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
155 | { "guz" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
156 | { "ha" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
157 | { "he" , 0x0022, 0x0022, 0x0027, 0x0027 }, |
158 | { "hi" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
159 | { "hr" , 0x201e, 0x201c, 0x201a, 0x2018 }, |
160 | { "hu" , 0x201e, 0x201d, 0x00bb, 0x00ab }, |
161 | { "id" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
162 | { "ig" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
163 | { "it" , 0x00ab, 0x00bb, 0x201c, 0x201d }, |
164 | { "ja" , 0x300c, 0x300d, 0x300e, 0x300f }, |
165 | { "jgo" , 0x00ab, 0x00bb, 0x2039, 0x203a }, |
166 | { "jmc" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
167 | { "kab" , 0x00ab, 0x00bb, 0x201c, 0x201d }, |
168 | { "kam" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
169 | { "kde" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
170 | { "kea" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
171 | { "khq" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
172 | { "ki" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
173 | { "kkj" , 0x00ab, 0x00bb, 0x2039, 0x203a }, |
174 | { "kln" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
175 | { "km" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
176 | { "kn" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
177 | { "ko" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
178 | { "ksb" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
179 | { "ksf" , 0x00ab, 0x00bb, 0x2018, 0x2019 }, |
180 | { "lag" , 0x201d, 0x201d, 0x2019, 0x2019 }, |
181 | { "lg" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
182 | { "ln" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
183 | { "lo" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
184 | { "lt" , 0x201e, 0x201c, 0x201e, 0x201c }, |
185 | { "lu" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
186 | { "luo" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
187 | { "luy" , 0x201e, 0x201c, 0x201a, 0x2018 }, |
188 | { "lv" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
189 | { "mas" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
190 | { "mer" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
191 | { "mfe" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
192 | { "mg" , 0x00ab, 0x00bb, 0x201c, 0x201d }, |
193 | { "mgo" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
194 | { "mk" , 0x201e, 0x201c, 0x201a, 0x2018 }, |
195 | { "ml" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
196 | { "mr" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
197 | { "ms" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
198 | { "mua" , 0x00ab, 0x00bb, 0x201c, 0x201d }, |
199 | { "my" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
200 | { "naq" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
201 | { "nb" , 0x00ab, 0x00bb, 0x2018, 0x2019 }, |
202 | { "nd" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
203 | { "nl" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
204 | { "nmg" , 0x201e, 0x201d, 0x00ab, 0x00bb }, |
205 | { "nn" , 0x00ab, 0x00bb, 0x2018, 0x2019 }, |
206 | { "nnh" , 0x00ab, 0x00bb, 0x201c, 0x201d }, |
207 | { "nus" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
208 | { "nyn" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
209 | { "pl" , 0x201e, 0x201d, 0x00ab, 0x00bb }, |
210 | { "pt" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
211 | { "pt-pt" , 0x00ab, 0x00bb, 0x201c, 0x201d }, |
212 | { "rn" , 0x201d, 0x201d, 0x2019, 0x2019 }, |
213 | { "ro" , 0x201e, 0x201d, 0x00ab, 0x00bb }, |
214 | { "rof" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
215 | { "ru" , 0x00ab, 0x00bb, 0x201e, 0x201c }, |
216 | { "rw" , 0x00ab, 0x00bb, 0x2018, 0x2019 }, |
217 | { "rwk" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
218 | { "saq" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
219 | { "sbp" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
220 | { "seh" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
221 | { "ses" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
222 | { "sg" , 0x00ab, 0x00bb, 0x201c, 0x201d }, |
223 | { "shi" , 0x00ab, 0x00bb, 0x201e, 0x201d }, |
224 | { "shi-tfng" , 0x00ab, 0x00bb, 0x201e, 0x201d }, |
225 | { "si" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
226 | { "sk" , 0x201e, 0x201c, 0x201a, 0x2018 }, |
227 | { "sl" , 0x201e, 0x201c, 0x201a, 0x2018 }, |
228 | { "sn" , 0x201d, 0x201d, 0x2019, 0x2019 }, |
229 | { "so" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
230 | { "sq" , 0x201e, 0x201c, 0x201a, 0x2018 }, |
231 | { "sr" , 0x201e, 0x201c, 0x201a, 0x2018 }, |
232 | { "sr-latn" , 0x201e, 0x201c, 0x201a, 0x2018 }, |
233 | { "sv" , 0x201d, 0x201d, 0x2019, 0x2019 }, |
234 | { "sw" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
235 | { "swc" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
236 | { "ta" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
237 | { "te" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
238 | { "teo" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
239 | { "th" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
240 | { "ti-er" , 0x2018, 0x2019, 0x201c, 0x201d }, |
241 | { "to" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
242 | { "tr" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
243 | { "twq" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
244 | { "tzm" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
245 | { "uk" , 0x00ab, 0x00bb, 0x201e, 0x201c }, |
246 | { "ur" , 0x201d, 0x201c, 0x2019, 0x2018 }, |
247 | { "vai" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
248 | { "vai-latn" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
249 | { "vi" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
250 | { "vun" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
251 | { "xh" , 0x2018, 0x2019, 0x201c, 0x201d }, |
252 | { "xog" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
253 | { "yav" , 0x00ab, 0x00bb, 0x00ab, 0x00bb }, |
254 | { "yo" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
255 | { "zh" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
256 | { "zh-hant" , 0x300c, 0x300d, 0x300e, 0x300f }, |
257 | { "zu" , 0x201c, 0x201d, 0x2018, 0x2019 }, |
258 | }; |
259 | |
260 | const unsigned maxLanguageLength = 8; |
261 | |
262 | #if !ASSERT_DISABLED |
263 | // One time check that the table meets the constraints that the code below relies on. |
264 | |
265 | static bool didOneTimeCheck = false; |
266 | if (!didOneTimeCheck) { |
267 | didOneTimeCheck = true; |
268 | |
269 | checkNumberOfDistinctQuoteCharacters(quotationMark); |
270 | checkNumberOfDistinctQuoteCharacters(apostrophe); |
271 | |
272 | for (unsigned i = 0; i < WTF_ARRAY_LENGTH(quoteTable); ++i) { |
273 | ASSERT(strlen(quoteTable[i].language) <= maxLanguageLength); |
274 | |
275 | if (i) |
276 | ASSERT(strcmp(quoteTable[i - 1].language, quoteTable[i].language) < 0); |
277 | |
278 | for (unsigned j = 0; UChar character = quoteTable[i].language[j]; ++j) |
279 | ASSERT(isASCIILower(character) || character == '-'); |
280 | |
281 | checkNumberOfDistinctQuoteCharacters(quoteTable[i].open1); |
282 | checkNumberOfDistinctQuoteCharacters(quoteTable[i].close1); |
283 | checkNumberOfDistinctQuoteCharacters(quoteTable[i].open2); |
284 | checkNumberOfDistinctQuoteCharacters(quoteTable[i].close2); |
285 | } |
286 | } |
287 | #endif |
288 | |
289 | unsigned length = language.length(); |
290 | if (!length || length > maxLanguageLength) |
291 | return 0; |
292 | |
293 | char languageKeyBuffer[maxLanguageLength + 1]; |
294 | for (unsigned i = 0; i < length; ++i) { |
295 | UChar character = toASCIILower(language[i]); |
296 | if (!(isASCIILower(character) || character == '-')) |
297 | return 0; |
298 | languageKeyBuffer[i] = static_cast<char>(character); |
299 | } |
300 | languageKeyBuffer[length] = 0; |
301 | |
302 | QuotesForLanguage languageKey = { languageKeyBuffer, 0, 0, 0, 0 }; |
303 | |
304 | return static_cast<const QuotesForLanguage*>(bsearch(&languageKey, |
305 | quoteTable, WTF_ARRAY_LENGTH(quoteTable), sizeof(quoteTable[0]), quoteTableLanguageComparisonFunction)); |
306 | } |
307 | |
308 | static StringImpl* stringForQuoteCharacter(UChar character) |
309 | { |
310 | // Use linear search because there is a small number of distinct characters, thus binary search is unneeded. |
311 | ASSERT(character); |
312 | struct StringForCharacter { |
313 | UChar character; |
314 | StringImpl* string; |
315 | }; |
316 | static StringForCharacter strings[maxDistinctQuoteCharacters]; |
317 | for (unsigned i = 0; i < maxDistinctQuoteCharacters; ++i) { |
318 | if (strings[i].character == character) |
319 | return strings[i].string; |
320 | if (!strings[i].character) { |
321 | strings[i].character = character; |
322 | strings[i].string = &StringImpl::create8BitIfPossible(&character, 1).leakRef(); |
323 | return strings[i].string; |
324 | } |
325 | } |
326 | ASSERT_NOT_REACHED(); |
327 | return StringImpl::empty(); |
328 | } |
329 | |
330 | static inline StringImpl* quotationMarkString() |
331 | { |
332 | static StringImpl* quotationMarkString = stringForQuoteCharacter(quotationMark); |
333 | return quotationMarkString; |
334 | } |
335 | |
336 | static inline StringImpl* apostropheString() |
337 | { |
338 | static StringImpl* apostropheString = stringForQuoteCharacter(apostrophe); |
339 | return apostropheString; |
340 | } |
341 | |
342 | static RenderTextFragment* quoteTextRenderer(RenderObject* lastChild) |
343 | { |
344 | if (!lastChild) |
345 | return nullptr; |
346 | |
347 | if (!is<RenderTextFragment>(lastChild)) |
348 | return nullptr; |
349 | |
350 | return downcast<RenderTextFragment>(lastChild); |
351 | } |
352 | |
353 | void RenderQuote::updateTextRenderer(RenderTreeBuilder& builder) |
354 | { |
355 | ASSERT_WITH_SECURITY_IMPLICATION(document().inRenderTreeUpdate()); |
356 | String text = computeText(); |
357 | if (m_text == text) |
358 | return; |
359 | m_text = text; |
360 | if (auto* renderText = quoteTextRenderer(lastChild())) { |
361 | renderText->setContentString(m_text); |
362 | renderText->dirtyLineBoxes(false); |
363 | return; |
364 | } |
365 | builder.attach(*this, createRenderer<RenderTextFragment>(document(), m_text)); |
366 | } |
367 | |
368 | String RenderQuote::computeText() const |
369 | { |
370 | if (m_depth < 0) |
371 | return emptyString(); |
372 | bool isOpenQuote = false; |
373 | switch (m_type) { |
374 | case QuoteType::NoOpenQuote: |
375 | case QuoteType::NoCloseQuote: |
376 | return emptyString(); |
377 | case QuoteType::OpenQuote: |
378 | isOpenQuote = true; |
379 | FALLTHROUGH; |
380 | case QuoteType::CloseQuote: |
381 | if (const QuotesData* quotes = style().quotes()) |
382 | return isOpenQuote ? quotes->openQuote(m_depth).impl() : quotes->closeQuote(m_depth).impl(); |
383 | if (const QuotesForLanguage* quotes = quotesForLanguage(style().locale())) |
384 | return stringForQuoteCharacter(isOpenQuote ? (m_depth ? quotes->open2 : quotes->open1) : (m_depth ? quotes->close2 : quotes->close1)); |
385 | // FIXME: Should the default be the quotes for "en" rather than straight quotes? |
386 | return m_depth ? apostropheString() : quotationMarkString(); |
387 | } |
388 | ASSERT_NOT_REACHED(); |
389 | return emptyString(); |
390 | } |
391 | |
392 | bool RenderQuote::isOpen() const |
393 | { |
394 | switch (m_type) { |
395 | case QuoteType::OpenQuote: |
396 | case QuoteType::NoOpenQuote: |
397 | return true; |
398 | case QuoteType::CloseQuote: |
399 | case QuoteType::NoCloseQuote: |
400 | return false; |
401 | } |
402 | ASSERT_NOT_REACHED(); |
403 | return false; |
404 | } |
405 | |
406 | void RenderQuote::updateRenderer(RenderTreeBuilder& builder, RenderQuote* previousQuote) |
407 | { |
408 | ASSERT_WITH_SECURITY_IMPLICATION(document().inRenderTreeUpdate()); |
409 | int depth = -1; |
410 | if (previousQuote) { |
411 | depth = previousQuote->m_depth; |
412 | if (previousQuote->isOpen()) |
413 | ++depth; |
414 | } |
415 | |
416 | if (!isOpen()) |
417 | --depth; |
418 | else if (depth < 0) |
419 | depth = 0; |
420 | |
421 | if (m_depth == depth && !m_needsTextUpdate) |
422 | return; |
423 | |
424 | m_depth = depth; |
425 | m_needsTextUpdate = false; |
426 | updateTextRenderer(builder); |
427 | } |
428 | |
429 | } // namespace WebCore |
430 | |