Assert that Node::insertedInto doesn't fire an event
[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         bool attrsOk;
93         const HashMap<String, String> attrs = parseAttributes(data(), attrsOk);
94         if (!attrsOk)
95             return;
96         HashMap<String, String>::const_iterator i = attrs.find("type");
97         String type;
98         if (i != attrs.end())
99             type = i->value;
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" ||
104                    type == "application/xhtml+xml" || type == "application/rss+xml" || type == "application/atom+xml");
105         if (!m_isCSS && !m_isXSL)
106 #else
107         if (!m_isCSS)
108 #endif
109             return;
110
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");
116
117         if (m_alternate && m_title.isEmpty())
118             return;
119
120         if (href.length() > 1 && href[0] == '#') {
121             m_localHref = href.substring(1);
122 #if ENABLE(XSLT)
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.
125             if (m_isXSL) {
126                 URL finalURL(ParsedURLString, m_localHref);
127                 m_sheet = XSLStyleSheet::createEmbedded(this, finalURL);
128                 m_loading = false;
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}).valueOr(nullptr);
166             } else
167 #endif
168             {
169                 String charset = attrs.get("charset");
170                 CachedResourceRequest request(document().completeURL(href), CachedResourceLoader::defaultCachedResourceOptions(), std::nullopt, charset.isEmpty() ? document().charset() : WTFMove(charset));
171
172                 m_cachedSheet = document().cachedResourceLoader().requestCSSStyleSheet(WTFMove(request)).valueOr(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().styleScope().flushPendingUpdate();
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().styleScope().flushPendingUpdate();
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));
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::InsertionNotificationRequest ProcessingInstruction::insertedInto(ContainerNode& insertionPoint)
278 {
279     CharacterData::insertedInto(insertionPoint);
280     if (!insertionPoint.isConnected())
281         return InsertionDone;
282     document().styleScope().addStyleSheetCandidateNode(*this, m_createdByParser);
283     return InsertionShouldCallFinishedInsertingSubtree;
284 }
285
286 void ProcessingInstruction::finishedInsertingSubtree()
287 {
288     checkStyleSheet();
289 }
290
291 void ProcessingInstruction::removedFrom(ContainerNode& insertionPoint)
292 {
293     CharacterData::removedFrom(insertionPoint);
294     if (!insertionPoint.isConnected())
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