Home | History | Annotate | Download | only in rendering
      1 /**
      2  * Copyright (C) 2011 Nokia Inc.  All rights reserved.
      3  * Copyright (C) 2012 Google Inc. All rights reserved.
      4  *
      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.
      9  *
     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.
     14  *
     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.
     19  *
     20  */
     21 
     22 #include "config.h"
     23 #include "core/rendering/RenderQuote.h"
     24 
     25 #include "core/rendering/RenderTextFragment.h"
     26 #include "core/rendering/RenderView.h"
     27 #include "wtf/StdLibExtras.h"
     28 #include "wtf/text/AtomicString.h"
     29 
     30 #include <algorithm>
     31 
     32 namespace blink {
     33 
     34 RenderQuote::RenderQuote(Document* node, QuoteType quote)
     35     : RenderInline(0)
     36     , m_type(quote)
     37     , m_depth(0)
     38     , m_next(nullptr)
     39     , m_previous(nullptr)
     40     , m_attached(false)
     41 {
     42     setDocumentForAnonymous(node);
     43 }
     44 
     45 RenderQuote::~RenderQuote()
     46 {
     47     ASSERT(!m_attached);
     48     ASSERT(!m_next && !m_previous);
     49 }
     50 
     51 void RenderQuote::trace(Visitor* visitor)
     52 {
     53     visitor->trace(m_next);
     54     visitor->trace(m_previous);
     55     RenderInline::trace(visitor);
     56 }
     57 
     58 void RenderQuote::willBeDestroyed()
     59 {
     60     detachQuote();
     61     RenderInline::willBeDestroyed();
     62 }
     63 
     64 void RenderQuote::willBeRemovedFromTree()
     65 {
     66     RenderInline::willBeRemovedFromTree();
     67     detachQuote();
     68 }
     69 
     70 void RenderQuote::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
     71 {
     72     RenderInline::styleDidChange(diff, oldStyle);
     73     updateText();
     74 }
     75 
     76 struct Language {
     77     const char* lang;
     78     UChar open1;
     79     UChar close1;
     80     UChar open2;
     81     UChar close2;
     82     QuotesData* data;
     83 
     84     bool operator<(const Language& b) const { return strcmp(lang, b.lang) < 0; }
     85 };
     86 
     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 },
    237 };
    238 
    239 const QuotesData* quotesDataForLanguage(const AtomicString& lang)
    240 {
    241     if (lang.isNull())
    242         return 0;
    243 
    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.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))
    250         return 0;
    251 
    252     if (!match->data)
    253         match->data = QuotesData::create(match->open1, match->close1, match->open2, match->close2).leakRef();
    254 
    255     return match->data;
    256 }
    257 
    258 static const QuotesData* basicQuotesData()
    259 {
    260     // FIXME: The default quotes should be the fancy quotes for "en".
    261     DEFINE_STATIC_REF(QuotesData, staticBasicQuotes, (QuotesData::create('"', '"', '\'', '\'')));
    262     return staticBasicQuotes;
    263 }
    264 
    265 void RenderQuote::updateText()
    266 {
    267     String text = computeText();
    268     if (m_text == text)
    269         return;
    270 
    271     m_text = text;
    272 
    273     while (RenderObject* child = lastChild())
    274         child->destroy();
    275 
    276     RenderTextFragment* fragment = new RenderTextFragment(&document(), m_text.impl());
    277     fragment->setStyle(style());
    278     addChild(fragment);
    279 }
    280 
    281 String RenderQuote::computeText() const
    282 {
    283     switch (m_type) {
    284     case NO_OPEN_QUOTE:
    285     case NO_CLOSE_QUOTE:
    286         return emptyString();
    287     case CLOSE_QUOTE:
    288         return quotesData()->getCloseQuote(m_depth - 1).impl();
    289     case OPEN_QUOTE:
    290         return quotesData()->getOpenQuote(m_depth).impl();
    291     }
    292     ASSERT_NOT_REACHED();
    293     return emptyString();
    294 }
    295 
    296 const QuotesData* RenderQuote::quotesData() const
    297 {
    298     if (const QuotesData* customQuotes = style()->quotes())
    299         return customQuotes;
    300 
    301     if (const QuotesData* quotes = quotesDataForLanguage(style()->locale()))
    302         return quotes;
    303 
    304     return basicQuotesData();
    305 }
    306 
    307 void RenderQuote::attachQuote()
    308 {
    309     ASSERT(view());
    310     ASSERT(!m_attached);
    311     ASSERT(!m_next && !m_previous);
    312     ASSERT(isRooted());
    313 
    314     if (!view()->renderQuoteHead()) {
    315         view()->setRenderQuoteHead(this);
    316         m_attached = true;
    317         return;
    318     }
    319 
    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())
    324             continue;
    325         m_previous = toRenderQuote(predecessor);
    326         m_next = m_previous->m_next;
    327         m_previous->m_next = this;
    328         if (m_next)
    329             m_next->m_previous = this;
    330         break;
    331     }
    332 
    333     if (!m_previous) {
    334         m_next = view()->renderQuoteHead();
    335         view()->setRenderQuoteHead(this);
    336         if (m_next)
    337             m_next->m_previous = this;
    338     }
    339     m_attached = true;
    340 
    341     for (RenderQuote* quote = this; quote; quote = quote->m_next)
    342         quote->updateDepth();
    343 
    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);
    348 }
    349 
    350 void RenderQuote::detachQuote()
    351 {
    352     ASSERT(!m_next || m_next->m_attached);
    353     ASSERT(!m_previous || m_previous->m_attached);
    354     if (!m_attached)
    355         return;
    356     if (m_previous)
    357         m_previous->m_next = m_next;
    358     else if (view())
    359         view()->setRenderQuoteHead(m_next);
    360     if (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();
    365     }
    366     m_attached = false;
    367     m_next = nullptr;
    368     m_previous = nullptr;
    369     m_depth = 0;
    370 }
    371 
    372 void RenderQuote::updateDepth()
    373 {
    374     ASSERT(m_attached);
    375     int oldDepth = m_depth;
    376     m_depth = 0;
    377     if (m_previous) {
    378         m_depth = m_previous->m_depth;
    379         switch (m_previous->m_type) {
    380         case OPEN_QUOTE:
    381         case NO_OPEN_QUOTE:
    382             m_depth++;
    383             break;
    384         case CLOSE_QUOTE:
    385         case NO_CLOSE_QUOTE:
    386             if (m_depth)
    387                 m_depth--;
    388             break;
    389         }
    390     }
    391     if (oldDepth != m_depth)
    392         updateText();
    393 }
    394 
    395 } // namespace blink
    396