1/* 2 * Copyright (C) 2000 Peter Kelly (pmk@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 37namespace blink { 38 39inline 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} 49 50PassRefPtrWillBeRawPtr<ProcessingInstruction> ProcessingInstruction::create(Document& document, const String& target, const String& data) 51{ 52 return adoptRefWillBeNoop(new ProcessingInstruction(document, target, data)); 53} 54 55ProcessingInstruction::~ProcessingInstruction() 56{ 57#if !ENABLE(OILPAN) 58 if (m_sheet) 59 clearSheet(); 60 61 // FIXME: ProcessingInstruction should not be in document here. 62 // However, if we add ASSERT(!inDocument()), fast/xsl/xslt-entity.xml 63 // crashes. We need to investigate ProcessingInstruction lifetime. 64 if (inDocument()) { 65 if (m_isCSS) 66 document().styleEngine()->removeStyleSheetCandidateNode(this); 67 else if (m_isXSL) 68 document().styleEngine()->removeXSLStyleSheet(this); 69 } 70#endif 71} 72 73String ProcessingInstruction::nodeName() const 74{ 75 return m_target; 76} 77 78Node::NodeType ProcessingInstruction::nodeType() const 79{ 80 return PROCESSING_INSTRUCTION_NODE; 81} 82 83PassRefPtrWillBeRawPtr<Node> ProcessingInstruction::cloneNode(bool /*deep*/) 84{ 85 // FIXME: Is it a problem that this does not copy m_localHref? 86 // What about other data members? 87 return create(document(), m_target, m_data); 88} 89 90void ProcessingInstruction::didAttributeChanged() 91{ 92 if (m_sheet) 93 clearSheet(); 94 95 String href; 96 String charset; 97 if (!checkStyleSheet(href, charset)) 98 return; 99 process(href, charset); 100} 101 102bool ProcessingInstruction::checkStyleSheet(String& href, String& charset) 103{ 104 if (m_target != "xml-stylesheet" || !document().frame() || parentNode() != document()) 105 return false; 106 107 // see http://www.w3.org/TR/xml-stylesheet/ 108 // ### support stylesheet included in a fragment of this (or another) document 109 // ### make sure this gets called when adding from javascript 110 bool attrsOk; 111 const HashMap<String, String> attrs = parseAttributes(m_data, attrsOk); 112 if (!attrsOk) 113 return false; 114 HashMap<String, String>::const_iterator i = attrs.find("type"); 115 String type; 116 if (i != attrs.end()) 117 type = i->value; 118 119 m_isCSS = type.isEmpty() || type == "text/css"; 120 m_isXSL = (type == "text/xml" || type == "text/xsl" || type == "application/xml" || type == "application/xhtml+xml" || type == "application/rss+xml" || type == "application/atom+xml"); 121 if (!m_isCSS && !m_isXSL) 122 return false; 123 124 href = attrs.get("href"); 125 charset = attrs.get("charset"); 126 String alternate = attrs.get("alternate"); 127 m_alternate = alternate == "yes"; 128 m_title = attrs.get("title"); 129 m_media = attrs.get("media"); 130 131 return !m_alternate || !m_title.isEmpty(); 132} 133 134void ProcessingInstruction::process(const String& href, const String& charset) 135{ 136 if (href.length() > 1 && href[0] == '#') { 137 m_localHref = href.substring(1); 138 // We need to make a synthetic XSLStyleSheet that is embedded. 139 // It needs to be able to kick off import/include loads that 140 // can hang off some parent sheet. 141 if (m_isXSL) { 142 KURL finalURL(ParsedURLString, m_localHref); 143 m_sheet = XSLStyleSheet::createEmbedded(this, finalURL); 144 m_loading = false; 145 } 146 return; 147 } 148 149 clearResource(); 150 151 String url = document().completeURL(href).string(); 152 153 ResourcePtr<StyleSheetResource> resource; 154 FetchRequest request(ResourceRequest(document().completeURL(href)), FetchInitiatorTypeNames::processinginstruction); 155 if (m_isXSL) { 156 resource = document().fetcher()->fetchXSLStyleSheet(request); 157 } else { 158 request.setCharset(charset.isEmpty() ? document().charset() : charset); 159 resource = document().fetcher()->fetchCSSStyleSheet(request); 160 } 161 162 if (resource) { 163 m_loading = true; 164 document().styleEngine()->addPendingSheet(); 165 setResource(resource); 166 } 167} 168 169bool ProcessingInstruction::isLoading() const 170{ 171 if (m_loading) 172 return true; 173 if (!m_sheet) 174 return false; 175 return m_sheet->isLoading(); 176} 177 178bool ProcessingInstruction::sheetLoaded() 179{ 180 if (!isLoading()) { 181 document().styleEngine()->removePendingSheet(this); 182 return true; 183 } 184 return false; 185} 186 187void ProcessingInstruction::setCSSStyleSheet(const String& href, const KURL& baseURL, const String& charset, const CSSStyleSheetResource* sheet) 188{ 189 if (!inDocument()) { 190 ASSERT(!m_sheet); 191 return; 192 } 193 194 ASSERT(m_isCSS); 195 CSSParserContext parserContext(document(), 0, baseURL, charset); 196 197 RefPtrWillBeRawPtr<StyleSheetContents> newSheet = StyleSheetContents::create(href, parserContext); 198 199 RefPtrWillBeRawPtr<CSSStyleSheet> cssSheet = CSSStyleSheet::create(newSheet, this); 200 cssSheet->setDisabled(m_alternate); 201 cssSheet->setTitle(m_title); 202 cssSheet->setMediaQueries(MediaQuerySet::create(m_media)); 203 204 m_sheet = cssSheet.release(); 205 206 // We don't need the cross-origin security check here because we are 207 // getting the sheet text in "strict" mode. This enforces a valid CSS MIME 208 // type. 209 parseStyleSheet(sheet->sheetText(true)); 210} 211 212void ProcessingInstruction::setXSLStyleSheet(const String& href, const KURL& baseURL, const String& sheet) 213{ 214 if (!inDocument()) { 215 ASSERT(!m_sheet); 216 return; 217 } 218 219 ASSERT(m_isXSL); 220 m_sheet = XSLStyleSheet::create(this, href, baseURL); 221 RefPtrWillBeRawPtr<Document> protect(&document()); 222 parseStyleSheet(sheet); 223} 224 225void 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 241void 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 250Node::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 268void 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 291void ProcessingInstruction::clearSheet() 292{ 293 ASSERT(m_sheet); 294 if (m_sheet->isLoading()) 295 document().styleEngine()->removePendingSheet(this); 296 m_sheet.release()->clearOwnerNode(); 297} 298 299void ProcessingInstruction::trace(Visitor* visitor) 300{ 301 visitor->trace(m_sheet); 302 CharacterData::trace(visitor); 303} 304 305} // namespace 306