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