9673dd28276ab50dfc76182f9c13576ed65c7548
[WebKit-https.git] / Source / WebCore / dom / ProcessingInstruction.cpp
1 /*
2  * Copyright (C) 2000 Peter Kelly (pmk@post.com)
3  * Copyright (C) 2006, 2008, 2009 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 "ExceptionCode.h"
32 #include "Frame.h"
33 #include "FrameLoader.h"
34 #include "XSLStyleSheet.h"
35 #include "XMLDocumentParser.h" // for parseAttributes()
36 #include "MediaList.h"
37 #include "StyleScope.h"
38 #include "StyleSheetContents.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 (inDocument())
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     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(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 #if ENABLE(XSLT)
99         m_isXSL = (type == "text/xml" || type == "text/xsl" || type == "application/xml" ||
100                    type == "application/xhtml+xml" || type == "application/rss+xml" || type == "application/atom+xml");
101         if (!m_isCSS && !m_isXSL)
102 #else
103         if (!m_isCSS)
104 #endif
105             return;
106
107         String href = attrs.get("href");
108         String alternate = attrs.get("alternate");
109         m_alternate = alternate == "yes";
110         m_title = attrs.get("title");
111         m_media = attrs.get("media");
112
113         if (m_alternate && m_title.isEmpty())
114             return;
115
116         if (href.length() > 1 && href[0] == '#') {
117             m_localHref = href.substring(1);
118 #if ENABLE(XSLT)
119             // We need to make a synthetic XSLStyleSheet that is embedded.  It needs to be able
120             // to kick off import/include loads that can hang off some parent sheet.
121             if (m_isXSL) {
122                 URL finalURL(ParsedURLString, m_localHref);
123                 m_sheet = XSLStyleSheet::createEmbedded(this, finalURL);
124                 m_loading = false;
125             }
126 #endif
127         } else {
128             if (m_cachedSheet) {
129                 m_cachedSheet->removeClient(*this);
130                 m_cachedSheet = nullptr;
131             }
132
133             String url = document().completeURL(href).string();
134             if (!dispatchBeforeLoadEvent(url))
135                 return;
136
137             m_loading = true;
138             document().styleScope().addPendingSheet();
139
140 #if ENABLE(XSLT)
141             if (m_isXSL) {
142                 auto options = CachedResourceLoader::defaultCachedResourceOptions();
143                 options.mode = FetchOptions::Mode::SameOrigin;
144                 m_cachedSheet = document().cachedResourceLoader().requestXSLStyleSheet({ResourceRequest(document().completeURL(href)), options});
145             } else
146 #endif
147             {
148                 CachedResourceRequest request(ResourceRequest(document().completeURL(href)));
149                 String charset = attrs.get("charset");
150                 if (charset.isEmpty())
151                     charset = document().charset();
152                 request.setCharset(charset);
153
154                 m_cachedSheet = document().cachedResourceLoader().requestCSSStyleSheet(WTFMove(request));
155             }
156             if (m_cachedSheet)
157                 m_cachedSheet->addClient(*this);
158             else {
159                 // The request may have been denied if (for example) the stylesheet is local and the document is remote.
160                 m_loading = false;
161                 document().styleScope().removePendingSheet();
162             }
163         }
164     }
165 }
166
167 bool ProcessingInstruction::isLoading() const
168 {
169     if (m_loading)
170         return true;
171     if (!m_sheet)
172         return false;
173     return m_sheet->isLoading();
174 }
175
176 bool ProcessingInstruction::sheetLoaded()
177 {
178     if (!isLoading()) {
179         document().styleScope().removePendingSheet();
180         return true;
181     }
182     return false;
183 }
184
185 void ProcessingInstruction::setCSSStyleSheet(const String& href, const URL& baseURL, const String& charset, const CachedCSSStyleSheet* sheet)
186 {
187     if (!inDocument()) {
188         ASSERT(!m_sheet);
189         return;
190     }
191
192     ASSERT(m_isCSS);
193     CSSParserContext parserContext(document(), baseURL, charset);
194
195     auto cssSheet = CSSStyleSheet::create(StyleSheetContents::create(href, parserContext), *this);
196     cssSheet.get().setDisabled(m_alternate);
197     cssSheet.get().setTitle(m_title);
198     cssSheet.get().setMediaQueries(MediaQuerySet::create(m_media));
199
200     m_sheet = WTFMove(cssSheet);
201
202     // We don't need the cross-origin security check here because we are
203     // getting the sheet text in "strict" mode. This enforces a valid CSS MIME
204     // type.
205     parseStyleSheet(sheet->sheetText());
206 }
207
208 #if ENABLE(XSLT)
209 void ProcessingInstruction::setXSLStyleSheet(const String& href, const URL& baseURL, const String& sheet)
210 {
211     ASSERT(m_isXSL);
212     m_sheet = XSLStyleSheet::create(this, href, baseURL);
213     Ref<Document> protect(document());
214     parseStyleSheet(sheet);
215 }
216 #endif
217
218 void ProcessingInstruction::parseStyleSheet(const String& sheet)
219 {
220     if (m_isCSS)
221         downcast<CSSStyleSheet>(*m_sheet).contents().parseString(sheet);
222 #if ENABLE(XSLT)
223     else if (m_isXSL)
224         downcast<XSLStyleSheet>(*m_sheet).parseString(sheet);
225 #endif
226
227     if (m_cachedSheet)
228         m_cachedSheet->removeClient(*this);
229     m_cachedSheet = nullptr;
230
231     m_loading = false;
232
233     if (m_isCSS)
234         downcast<CSSStyleSheet>(*m_sheet).contents().checkLoaded();
235 #if ENABLE(XSLT)
236     else if (m_isXSL)
237         downcast<XSLStyleSheet>(*m_sheet).checkLoaded();
238 #endif
239 }
240
241 void ProcessingInstruction::addSubresourceAttributeURLs(ListHashSet<URL>& urls) const
242 {
243     if (!sheet())
244         return;
245     
246     addSubresourceURL(urls, sheet()->baseURL());
247 }
248
249 Node::InsertionNotificationRequest ProcessingInstruction::insertedInto(ContainerNode& insertionPoint)
250 {
251     CharacterData::insertedInto(insertionPoint);
252     if (!insertionPoint.inDocument())
253         return InsertionDone;
254     document().styleScope().addStyleSheetCandidateNode(*this, m_createdByParser);
255     checkStyleSheet();
256     return InsertionDone;
257 }
258
259 void ProcessingInstruction::removedFrom(ContainerNode& insertionPoint)
260 {
261     CharacterData::removedFrom(insertionPoint);
262     if (!insertionPoint.inDocument())
263         return;
264     
265     document().styleScope().removeStyleSheetCandidateNode(*this);
266
267     if (m_sheet) {
268         ASSERT(m_sheet->ownerNode() == this);
269         m_sheet->clearOwnerNode();
270         m_sheet = nullptr;
271     }
272
273     if (m_loading) {
274         m_loading = false;
275         document().styleScope().removePendingSheet();
276     }
277
278     // If we're in document teardown, then we don't need to do any notification of our sheet's removal.
279     if (document().hasLivingRenderTree())
280         document().styleScope().didChangeContentsOrInterpretation();
281 }
282
283 void ProcessingInstruction::finishParsingChildren()
284 {
285     m_createdByParser = false;
286     CharacterData::finishParsingChildren();
287 }
288
289 } // namespace