1 /* 2 * Copyright (C) 2000 Peter Kelly (pmk (at) post.com) 3 * Copyright (C) 2006, 2008, 2009 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 "core/dom/ProcessingInstruction.h" 23 24 #include "core/FetchInitiatorTypeNames.h" 25 #include "core/css/CSSStyleSheet.h" 26 #include "core/css/MediaList.h" 27 #include "core/css/StyleSheetContents.h" 28 #include "core/dom/Document.h" 29 #include "core/dom/StyleEngine.h" 30 #include "core/fetch/CSSStyleSheetResource.h" 31 #include "core/fetch/FetchRequest.h" 32 #include "core/fetch/ResourceFetcher.h" 33 #include "core/fetch/XSLStyleSheetResource.h" 34 #include "core/xml/XSLStyleSheet.h" 35 #include "core/xml/parser/XMLDocumentParser.h" // for parseAttributes() 36 37 namespace WebCore { 38 39 inline ProcessingInstruction::ProcessingInstruction(Document& document, const String& target, const String& data) 40 : CharacterData(document, data, CreateOther) 41 , m_target(target) 42 , m_loading(false) 43 , m_alternate(false) 44 , m_createdByParser(false) 45 , m_isCSS(false) 46 , m_isXSL(false) 47 { 48 ScriptWrappable::init(this); 49 } 50 51 PassRefPtrWillBeRawPtr<ProcessingInstruction> ProcessingInstruction::create(Document& document, const String& target, const String& data) 52 { 53 return adoptRefWillBeNoop(new ProcessingInstruction(document, target, data)); 54 } 55 56 ProcessingInstruction::~ProcessingInstruction() 57 { 58 #if !ENABLE(OILPAN) 59 if (m_sheet) 60 clearSheet(); 61 62 // FIXME: ProcessingInstruction should not be in document here. 63 // However, if we add ASSERT(!inDocument()), fast/xsl/xslt-entity.xml 64 // crashes. We need to investigate ProcessingInstruction lifetime. 65 if (inDocument()) { 66 if (m_isCSS) 67 document().styleEngine()->removeStyleSheetCandidateNode(this); 68 else if (m_isXSL) 69 document().styleEngine()->removeXSLStyleSheet(this); 70 } 71 #endif 72 } 73 74 String ProcessingInstruction::nodeName() const 75 { 76 return m_target; 77 } 78 79 Node::NodeType ProcessingInstruction::nodeType() const 80 { 81 return PROCESSING_INSTRUCTION_NODE; 82 } 83 84 PassRefPtrWillBeRawPtr<Node> ProcessingInstruction::cloneNode(bool /*deep*/) 85 { 86 // FIXME: Is it a problem that this does not copy m_localHref? 87 // What about other data members? 88 return create(document(), m_target, m_data); 89 } 90 91 void ProcessingInstruction::didAttributeChanged() 92 { 93 if (m_sheet) 94 clearSheet(); 95 96 String href; 97 String charset; 98 if (!checkStyleSheet(href, charset)) 99 return; 100 process(href, charset); 101 } 102 103 bool ProcessingInstruction::checkStyleSheet(String& href, String& charset) 104 { 105 if (m_target != "xml-stylesheet" || !document().frame() || parentNode() != document()) 106 return false; 107 108 // see http://www.w3.org/TR/xml-stylesheet/ 109 // ### support stylesheet included in a fragment of this (or another) document 110 // ### make sure this gets called when adding from javascript 111 bool attrsOk; 112 const HashMap<String, String> attrs = parseAttributes(m_data, attrsOk); 113 if (!attrsOk) 114 return false; 115 HashMap<String, String>::const_iterator i = attrs.find("type"); 116 String type; 117 if (i != attrs.end()) 118 type = i->value; 119 120 m_isCSS = type.isEmpty() || type == "text/css"; 121 m_isXSL = (type == "text/xml" || type == "text/xsl" || type == "application/xml" || type == "application/xhtml+xml" || type == "application/rss+xml" || type == "application/atom+xml"); 122 if (!m_isCSS && !m_isXSL) 123 return false; 124 125 href = attrs.get("href"); 126 charset = attrs.get("charset"); 127 String alternate = attrs.get("alternate"); 128 m_alternate = alternate == "yes"; 129 m_title = attrs.get("title"); 130 m_media = attrs.get("media"); 131 132 return !m_alternate || !m_title.isEmpty(); 133 } 134 135 void ProcessingInstruction::process(const String& href, const String& charset) 136 { 137 if (href.length() > 1 && href[0] == '#') { 138 m_localHref = href.substring(1); 139 // We need to make a synthetic XSLStyleSheet that is embedded. 140 // It needs to be able to kick off import/include loads that 141 // can hang off some parent sheet. 142 if (m_isXSL) { 143 KURL finalURL(ParsedURLString, m_localHref); 144 m_sheet = XSLStyleSheet::createEmbedded(this, finalURL); 145 m_loading = false; 146 } 147 return; 148 } 149 150 clearResource(); 151 152 String url = document().completeURL(href).string(); 153 154 ResourcePtr<StyleSheetResource> resource; 155 FetchRequest request(ResourceRequest(document().completeURL(href)), FetchInitiatorTypeNames::processinginstruction); 156 if (m_isXSL) { 157 resource = document().fetcher()->fetchXSLStyleSheet(request); 158 } else { 159 request.setCharset(charset.isEmpty() ? document().charset() : charset); 160 resource = document().fetcher()->fetchCSSStyleSheet(request); 161 } 162 163 if (resource) { 164 m_loading = true; 165 document().styleEngine()->addPendingSheet(); 166 setResource(resource); 167 } 168 } 169 170 bool ProcessingInstruction::isLoading() const 171 { 172 if (m_loading) 173 return true; 174 if (!m_sheet) 175 return false; 176 return m_sheet->isLoading(); 177 } 178 179 bool ProcessingInstruction::sheetLoaded() 180 { 181 if (!isLoading()) { 182 document().styleEngine()->removePendingSheet(this); 183 return true; 184 } 185 return false; 186 } 187 188 void ProcessingInstruction::setCSSStyleSheet(const String& href, const KURL& baseURL, const String& charset, const CSSStyleSheetResource* sheet) 189 { 190 if (!inDocument()) { 191 ASSERT(!m_sheet); 192 return; 193 } 194 195 ASSERT(m_isCSS); 196 CSSParserContext parserContext(document(), 0, baseURL, charset); 197 198 RefPtrWillBeRawPtr<StyleSheetContents> newSheet = StyleSheetContents::create(href, parserContext); 199 200 RefPtrWillBeRawPtr<CSSStyleSheet> cssSheet = CSSStyleSheet::create(newSheet, this); 201 cssSheet->setDisabled(m_alternate); 202 cssSheet->setTitle(m_title); 203 cssSheet->setMediaQueries(MediaQuerySet::create(m_media)); 204 205 m_sheet = cssSheet.release(); 206 207 // We don't need the cross-origin security check here because we are 208 // getting the sheet text in "strict" mode. This enforces a valid CSS MIME 209 // type. 210 parseStyleSheet(sheet->sheetText(true)); 211 } 212 213 void ProcessingInstruction::setXSLStyleSheet(const String& href, const KURL& baseURL, const String& sheet) 214 { 215 if (!inDocument()) { 216 ASSERT(!m_sheet); 217 return; 218 } 219 220 ASSERT(m_isXSL); 221 m_sheet = XSLStyleSheet::create(this, href, baseURL); 222 parseStyleSheet(sheet); 223 } 224 225 void ProcessingInstruction::parseStyleSheet(const String& sheet) 226 { 227 if (m_isCSS) 228 toCSSStyleSheet(m_sheet.get())->contents()->parseString(sheet); 229 else if (m_isXSL) 230 toXSLStyleSheet(m_sheet.get())->parseString(sheet); 231 232 clearResource(); 233 m_loading = false; 234 235 if (m_isCSS) 236 toCSSStyleSheet(m_sheet.get())->contents()->checkLoaded(); 237 else if (m_isXSL) 238 toXSLStyleSheet(m_sheet.get())->checkLoaded(); 239 } 240 241 void ProcessingInstruction::setCSSStyleSheet(PassRefPtrWillBeRawPtr<CSSStyleSheet> sheet) 242 { 243 ASSERT(!resource()); 244 ASSERT(!m_loading); 245 m_sheet = sheet; 246 sheet->setTitle(m_title); 247 sheet->setDisabled(m_alternate); 248 } 249 250 Node::InsertionNotificationRequest ProcessingInstruction::insertedInto(ContainerNode* insertionPoint) 251 { 252 CharacterData::insertedInto(insertionPoint); 253 if (!insertionPoint->inDocument()) 254 return InsertionDone; 255 256 String href; 257 String charset; 258 bool isValid = checkStyleSheet(href, charset); 259 if (m_isCSS) 260 document().styleEngine()->addStyleSheetCandidateNode(this, m_createdByParser); 261 else if (m_isXSL) 262 document().styleEngine()->addXSLStyleSheet(this, m_createdByParser); 263 if (isValid) 264 process(href, charset); 265 return InsertionDone; 266 } 267 268 void ProcessingInstruction::removedFrom(ContainerNode* insertionPoint) 269 { 270 CharacterData::removedFrom(insertionPoint); 271 if (!insertionPoint->inDocument()) 272 return; 273 274 if (m_isCSS) 275 document().styleEngine()->removeStyleSheetCandidateNode(this); 276 else if (m_isXSL) 277 document().styleEngine()->removeXSLStyleSheet(this); 278 279 RefPtrWillBeRawPtr<StyleSheet> removedSheet = m_sheet; 280 281 if (m_sheet) { 282 ASSERT(m_sheet->ownerNode() == this); 283 clearSheet(); 284 } 285 286 // If we're in document teardown, then we don't need to do any notification of our sheet's removal. 287 if (document().isActive()) 288 document().removedStyleSheet(removedSheet.get()); 289 } 290 291 void ProcessingInstruction::clearSheet() 292 { 293 ASSERT(m_sheet); 294 if (m_sheet->isLoading()) 295 document().styleEngine()->removePendingSheet(this); 296 m_sheet.release()->clearOwnerNode(); 297 } 298 299 void ProcessingInstruction::trace(Visitor* visitor) 300 { 301 visitor->trace(m_sheet); 302 CharacterData::trace(visitor); 303 } 304 305 } // namespace 306