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 "core/dom/ProcessingInstruction.h" 23 24 #include "FetchInitiatorTypeNames.h" 25 #include "core/css/CSSStyleSheet.h" 26 #include "core/css/MediaList.h" 27 #include "core/css/StyleSheetContents.h" 28 #include "core/dom/Document.h" 29 #include "core/dom/StyleEngine.h" 30 #include "core/fetch/CSSStyleSheetResource.h" 31 #include "core/fetch/FetchRequest.h" 32 #include "core/fetch/ResourceFetcher.h" 33 #include "core/fetch/XSLStyleSheetResource.h" 34 #include "core/xml/XSLStyleSheet.h" 35 #include "core/xml/parser/XMLDocumentParser.h" // for parseAttributes() 36 37 namespace WebCore { 38 39 inline ProcessingInstruction::ProcessingInstruction(Document& document, const String& target, const String& data) 40 : CharacterData(document, data, CreateOther) 41 , m_target(target) 42 , m_loading(false) 43 , m_alternate(false) 44 , m_createdByParser(false) 45 , m_isCSS(false) 46 , m_isXSL(false) 47 { 48 ScriptWrappable::init(this); 49 } 50 51 PassRefPtr<ProcessingInstruction> ProcessingInstruction::create(Document& document, const String& target, const String& data) 52 { 53 return adoptRef(new ProcessingInstruction(document, target, data)); 54 } 55 56 ProcessingInstruction::~ProcessingInstruction() 57 { 58 if (m_sheet) 59 m_sheet->clearOwnerNode(); 60 61 if (inDocument()) 62 document().styleEngine()->removeStyleSheetCandidateNode(this); 63 } 64 65 String ProcessingInstruction::nodeName() const 66 { 67 return m_target; 68 } 69 70 Node::NodeType ProcessingInstruction::nodeType() const 71 { 72 return PROCESSING_INSTRUCTION_NODE; 73 } 74 75 PassRefPtr<Node> ProcessingInstruction::cloneNode(bool /*deep*/) 76 { 77 // FIXME: Is it a problem that this does not copy m_localHref? 78 // What about other data members? 79 return create(document(), m_target, m_data); 80 } 81 82 void ProcessingInstruction::checkStyleSheet() 83 { 84 if (m_target == "xml-stylesheet" && document().frame() && parentNode() == document()) { 85 // see http://www.w3.org/TR/xml-stylesheet/ 86 // ### support stylesheet included in a fragment of this (or another) document 87 // ### make sure this gets called when adding from javascript 88 bool attrsOk; 89 const HashMap<String, String> attrs = parseAttributes(m_data, attrsOk); 90 if (!attrsOk) 91 return; 92 HashMap<String, String>::const_iterator i = attrs.find("type"); 93 String type; 94 if (i != attrs.end()) 95 type = i->value; 96 97 m_isCSS = type.isEmpty() || type == "text/css"; 98 m_isXSL = (type == "text/xml" || type == "text/xsl" || type == "application/xml" || 99 type == "application/xhtml+xml" || type == "application/rss+xml" || type == "application/atom+xml"); 100 if (!m_isCSS && !m_isXSL) 101 return; 102 103 String href = attrs.get("href"); 104 String alternate = attrs.get("alternate"); 105 m_alternate = alternate == "yes"; 106 m_title = attrs.get("title"); 107 m_media = attrs.get("media"); 108 109 if (m_alternate && m_title.isEmpty()) 110 return; 111 112 if (href.length() > 1 && href[0] == '#') { 113 m_localHref = href.substring(1); 114 // We need to make a synthetic XSLStyleSheet that is embedded. It needs to be able 115 // to kick off import/include loads that can hang off some parent sheet. 116 if (m_isXSL) { 117 KURL finalURL(ParsedURLString, m_localHref); 118 m_sheet = XSLStyleSheet::createEmbedded(this, finalURL); 119 m_loading = false; 120 } 121 } else { 122 clearResource(); 123 124 String url = document().completeURL(href).string(); 125 if (!dispatchBeforeLoadEvent(url)) 126 return; 127 128 ResourcePtr<StyleSheetResource> resource; 129 FetchRequest request(ResourceRequest(document().completeURL(href)), FetchInitiatorTypeNames::processinginstruction); 130 if (m_isXSL) { 131 resource = document().fetcher()->fetchXSLStyleSheet(request); 132 } else { 133 String charset = attrs.get("charset"); 134 if (charset.isEmpty()) 135 charset = document().charset(); 136 request.setCharset(charset); 137 resource = document().fetcher()->fetchCSSStyleSheet(request); 138 } 139 140 if (resource) { 141 m_loading = true; 142 document().styleEngine()->addPendingSheet(); 143 setResource(resource); 144 } 145 } 146 } 147 } 148 149 bool ProcessingInstruction::isLoading() const 150 { 151 if (m_loading) 152 return true; 153 if (!m_sheet) 154 return false; 155 return m_sheet->isLoading(); 156 } 157 158 bool ProcessingInstruction::sheetLoaded() 159 { 160 if (!isLoading()) { 161 document().styleEngine()->removePendingSheet(this); 162 return true; 163 } 164 return false; 165 } 166 167 void ProcessingInstruction::setCSSStyleSheet(const String& href, const KURL& baseURL, const String& charset, const CSSStyleSheetResource* sheet) 168 { 169 if (!inDocument()) { 170 ASSERT(!m_sheet); 171 return; 172 } 173 174 ASSERT(m_isCSS); 175 CSSParserContext parserContext(document(), baseURL, charset); 176 177 RefPtr<StyleSheetContents> newSheet = StyleSheetContents::create(href, parserContext); 178 179 RefPtr<CSSStyleSheet> cssSheet = CSSStyleSheet::create(newSheet, this); 180 cssSheet->setDisabled(m_alternate); 181 cssSheet->setTitle(m_title); 182 cssSheet->setMediaQueries(MediaQuerySet::create(m_media)); 183 184 m_sheet = cssSheet.release(); 185 186 // We don't need the cross-origin security check here because we are 187 // getting the sheet text in "strict" mode. This enforces a valid CSS MIME 188 // type. 189 parseStyleSheet(sheet->sheetText(true)); 190 } 191 192 void ProcessingInstruction::setXSLStyleSheet(const String& href, const KURL& baseURL, const String& sheet) 193 { 194 ASSERT(m_isXSL); 195 m_sheet = XSLStyleSheet::create(this, href, baseURL); 196 parseStyleSheet(sheet); 197 } 198 199 void ProcessingInstruction::parseStyleSheet(const String& sheet) 200 { 201 if (m_isCSS) 202 toCSSStyleSheet(m_sheet.get())->contents()->parseString(sheet); 203 else if (m_isXSL) 204 toXSLStyleSheet(m_sheet.get())->parseString(sheet); 205 206 clearResource(); 207 m_loading = false; 208 209 if (m_isCSS) 210 toCSSStyleSheet(m_sheet.get())->contents()->checkLoaded(); 211 else if (m_isXSL) 212 toXSLStyleSheet(m_sheet.get())->checkLoaded(); 213 } 214 215 void ProcessingInstruction::setCSSStyleSheet(PassRefPtr<CSSStyleSheet> sheet) 216 { 217 ASSERT(!resource()); 218 ASSERT(!m_loading); 219 m_sheet = sheet; 220 sheet->setTitle(m_title); 221 sheet->setDisabled(m_alternate); 222 } 223 224 void ProcessingInstruction::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const 225 { 226 if (!sheet()) 227 return; 228 229 addSubresourceURL(urls, sheet()->baseURL()); 230 } 231 232 Node::InsertionNotificationRequest ProcessingInstruction::insertedInto(ContainerNode* insertionPoint) 233 { 234 CharacterData::insertedInto(insertionPoint); 235 if (!insertionPoint->inDocument()) 236 return InsertionDone; 237 document().styleEngine()->addStyleSheetCandidateNode(this, m_createdByParser); 238 checkStyleSheet(); 239 return InsertionDone; 240 } 241 242 void ProcessingInstruction::removedFrom(ContainerNode* insertionPoint) 243 { 244 CharacterData::removedFrom(insertionPoint); 245 if (!insertionPoint->inDocument()) 246 return; 247 248 document().styleEngine()->removeStyleSheetCandidateNode(this); 249 250 RefPtr<StyleSheet> removedSheet = m_sheet; 251 252 if (m_sheet) { 253 ASSERT(m_sheet->ownerNode() == this); 254 m_sheet->clearOwnerNode(); 255 m_sheet = 0; 256 } 257 258 // If we're in document teardown, then we don't need to do any notification of our sheet's removal. 259 if (document().isActive()) 260 document().removedStyleSheet(removedSheet.get()); 261 } 262 263 void ProcessingInstruction::finishParsingChildren() 264 { 265 m_createdByParser = false; 266 CharacterData::finishParsingChildren(); 267 } 268 269 } // namespace 270