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 "DocLoader.h" 29 #include "ExceptionCode.h" 30 #include "Frame.h" 31 #include "FrameLoader.h" 32 #include "XSLStyleSheet.h" 33 #include "XMLTokenizer.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 #if ENABLE(XSLT) 47 , m_isXSL(false) 48 #endif 49 { 50 } 51 52 PassRefPtr<ProcessingInstruction> ProcessingInstruction::create(Document* document, const String& target, const String& data) 53 { 54 return adoptRef(new ProcessingInstruction(document, target, data)); 55 } 56 57 ProcessingInstruction::~ProcessingInstruction() 58 { 59 if (m_cachedSheet) 60 m_cachedSheet->removeClient(this); 61 } 62 63 void ProcessingInstruction::setData(const String& data, ExceptionCode&) 64 { 65 int oldLength = m_data.length(); 66 m_data = data; 67 document()->textRemoved(this, 0, oldLength); 68 checkStyleSheet(); 69 } 70 71 String ProcessingInstruction::nodeName() const 72 { 73 return m_target; 74 } 75 76 Node::NodeType ProcessingInstruction::nodeType() const 77 { 78 return PROCESSING_INSTRUCTION_NODE; 79 } 80 81 String ProcessingInstruction::nodeValue() const 82 { 83 return m_data; 84 } 85 86 void ProcessingInstruction::setNodeValue(const String& nodeValue, ExceptionCode& ec) 87 { 88 // NO_MODIFICATION_ALLOWED_ERR: taken care of by setData() 89 setData(nodeValue, ec); 90 } 91 92 PassRefPtr<Node> ProcessingInstruction::cloneNode(bool /*deep*/) 93 { 94 // FIXME: Is it a problem that this does not copy m_localHref? 95 // What about other data members? 96 return create(document(), m_target, m_data); 97 } 98 99 // DOM Section 1.1.1 100 bool ProcessingInstruction::childTypeAllowed(NodeType) 101 { 102 return false; 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->second; 119 120 bool isCSS = type.isEmpty() || type == "text/css"; 121 #if ENABLE(XSLT) 122 m_isXSL = (type == "text/xml" || type == "text/xsl" || type == "application/xml" || 123 type == "application/xhtml+xml" || type == "application/rss+xml" || type == "application/atom+xml"); 124 if (!isCSS && !m_isXSL) 125 #else 126 if (!isCSS) 127 #endif 128 return; 129 130 String href = attrs.get("href"); 131 String alternate = attrs.get("alternate"); 132 m_alternate = alternate == "yes"; 133 m_title = attrs.get("title"); 134 m_media = attrs.get("media"); 135 136 if (href.length() > 1 && href[0] == '#') { 137 m_localHref = href.substring(1); 138 #if ENABLE(XSLT) 139 // We need to make a synthetic XSLStyleSheet that is embedded. It needs to be able 140 // to kick off import/include loads that can hang off some parent sheet. 141 if (m_isXSL) { 142 KURL finalURL(ParsedURLString, m_localHref); 143 m_sheet = XSLStyleSheet::createInline(this, finalURL); 144 m_loading = false; 145 } 146 #endif 147 } else { 148 if (m_cachedSheet) { 149 m_cachedSheet->removeClient(this); 150 m_cachedSheet = 0; 151 } 152 153 String url = document()->completeURL(href).string(); 154 if (!dispatchBeforeLoadEvent(url)) 155 return; 156 157 m_loading = true; 158 document()->addPendingSheet(); 159 160 #if ENABLE(XSLT) 161 if (m_isXSL) 162 m_cachedSheet = document()->docLoader()->requestXSLStyleSheet(url); 163 else 164 #endif 165 { 166 String charset = attrs.get("charset"); 167 if (charset.isEmpty()) 168 charset = document()->frame()->loader()->encoding(); 169 170 m_cachedSheet = document()->docLoader()->requestCSSStyleSheet(url, charset); 171 } 172 if (m_cachedSheet) 173 m_cachedSheet->addClient(this); 174 else { 175 // The request may have been denied if (for example) the stylesheet is local and the document is remote. 176 m_loading = false; 177 document()->removePendingSheet(); 178 } 179 } 180 } 181 } 182 183 bool ProcessingInstruction::isLoading() const 184 { 185 if (m_loading) 186 return true; 187 if (!m_sheet) 188 return false; 189 return m_sheet->isLoading(); 190 } 191 192 bool ProcessingInstruction::sheetLoaded() 193 { 194 if (!isLoading()) { 195 document()->removePendingSheet(); 196 return true; 197 } 198 return false; 199 } 200 201 void ProcessingInstruction::setCSSStyleSheet(const String& href, const KURL& baseURL, const String& charset, const CachedCSSStyleSheet* sheet) 202 { 203 #if ENABLE(XSLT) 204 ASSERT(!m_isXSL); 205 #endif 206 RefPtr<CSSStyleSheet> newSheet = CSSStyleSheet::create(this, href, baseURL, charset); 207 m_sheet = newSheet; 208 // We don't need the cross-origin security check here because we are 209 // getting the sheet text in "strict" mode. This enforces a valid CSS MIME 210 // type. 211 parseStyleSheet(sheet->sheetText(true)); 212 newSheet->setTitle(m_title); 213 newSheet->setMedia(MediaList::create(newSheet.get(), m_media)); 214 newSheet->setDisabled(m_alternate); 215 } 216 217 #if ENABLE(XSLT) 218 void ProcessingInstruction::setXSLStyleSheet(const String& href, const KURL& baseURL, const String& sheet) 219 { 220 ASSERT(m_isXSL); 221 m_sheet = XSLStyleSheet::create(this, href, baseURL); 222 parseStyleSheet(sheet); 223 } 224 #endif 225 226 void ProcessingInstruction::parseStyleSheet(const String& sheet) 227 { 228 m_sheet->parseString(sheet, true); 229 if (m_cachedSheet) 230 m_cachedSheet->removeClient(this); 231 m_cachedSheet = 0; 232 233 m_loading = false; 234 m_sheet->checkLoaded(); 235 } 236 237 void ProcessingInstruction::setCSSStyleSheet(PassRefPtr<CSSStyleSheet> sheet) 238 { 239 ASSERT(!m_cachedSheet); 240 ASSERT(!m_loading); 241 m_sheet = sheet; 242 m_sheet->setTitle(m_title); 243 m_sheet->setDisabled(m_alternate); 244 } 245 246 bool ProcessingInstruction::offsetInCharacters() const 247 { 248 return true; 249 } 250 251 int ProcessingInstruction::maxCharacterOffset() const 252 { 253 return static_cast<int>(m_data.length()); 254 } 255 256 void ProcessingInstruction::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const 257 { 258 if (!sheet()) 259 return; 260 261 addSubresourceURL(urls, sheet()->baseURL()); 262 } 263 264 void ProcessingInstruction::insertedIntoDocument() 265 { 266 ContainerNode::insertedIntoDocument(); 267 document()->addStyleSheetCandidateNode(this, m_createdByParser); 268 checkStyleSheet(); 269 } 270 271 void ProcessingInstruction::removedFromDocument() 272 { 273 ContainerNode::removedFromDocument(); 274 275 document()->removeStyleSheetCandidateNode(this); 276 277 // FIXME: It's terrible to do a synchronous update of the style selector just because a <style> or <link> element got removed. 278 if (m_cachedSheet) 279 document()->updateStyleSelector(); 280 } 281 282 void ProcessingInstruction::finishParsingChildren() 283 { 284 m_createdByParser = false; 285 ContainerNode::finishParsingChildren(); 286 } 287 288 #ifdef ANDROID_INSTRUMENT 289 void* ProcessingInstruction::operator new(size_t size) 290 { 291 return Node::operator new(size); 292 } 293 294 void* ProcessingInstruction::operator new[](size_t size) 295 { 296 return Node::operator new[](size); 297 } 298 299 void ProcessingInstruction::operator delete(void* p, size_t size) 300 { 301 Node::operator delete(p, size); 302 } 303 304 void ProcessingInstruction::operator delete[](void* p, size_t size) 305 { 306 Node::operator delete[](p, size); 307 } 308 #endif 309 310 } // namespace 311