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 WebCore {
     33 
     34 RenderQuote::RenderQuote(Document* node, QuoteType quote)
     35     : RenderInline(0)
     36     , m_type(quote)
     37     , m_depth(0)
     38     , m_next(0)
     39     , m_previous(0)
     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::willBeDestroyed()
     52 {
     53     detachQuote();
     54     RenderInline::willBeDestroyed();
     55 }
     56 
     57 void RenderQuote::willBeRemovedFromTree()
     58 {
     59     RenderInline::willBeRemovedFromTree();
     60     detachQuote();
     61 }
     62 
     63 void RenderQuote::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
     64 {
     65     RenderInline::styleDidChange(diff, oldStyle);
     66     updateText();
     67 }
     68 
     69 struct Language {
     70     const char* lang;
     71     UChar open1;
     72     UChar close1;
     73     UChar open2;
     74     UChar close2;
     75     QuotesData* data;
     76 
     77     bool operator<(const Language& b) const { return strcmp(lang, b.lang) < 0; }
     78 };
     79 
     80 // Table of quotes from http://www.whatwg.org/specs/web-apps/current-work/multipage/rendering.html#quote
     81 Language languages[] = {
     82     { "af",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
     83     { "agq",           0x201e, 0x201d, 0x201a, 0x2019, 0 },
     84     { "ak",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
     85     { "am",            0x00ab, 0x00bb, 0x2039, 0x203a, 0 },
     86     { "ar",            0x201d, 0x201c, 0x2019, 0x2018, 0 },
     87     { "asa",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
     88     { "az-cyrl",       0x00ab, 0x00bb, 0x2039, 0x203a, 0 },
     89     { "bas",           0x00ab, 0x00bb, 0x201e, 0x201c, 0 },
     90     { "bem",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
     91     { "bez",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
     92     { "bg",            0x201e, 0x201c, 0x201a, 0x2018, 0 },
     93     { "bm",            0x00ab, 0x00bb, 0x201c, 0x201d, 0 },
     94     { "bn",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
     95     { "br",            0x00ab, 0x00bb, 0x2039, 0x203a, 0 },
     96     { "brx",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
     97     { "bs-cyrl" ,      0x201e, 0x201c, 0x201a, 0x2018, 0 },
     98     { "ca",            0x201c, 0x201d, 0x00ab, 0x00bb, 0 },
     99     { "cgg",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
    100     { "chr",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
    101     { "cs",            0x201e, 0x201c, 0x201a, 0x2018, 0 },
    102     { "da",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    103     { "dav",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
    104     { "de",            0x201e, 0x201c, 0x201a, 0x2018, 0 },
    105     { "de-ch",         0x00ab, 0x00bb, 0x2039, 0x203a, 0 },
    106     { "dje",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
    107     { "dua",           0x00ab, 0x00bb, 0x2018, 0x2019, 0 },
    108     { "dyo",           0x00ab, 0x00bb, 0x201c, 0x201d, 0 },
    109     { "dz",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    110     { "ebu",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
    111     { "ee",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    112     { "el",            0x00ab, 0x00bb, 0x201c, 0x201d, 0 },
    113     { "en",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    114     { "en-gb",         0x201c, 0x201d, 0x2018, 0x2019, 0 },
    115     { "es",            0x201c, 0x201d, 0x00ab, 0x00bb, 0 },
    116     { "et",            0x201e, 0x201c, 0x201a, 0x2018, 0 },
    117     { "eu",            0x201c, 0x201d, 0x00ab, 0x00bb, 0 },
    118     { "ewo",           0x00ab, 0x00bb, 0x201c, 0x201d, 0 },
    119     { "fa",            0x00ab, 0x00bb, 0x2039, 0x203a, 0 },
    120     { "ff",            0x201e, 0x201d, 0x201a, 0x2019, 0 },
    121     { "fi",            0x201d, 0x201d, 0x2019, 0x2019, 0 },
    122     { "fr",            0x00ab, 0x00bb, 0x00ab, 0x00bb, 0 },
    123     { "fr-ca",         0x00ab, 0x00bb, 0x2039, 0x203a, 0 },
    124     { "fr-ch",         0x00ab, 0x00bb, 0x2039, 0x203a, 0 },
    125     { "gsw",           0x00ab, 0x00bb, 0x2039, 0x203a, 0 },
    126     { "gu",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    127     { "guz",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
    128     { "ha",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    129     { "he",            0x0022, 0x0022, 0x0027, 0x0027, 0 },
    130     { "hi",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    131     { "hr",            0x201e, 0x201c, 0x201a, 0x2018, 0 },
    132     { "hu",            0x201e, 0x201d, 0x00bb, 0x00ab, 0 },
    133     { "id",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    134     { "ig",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    135     { "it",            0x00ab, 0x00bb, 0x201c, 0x201d, 0 },
    136     { "ja",            0x300c, 0x300d, 0x300e, 0x300f, 0 },
    137     { "jgo",           0x00ab, 0x00bb, 0x2039, 0x203a, 0 },
    138     { "jmc",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
    139     { "kab",           0x00ab, 0x00bb, 0x201c, 0x201d, 0 },
    140     { "kam",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
    141     { "kde",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
    142     { "kea",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
    143     { "khq",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
    144     { "ki",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    145     { "kkj",           0x00ab, 0x00bb, 0x2039, 0x203a, 0 },
    146     { "kln",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
    147     { "km",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    148     { "kn",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    149     { "ko",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    150     { "ksb",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
    151     { "ksf",           0x00ab, 0x00bb, 0x2018, 0x2019, 0 },
    152     { "lag",           0x201d, 0x201d, 0x2019, 0x2019, 0 },
    153     { "lg",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    154     { "ln",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    155     { "lo",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    156     { "lt",            0x201e, 0x201c, 0x201e, 0x201c, 0 },
    157     { "lu",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    158     { "luo",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
    159     { "luy",           0x201e, 0x201c, 0x201a, 0x2018, 0 },
    160     { "lv",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    161     { "mas",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
    162     { "mer",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
    163     { "mfe",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
    164     { "mg",            0x00ab, 0x00bb, 0x201c, 0x201d, 0 },
    165     { "mgo",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
    166     { "mk",            0x201e, 0x201c, 0x201a, 0x2018, 0 },
    167     { "ml",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    168     { "mr",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    169     { "ms",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    170     { "mua",           0x00ab, 0x00bb, 0x201c, 0x201d, 0 },
    171     { "my",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    172     { "naq",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
    173     { "nb",            0x00ab, 0x00bb, 0x2018, 0x2019, 0 },
    174     { "nd",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    175     { "nl",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    176     { "nmg",           0x201e, 0x201d, 0x00ab, 0x00bb, 0 },
    177     { "nn",            0x00ab, 0x00bb, 0x2018, 0x2019, 0 },
    178     { "nnh",           0x00ab, 0x00bb, 0x201c, 0x201d, 0 },
    179     { "nus",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
    180     { "nyn",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
    181     { "pl",            0x201e, 0x201d, 0x00ab, 0x00bb, 0 },
    182     { "pt",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    183     { "pt-pt",         0x00ab, 0x00bb, 0x201c, 0x201d, 0 },
    184     { "rn",            0x201d, 0x201d, 0x2019, 0x2019, 0 },
    185     { "ro",            0x201e, 0x201d, 0x00ab, 0x00bb, 0 },
    186     { "rof",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
    187     { "ru",            0x00ab, 0x00bb, 0x201e, 0x201c, 0 },
    188     { "rw",            0x00ab, 0x00bb, 0x2018, 0x2019, 0 },
    189     { "rwk",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
    190     { "saq",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
    191     { "sbp",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
    192     { "seh",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
    193     { "ses",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
    194     { "sg",            0x00ab, 0x00bb, 0x201c, 0x201d, 0 },
    195     { "shi",           0x00ab, 0x00bb, 0x201e, 0x201d, 0 },
    196     { "shi-tfng",      0x00ab, 0x00bb, 0x201e, 0x201d, 0 },
    197     { "si",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    198     { "sk",            0x201e, 0x201c, 0x201a, 0x2018, 0 },
    199     { "sl",            0x201e, 0x201c, 0x201a, 0x2018, 0 },
    200     { "sn",            0x201d, 0x201d, 0x2019, 0x2019, 0 },
    201     { "so",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    202     { "sq",            0x201e, 0x201c, 0x201a, 0x2018, 0 },
    203     { "sr",            0x201e, 0x201c, 0x201a, 0x2018, 0 },
    204     { "sr-latn",       0x201e, 0x201c, 0x201a, 0x2018, 0 },
    205     { "sv",            0x201d, 0x201d, 0x2019, 0x2019, 0 },
    206     { "sw",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    207     { "swc",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
    208     { "ta",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    209     { "te",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    210     { "teo",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
    211     { "th",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    212     { "ti-er",         0x2018, 0x2019, 0x201c, 0x201d, 0 },
    213     { "to",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    214     { "tr",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    215     { "twq",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
    216     { "tzm",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
    217     { "uk",            0x00ab, 0x00bb, 0x201e, 0x201c, 0 },
    218     { "ur",            0x201d, 0x201c, 0x2019, 0x2018, 0 },
    219     { "vai",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
    220     { "vai-latn",      0x201c, 0x201d, 0x2018, 0x2019, 0 },
    221     { "vi",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    222     { "vun",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
    223     { "xh",            0x2018, 0x2019, 0x201c, 0x201d, 0 },
    224     { "xog",           0x201c, 0x201d, 0x2018, 0x2019, 0 },
    225     { "yav",           0x00ab, 0x00bb, 0x00ab, 0x00bb, 0 },
    226     { "yo",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    227     { "zh",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    228     { "zh-hant",       0x300c, 0x300d, 0x300e, 0x300f, 0 },
    229     { "zu",            0x201c, 0x201d, 0x2018, 0x2019, 0 },
    230 };
    231 
    232 const QuotesData* quotesDataForLanguage(const AtomicString& lang)
    233 {
    234     if (lang.isNull())
    235         return 0;
    236 
    237     // This could be just a hash table, but doing that adds 200k to RenderQuote.o
    238     Language* languagesEnd = languages + WTF_ARRAY_LENGTH(languages);
    239     CString lowercaseLang = lang.string().lower().utf8();
    240     Language key = { lowercaseLang.data(), 0, 0, 0, 0, 0 };
    241     Language* match = std::lower_bound(languages, languagesEnd, key);
    242     if (match == languagesEnd || strcmp(match->lang, key.lang))
    243         return 0;
    244 
    245     if (!match->data)
    246         match->data = QuotesData::create(match->open1, match->close1, match->open2, match->close2).leakRef();
    247 
    248     return match->data;
    249 }
    250 
    251 static const QuotesData* basicQuotesData()
    252 {
    253     // FIXME: The default quotes should be the fancy quotes for "en".
    254     static QuotesData* staticBasicQuotes = QuotesData::create('"', '"', '\'', '\'').leakRef();
    255     return staticBasicQuotes;
    256 }
    257 
    258 void RenderQuote::updateText()
    259 {
    260     String text = computeText();
    261     if (m_text == text)
    262         return;
    263 
    264     m_text = text;
    265 
    266     while (RenderObject* child = lastChild())
    267         child->destroy();
    268 
    269     RenderTextFragment* fragment = new RenderTextFragment(document(), m_text.impl());
    270     fragment->setStyle(style());
    271     addChild(fragment);
    272 }
    273 
    274 String RenderQuote::computeText() const
    275 {
    276     switch (m_type) {
    277     case NO_OPEN_QUOTE:
    278     case NO_CLOSE_QUOTE:
    279         return emptyString();
    280     case CLOSE_QUOTE:
    281         return quotesData()->getCloseQuote(m_depth - 1).impl();
    282     case OPEN_QUOTE:
    283         return quotesData()->getOpenQuote(m_depth).impl();
    284     }
    285     ASSERT_NOT_REACHED();
    286     return emptyString();
    287 }
    288 
    289 const QuotesData* RenderQuote::quotesData() const
    290 {
    291     if (const QuotesData* customQuotes = style()->quotes())
    292         return customQuotes;
    293 
    294     if (const QuotesData* quotes = quotesDataForLanguage(style()->locale()))
    295         return quotes;
    296 
    297     return basicQuotesData();
    298 }
    299 
    300 void RenderQuote::attachQuote()
    301 {
    302     ASSERT(view());
    303     ASSERT(!m_attached);
    304     ASSERT(!m_next && !m_previous);
    305     ASSERT(isRooted());
    306 
    307     if (!view()->renderQuoteHead()) {
    308         view()->setRenderQuoteHead(this);
    309         m_attached = true;
    310         return;
    311     }
    312 
    313     for (RenderObject* predecessor = previousInPreOrder(); predecessor; predecessor = predecessor->previousInPreOrder()) {
    314         // Skip unattached predecessors to avoid having stale m_previous pointers
    315         // if the previous node is never attached and is then destroyed.
    316         if (!predecessor->isQuote() || !toRenderQuote(predecessor)->isAttached())
    317             continue;
    318         m_previous = toRenderQuote(predecessor);
    319         m_next = m_previous->m_next;
    320         m_previous->m_next = this;
    321         if (m_next)
    322             m_next->m_previous = this;
    323         break;
    324     }
    325 
    326     if (!m_previous) {
    327         m_next = view()->renderQuoteHead();
    328         view()->setRenderQuoteHead(this);
    329         if (m_next)
    330             m_next->m_previous = this;
    331     }
    332     m_attached = true;
    333 
    334     for (RenderQuote* quote = this; quote; quote = quote->m_next)
    335         quote->updateDepth();
    336 
    337     ASSERT(!m_next || m_next->m_attached);
    338     ASSERT(!m_next || m_next->m_previous == this);
    339     ASSERT(!m_previous || m_previous->m_attached);
    340     ASSERT(!m_previous || m_previous->m_next == this);
    341 }
    342 
    343 void RenderQuote::detachQuote()
    344 {
    345     ASSERT(!m_next || m_next->m_attached);
    346     ASSERT(!m_previous || m_previous->m_attached);
    347     if (!m_attached)
    348         return;
    349     if (m_previous)
    350         m_previous->m_next = m_next;
    351     else if (view())
    352         view()->setRenderQuoteHead(m_next);
    353     if (m_next)
    354         m_next->m_previous = m_previous;
    355     if (!documentBeingDestroyed()) {
    356         for (RenderQuote* quote = m_next; quote; quote = quote->m_next)
    357             quote->updateDepth();
    358     }
    359     m_attached = false;
    360     m_next = 0;
    361     m_previous = 0;
    362     m_depth = 0;
    363 }
    364 
    365 void RenderQuote::updateDepth()
    366 {
    367     ASSERT(m_attached);
    368     int oldDepth = m_depth;
    369     m_depth = 0;
    370     if (m_previous) {
    371         m_depth = m_previous->m_depth;
    372         switch (m_previous->m_type) {
    373         case OPEN_QUOTE:
    374         case NO_OPEN_QUOTE:
    375             m_depth++;
    376             break;
    377         case CLOSE_QUOTE:
    378         case NO_CLOSE_QUOTE:
    379             if (m_depth)
    380                 m_depth--;
    381             break;
    382         }
    383     }
    384     if (oldDepth != m_depth)
    385         updateText();
    386 }
    387 
    388 } // namespace WebCore
    389