2 * Copyright (C) 2000 Peter Kelly (pmk@post.com)
3 * Copyright (C) 2006-2017 Apple Inc. All rights reserved.
4 * Copyright (C) 2013 Samsung Electronics. All rights reserved.
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.
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.
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.
23 #include "ProcessingInstruction.h"
25 #include "CSSStyleSheet.h"
26 #include "CachedCSSStyleSheet.h"
27 #include "CachedResourceLoader.h"
28 #include "CachedResourceRequest.h"
29 #include "CachedXSLStyleSheet.h"
32 #include "FrameLoader.h"
33 #include "MediaList.h"
34 #include "StyleScope.h"
35 #include "StyleSheetContents.h"
36 #include "XMLDocumentParser.h"
37 #include "XSLStyleSheet.h"
38 #include <wtf/SetForScope.h>
42 inline ProcessingInstruction::ProcessingInstruction(Document& document, const String& target, const String& data)
43 : CharacterData(document, data, CreateOther)
48 Ref<ProcessingInstruction> ProcessingInstruction::create(Document& document, const String& target, const String& data)
50 return adoptRef(*new ProcessingInstruction(document, target, data));
53 ProcessingInstruction::~ProcessingInstruction()
56 m_sheet->clearOwnerNode();
59 m_cachedSheet->removeClient(*this);
62 document().styleScope().removeStyleSheetCandidateNode(*this);
65 String ProcessingInstruction::nodeName() const
70 Node::NodeType ProcessingInstruction::nodeType() const
72 return PROCESSING_INSTRUCTION_NODE;
75 Ref<Node> ProcessingInstruction::cloneNodeInternal(Document& targetDocument, CloningOperation)
77 // FIXME: Is it a problem that this does not copy m_localHref?
78 // What about other data members?
79 return create(targetDocument, m_target, data());
82 void ProcessingInstruction::checkStyleSheet()
84 // Prevent recursive loading of stylesheet.
85 if (m_isHandlingBeforeLoad)
88 if (m_target == "xml-stylesheet" && document().frame() && parentNode() == &document()) {
89 // see http://www.w3.org/TR/xml-stylesheet/
90 // ### support stylesheet included in a fragment of this (or another) document
91 // ### make sure this gets called when adding from javascript
93 const HashMap<String, String> attrs = parseAttributes(data(), attrsOk);
96 HashMap<String, String>::const_iterator i = attrs.find("type");
101 m_isCSS = type.isEmpty() || type == "text/css";
103 m_isXSL = (type == "text/xml" || type == "text/xsl" || type == "application/xml" ||
104 type == "application/xhtml+xml" || type == "application/rss+xml" || type == "application/atom+xml");
105 if (!m_isCSS && !m_isXSL)
111 String href = attrs.get("href");
112 String alternate = attrs.get("alternate");
113 m_alternate = alternate == "yes";
114 m_title = attrs.get("title");
115 m_media = attrs.get("media");
117 if (m_alternate && m_title.isEmpty())
120 if (href.length() > 1 && href[0] == '#') {
121 m_localHref = href.substring(1);
123 // We need to make a synthetic XSLStyleSheet that is embedded. It needs to be able
124 // to kick off import/include loads that can hang off some parent sheet.
126 URL finalURL(ParsedURLString, m_localHref);
127 m_sheet = XSLStyleSheet::createEmbedded(this, finalURL);
133 m_cachedSheet->removeClient(*this);
134 m_cachedSheet = nullptr;
139 document().styleScope().removePendingSheet(*this);
142 Ref<Document> originalDocument = document();
144 String url = document().completeURL(href).string();
147 SetForScope<bool> change(m_isHandlingBeforeLoad, true);
148 if (!dispatchBeforeLoadEvent(url))
152 bool didEventListenerDisconnectThisElement = !isConnected() || &document() != originalDocument.ptr();
153 if (didEventListenerDisconnectThisElement)
157 document().styleScope().addPendingSheet(*this);
159 ASSERT_WITH_SECURITY_IMPLICATION(!m_cachedSheet);
163 auto options = CachedResourceLoader::defaultCachedResourceOptions();
164 options.mode = FetchOptions::Mode::SameOrigin;
165 m_cachedSheet = document().cachedResourceLoader().requestXSLStyleSheet({ResourceRequest(document().completeURL(href)), options}).valueOr(nullptr);
169 String charset = attrs.get("charset");
170 CachedResourceRequest request(document().completeURL(href), CachedResourceLoader::defaultCachedResourceOptions(), std::nullopt, charset.isEmpty() ? document().charset() : WTFMove(charset));
172 m_cachedSheet = document().cachedResourceLoader().requestCSSStyleSheet(WTFMove(request)).valueOr(nullptr);
175 m_cachedSheet->addClient(*this);
177 // The request may have been denied if (for example) the stylesheet is local and the document is remote.
179 document().styleScope().removePendingSheet(*this);
182 document().styleScope().flushPendingUpdate();
189 bool ProcessingInstruction::isLoading() const
195 return m_sheet->isLoading();
198 bool ProcessingInstruction::sheetLoaded()
201 if (document().styleScope().hasPendingSheet(*this))
202 document().styleScope().removePendingSheet(*this);
205 document().styleScope().flushPendingUpdate();
212 void ProcessingInstruction::setCSSStyleSheet(const String& href, const URL& baseURL, const String& charset, const CachedCSSStyleSheet* sheet)
214 if (!isConnected()) {
220 CSSParserContext parserContext(document(), baseURL, charset);
222 auto cssSheet = CSSStyleSheet::create(StyleSheetContents::create(href, parserContext), *this);
223 cssSheet.get().setDisabled(m_alternate);
224 cssSheet.get().setTitle(m_title);
225 cssSheet.get().setMediaQueries(MediaQuerySet::create(m_media));
227 m_sheet = WTFMove(cssSheet);
229 // We don't need the cross-origin security check here because we are
230 // getting the sheet text in "strict" mode. This enforces a valid CSS MIME
232 Ref<Document> protect(document());
233 parseStyleSheet(sheet->sheetText());
237 void ProcessingInstruction::setXSLStyleSheet(const String& href, const URL& baseURL, const String& sheet)
240 m_sheet = XSLStyleSheet::create(this, href, baseURL);
241 Ref<Document> protect(document());
242 parseStyleSheet(sheet);
246 void ProcessingInstruction::parseStyleSheet(const String& sheet)
249 downcast<CSSStyleSheet>(*m_sheet).contents().parseString(sheet);
252 downcast<XSLStyleSheet>(*m_sheet).parseString(sheet);
256 m_cachedSheet->removeClient(*this);
257 m_cachedSheet = nullptr;
262 downcast<CSSStyleSheet>(*m_sheet).contents().checkLoaded();
265 downcast<XSLStyleSheet>(*m_sheet).checkLoaded();
269 void ProcessingInstruction::addSubresourceAttributeURLs(ListHashSet<URL>& urls) const
274 addSubresourceURL(urls, sheet()->baseURL());
277 Node::InsertionNotificationRequest ProcessingInstruction::insertedInto(ContainerNode& insertionPoint)
279 CharacterData::insertedInto(insertionPoint);
280 if (!insertionPoint.isConnected())
281 return InsertionDone;
282 document().styleScope().addStyleSheetCandidateNode(*this, m_createdByParser);
283 return InsertionShouldCallFinishedInsertingSubtree;
286 void ProcessingInstruction::finishedInsertingSubtree()
291 void ProcessingInstruction::removedFrom(ContainerNode& insertionPoint)
293 CharacterData::removedFrom(insertionPoint);
294 if (!insertionPoint.isConnected())
297 document().styleScope().removeStyleSheetCandidateNode(*this);
300 ASSERT(m_sheet->ownerNode() == this);
301 m_sheet->clearOwnerNode();
307 document().styleScope().removePendingSheet(*this);
310 document().styleScope().didChangeActiveStyleSheetCandidates();
313 void ProcessingInstruction::finishParsingChildren()
315 m_createdByParser = false;
316 CharacterData::finishParsingChildren();