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