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 "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 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 , m_isCSS(false) 47#if ENABLE(XSLT) 48 , m_isXSL(false) 49#endif 50{ 51} 52 53PassRefPtr<ProcessingInstruction> ProcessingInstruction::create(Document* document, const String& target, const String& data) 54{ 55 return adoptRef(new ProcessingInstruction(document, target, data)); 56} 57 58ProcessingInstruction::~ProcessingInstruction() 59{ 60 if (m_sheet) 61 m_sheet->clearOwnerNode(); 62 63 if (m_cachedSheet) 64 m_cachedSheet->removeClient(this); 65} 66 67void 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 75String ProcessingInstruction::nodeName() const 76{ 77 return m_target; 78} 79 80Node::NodeType ProcessingInstruction::nodeType() const 81{ 82 return PROCESSING_INSTRUCTION_NODE; 83} 84 85String ProcessingInstruction::nodeValue() const 86{ 87 return m_data; 88} 89 90void ProcessingInstruction::setNodeValue(const String& nodeValue, ExceptionCode& ec) 91{ 92 // NO_MODIFICATION_ALLOWED_ERR: taken care of by setData() 93 setData(nodeValue, ec); 94} 95 96PassRefPtr<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 104bool ProcessingInstruction::childTypeAllowed(NodeType) const 105{ 106 return false; 107} 108 109void 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 187bool 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 196bool ProcessingInstruction::sheetLoaded() 197{ 198 if (!isLoading()) { 199 document()->removePendingSheet(); 200 return true; 201 } 202 return false; 203} 204 205void 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) 225void 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 233void 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 244void 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 253bool ProcessingInstruction::offsetInCharacters() const 254{ 255 return true; 256} 257 258int ProcessingInstruction::maxCharacterOffset() const 259{ 260 return static_cast<int>(m_data.length()); 261} 262 263void ProcessingInstruction::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const 264{ 265 if (!sheet()) 266 return; 267 268 addSubresourceURL(urls, sheet()->baseURL()); 269} 270 271void ProcessingInstruction::insertedIntoDocument() 272{ 273 ContainerNode::insertedIntoDocument(); 274 document()->addStyleSheetCandidateNode(this, m_createdByParser); 275 checkStyleSheet(); 276} 277 278void 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 294void ProcessingInstruction::finishParsingChildren() 295{ 296 m_createdByParser = false; 297 ContainerNode::finishParsingChildren(); 298} 299 300} // namespace 301