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
31namespace WebCore {
32
33static 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
40StyleElement::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
49StyleElement::~StyleElement()
50{
51}
52
53void 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
64void 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
81void StyleElement::childrenChanged(Element* element)
82{
83    ASSERT(element);
84    if (m_createdByParser)
85        return;
86
87    process(element);
88}
89
90void StyleElement::finishParsingChildren(Element* element)
91{
92    ASSERT(element);
93    process(element);
94    m_createdByParser = false;
95}
96
97void 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
130void 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
162bool StyleElement::isLoading() const
163{
164    if (m_loading)
165        return true;
166    return m_sheet ? m_sheet->isLoading() : false;
167}
168
169bool 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