Home | History | Annotate | Download | only in dom
      1 /*
      2  * Copyright (C) 2006, 2007 Rob Buis
      3  * Copyright (C) 2008 Apple, 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 #include "config.h"
     22 #include "StyleElement.h"
     23 
     24 #include "Attribute.h"
     25 #include "Document.h"
     26 #include "Element.h"
     27 #include "MediaList.h"
     28 #include "MediaQueryEvaluator.h"
     29 #include "ScriptableDocumentParser.h"
     30 
     31 namespace WebCore {
     32 
     33 static bool isValidStyleChild(Node* node)
     34 {
     35     ASSERT(node);
     36     Node::NodeType nodeType = node->nodeType();
     37     return nodeType == Node::TEXT_NODE || nodeType == Node::CDATA_SECTION_NODE;
     38 }
     39 
     40 StyleElement::StyleElement(Document* document, bool createdByParser)
     41     : m_createdByParser(createdByParser)
     42     , m_loading(false)
     43     , m_startLineNumber(0)
     44 {
     45     if (createdByParser && document && document->scriptableDocumentParser())
     46         m_startLineNumber = document->scriptableDocumentParser()->lineNumber();
     47 }
     48 
     49 StyleElement::~StyleElement()
     50 {
     51 }
     52 
     53 void StyleElement::insertedIntoDocument(Document* document, Element* element)
     54 {
     55     ASSERT(document);
     56     ASSERT(element);
     57     document->addStyleSheetCandidateNode(element, m_createdByParser);
     58     if (m_createdByParser)
     59         return;
     60 
     61     process(element);
     62 }
     63 
     64 void StyleElement::removedFromDocument(Document* document, Element* element)
     65 {
     66     ASSERT(document);
     67     ASSERT(element);
     68     document->removeStyleSheetCandidateNode(element);
     69 
     70     if (m_sheet) {
     71         ASSERT(m_sheet->ownerNode() == element);
     72         m_sheet->clearOwnerNode();
     73         m_sheet = 0;
     74     }
     75 
     76     // If we're in document teardown, then we don't need to do any notification of our sheet's removal.
     77     if (document->renderer())
     78         document->styleSelectorChanged(DeferRecalcStyle);
     79 }
     80 
     81 void StyleElement::childrenChanged(Element* element)
     82 {
     83     ASSERT(element);
     84     if (m_createdByParser)
     85         return;
     86 
     87     process(element);
     88 }
     89 
     90 void StyleElement::finishParsingChildren(Element* element)
     91 {
     92     ASSERT(element);
     93     process(element);
     94     m_createdByParser = false;
     95 }
     96 
     97 void StyleElement::process(Element* e)
     98 {
     99     if (!e || !e->inDocument())
    100         return;
    101 
    102     unsigned resultLength = 0;
    103     for (Node* c = e->firstChild(); c; c = c->nextSibling()) {
    104         if (isValidStyleChild(c)) {
    105             unsigned length = c->nodeValue().length();
    106             if (length > std::numeric_limits<unsigned>::max() - resultLength) {
    107                 createSheet(e, m_startLineNumber, "");
    108                 return;
    109             }
    110             resultLength += length;
    111         }
    112     }
    113     UChar* text;
    114     String sheetText = String::createUninitialized(resultLength, text);
    115 
    116     UChar* p = text;
    117     for (Node* c = e->firstChild(); c; c = c->nextSibling()) {
    118         if (isValidStyleChild(c)) {
    119             String nodeValue = c->nodeValue();
    120             unsigned nodeLength = nodeValue.length();
    121             memcpy(p, nodeValue.characters(), nodeLength * sizeof(UChar));
    122             p += nodeLength;
    123         }
    124     }
    125     ASSERT(p == text + resultLength);
    126 
    127     createSheet(e, m_startLineNumber, sheetText);
    128 }
    129 
    130 void StyleElement::createSheet(Element* e, int startLineNumber, const String& text)
    131 {
    132     ASSERT(e);
    133     ASSERT(e->inDocument());
    134     Document* document = e->document();
    135     if (m_sheet) {
    136         if (m_sheet->isLoading())
    137             document->removePendingSheet();
    138         m_sheet = 0;
    139     }
    140 
    141     // If type is empty or CSS, this is a CSS style sheet.
    142     const AtomicString& type = this->type();
    143     if (type.isEmpty() || (e->isHTMLElement() ? equalIgnoringCase(type, "text/css") : (type == "text/css"))) {
    144         RefPtr<MediaList> mediaList = MediaList::create(media(), e->isHTMLElement());
    145         MediaQueryEvaluator screenEval("screen", true);
    146         MediaQueryEvaluator printEval("print", true);
    147         if (screenEval.eval(mediaList.get()) || printEval.eval(mediaList.get())) {
    148             document->addPendingSheet();
    149             m_loading = true;
    150             m_sheet = CSSStyleSheet::create(e, String(), KURL(), document->inputEncoding());
    151             m_sheet->parseStringAtLine(text, !document->inQuirksMode(), startLineNumber);
    152             m_sheet->setMedia(mediaList.get());
    153             m_sheet->setTitle(e->title());
    154             m_loading = false;
    155         }
    156     }
    157 
    158     if (m_sheet)
    159         m_sheet->checkLoaded();
    160 }
    161 
    162 bool StyleElement::isLoading() const
    163 {
    164     if (m_loading)
    165         return true;
    166     return m_sheet ? m_sheet->isLoading() : false;
    167 }
    168 
    169 bool StyleElement::sheetLoaded(Document* document)
    170 {
    171     ASSERT(document);
    172     if (isLoading())
    173         return false;
    174 
    175     document->removePendingSheet();
    176     return true;
    177 }
    178 
    179 }
    180