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 "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 36namespace WebCore { 37 38inline 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 52PassRefPtr<ProcessingInstruction> ProcessingInstruction::create(Document* document, const String& target, const String& data) 53{ 54 return adoptRef(new ProcessingInstruction(document, target, data)); 55} 56 57ProcessingInstruction::~ProcessingInstruction() 58{ 59 if (m_cachedSheet) 60 m_cachedSheet->removeClient(this); 61} 62 63void 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 71String ProcessingInstruction::nodeName() const 72{ 73 return m_target; 74} 75 76Node::NodeType ProcessingInstruction::nodeType() const 77{ 78 return PROCESSING_INSTRUCTION_NODE; 79} 80 81String ProcessingInstruction::nodeValue() const 82{ 83 return m_data; 84} 85 86void ProcessingInstruction::setNodeValue(const String& nodeValue, ExceptionCode& ec) 87{ 88 // NO_MODIFICATION_ALLOWED_ERR: taken care of by setData() 89 setData(nodeValue, ec); 90} 91 92PassRefPtr<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 100bool ProcessingInstruction::childTypeAllowed(NodeType) 101{ 102 return false; 103} 104 105void 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 183bool 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 192bool ProcessingInstruction::sheetLoaded() 193{ 194 if (!isLoading()) { 195 document()->removePendingSheet(); 196 return true; 197 } 198 return false; 199} 200 201void 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) 218void 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 226void 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 237void 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 246bool ProcessingInstruction::offsetInCharacters() const 247{ 248 return true; 249} 250 251int ProcessingInstruction::maxCharacterOffset() const 252{ 253 return static_cast<int>(m_data.length()); 254} 255 256void ProcessingInstruction::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const 257{ 258 if (!sheet()) 259 return; 260 261 addSubresourceURL(urls, sheet()->baseURL()); 262} 263 264void ProcessingInstruction::insertedIntoDocument() 265{ 266 ContainerNode::insertedIntoDocument(); 267 document()->addStyleSheetCandidateNode(this, m_createdByParser); 268 checkStyleSheet(); 269} 270 271void 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 282void ProcessingInstruction::finishParsingChildren() 283{ 284 m_createdByParser = false; 285 ContainerNode::finishParsingChildren(); 286} 287 288#ifdef ANDROID_INSTRUMENT 289void* ProcessingInstruction::operator new(size_t size) 290{ 291 return Node::operator new(size); 292} 293 294void* ProcessingInstruction::operator new[](size_t size) 295{ 296 return Node::operator new[](size); 297} 298 299void ProcessingInstruction::operator delete(void* p, size_t size) 300{ 301 Node::operator delete(p, size); 302} 303 304void ProcessingInstruction::operator delete[](void* p, size_t size) 305{ 306 Node::operator delete[](p, size); 307} 308#endif 309 310} // namespace 311