| 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 |