OLD | NEW |
1 /** | 1 /** |
2 * Copyright (C) 2011 Nokia Inc. All rights reserved. | 2 * Copyright (C) 2011 Nokia Inc. All rights reserved. |
3 * | 3 * |
4 * This library is free software; you can redistribute it and/or | 4 * This library is free software; you can redistribute it and/or |
5 * modify it under the terms of the GNU Library General Public | 5 * modify it under the terms of the GNU Library General Public |
6 * License as published by the Free Software Foundation; either | 6 * License as published by the Free Software Foundation; either |
7 * version 2 of the License, or (at your option) any later version. | 7 * version 2 of the License, or (at your option) any later version. |
8 * | 8 * |
9 * This library is distributed in the hope that it will be useful, | 9 * This library is distributed in the hope that it will be useful, |
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 * Library General Public License for more details. | 12 * Library General Public License for more details. |
13 * | 13 * |
14 * You should have received a copy of the GNU Library General Public License | 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 | 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, | 16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
17 * Boston, MA 02110-1301, USA. | 17 * Boston, MA 02110-1301, USA. |
18 * | 18 * |
19 */ | 19 */ |
20 | 20 |
21 #include "config.h" | 21 #include "config.h" |
22 #include "RenderQuote.h" | 22 #include "RenderQuote.h" |
23 | 23 |
24 #include "Document.h" | |
25 #include "QuotesData.h" | |
26 #include "RenderStyle.h" | |
27 #include <wtf/text/AtomicString.h> | 24 #include <wtf/text/AtomicString.h> |
28 | 25 |
29 #define UNKNOWN_DEPTH -1 | 26 #define U(x) ((const UChar*)L##x) |
30 | 27 |
31 namespace WebCore { | 28 namespace WebCore { |
32 static inline void adjustDepth(int &depth, QuoteType type) | |
33 { | |
34 switch (type) { | |
35 case OPEN_QUOTE: | |
36 case NO_OPEN_QUOTE: | |
37 ++depth; | |
38 break; | |
39 case CLOSE_QUOTE: | |
40 case NO_CLOSE_QUOTE: | |
41 if (depth) | |
42 --depth; | |
43 break; | |
44 default: | |
45 ASSERT_NOT_REACHED(); | |
46 } | |
47 } | |
48 | 29 |
49 RenderQuote::RenderQuote(Document* node, QuoteType quote) | 30 RenderQuote::RenderQuote(Document* node, QuoteType quote) |
50 : RenderText(node, StringImpl::empty()) | 31 : RenderText(node, StringImpl::empty()) |
51 , m_type(quote) | 32 , m_type(quote) |
52 , m_depth(UNKNOWN_DEPTH) | 33 , m_depth(0) |
53 , m_next(0) | 34 , m_next(0) |
54 , m_previous(0) | 35 , m_previous(0) |
| 36 , m_attached(false) |
55 { | 37 { |
56 view()->addRenderQuote(); | |
57 } | 38 } |
58 | 39 |
59 RenderQuote::~RenderQuote() | 40 RenderQuote::~RenderQuote() |
60 { | 41 { |
| 42 ASSERT(!m_attached); |
| 43 ASSERT(!m_next && !m_previous); |
61 } | 44 } |
62 | 45 |
63 void RenderQuote::willBeDestroyed() | 46 void RenderQuote::willBeDestroyed() |
64 { | 47 { |
65 if (view()) | 48 detachQuote(); |
66 view()->removeRenderQuote(); | |
67 RenderText::willBeDestroyed(); | 49 RenderText::willBeDestroyed(); |
68 } | 50 } |
69 | 51 |
70 const char* RenderQuote::renderName() const | |
71 { | |
72 return "RenderQuote"; | |
73 } | |
74 | |
75 // This function places a list of quote renderers starting at "this" in the list
of quote renderers already | |
76 // in the document's renderer tree. | |
77 // The assumptions are made (for performance): | |
78 // 1. The list of quotes already in the renderers tree of the document is alread
y in a consistent state | |
79 // (All quote renderers are linked and have the correct depth set) | |
80 // 2. The quote renderers of the inserted list are in a tree of renderers of the
ir own which has been just | |
81 // inserted in the main renderer tree with its root as child of some renderer. | |
82 // 3. The quote renderers in the inserted list have depths consistent with their
position in the list relative | |
83 // to "this", thus if "this" does not need to change its depth upon insertion, t
he other renderers in the list don't | |
84 // need to either. | |
85 void RenderQuote::placeQuote() | |
86 { | |
87 RenderQuote* head = this; | |
88 ASSERT(!head->m_previous); | |
89 RenderQuote* tail = 0; | |
90 for (RenderObject* predecessor = head->previousInPreOrder(); predecessor; pr
edecessor = predecessor->previousInPreOrder()) { | |
91 if (!predecessor->isQuote()) | |
92 continue; | |
93 head->m_previous = toRenderQuote(predecessor); | |
94 if (head->m_previous->m_next) { | |
95 // We need to splice the list of quotes headed by head into the docu
ment's list of quotes. | |
96 tail = head; | |
97 while (tail->m_next) | |
98 tail = tail->m_next; | |
99 tail->m_next = head->m_previous->m_next; | |
100 ASSERT(tail->m_next->m_previous == head->m_previous); | |
101 tail->m_next->m_previous = tail; | |
102 tail = tail->m_next; // This marks the splicing point here there may
be a depth discontinuity | |
103 } | |
104 head->m_previous->m_next = head; | |
105 ASSERT(head->m_previous->m_depth != UNKNOWN_DEPTH); | |
106 break; | |
107 } | |
108 int newDepth; | |
109 if (!head->m_previous) { | |
110 newDepth = 0; | |
111 goto skipNewDepthCalc; | |
112 } | |
113 newDepth = head->m_previous->m_depth; | |
114 do { | |
115 adjustDepth(newDepth, head->m_previous->m_type); | |
116 skipNewDepthCalc: | |
117 if (head->m_depth == newDepth) { // All remaining depth should be correc
t except if splicing was done. | |
118 if (!tail) // We've done the post splicing section already or there
was no splicing. | |
119 break; | |
120 head = tail; // Continue after the splicing point | |
121 tail = 0; // Mark the possible splicing point discontinuity fixed. | |
122 newDepth = head->m_previous->m_depth; | |
123 continue; | |
124 } | |
125 head->m_depth = newDepth; | |
126 // FIXME: If the width and height of the quotation characters does not c
hange we may only need to | |
127 // Invalidate the renderer's area not a relayout. | |
128 head->setNeedsLayoutAndPrefWidthsRecalc(); | |
129 head = head->m_next; | |
130 if (head == tail) // We are at the splicing point | |
131 tail = 0; // Mark the possible depth discontinuity fixed. | |
132 } while (head); | |
133 } | |
134 | |
135 #define U(x) ((const UChar*)L##x) | |
136 | |
137 typedef HashMap<AtomicString, const QuotesData*, CaseFoldingHash> QuotesMap; | 52 typedef HashMap<AtomicString, const QuotesData*, CaseFoldingHash> QuotesMap; |
138 | 53 |
139 static const QuotesMap& quotesDataLanguageMap() | 54 static const QuotesMap& quotesDataLanguageMap() |
140 { | 55 { |
141 DEFINE_STATIC_LOCAL(QuotesMap, staticQuotesMap, ()); | 56 DEFINE_STATIC_LOCAL(QuotesMap, staticQuotesMap, ()); |
142 if (staticQuotesMap.size()) | 57 if (staticQuotesMap.size()) |
143 return staticQuotesMap; | 58 return staticQuotesMap; |
144 // FIXME: Expand this table to include all the languages in https://bug-3234
-attachments.webkit.org/attachment.cgi?id=2135 | 59 // FIXME: Expand this table to include all the languages in https://bug-3234
-attachments.webkit.org/attachment.cgi?id=2135 |
145 staticQuotesMap.set("en", QuotesData::create(U("\x201C"), U("\x201D"), U("\x
2018"), U("\x2019")).leakRef()); | 60 staticQuotesMap.set("en", QuotesData::create(U("\x201C"), U("\x201D"), U("\x
2018"), U("\x2019")).leakRef()); |
146 staticQuotesMap.set("no", QuotesData::create(U("\x00AB"), U("\x00BB"), U("\x
2039"), U("\x203A")).leakRef()); | 61 staticQuotesMap.set("no", QuotesData::create(U("\x00AB"), U("\x00BB"), U("\x
2039"), U("\x203A")).leakRef()); |
147 staticQuotesMap.set("ro", QuotesData::create(U("\x201E"), U("\x201D")).leakR
ef()); | 62 staticQuotesMap.set("ro", QuotesData::create(U("\x201E"), U("\x201D")).leakR
ef()); |
148 staticQuotesMap.set("ru", QuotesData::create(U("\x00AB"), U("\x00BB"), U("\x
201E"), U("\x201C")).leakRef()); | 63 staticQuotesMap.set("ru", QuotesData::create(U("\x00AB"), U("\x00BB"), U("\x
201E"), U("\x201C")).leakRef()); |
149 return staticQuotesMap; | 64 return staticQuotesMap; |
150 } | 65 } |
151 | 66 |
152 static const QuotesData* basicQuotesData() | 67 static const QuotesData* basicQuotesData() |
153 { | 68 { |
154 static const QuotesData* staticBasicQuotes = QuotesData::create(U("\""), U("
\""), U("'"), U("'")).leakRef(); | 69 static const QuotesData* staticBasicQuotes = QuotesData::create(U("\""), U("
\""), U("'"), U("'")).leakRef(); |
155 return staticBasicQuotes; | 70 return staticBasicQuotes; |
156 } | 71 } |
157 | 72 |
158 PassRefPtr<StringImpl> RenderQuote::originalText() const | 73 PassRefPtr<StringImpl> RenderQuote::originalText() const |
159 { | 74 { |
160 if (!parent()) | |
161 return 0; | |
162 ASSERT(m_depth != UNKNOWN_DEPTH); | |
163 const QuotesData* quotes = quotesData(); | |
164 switch (m_type) { | 75 switch (m_type) { |
165 case NO_OPEN_QUOTE: | 76 case NO_OPEN_QUOTE: |
166 case NO_CLOSE_QUOTE: | 77 case NO_CLOSE_QUOTE: |
167 return StringImpl::empty(); | 78 return StringImpl::empty(); |
168 case CLOSE_QUOTE: | 79 case CLOSE_QUOTE: |
169 // FIXME: When m_depth is 0 we should return empty string. | 80 // FIXME: When m_depth is 0 we should return empty string. |
170 return quotes->getCloseQuote(std::max(m_depth - 1, 0)).impl(); | 81 return quotesData()->getCloseQuote(std::max(m_depth - 1, 0)).impl(); |
171 case OPEN_QUOTE: | 82 case OPEN_QUOTE: |
172 return quotes->getOpenQuote(m_depth).impl(); | 83 return quotesData()->getOpenQuote(m_depth).impl(); |
173 default: | |
174 ASSERT_NOT_REACHED(); | |
175 return StringImpl::empty(); | |
176 } | 84 } |
| 85 ASSERT_NOT_REACHED(); |
| 86 return StringImpl::empty(); |
177 } | 87 } |
178 | 88 |
179 void RenderQuote::computePreferredLogicalWidths(float lead) | 89 void RenderQuote::computePreferredLogicalWidths(float lead) |
180 { | 90 { |
| 91 if (!m_attached) |
| 92 attachQuote(); |
181 setTextInternal(originalText()); | 93 setTextInternal(originalText()); |
182 RenderText::computePreferredLogicalWidths(lead); | 94 RenderText::computePreferredLogicalWidths(lead); |
183 } | 95 } |
184 | 96 |
185 const QuotesData* RenderQuote::quotesData() const | 97 const QuotesData* RenderQuote::quotesData() const |
186 { | 98 { |
187 if (QuotesData* customQuotes = style()->quotes()) | 99 if (QuotesData* customQuotes = style()->quotes()) |
188 return customQuotes; | 100 return customQuotes; |
189 | 101 |
190 AtomicString language = style()->locale(); | 102 AtomicString language = style()->locale(); |
191 if (language.isNull()) | 103 if (language.isNull()) |
192 return basicQuotesData(); | 104 return basicQuotesData(); |
193 const QuotesData* quotes = quotesDataLanguageMap().get(language); | 105 const QuotesData* quotes = quotesDataLanguageMap().get(language); |
194 if (!quotes) | 106 if (!quotes) |
195 return basicQuotesData(); | 107 return basicQuotesData(); |
196 return quotes; | 108 return quotes; |
197 } | 109 } |
198 | 110 |
199 void RenderQuote::rendererSubtreeAttached(RenderObject* renderer) | 111 void RenderQuote::attachQuote() |
200 { | 112 { |
201 ASSERT(renderer->view()); | 113 ASSERT(view()); |
202 if (!renderer->view()->hasRenderQuotes()) | 114 ASSERT(!m_attached); |
| 115 ASSERT(!m_next && !m_previous); |
| 116 |
| 117 // FIXME: Don't set pref widths dirty during layout. See updateDepth() for |
| 118 // more detail. |
| 119 if (!isRooted()) { |
| 120 setNeedsLayoutAndPrefWidthsRecalc(); |
203 return; | 121 return; |
204 for (RenderObject* descendant = renderer; descendant; descendant = descendan
t->nextInPreOrder(renderer)) | 122 } |
205 if (descendant->isQuote()) { | 123 |
206 toRenderQuote(descendant)->placeQuote(); | 124 if (!view()->renderQuoteHead()) { |
| 125 view()->setRenderQuoteHead(this); |
| 126 m_attached = true; |
| 127 return; |
| 128 } |
| 129 |
| 130 for (RenderObject* predecessor = previousInPreOrder(); predecessor; predeces
sor = predecessor->previousInPreOrder()) { |
| 131 if (!predecessor->isQuote()) |
| 132 continue; |
| 133 m_previous = toRenderQuote(predecessor); |
| 134 m_next = m_previous->m_next; |
| 135 m_previous->m_next = this; |
| 136 if (m_next) |
| 137 m_next->m_previous = this; |
| 138 break; |
| 139 } |
| 140 |
| 141 if (!m_previous) { |
| 142 m_next = view()->renderQuoteHead(); |
| 143 view()->setRenderQuoteHead(this); |
| 144 } |
| 145 m_attached = true; |
| 146 for (RenderQuote* quote = this; quote; quote = quote->m_next) |
| 147 quote->updateDepth(); |
| 148 |
| 149 ASSERT(!m_next || m_next->m_attached); |
| 150 ASSERT(!m_previous || m_previous->m_attached); |
| 151 } |
| 152 |
| 153 void RenderQuote::detachQuote() |
| 154 { |
| 155 ASSERT(!m_next || m_next->m_attached); |
| 156 ASSERT(!m_previous || m_previous->m_attached); |
| 157 if (!m_attached) |
| 158 return; |
| 159 if (m_previous) |
| 160 m_previous->m_next = m_next; |
| 161 else if (view()) |
| 162 view()->setRenderQuoteHead(m_next); |
| 163 if (m_next) |
| 164 m_next->m_previous = m_previous; |
| 165 if (!documentBeingDestroyed()) { |
| 166 for (RenderQuote* quote = m_next; quote; quote = quote->m_next) |
| 167 quote->updateDepth(); |
| 168 } |
| 169 m_attached = false; |
| 170 m_next = 0; |
| 171 m_previous = 0; |
| 172 m_depth = 0; |
| 173 } |
| 174 |
| 175 void RenderQuote::updateDepth() |
| 176 { |
| 177 ASSERT(m_attached); |
| 178 int oldDepth = m_depth; |
| 179 m_depth = 0; |
| 180 if (m_previous) { |
| 181 m_depth = m_previous->m_depth; |
| 182 switch (m_previous->m_type) { |
| 183 case OPEN_QUOTE: |
| 184 case NO_OPEN_QUOTE: |
| 185 m_depth++; |
| 186 break; |
| 187 case CLOSE_QUOTE: |
| 188 case NO_CLOSE_QUOTE: |
| 189 if (m_depth) |
| 190 m_depth--; |
207 break; | 191 break; |
208 } | 192 } |
209 } | 193 } |
210 | 194 // FIXME: Don't call setNeedsLayout or dirty our preferred widths during lay
out. |
211 void RenderQuote::rendererRemovedFromTree(RenderObject* renderer) | 195 // This is likely to fail anyway as one of our ancestor will call setNeedsLa
yout(false), |
212 { | 196 // preventing the future layout to occur on |this|. The solution is to move
that to a |
213 ASSERT(renderer->view()); | 197 // pre-layout phase. |
214 if (!renderer->view()->hasRenderQuotes()) | 198 if (oldDepth != m_depth) |
215 return; | |
216 for (RenderObject* descendant = renderer; descendant; descendant = descendan
t->nextInPreOrder(renderer)) | |
217 if (descendant->isQuote()) { | |
218 RenderQuote* removedQuote = toRenderQuote(descendant); | |
219 RenderQuote* lastQuoteBefore = removedQuote->m_previous; | |
220 removedQuote->m_previous = 0; | |
221 int depth = removedQuote->m_depth; | |
222 for (descendant = descendant->nextInPreOrder(renderer); descendant;
descendant = descendant->nextInPreOrder(renderer)) | |
223 if (descendant->isQuote()) | |
224 removedQuote = toRenderQuote(descendant); | |
225 RenderQuote* quoteAfter = removedQuote->m_next; | |
226 removedQuote->m_next = 0; | |
227 if (lastQuoteBefore) | |
228 lastQuoteBefore->m_next = quoteAfter; | |
229 if (quoteAfter) { | |
230 quoteAfter->m_previous = lastQuoteBefore; | |
231 do { | |
232 if (depth == quoteAfter->m_depth) | |
233 break; | |
234 quoteAfter->m_depth = depth; | |
235 quoteAfter->setNeedsLayoutAndPrefWidthsRecalc(); | |
236 adjustDepth(depth, quoteAfter->m_type); | |
237 quoteAfter = quoteAfter->m_next; | |
238 } while (quoteAfter); | |
239 } | |
240 break; | |
241 } | |
242 } | |
243 | |
244 void RenderQuote::styleDidChange(StyleDifference diff, const RenderStyle* oldSty
le) | |
245 { | |
246 const QuotesData* newQuotes = style()->quotes(); | |
247 const QuotesData* oldQuotes = oldStyle ? oldStyle->quotes() : 0; | |
248 if (!QuotesData::equals(newQuotes, oldQuotes)) | |
249 setNeedsLayoutAndPrefWidthsRecalc(); | 199 setNeedsLayoutAndPrefWidthsRecalc(); |
250 RenderText::styleDidChange(diff, oldStyle); | |
251 } | 200 } |
252 | 201 |
253 } // namespace WebCore | 202 } // namespace WebCore |
OLD | NEW |