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