1/* 2 * Copyright (C) 2000 Peter Kelly (pmk@post.com) 3 * Copyright (C) 2006, 2008, 2009 Apple Inc. All rights reserved. 4 * Copyright (C) 2013 Samsung Electronics. All rights reserved. 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Library General Public 8 * License as published by the Free Software Foundation; either 9 * version 2 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Library General Public License for more details. 15 * 16 * You should have received a copy of the GNU Library General Public License 17 * along with this library; see the file COPYING.LIB. If not, write to 18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA. 20 */ 21 22#include "config.h" 23#include "ProcessingInstruction.h" 24 25#include "CSSStyleSheet.h" 26#include "CachedCSSStyleSheet.h" 27#include "CachedResourceLoader.h" 28#include "CachedResourceRequest.h" 29#include "CachedXSLStyleSheet.h" 30#include "Document.h" 31#include "ExceptionCode.h" 32#include "Frame.h" 33#include "FrameLoader.h" 34#include "XSLStyleSheet.h" 35#include "XMLDocumentParser.h" // for parseAttributes() 36#include "MediaList.h" 37#include "StyleSheetContents.h" 38 39namespace WebCore { 40 41inline ProcessingInstruction::ProcessingInstruction(Document& document, const String& target, const String& data) 42 : CharacterData(document, data, CreateOther) 43 , m_target(target) 44 , m_cachedSheet(0) 45 , m_loading(false) 46 , m_alternate(false) 47 , m_createdByParser(false) 48 , m_isCSS(false) 49#if ENABLE(XSLT) 50 , m_isXSL(false) 51#endif 52{ 53} 54 55PassRefPtr<ProcessingInstruction> ProcessingInstruction::create(Document& document, const String& target, const String& data) 56{ 57 return adoptRef(new ProcessingInstruction(document, target, data)); 58} 59 60ProcessingInstruction::~ProcessingInstruction() 61{ 62 if (m_sheet) 63 m_sheet->clearOwnerNode(); 64 65 if (m_cachedSheet) 66 m_cachedSheet->removeClient(this); 67 68 if (inDocument()) 69 document().styleSheetCollection().removeStyleSheetCandidateNode(*this); 70} 71 72String ProcessingInstruction::nodeName() const 73{ 74 return m_target; 75} 76 77Node::NodeType ProcessingInstruction::nodeType() const 78{ 79 return PROCESSING_INSTRUCTION_NODE; 80} 81 82PassRefPtr<Node> ProcessingInstruction::cloneNode(bool /*deep*/) 83{ 84 // FIXME: Is it a problem that this does not copy m_localHref? 85 // What about other data members? 86 return create(document(), m_target, data()); 87} 88 89void ProcessingInstruction::checkStyleSheet() 90{ 91 if (m_target == "xml-stylesheet" && document().frame() && parentNode() == &document()) { 92 // see http://www.w3.org/TR/xml-stylesheet/ 93 // ### support stylesheet included in a fragment of this (or another) document 94 // ### make sure this gets called when adding from javascript 95 bool attrsOk; 96 const HashMap<String, String> attrs = parseAttributes(data(), attrsOk); 97 if (!attrsOk) 98 return; 99 HashMap<String, String>::const_iterator i = attrs.find("type"); 100 String type; 101 if (i != attrs.end()) 102 type = i->value; 103 104 m_isCSS = type.isEmpty() || type == "text/css"; 105#if ENABLE(XSLT) 106 m_isXSL = (type == "text/xml" || type == "text/xsl" || type == "application/xml" || 107 type == "application/xhtml+xml" || type == "application/rss+xml" || type == "application/atom+xml"); 108 if (!m_isCSS && !m_isXSL) 109#else 110 if (!m_isCSS) 111#endif 112 return; 113 114 String href = attrs.get("href"); 115 String alternate = attrs.get("alternate"); 116 m_alternate = alternate == "yes"; 117 m_title = attrs.get("title"); 118 m_media = attrs.get("media"); 119 120 if (m_alternate && m_title.isEmpty()) 121 return; 122 123 if (href.length() > 1 && href[0] == '#') { 124 m_localHref = href.substring(1); 125#if ENABLE(XSLT) 126 // We need to make a synthetic XSLStyleSheet that is embedded. It needs to be able 127 // to kick off import/include loads that can hang off some parent sheet. 128 if (m_isXSL) { 129 URL finalURL(ParsedURLString, m_localHref); 130 m_sheet = XSLStyleSheet::createEmbedded(this, finalURL); 131 m_loading = false; 132 } 133#endif 134 } else { 135 if (m_cachedSheet) { 136 m_cachedSheet->removeClient(this); 137 m_cachedSheet = 0; 138 } 139 140 String url = document().completeURL(href).string(); 141 if (!dispatchBeforeLoadEvent(url)) 142 return; 143 144 m_loading = true; 145 document().styleSheetCollection().addPendingSheet(); 146 147 CachedResourceRequest request(ResourceRequest(document().completeURL(href))); 148#if ENABLE(XSLT) 149 if (m_isXSL) 150 m_cachedSheet = document().cachedResourceLoader()->requestXSLStyleSheet(request); 151 else 152#endif 153 { 154 String charset = attrs.get("charset"); 155 if (charset.isEmpty()) 156 charset = document().charset(); 157 request.setCharset(charset); 158 159 m_cachedSheet = document().cachedResourceLoader()->requestCSSStyleSheet(request); 160 } 161 if (m_cachedSheet) 162 m_cachedSheet->addClient(this); 163 else { 164 // The request may have been denied if (for example) the stylesheet is local and the document is remote. 165 m_loading = false; 166 document().styleSheetCollection().removePendingSheet(); 167 } 168 } 169 } 170} 171 172bool ProcessingInstruction::isLoading() const 173{ 174 if (m_loading) 175 return true; 176 if (!m_sheet) 177 return false; 178 return m_sheet->isLoading(); 179} 180 181bool ProcessingInstruction::sheetLoaded() 182{ 183 if (!isLoading()) { 184 document().styleSheetCollection().removePendingSheet(); 185 return true; 186 } 187 return false; 188} 189 190void ProcessingInstruction::setCSSStyleSheet(const String& href, const URL& baseURL, const String& charset, const CachedCSSStyleSheet* sheet) 191{ 192 if (!inDocument()) { 193 ASSERT(!m_sheet); 194 return; 195 } 196 197 ASSERT(m_isCSS); 198 CSSParserContext parserContext(document(), baseURL, charset); 199 200 auto cssSheet = CSSStyleSheet::create(StyleSheetContents::create(href, parserContext), this); 201 cssSheet.get().setDisabled(m_alternate); 202 cssSheet.get().setTitle(m_title); 203 cssSheet.get().setMediaQueries(MediaQuerySet::create(m_media)); 204 205 m_sheet = WTF::move(cssSheet); 206 207 // We don't need the cross-origin security check here because we are 208 // getting the sheet text in "strict" mode. This enforces a valid CSS MIME 209 // type. 210 parseStyleSheet(sheet->sheetText(true)); 211} 212 213#if ENABLE(XSLT) 214void ProcessingInstruction::setXSLStyleSheet(const String& href, const URL& baseURL, const String& sheet) 215{ 216 ASSERT(m_isXSL); 217 m_sheet = XSLStyleSheet::create(this, href, baseURL); 218 parseStyleSheet(sheet); 219} 220#endif 221 222void ProcessingInstruction::parseStyleSheet(const String& sheet) 223{ 224 if (m_isCSS) 225 static_cast<CSSStyleSheet*>(m_sheet.get())->contents().parseString(sheet); 226#if ENABLE(XSLT) 227 else if (m_isXSL) 228 static_cast<XSLStyleSheet*>(m_sheet.get())->parseString(sheet); 229#endif 230 231 if (m_cachedSheet) 232 m_cachedSheet->removeClient(this); 233 m_cachedSheet = 0; 234 235 m_loading = false; 236 237 if (m_isCSS) 238 static_cast<CSSStyleSheet*>(m_sheet.get())->contents().checkLoaded(); 239#if ENABLE(XSLT) 240 else if (m_isXSL) 241 static_cast<XSLStyleSheet*>(m_sheet.get())->checkLoaded(); 242#endif 243} 244 245void ProcessingInstruction::setCSSStyleSheet(PassRefPtr<CSSStyleSheet> sheet) 246{ 247 ASSERT(!m_cachedSheet); 248 ASSERT(!m_loading); 249 m_sheet = sheet; 250 sheet->setTitle(m_title); 251 sheet->setDisabled(m_alternate); 252} 253 254void ProcessingInstruction::addSubresourceAttributeURLs(ListHashSet<URL>& urls) const 255{ 256 if (!sheet()) 257 return; 258 259 addSubresourceURL(urls, sheet()->baseURL()); 260} 261 262Node::InsertionNotificationRequest ProcessingInstruction::insertedInto(ContainerNode& insertionPoint) 263{ 264 CharacterData::insertedInto(insertionPoint); 265 if (!insertionPoint.inDocument()) 266 return InsertionDone; 267 document().styleSheetCollection().addStyleSheetCandidateNode(*this, m_createdByParser); 268 checkStyleSheet(); 269 return InsertionDone; 270} 271 272void ProcessingInstruction::removedFrom(ContainerNode& insertionPoint) 273{ 274 CharacterData::removedFrom(insertionPoint); 275 if (!insertionPoint.inDocument()) 276 return; 277 278 document().styleSheetCollection().removeStyleSheetCandidateNode(*this); 279 280 if (m_sheet) { 281 ASSERT(m_sheet->ownerNode() == this); 282 m_sheet->clearOwnerNode(); 283 m_sheet = 0; 284 } 285 286 // If we're in document teardown, then we don't need to do any notification of our sheet's removal. 287 if (document().hasLivingRenderTree()) 288 document().styleResolverChanged(DeferRecalcStyle); 289} 290 291void ProcessingInstruction::finishParsingChildren() 292{ 293 m_createdByParser = false; 294 CharacterData::finishParsingChildren(); 295} 296 297} // namespace 298