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" |
24 #include <wtf/text/AtomicString.h> | 27 #include <wtf/text/AtomicString.h> |
25 | 28 |
26 #define U(x) ((const UChar*)L##x) | 29 #define UNKNOWN_DEPTH -1 |
27 | 30 |
28 namespace WebCore { | 31 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 } |
29 | 48 |
30 RenderQuote::RenderQuote(Document* node, QuoteType quote) | 49 RenderQuote::RenderQuote(Document* node, QuoteType quote) |
31 : RenderText(node, StringImpl::empty()) | 50 : RenderText(node, StringImpl::empty()) |
32 , m_type(quote) | 51 , m_type(quote) |
33 , m_depth(0) | 52 , m_depth(UNKNOWN_DEPTH) |
34 , m_next(0) | 53 , m_next(0) |
35 , m_previous(0) | 54 , m_previous(0) |
36 , m_attached(false) | |
37 { | 55 { |
| 56 view()->addRenderQuote(); |
38 } | 57 } |
39 | 58 |
40 RenderQuote::~RenderQuote() | 59 RenderQuote::~RenderQuote() |
41 { | 60 { |
42 ASSERT(!m_attached); | |
43 ASSERT(!m_next && !m_previous); | |
44 } | 61 } |
45 | 62 |
46 void RenderQuote::willBeDestroyed() | 63 void RenderQuote::willBeDestroyed() |
47 { | 64 { |
48 detachQuote(); | 65 if (view()) |
| 66 view()->removeRenderQuote(); |
49 RenderText::willBeDestroyed(); | 67 RenderText::willBeDestroyed(); |
50 } | 68 } |
51 | 69 |
| 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 |
52 typedef HashMap<AtomicString, const QuotesData*, CaseFoldingHash> QuotesMap; | 137 typedef HashMap<AtomicString, const QuotesData*, CaseFoldingHash> QuotesMap; |
53 | 138 |
54 static const QuotesMap& quotesDataLanguageMap() | 139 static const QuotesMap& quotesDataLanguageMap() |
55 { | 140 { |
56 DEFINE_STATIC_LOCAL(QuotesMap, staticQuotesMap, ()); | 141 DEFINE_STATIC_LOCAL(QuotesMap, staticQuotesMap, ()); |
57 if (staticQuotesMap.size()) | 142 if (staticQuotesMap.size()) |
58 return staticQuotesMap; | 143 return staticQuotesMap; |
59 // FIXME: Expand this table to include all the languages in https://bug-3234
-attachments.webkit.org/attachment.cgi?id=2135 | 144 // FIXME: Expand this table to include all the languages in https://bug-3234
-attachments.webkit.org/attachment.cgi?id=2135 |
60 staticQuotesMap.set("en", QuotesData::create(U("\x201C"), U("\x201D"), U("\x
2018"), U("\x2019")).leakRef()); | 145 staticQuotesMap.set("en", QuotesData::create(U("\x201C"), U("\x201D"), U("\x
2018"), U("\x2019")).leakRef()); |
61 staticQuotesMap.set("no", QuotesData::create(U("\x00AB"), U("\x00BB"), U("\x
2039"), U("\x203A")).leakRef()); | 146 staticQuotesMap.set("no", QuotesData::create(U("\x00AB"), U("\x00BB"), U("\x
2039"), U("\x203A")).leakRef()); |
62 staticQuotesMap.set("ro", QuotesData::create(U("\x201E"), U("\x201D")).leakR
ef()); | 147 staticQuotesMap.set("ro", QuotesData::create(U("\x201E"), U("\x201D")).leakR
ef()); |
63 staticQuotesMap.set("ru", QuotesData::create(U("\x00AB"), U("\x00BB"), U("\x
201E"), U("\x201C")).leakRef()); | 148 staticQuotesMap.set("ru", QuotesData::create(U("\x00AB"), U("\x00BB"), U("\x
201E"), U("\x201C")).leakRef()); |
64 return staticQuotesMap; | 149 return staticQuotesMap; |
65 } | 150 } |
66 | 151 |
67 static const QuotesData* basicQuotesData() | 152 static const QuotesData* basicQuotesData() |
68 { | 153 { |
69 static const QuotesData* staticBasicQuotes = QuotesData::create(U("\""), U("
\""), U("'"), U("'")).leakRef(); | 154 static const QuotesData* staticBasicQuotes = QuotesData::create(U("\""), U("
\""), U("'"), U("'")).leakRef(); |
70 return staticBasicQuotes; | 155 return staticBasicQuotes; |
71 } | 156 } |
72 | 157 |
73 PassRefPtr<StringImpl> RenderQuote::originalText() const | 158 PassRefPtr<StringImpl> RenderQuote::originalText() const |
74 { | 159 { |
| 160 if (!parent()) |
| 161 return 0; |
| 162 ASSERT(m_depth != UNKNOWN_DEPTH); |
| 163 const QuotesData* quotes = quotesData(); |
75 switch (m_type) { | 164 switch (m_type) { |
76 case NO_OPEN_QUOTE: | 165 case NO_OPEN_QUOTE: |
77 case NO_CLOSE_QUOTE: | 166 case NO_CLOSE_QUOTE: |
78 return StringImpl::empty(); | 167 return StringImpl::empty(); |
79 case CLOSE_QUOTE: | 168 case CLOSE_QUOTE: |
80 // FIXME: When m_depth is 0 we should return empty string. | 169 // FIXME: When m_depth is 0 we should return empty string. |
81 return quotesData()->getCloseQuote(std::max(m_depth - 1, 0)).impl(); | 170 return quotes->getCloseQuote(std::max(m_depth - 1, 0)).impl(); |
82 case OPEN_QUOTE: | 171 case OPEN_QUOTE: |
83 return quotesData()->getOpenQuote(m_depth).impl(); | 172 return quotes->getOpenQuote(m_depth).impl(); |
| 173 default: |
| 174 ASSERT_NOT_REACHED(); |
| 175 return StringImpl::empty(); |
84 } | 176 } |
85 ASSERT_NOT_REACHED(); | |
86 return StringImpl::empty(); | |
87 } | 177 } |
88 | 178 |
89 void RenderQuote::computePreferredLogicalWidths(float lead) | 179 void RenderQuote::computePreferredLogicalWidths(float lead) |
90 { | 180 { |
91 if (!m_attached) | |
92 attachQuote(); | |
93 setTextInternal(originalText()); | 181 setTextInternal(originalText()); |
94 RenderText::computePreferredLogicalWidths(lead); | 182 RenderText::computePreferredLogicalWidths(lead); |
95 } | 183 } |
96 | 184 |
97 const QuotesData* RenderQuote::quotesData() const | 185 const QuotesData* RenderQuote::quotesData() const |
98 { | 186 { |
99 if (QuotesData* customQuotes = style()->quotes()) | 187 if (QuotesData* customQuotes = style()->quotes()) |
100 return customQuotes; | 188 return customQuotes; |
101 | 189 |
102 AtomicString language = style()->locale(); | 190 AtomicString language = style()->locale(); |
103 if (language.isNull()) | 191 if (language.isNull()) |
104 return basicQuotesData(); | 192 return basicQuotesData(); |
105 const QuotesData* quotes = quotesDataLanguageMap().get(language); | 193 const QuotesData* quotes = quotesDataLanguageMap().get(language); |
106 if (!quotes) | 194 if (!quotes) |
107 return basicQuotesData(); | 195 return basicQuotesData(); |
108 return quotes; | 196 return quotes; |
109 } | 197 } |
110 | 198 |
111 void RenderQuote::attachQuote() | 199 void RenderQuote::rendererSubtreeAttached(RenderObject* renderer) |
112 { | 200 { |
113 ASSERT(view()); | 201 ASSERT(renderer->view()); |
114 ASSERT(!m_attached); | 202 if (!renderer->view()->hasRenderQuotes()) |
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(); | |
121 return; | 203 return; |
122 } | 204 for (RenderObject* descendant = renderer; descendant; descendant = descendan
t->nextInPreOrder(renderer)) |
123 | 205 if (descendant->isQuote()) { |
124 if (!view()->renderQuoteHead()) { | 206 toRenderQuote(descendant)->placeQuote(); |
125 view()->setRenderQuoteHead(this); | 207 break; |
126 m_attached = true; | 208 } |
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 } | 209 } |
152 | 210 |
153 void RenderQuote::detachQuote() | 211 void RenderQuote::rendererRemovedFromTree(RenderObject* renderer) |
154 { | 212 { |
155 ASSERT(!m_next || m_next->m_attached); | 213 ASSERT(renderer->view()); |
156 ASSERT(!m_previous || m_previous->m_attached); | 214 if (!renderer->view()->hasRenderQuotes()) |
157 if (!m_attached) | |
158 return; | 215 return; |
159 if (m_previous) | 216 for (RenderObject* descendant = renderer; descendant; descendant = descendan
t->nextInPreOrder(renderer)) |
160 m_previous->m_next = m_next; | 217 if (descendant->isQuote()) { |
161 else if (view()) | 218 RenderQuote* removedQuote = toRenderQuote(descendant); |
162 view()->setRenderQuoteHead(m_next); | 219 RenderQuote* lastQuoteBefore = removedQuote->m_previous; |
163 if (m_next) | 220 removedQuote->m_previous = 0; |
164 m_next->m_previous = m_previous; | 221 int depth = removedQuote->m_depth; |
165 if (!documentBeingDestroyed()) { | 222 for (descendant = descendant->nextInPreOrder(renderer); descendant;
descendant = descendant->nextInPreOrder(renderer)) |
166 for (RenderQuote* quote = m_next; quote; quote = quote->m_next) | 223 if (descendant->isQuote()) |
167 quote->updateDepth(); | 224 removedQuote = toRenderQuote(descendant); |
168 } | 225 RenderQuote* quoteAfter = removedQuote->m_next; |
169 m_attached = false; | 226 removedQuote->m_next = 0; |
170 m_next = 0; | 227 if (lastQuoteBefore) |
171 m_previous = 0; | 228 lastQuoteBefore->m_next = quoteAfter; |
172 m_depth = 0; | 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 } |
173 } | 242 } |
174 | 243 |
175 void RenderQuote::updateDepth() | 244 void RenderQuote::styleDidChange(StyleDifference diff, const RenderStyle* oldSty
le) |
176 { | 245 { |
177 ASSERT(m_attached); | 246 const QuotesData* newQuotes = style()->quotes(); |
178 int oldDepth = m_depth; | 247 const QuotesData* oldQuotes = oldStyle ? oldStyle->quotes() : 0; |
179 m_depth = 0; | 248 if (!QuotesData::equals(newQuotes, oldQuotes)) |
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--; | |
191 break; | |
192 } | |
193 } | |
194 // FIXME: Don't call setNeedsLayout or dirty our preferred widths during lay
out. | |
195 // This is likely to fail anyway as one of our ancestor will call setNeedsLa
yout(false), | |
196 // preventing the future layout to occur on |this|. The solution is to move
that to a | |
197 // pre-layout phase. | |
198 if (oldDepth != m_depth) | |
199 setNeedsLayoutAndPrefWidthsRecalc(); | 249 setNeedsLayoutAndPrefWidthsRecalc(); |
| 250 RenderText::styleDidChange(diff, oldStyle); |
200 } | 251 } |
201 | 252 |
202 } // namespace WebCore | 253 } // namespace WebCore |
OLD | NEW |