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 "ProcessingInstruction.h" 23 24 #include "CSSStyleSheet.h" 25 #include "CachedCSSStyleSheet.h" 26 #include "CachedXSLStyleSheet.h" 27 #include "Document.h" 28 #include "CachedResourceLoader.h" 29 #include "ExceptionCode.h" 30 #include "Frame.h" 31 #include "FrameLoader.h" 32 #include "XSLStyleSheet.h" 33 #include "XMLDocumentParser.h" // for parseAttributes() 34 #include "MediaList.h" 35 36 namespace WebCore { 37 38 inline ProcessingInstruction::ProcessingInstruction(Document* document, const String& target, const String& data) 39 : ContainerNode(document) 40 , m_target(target) 41 , m_data(data) 42 , m_cachedSheet(0) 43 , m_loading(false) 44 , m_alternate(false) 45 , m_createdByParser(false) 46 , m_isCSS(false) 47 #if ENABLE(XSLT) 48 , m_isXSL(false) 49 #endif 50 { 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_cachedSheet) 64 m_cachedSheet->removeClient(this); 65 } 66 67 void ProcessingInstruction::setData(const String& data, ExceptionCode&) 68 { 69 int oldLength = m_data.length(); 70 m_data = data; 71 document()->textRemoved(this, 0, oldLength); 72 checkStyleSheet(); 73 } 74 75 String ProcessingInstruction::nodeName() const 76 { 77 return m_target; 78 } 79 80 Node::NodeType ProcessingInstruction::nodeType() const 81 { 82 return PROCESSING_INSTRUCTION_NODE; 83 } 84 85 String ProcessingInstruction::nodeValue() const 86 { 87 return m_data; 88 } 89 90 void ProcessingInstruction::setNodeValue(const String& nodeValue, ExceptionCode& ec) 91 { 92 // NO_MODIFICATION_ALLOWED_ERR: taken care of by setData() 93 setData(nodeValue, ec); 94 } 95 96 PassRefPtr<Node> ProcessingInstruction::cloneNode(bool /*deep*/) 97 { 98 // FIXME: Is it a problem that this does not copy m_localHref? 99 // What about other data members? 100 return create(document(), m_target, m_data); 101 } 102 103 // DOM Section 1.1.1 104 bool ProcessingInstruction::childTypeAllowed(NodeType) const 105 { 106 return false; 107 } 108 109 void ProcessingInstruction::checkStyleSheet() 110 { 111 if (m_target == "xml-stylesheet" && document()->frame() && parentNode() == document()) { 112 // see http://www.w3.org/TR/xml-stylesheet/ 113 // ### support stylesheet included in a fragment of this (or another) document 114 // ### make sure this gets called when adding from javascript 115 bool attrsOk; 116 const HashMap<String, String> attrs = parseAttributes(m_data, attrsOk); 117 if (!attrsOk) 118 return; 119 HashMap<String, String>::const_iterator i = attrs.find("type"); 120 String type; 121 if (i != attrs.end()) 122 type = i->second; 123 124 m_isCSS = type.isEmpty() || type == "text/css"; 125 #if ENABLE(XSLT) 126 m_isXSL = (type == "text/xml" || type == "text/xsl" || type == "application/xml" || 127 type == "application/xhtml+xml" || type == "application/rss+xml" || type == "application/atom+xml"); 128 if (!m_isCSS && !m_isXSL) 129 #else 130 if (!m_isCSS) 131 #endif 132 return; 133 134 String href = attrs.get("href"); 135 String alternate = attrs.get("alternate"); 136 m_alternate = alternate == "yes"; 137 m_title = attrs.get("title"); 138 m_media = attrs.get("media"); 139 140 if (href.length() > 1 && href[0] == '#') { 141 m_localHref = href.substring(1); 142 #if ENABLE(XSLT) 143 // We need to make a synthetic XSLStyleSheet that is embedded. It needs to be able 144 // to kick off import/include loads that can hang off some parent sheet. 145 if (m_isXSL) { 146 KURL finalURL(ParsedURLString, m_localHref); 147 m_sheet = XSLStyleSheet::createEmbedded(this, finalURL); 148 m_loading = false; 149 } 150 #endif 151 } else { 152 if (m_cachedSheet) { 153 m_cachedSheet->removeClient(this); 154 m_cachedSheet = 0; 155 } 156 157 String url = document()->completeURL(href).string(); 158 if (!dispatchBeforeLoadEvent(url)) 159 return; 160 161 m_loading = true; 162 document()->addPendingSheet(); 163 164 #if ENABLE(XSLT) 165 if (m_isXSL) 166 m_cachedSheet = document()->cachedResourceLoader()->requestXSLStyleSheet(url); 167 else 168 #endif 169 { 170 String charset = attrs.get("charset"); 171 if (charset.isEmpty()) 172 charset = document()->charset(); 173 174 m_cachedSheet = document()->cachedResourceLoader()->requestCSSStyleSheet(url, charset); 175 } 176 if (m_cachedSheet) 177 m_cachedSheet->addClient(this); 178 else { 179 // The request may have been denied if (for example) the stylesheet is local and the document is remote. 180 m_loading = false; 181 document()->removePendingSheet(); 182 } 183 } 184 } 185 } 186 187 bool ProcessingInstruction::isLoading() const 188 { 189 if (m_loading) 190 return true; 191 if (!m_sheet) 192 return false; 193 return m_sheet->isLoading(); 194 } 195 196 bool ProcessingInstruction::sheetLoaded() 197 { 198 if (!isLoading()) { 199 document()->removePendingSheet(); 200 return true; 201 } 202 return false; 203 } 204 205 void ProcessingInstruction::setCSSStyleSheet(const String& href, const KURL& baseURL, const String& charset, const CachedCSSStyleSheet* sheet) 206 { 207 if (!inDocument()) { 208 ASSERT(!m_sheet); 209 return; 210 } 211 212 ASSERT(m_isCSS); 213 RefPtr<CSSStyleSheet> newSheet = CSSStyleSheet::create(this, href, baseURL, charset); 214 m_sheet = newSheet; 215 // We don't need the cross-origin security check here because we are 216 // getting the sheet text in "strict" mode. This enforces a valid CSS MIME 217 // type. 218 parseStyleSheet(sheet->sheetText(true)); 219 newSheet->setTitle(m_title); 220 newSheet->setMedia(MediaList::create(newSheet.get(), m_media)); 221 newSheet->setDisabled(m_alternate); 222 } 223 224 #if ENABLE(XSLT) 225 void ProcessingInstruction::setXSLStyleSheet(const String& href, const KURL& baseURL, const String& sheet) 226 { 227 ASSERT(m_isXSL); 228 m_sheet = XSLStyleSheet::create(this, href, baseURL); 229 parseStyleSheet(sheet); 230 } 231 #endif 232 233 void ProcessingInstruction::parseStyleSheet(const String& sheet) 234 { 235 m_sheet->parseString(sheet, true); 236 if (m_cachedSheet) 237 m_cachedSheet->removeClient(this); 238 m_cachedSheet = 0; 239 240 m_loading = false; 241 m_sheet->checkLoaded(); 242 } 243 244 void ProcessingInstruction::setCSSStyleSheet(PassRefPtr<CSSStyleSheet> sheet) 245 { 246 ASSERT(!m_cachedSheet); 247 ASSERT(!m_loading); 248 m_sheet = sheet; 249 m_sheet->setTitle(m_title); 250 m_sheet->setDisabled(m_alternate); 251 } 252 253 bool ProcessingInstruction::offsetInCharacters() const 254 { 255 return true; 256 } 257 258 int ProcessingInstruction::maxCharacterOffset() const 259 { 260 return static_cast<int>(m_data.length()); 261 } 262 263 void ProcessingInstruction::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const 264 { 265 if (!sheet()) 266 return; 267 268 addSubresourceURL(urls, sheet()->baseURL()); 269 } 270 271 void ProcessingInstruction::insertedIntoDocument() 272 { 273 ContainerNode::insertedIntoDocument(); 274 document()->addStyleSheetCandidateNode(this, m_createdByParser); 275 checkStyleSheet(); 276 } 277 278 void ProcessingInstruction::removedFromDocument() 279 { 280 ContainerNode::removedFromDocument(); 281 282 document()->removeStyleSheetCandidateNode(this); 283 284 if (m_sheet) { 285 ASSERT(m_sheet->ownerNode() == this); 286 m_sheet->clearOwnerNode(); 287 m_sheet = 0; 288 } 289 290 if (m_cachedSheet) 291 document()->styleSelectorChanged(DeferRecalcStyle); 292 } 293 294 void ProcessingInstruction::finishParsingChildren() 295 { 296 m_createdByParser = false; 297 ContainerNode::finishParsingChildren(); 298 } 299 300 } // namespace 301