Update std::expected to match libc++ coding style
[WebKit-https.git] / Source / WebCore / dom / ProcessingInstruction.cpp
1 /*
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.
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 "StyleScope.h"
35 #include "StyleSheetContents.h"
36 #include "XMLDocumentParser.h"
37 #include "XSLStyleSheet.h"
38 #include <wtf/SetForScope.h>
39
40 namespace WebCore {
41
42 inline ProcessingInstruction::ProcessingInstruction(Document& document, const String& target, const String& data)
43     : CharacterData(document, data, CreateOther)
44     , m_target(target)
45 {
46 }
47
48 Ref<ProcessingInstruction> ProcessingInstruction::create(Document& document, const String& target, const String& data)
49 {
50     return adoptRef(*new ProcessingInstruction(document, target, data));
51 }
52
53 ProcessingInstruction::~ProcessingInstruction()
54 {
55     if (m_sheet)
56         m_sheet->clearOwnerNode();
57
58     if (m_cachedSheet)
59         m_cachedSheet->removeClient(*this);
60
61     if (isConnected())
62         document().styleScope().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 Ref<Node> ProcessingInstruction::cloneNodeInternal(Document& targetDocument, CloningOperation)
76 {
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());
80 }
81
82 void ProcessingInstruction::checkStyleSheet()
83 {
84     // Prevent recursive loading of stylesheet.
85     if (m_isHandlingBeforeLoad)
86         return;
87
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
92         auto attributes = parseAttributes(data());
93         if (!attributes)
94             return;
95         String type = attributes->get("type");
96
97         m_isCSS = type.isEmpty() || type == "text/css";
98 #if ENABLE(XSLT)
99         m_isXSL = type == "text/xml" || type == "text/xsl" || type == "application/xml" || type == "application/xhtml+xml" || type == "application/rss+xml" || type == "application/atom+xml";
100         if (!m_isCSS && !m_isXSL)
101 #else
102         if (!m_isCSS)
103 #endif
104             return;
105
106         String href = attributes->get("href");
107         String alternate = attributes->get("alternate");
108         m_alternate = alternate == "yes";
109         m_title = attributes->get("title");
110         m_media = attributes->get("media");
111
112         if (m_alternate && m_title.isEmpty())
113             return;
114
115         if (href.length() > 1 && href[0] == '#') {
116             m_localHref = href.substring(1);
117 #if ENABLE(XSLT)
118             // We need to make a synthetic XSLStyleSheet that is embedded.  It needs to be able
119             // to kick off import/include loads that can hang off some parent sheet.
120             if (m_isXSL) {
121                 URL finalURL(ParsedURLString, m_localHref);
122                 m_sheet = XSLStyleSheet::createEmbedded(this, finalURL);
123                 m_loading = false;
124                 document().scheduleToApplyXSLTransforms();
125             }
126 #endif
127         } else {
128             if (m_cachedSheet) {
129                 m_cachedSheet->removeClient(*this);
130                 m_cachedSheet = nullptr;
131             }
132
133             if (m_loading) {
134                 m_loading = false;
135                 document().styleScope().removePendingSheet(*this);
136             }
137
138             Ref<Document> originalDocument = document();
139
140             String url = document().completeURL(href).string();
141
142             {
143             SetForScope<bool> change(m_isHandlingBeforeLoad, true);
144             if (!dispatchBeforeLoadEvent(url))
145                 return;
146             }
147
148             bool didEventListenerDisconnectThisElement = !isConnected() || &document() != originalDocument.ptr();
149             if (didEventListenerDisconnectThisElement)
150                 return;
151             
152             m_loading = true;
153             document().styleScope().addPendingSheet(*this);
154
155             ASSERT_WITH_SECURITY_IMPLICATION(!m_cachedSheet);
156
157 #if ENABLE(XSLT)
158             if (m_isXSL) {
159                 auto options = CachedResourceLoader::defaultCachedResourceOptions();
160                 options.mode = FetchOptions::Mode::SameOrigin;
161                 m_cachedSheet = document().cachedResourceLoader().requestXSLStyleSheet({ResourceRequest(document().completeURL(href)), options}).value_or(nullptr);
162             } else
163 #endif
164             {
165                 String charset = attributes->get("charset");
166                 CachedResourceRequest request(document().completeURL(href), CachedResourceLoader::defaultCachedResourceOptions(), std::nullopt, charset.isEmpty() ? document().charset() : WTFMove(charset));
167
168                 m_cachedSheet = document().cachedResourceLoader().requestCSSStyleSheet(WTFMove(request)).value_or(nullptr);
169             }
170             if (m_cachedSheet)
171                 m_cachedSheet->addClient(*this);
172             else {
173                 // The request may have been denied if (for example) the stylesheet is local and the document is remote.
174                 m_loading = false;
175                 document().styleScope().removePendingSheet(*this);
176 #if ENABLE(XSLT)
177                 if (m_isXSL)
178                     document().scheduleToApplyXSLTransforms();
179 #endif
180             }
181         }
182     }
183 }
184
185 bool ProcessingInstruction::isLoading() const
186 {
187     if (m_loading)
188         return true;
189     if (!m_sheet)
190         return false;
191     return m_sheet->isLoading();
192 }
193
194 bool ProcessingInstruction::sheetLoaded()
195 {
196     if (!isLoading()) {
197         if (document().styleScope().hasPendingSheet(*this))
198             document().styleScope().removePendingSheet(*this);
199 #if ENABLE(XSLT)
200         if (m_isXSL)
201             document().scheduleToApplyXSLTransforms();
202 #endif
203         return true;
204     }
205     return false;
206 }
207
208 void ProcessingInstruction::setCSSStyleSheet(const String& href, const URL& baseURL, const String& charset, const CachedCSSStyleSheet* sheet)
209 {
210     if (!isConnected()) {
211         ASSERT(!m_sheet);
212         return;
213     }
214
215     ASSERT(m_isCSS);
216     CSSParserContext parserContext(document(), baseURL, charset);
217
218     auto cssSheet = CSSStyleSheet::create(StyleSheetContents::create(href, parserContext), *this);
219     cssSheet.get().setDisabled(m_alternate);
220     cssSheet.get().setTitle(m_title);
221     cssSheet.get().setMediaQueries(MediaQuerySet::create(m_media));
222
223     m_sheet = WTFMove(cssSheet);
224
225     // We don't need the cross-origin security check here because we are
226     // getting the sheet text in "strict" mode. This enforces a valid CSS MIME
227     // type.
228     Ref<Document> protect(document());
229     parseStyleSheet(sheet->sheetText());
230 }
231
232 #if ENABLE(XSLT)
233 void ProcessingInstruction::setXSLStyleSheet(const String& href, const URL& baseURL, const String& sheet)
234 {
235     ASSERT(m_isXSL);
236     m_sheet = XSLStyleSheet::create(this, href, baseURL);
237     Ref<Document> protect(document());
238     parseStyleSheet(sheet);
239 }
240 #endif
241
242 void ProcessingInstruction::parseStyleSheet(const String& sheet)
243 {
244     if (m_isCSS)
245         downcast<CSSStyleSheet>(*m_sheet).contents().parseString(sheet);
246 #if ENABLE(XSLT)
247     else if (m_isXSL)
248         downcast<XSLStyleSheet>(*m_sheet).parseString(sheet);
249 #endif
250
251     if (m_cachedSheet)
252         m_cachedSheet->removeClient(*this);
253     m_cachedSheet = nullptr;
254
255     m_loading = false;
256
257     if (m_isCSS)
258         downcast<CSSStyleSheet>(*m_sheet).contents().checkLoaded();
259 #if ENABLE(XSLT)
260     else if (m_isXSL)
261         downcast<XSLStyleSheet>(*m_sheet).checkLoaded();
262 #endif
263 }
264
265 void ProcessingInstruction::addSubresourceAttributeURLs(ListHashSet<URL>& urls) const
266 {
267     if (!sheet())
268         return;
269     
270     addSubresourceURL(urls, sheet()->baseURL());
271 }
272
273 Node::InsertedIntoAncestorResult ProcessingInstruction::insertedIntoAncestor(InsertionType insertionType, ContainerNode& parentOfInsertedTree)
274 {
275     CharacterData::insertedIntoAncestor(insertionType, parentOfInsertedTree);
276     if (!insertionType.connectedToDocument)
277         return InsertedIntoAncestorResult::Done;
278     document().styleScope().addStyleSheetCandidateNode(*this, m_createdByParser);
279     return InsertedIntoAncestorResult::NeedsPostInsertionCallback;
280 }
281
282 void ProcessingInstruction::didFinishInsertingNode()
283 {
284     checkStyleSheet();
285 }
286
287 void ProcessingInstruction::removedFromAncestor(RemovalType removalType, ContainerNode& oldParentOfRemovedTree)
288 {
289     CharacterData::removedFromAncestor(removalType, oldParentOfRemovedTree);
290     if (!removalType.disconnectedFromDocument)
291         return;
292     
293     document().styleScope().removeStyleSheetCandidateNode(*this);
294
295     if (m_sheet) {
296         ASSERT(m_sheet->ownerNode() == this);
297         m_sheet->clearOwnerNode();
298         m_sheet = nullptr;
299     }
300
301     if (m_loading) {
302         m_loading = false;
303         document().styleScope().removePendingSheet(*this);
304     }
305
306     document().styleScope().didChangeActiveStyleSheetCandidates();
307 }
308
309 void ProcessingInstruction::finishParsingChildren()
310 {
311     m_createdByParser = false;
312     CharacterData::finishParsingChildren();
313 }
314
315 } // namespace