Delete WebMetal implementation in favor of WebGPU
[WebKit-https.git] / Source / WebCore / dom / ProcessingInstruction.cpp
1 /*
2  * Copyright (C) 2000 Peter Kelly (pmk@post.com)
3  * Copyright (C) 2006-2018 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 "Frame.h"
32 #include "FrameLoader.h"
33 #include "MediaList.h"
34 #include "MediaQueryParser.h"
35 #include "StyleScope.h"
36 #include "StyleSheetContents.h"
37 #include "XMLDocumentParser.h"
38 #include "XSLStyleSheet.h"
39 #include <wtf/IsoMallocInlines.h>
40 #include <wtf/SetForScope.h>
41
42 namespace WebCore {
43
44 WTF_MAKE_ISO_ALLOCATED_IMPL(ProcessingInstruction);
45
46 inline ProcessingInstruction::ProcessingInstruction(Document& document, const String& target, const String& data)
47     : CharacterData(document, data, CreateOther)
48     , m_target(target)
49 {
50 }
51
52 Ref<ProcessingInstruction> ProcessingInstruction::create(Document& document, const String& target, const String& data)
53 {
54     return adoptRef(*new ProcessingInstruction(document, target, data));
55 }
56
57 ProcessingInstruction::~ProcessingInstruction()
58 {
59     if (m_sheet)
60         m_sheet->clearOwnerNode();
61
62     if (m_cachedSheet)
63         m_cachedSheet->removeClient(*this);
64
65     if (isConnected())
66         document().styleScope().removeStyleSheetCandidateNode(*this);
67 }
68
69 String ProcessingInstruction::nodeName() const
70 {
71     return m_target;
72 }
73
74 Node::NodeType ProcessingInstruction::nodeType() const
75 {
76     return PROCESSING_INSTRUCTION_NODE;
77 }
78
79 Ref<Node> ProcessingInstruction::cloneNodeInternal(Document& targetDocument, CloningOperation)
80 {
81     // FIXME: Is it a problem that this does not copy m_localHref?
82     // What about other data members?
83     return create(targetDocument, m_target, data());
84 }
85
86 void ProcessingInstruction::checkStyleSheet()
87 {
88     // Prevent recursive loading of stylesheet.
89     if (m_isHandlingBeforeLoad)
90         return;
91
92     if (m_target == "xml-stylesheet" && document().frame() && parentNode() == &document()) {
93         // see http://www.w3.org/TR/xml-stylesheet/
94         // ### support stylesheet included in a fragment of this (or another) document
95         // ### make sure this gets called when adding from javascript
96         auto attributes = parseAttributes(data());
97         if (!attributes)
98             return;
99         String type = attributes->get("type");
100
101         m_isCSS = type.isEmpty() || type == "text/css";
102 #if ENABLE(XSLT)
103         m_isXSL = type == "text/xml" || type == "text/xsl" || type == "application/xml" || type == "application/xhtml+xml" || type == "application/rss+xml" || type == "application/atom+xml";
104         if (!m_isCSS && !m_isXSL)
105 #else
106         if (!m_isCSS)
107 #endif
108             return;
109
110         String href = attributes->get("href");
111         String alternate = attributes->get("alternate");
112         m_alternate = alternate == "yes";
113         m_title = attributes->get("title");
114         m_media = attributes->get("media");
115
116         if (m_alternate && m_title.isEmpty())
117             return;
118
119         if (href.length() > 1 && href[0] == '#') {
120             m_localHref = href.substring(1);
121 #if ENABLE(XSLT)
122             // We need to make a synthetic XSLStyleSheet that is embedded.  It needs to be able
123             // to kick off import/include loads that can hang off some parent sheet.
124             if (m_isXSL) {
125                 URL finalURL({ }, m_localHref);
126                 m_sheet = XSLStyleSheet::createEmbedded(this, finalURL);
127                 m_loading = false;
128                 document().scheduleToApplyXSLTransforms();
129             }
130 #endif
131         } else {
132             if (m_cachedSheet) {
133                 m_cachedSheet->removeClient(*this);
134                 m_cachedSheet = nullptr;
135             }
136
137             if (m_loading) {
138                 m_loading = false;
139                 document().styleScope().removePendingSheet(*this);
140             }
141
142             Ref<Document> originalDocument = document();
143
144             String url = document().completeURL(href).string();
145
146             {
147             SetForScope<bool> change(m_isHandlingBeforeLoad, true);
148             if (!dispatchBeforeLoadEvent(url))
149                 return;
150             }
151
152             bool didEventListenerDisconnectThisElement = !isConnected() || &document() != originalDocument.ptr();
153             if (didEventListenerDisconnectThisElement)
154                 return;
155             
156             m_loading = true;
157             document().styleScope().addPendingSheet(*this);
158
159             ASSERT_WITH_SECURITY_IMPLICATION(!m_cachedSheet);
160
161 #if ENABLE(XSLT)
162             if (m_isXSL) {
163                 auto options = CachedResourceLoader::defaultCachedResourceOptions();
164                 options.mode = FetchOptions::Mode::SameOrigin;
165                 m_cachedSheet = document().cachedResourceLoader().requestXSLStyleSheet({ResourceRequest(document().completeURL(href)), options}).value_or(nullptr);
166             } else
167 #endif
168             {
169                 String charset = attributes->get("charset");
170                 CachedResourceRequest request(document().completeURL(href), CachedResourceLoader::defaultCachedResourceOptions(), WTF::nullopt, charset.isEmpty() ? document().charset() : WTFMove(charset));
171
172                 m_cachedSheet = document().cachedResourceLoader().requestCSSStyleSheet(WTFMove(request)).value_or(nullptr);
173             }
174             if (m_cachedSheet)
175                 m_cachedSheet->addClient(*this);
176             else {
177                 // The request may have been denied if (for example) the stylesheet is local and the document is remote.
178                 m_loading = false;
179                 document().styleScope().removePendingSheet(*this);
180 #if ENABLE(XSLT)
181                 if (m_isXSL)
182                     document().scheduleToApplyXSLTransforms();
183 #endif
184             }
185         }
186     }
187 }
188
189 bool ProcessingInstruction::isLoading() const
190 {
191     if (m_loading)
192         return true;
193     if (!m_sheet)
194         return false;
195     return m_sheet->isLoading();
196 }
197
198 bool ProcessingInstruction::sheetLoaded()
199 {
200     if (!isLoading()) {
201         if (document().styleScope().hasPendingSheet(*this))
202             document().styleScope().removePendingSheet(*this);
203 #if ENABLE(XSLT)
204         if (m_isXSL)
205             document().scheduleToApplyXSLTransforms();
206 #endif
207         return true;
208     }
209     return false;
210 }
211
212 void ProcessingInstruction::setCSSStyleSheet(const String& href, const URL& baseURL, const String& charset, const CachedCSSStyleSheet* sheet)
213 {
214     if (!isConnected()) {
215         ASSERT(!m_sheet);
216         return;
217     }
218
219     ASSERT(m_isCSS);
220     CSSParserContext parserContext(document(), baseURL, charset);
221
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, MediaQueryParserContext(document())));
226
227     m_sheet = WTFMove(cssSheet);
228
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
231     // type.
232     Ref<Document> protect(document());
233     parseStyleSheet(sheet->sheetText());
234 }
235
236 #if ENABLE(XSLT)
237 void ProcessingInstruction::setXSLStyleSheet(const String& href, const URL& baseURL, const String& sheet)
238 {
239     ASSERT(m_isXSL);
240     m_sheet = XSLStyleSheet::create(this, href, baseURL);
241     Ref<Document> protect(document());
242     parseStyleSheet(sheet);
243 }
244 #endif
245
246 void ProcessingInstruction::parseStyleSheet(const String& sheet)
247 {
248     if (m_isCSS)
249         downcast<CSSStyleSheet>(*m_sheet).contents().parseString(sheet);
250 #if ENABLE(XSLT)
251     else if (m_isXSL)
252         downcast<XSLStyleSheet>(*m_sheet).parseString(sheet);
253 #endif
254
255     if (m_cachedSheet)
256         m_cachedSheet->removeClient(*this);
257     m_cachedSheet = nullptr;
258
259     m_loading = false;
260
261     if (m_isCSS)
262         downcast<CSSStyleSheet>(*m_sheet).contents().checkLoaded();
263 #if ENABLE(XSLT)
264     else if (m_isXSL)
265         downcast<XSLStyleSheet>(*m_sheet).checkLoaded();
266 #endif
267 }
268
269 void ProcessingInstruction::addSubresourceAttributeURLs(ListHashSet<URL>& urls) const
270 {
271     if (!sheet())
272         return;
273     
274     addSubresourceURL(urls, sheet()->baseURL());
275 }
276
277 Node::InsertedIntoAncestorResult ProcessingInstruction::insertedIntoAncestor(InsertionType insertionType, ContainerNode& parentOfInsertedTree)
278 {
279     CharacterData::insertedIntoAncestor(insertionType, parentOfInsertedTree);
280     if (!insertionType.connectedToDocument)
281         return InsertedIntoAncestorResult::Done;
282     document().styleScope().addStyleSheetCandidateNode(*this, m_createdByParser);
283     return InsertedIntoAncestorResult::NeedsPostInsertionCallback;
284 }
285
286 void ProcessingInstruction::didFinishInsertingNode()
287 {
288     checkStyleSheet();
289 }
290
291 void ProcessingInstruction::removedFromAncestor(RemovalType removalType, ContainerNode& oldParentOfRemovedTree)
292 {
293     CharacterData::removedFromAncestor(removalType, oldParentOfRemovedTree);
294     if (!removalType.disconnectedFromDocument)
295         return;
296     
297     document().styleScope().removeStyleSheetCandidateNode(*this);
298
299     if (m_sheet) {
300         ASSERT(m_sheet->ownerNode() == this);
301         m_sheet->clearOwnerNode();
302         m_sheet = nullptr;
303     }
304
305     if (m_loading) {
306         m_loading = false;
307         document().styleScope().removePendingSheet(*this);
308     }
309
310     document().styleScope().didChangeActiveStyleSheetCandidates();
311 }
312
313 void ProcessingInstruction::finishParsingChildren()
314 {
315     m_createdByParser = false;
316     CharacterData::finishParsingChildren();
317 }
318
319 } // namespace