9d77e1b20c207379f8ba7bb3afeec4595a450a8d
[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 "AuthorStyleSheets.h"
26 #include "CSSStyleSheet.h"
27 #include "CachedCSSStyleSheet.h"
28 #include "CachedResourceLoader.h"
29 #include "CachedResourceRequest.h"
30 #include "CachedXSLStyleSheet.h"
31 #include "Document.h"
32 #include "ExceptionCode.h"
33 #include "Frame.h"
34 #include "FrameLoader.h"
35 #include "XSLStyleSheet.h"
36 #include "XMLDocumentParser.h" // for parseAttributes()
37 #include "MediaList.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     , m_cachedSheet(0)
46     , m_loading(false)
47     , m_alternate(false)
48     , m_createdByParser(false)
49     , m_isCSS(false)
50 #if ENABLE(XSLT)
51     , m_isXSL(false)
52 #endif
53 {
54 }
55
56 Ref<ProcessingInstruction> ProcessingInstruction::create(Document& document, const String& target, const String& data)
57 {
58     return adoptRef(*new ProcessingInstruction(document, target, data));
59 }
60
61 ProcessingInstruction::~ProcessingInstruction()
62 {
63     if (m_sheet)
64         m_sheet->clearOwnerNode();
65
66     if (m_cachedSheet)
67         m_cachedSheet->removeClient(this);
68
69     if (inDocument())
70         document().authorStyleSheets().removeStyleSheetCandidateNode(*this);
71 }
72
73 String ProcessingInstruction::nodeName() const
74 {
75     return m_target;
76 }
77
78 Node::NodeType ProcessingInstruction::nodeType() const
79 {
80     return PROCESSING_INSTRUCTION_NODE;
81 }
82
83 Ref<Node> ProcessingInstruction::cloneNodeInternal(Document& targetDocument, CloningOperation)
84 {
85     // FIXME: Is it a problem that this does not copy m_localHref?
86     // What about other data members?
87     return create(targetDocument, m_target, data());
88 }
89
90 void ProcessingInstruction::checkStyleSheet()
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         bool attrsOk;
97         const HashMap<String, String> attrs = parseAttributes(data(), attrsOk);
98         if (!attrsOk)
99             return;
100         HashMap<String, String>::const_iterator i = attrs.find("type");
101         String type;
102         if (i != attrs.end())
103             type = i->value;
104
105         m_isCSS = type.isEmpty() || type == "text/css";
106 #if ENABLE(XSLT)
107         m_isXSL = (type == "text/xml" || type == "text/xsl" || type == "application/xml" ||
108                    type == "application/xhtml+xml" || type == "application/rss+xml" || type == "application/atom+xml");
109         if (!m_isCSS && !m_isXSL)
110 #else
111         if (!m_isCSS)
112 #endif
113             return;
114
115         String href = attrs.get("href");
116         String alternate = attrs.get("alternate");
117         m_alternate = alternate == "yes";
118         m_title = attrs.get("title");
119         m_media = attrs.get("media");
120
121         if (m_alternate && m_title.isEmpty())
122             return;
123
124         if (href.length() > 1 && href[0] == '#') {
125             m_localHref = href.substring(1);
126 #if ENABLE(XSLT)
127             // We need to make a synthetic XSLStyleSheet that is embedded.  It needs to be able
128             // to kick off import/include loads that can hang off some parent sheet.
129             if (m_isXSL) {
130                 URL finalURL(ParsedURLString, m_localHref);
131                 m_sheet = XSLStyleSheet::createEmbedded(this, finalURL);
132                 m_loading = false;
133             }
134 #endif
135         } else {
136             if (m_cachedSheet) {
137                 m_cachedSheet->removeClient(this);
138                 m_cachedSheet = 0;
139             }
140             
141             String url = document().completeURL(href).string();
142             if (!dispatchBeforeLoadEvent(url))
143                 return;
144             
145             m_loading = true;
146             document().authorStyleSheets().addPendingSheet();
147             
148             CachedResourceRequest request(ResourceRequest(document().completeURL(href)));
149 #if ENABLE(XSLT)
150             if (m_isXSL)
151                 m_cachedSheet = document().cachedResourceLoader().requestXSLStyleSheet(request);
152             else
153 #endif
154             {
155                 String charset = attrs.get("charset");
156                 if (charset.isEmpty())
157                     charset = document().charset();
158                 request.setCharset(charset);
159
160                 m_cachedSheet = document().cachedResourceLoader().requestCSSStyleSheet(request);
161             }
162             if (m_cachedSheet)
163                 m_cachedSheet->addClient(this);
164             else {
165                 // The request may have been denied if (for example) the stylesheet is local and the document is remote.
166                 m_loading = false;
167                 document().authorStyleSheets().removePendingSheet();
168             }
169         }
170     }
171 }
172
173 bool ProcessingInstruction::isLoading() const
174 {
175     if (m_loading)
176         return true;
177     if (!m_sheet)
178         return false;
179     return m_sheet->isLoading();
180 }
181
182 bool ProcessingInstruction::sheetLoaded()
183 {
184     if (!isLoading()) {
185         document().authorStyleSheets().removePendingSheet();
186         return true;
187     }
188     return false;
189 }
190
191 void ProcessingInstruction::setCSSStyleSheet(const String& href, const URL& baseURL, const String& charset, const CachedCSSStyleSheet* sheet)
192 {
193     if (!inDocument()) {
194         ASSERT(!m_sheet);
195         return;
196     }
197
198     ASSERT(m_isCSS);
199     CSSParserContext parserContext(document(), baseURL, charset);
200
201     auto cssSheet = CSSStyleSheet::create(StyleSheetContents::create(href, parserContext), this);
202     cssSheet.get().setDisabled(m_alternate);
203     cssSheet.get().setTitle(m_title);
204     cssSheet.get().setMediaQueries(MediaQuerySet::create(m_media));
205
206     m_sheet = WTF::move(cssSheet);
207
208     // We don't need the cross-origin security check here because we are
209     // getting the sheet text in "strict" mode. This enforces a valid CSS MIME
210     // type.
211     parseStyleSheet(sheet->sheetText());
212 }
213
214 #if ENABLE(XSLT)
215 void ProcessingInstruction::setXSLStyleSheet(const String& href, const URL& baseURL, const String& sheet)
216 {
217     ASSERT(m_isXSL);
218     m_sheet = XSLStyleSheet::create(this, href, baseURL);
219     Ref<Document> protect(document());
220     parseStyleSheet(sheet);
221 }
222 #endif
223
224 void ProcessingInstruction::parseStyleSheet(const String& sheet)
225 {
226     if (m_isCSS)
227         downcast<CSSStyleSheet>(*m_sheet).contents().parseString(sheet);
228 #if ENABLE(XSLT)
229     else if (m_isXSL)
230         downcast<XSLStyleSheet>(*m_sheet).parseString(sheet);
231 #endif
232
233     if (m_cachedSheet)
234         m_cachedSheet->removeClient(this);
235     m_cachedSheet = 0;
236
237     m_loading = false;
238
239     if (m_isCSS)
240         downcast<CSSStyleSheet>(*m_sheet).contents().checkLoaded();
241 #if ENABLE(XSLT)
242     else if (m_isXSL)
243         downcast<XSLStyleSheet>(*m_sheet).checkLoaded();
244 #endif
245 }
246
247 void ProcessingInstruction::setCSSStyleSheet(PassRefPtr<CSSStyleSheet> sheet)
248 {
249     ASSERT(!m_cachedSheet);
250     ASSERT(!m_loading);
251     m_sheet = sheet;
252     sheet->setTitle(m_title);
253     sheet->setDisabled(m_alternate);
254 }
255
256 void ProcessingInstruction::addSubresourceAttributeURLs(ListHashSet<URL>& urls) const
257 {
258     if (!sheet())
259         return;
260     
261     addSubresourceURL(urls, sheet()->baseURL());
262 }
263
264 Node::InsertionNotificationRequest ProcessingInstruction::insertedInto(ContainerNode& insertionPoint)
265 {
266     CharacterData::insertedInto(insertionPoint);
267     if (!insertionPoint.inDocument())
268         return InsertionDone;
269     document().authorStyleSheets().addStyleSheetCandidateNode(*this, m_createdByParser);
270     checkStyleSheet();
271     return InsertionDone;
272 }
273
274 void ProcessingInstruction::removedFrom(ContainerNode& insertionPoint)
275 {
276     CharacterData::removedFrom(insertionPoint);
277     if (!insertionPoint.inDocument())
278         return;
279     
280     document().authorStyleSheets().removeStyleSheetCandidateNode(*this);
281
282     if (m_sheet) {
283         ASSERT(m_sheet->ownerNode() == this);
284         m_sheet->clearOwnerNode();
285         m_sheet = nullptr;
286     }
287
288     // If we're in document teardown, then we don't need to do any notification of our sheet's removal.
289     if (document().hasLivingRenderTree())
290         document().styleResolverChanged(DeferRecalcStyle);
291 }
292
293 void ProcessingInstruction::finishParsingChildren()
294 {
295     m_createdByParser = false;
296     CharacterData::finishParsingChildren();
297 }
298
299 } // namespace