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