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 "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/DocumentStyleSheetCollection.h" 30 #include "core/loader/cache/CSSStyleSheetResource.h" 31 #include "core/loader/cache/FetchRequest.h" 32 #include "core/loader/cache/ResourceFetcher.h" 33 #include "core/loader/cache/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 : Node(document, CreateOther) 41 , m_target(target) 42 , m_data(data) 43 , m_resource(0) 44 , m_loading(false) 45 , m_alternate(false) 46 , m_createdByParser(false) 47 , m_isCSS(false) 48 , m_isXSL(false) 49 { 50 ScriptWrappable::init(this); 51 } 52 53 PassRefPtr<ProcessingInstruction> ProcessingInstruction::create(Document* document, const String& target, const String& data) 54 { 55 return adoptRef(new ProcessingInstruction(document, target, data)); 56 } 57 58 ProcessingInstruction::~ProcessingInstruction() 59 { 60 if (m_sheet) 61 m_sheet->clearOwnerNode(); 62 63 if (m_resource) 64 m_resource->removeClient(this); 65 66 if (inDocument()) 67 document()->styleSheetCollection()->removeStyleSheetCandidateNode(this); 68 } 69 70 void ProcessingInstruction::setData(const String& data) 71 { 72 int oldLength = m_data.length(); 73 m_data = data; 74 document()->textRemoved(this, 0, oldLength); 75 checkStyleSheet(); 76 } 77 78 String ProcessingInstruction::nodeName() const 79 { 80 return m_target; 81 } 82 83 Node::NodeType ProcessingInstruction::nodeType() const 84 { 85 return PROCESSING_INSTRUCTION_NODE; 86 } 87 88 String ProcessingInstruction::nodeValue() const 89 { 90 return m_data; 91 } 92 93 void ProcessingInstruction::setNodeValue(const String& nodeValue) 94 { 95 setData(nodeValue); 96 } 97 98 PassRefPtr<Node> ProcessingInstruction::cloneNode(bool /*deep*/) 99 { 100 // FIXME: Is it a problem that this does not copy m_localHref? 101 // What about other data members? 102 return create(document(), m_target, m_data); 103 } 104 105 void ProcessingInstruction::checkStyleSheet() 106 { 107 if (m_target == "xml-stylesheet" && document()->frame() && parentNode() == document()) { 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; 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" || 122 type == "application/xhtml+xml" || type == "application/rss+xml" || type == "application/atom+xml"); 123 if (!m_isCSS && !m_isXSL) 124 return; 125 126 String href = attrs.get("href"); 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 if (m_alternate && m_title.isEmpty()) 133 return; 134 135 if (href.length() > 1 && href[0] == '#') { 136 m_localHref = href.substring(1); 137 // We need to make a synthetic XSLStyleSheet that is embedded. It needs to be able 138 // to kick off import/include loads that can hang off some parent sheet. 139 if (m_isXSL) { 140 KURL finalURL(ParsedURLString, m_localHref); 141 m_sheet = XSLStyleSheet::createEmbedded(this, finalURL); 142 m_loading = false; 143 } 144 } else { 145 if (m_resource) { 146 m_resource->removeClient(this); 147 m_resource = 0; 148 } 149 150 String url = document()->completeURL(href).string(); 151 if (!dispatchBeforeLoadEvent(url)) 152 return; 153 154 m_loading = true; 155 document()->styleSheetCollection()->addPendingSheet(); 156 FetchRequest request(ResourceRequest(document()->completeURL(href)), FetchInitiatorTypeNames::processinginstruction); 157 if (m_isXSL) 158 m_resource = document()->fetcher()->requestXSLStyleSheet(request); 159 else 160 { 161 String charset = attrs.get("charset"); 162 if (charset.isEmpty()) 163 charset = document()->charset(); 164 request.setCharset(charset); 165 166 m_resource = document()->fetcher()->requestCSSStyleSheet(request); 167 } 168 if (m_resource) 169 m_resource->addClient(this); 170 else { 171 // The request may have been denied if (for example) the stylesheet is local and the document is remote. 172 m_loading = false; 173 document()->styleSheetCollection()->removePendingSheet(); 174 } 175 } 176 } 177 } 178 179 bool ProcessingInstruction::isLoading() const 180 { 181 if (m_loading) 182 return true; 183 if (!m_sheet) 184 return false; 185 return m_sheet->isLoading(); 186 } 187 188 bool ProcessingInstruction::sheetLoaded() 189 { 190 if (!isLoading()) { 191 document()->styleSheetCollection()->removePendingSheet(); 192 return true; 193 } 194 return false; 195 } 196 197 void ProcessingInstruction::setCSSStyleSheet(const String& href, const KURL& baseURL, const String& charset, const CSSStyleSheetResource* sheet) 198 { 199 if (!inDocument()) { 200 ASSERT(!m_sheet); 201 return; 202 } 203 204 ASSERT(m_isCSS); 205 CSSParserContext parserContext(document(), baseURL, charset); 206 207 RefPtr<StyleSheetContents> newSheet = StyleSheetContents::create(href, parserContext); 208 209 RefPtr<CSSStyleSheet> cssSheet = CSSStyleSheet::create(newSheet, this); 210 cssSheet->setDisabled(m_alternate); 211 cssSheet->setTitle(m_title); 212 cssSheet->setMediaQueries(MediaQuerySet::create(m_media)); 213 214 m_sheet = cssSheet.release(); 215 216 // We don't need the cross-origin security check here because we are 217 // getting the sheet text in "strict" mode. This enforces a valid CSS MIME 218 // type. 219 parseStyleSheet(sheet->sheetText(true)); 220 } 221 222 void ProcessingInstruction::setXSLStyleSheet(const String& href, const KURL& baseURL, const String& sheet) 223 { 224 ASSERT(m_isXSL); 225 m_sheet = XSLStyleSheet::create(this, href, baseURL); 226 parseStyleSheet(sheet); 227 } 228 229 void ProcessingInstruction::parseStyleSheet(const String& sheet) 230 { 231 if (m_isCSS) 232 static_cast<CSSStyleSheet*>(m_sheet.get())->contents()->parseString(sheet); 233 else if (m_isXSL) 234 static_cast<XSLStyleSheet*>(m_sheet.get())->parseString(sheet); 235 236 if (m_resource) 237 m_resource->removeClient(this); 238 m_resource = 0; 239 240 m_loading = false; 241 242 if (m_isCSS) 243 static_cast<CSSStyleSheet*>(m_sheet.get())->contents()->checkLoaded(); 244 else if (m_isXSL) 245 static_cast<XSLStyleSheet*>(m_sheet.get())->checkLoaded(); 246 } 247 248 void ProcessingInstruction::setCSSStyleSheet(PassRefPtr<CSSStyleSheet> sheet) 249 { 250 ASSERT(!m_resource); 251 ASSERT(!m_loading); 252 m_sheet = sheet; 253 sheet->setTitle(m_title); 254 sheet->setDisabled(m_alternate); 255 } 256 257 bool ProcessingInstruction::offsetInCharacters() const 258 { 259 return true; 260 } 261 262 int ProcessingInstruction::maxCharacterOffset() const 263 { 264 return static_cast<int>(m_data.length()); 265 } 266 267 void ProcessingInstruction::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const 268 { 269 if (!sheet()) 270 return; 271 272 addSubresourceURL(urls, sheet()->baseURL()); 273 } 274 275 Node::InsertionNotificationRequest ProcessingInstruction::insertedInto(ContainerNode* insertionPoint) 276 { 277 Node::insertedInto(insertionPoint); 278 if (!insertionPoint->inDocument()) 279 return InsertionDone; 280 document()->styleSheetCollection()->addStyleSheetCandidateNode(this, m_createdByParser); 281 checkStyleSheet(); 282 return InsertionDone; 283 } 284 285 void ProcessingInstruction::removedFrom(ContainerNode* insertionPoint) 286 { 287 Node::removedFrom(insertionPoint); 288 if (!insertionPoint->inDocument()) 289 return; 290 291 document()->styleSheetCollection()->removeStyleSheetCandidateNode(this); 292 293 RefPtr<StyleSheet> removedSheet = m_sheet; 294 295 if (m_sheet) { 296 ASSERT(m_sheet->ownerNode() == this); 297 m_sheet->clearOwnerNode(); 298 m_sheet = 0; 299 } 300 301 // If we're in document teardown, then we don't need to do any notification of our sheet's removal. 302 if (document()->renderer()) 303 document()->removedStyleSheet(removedSheet.get()); 304 } 305 306 void ProcessingInstruction::finishParsingChildren() 307 { 308 m_createdByParser = false; 309 Node::finishParsingChildren(); 310 } 311 312 } // namespace 313