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