Land initial support for XSLT using xml-stylesheet PIs.
[WebKit-https.git] / WebCore / khtml / xml / dom_xmlimpl.cpp
1 /**
2  * This file is part of the DOM implementation for KDE.
3  *
4  * Copyright (C) 2000 Peter Kelly (pmk@post.com)
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., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 #include "dom/dom_exception.h"
23
24 #include "xml/dom_xmlimpl.h"
25 #include "xml/dom_docimpl.h"
26 #include "xml/dom_stringimpl.h"
27 #include "css/css_stylesheetimpl.h"
28 #ifdef KHTML_XSLT
29 #include "xsl_stylesheetimpl.h"
30 #endif
31 #include "misc/loader.h"
32 #include "xml/xml_tokenizer.h"
33
34 using khtml::parseAttributes;
35
36 namespace DOM {
37
38 EntityImpl::EntityImpl(DocumentPtr *doc) : NodeBaseImpl(doc)
39 {
40     m_publicId = 0;
41     m_systemId = 0;
42     m_notationName = 0;
43     m_name = 0;
44 }
45
46 EntityImpl::EntityImpl(DocumentPtr *doc, DOMString _name) : NodeBaseImpl(doc)
47 {
48     m_publicId = 0;
49     m_systemId = 0;
50     m_notationName = 0;
51     m_name = _name.implementation();
52     if (m_name)
53         m_name->ref();
54 }
55
56 EntityImpl::EntityImpl(DocumentPtr *doc, DOMString _publicId, DOMString _systemId, DOMString _notationName) : NodeBaseImpl(doc)
57 {
58     m_publicId = _publicId.implementation();
59     if (m_publicId)
60         m_publicId->ref();
61     m_systemId = _systemId.implementation();
62     if (m_systemId)
63         m_systemId->ref();
64     m_notationName = _notationName.implementation();
65     if (m_notationName)
66         m_notationName->ref();
67     m_name = 0;
68 }
69
70
71 EntityImpl::~EntityImpl()
72 {
73     if (m_publicId)
74         m_publicId->deref();
75     if (m_systemId)
76         m_systemId->deref();
77     if (m_notationName)
78         m_notationName->deref();
79     if (m_name)
80         m_name->deref();
81 }
82
83 DOMString EntityImpl::publicId() const
84 {
85     return m_publicId;
86 }
87
88 DOMString EntityImpl::systemId() const
89 {
90     return m_systemId;
91 }
92
93 DOMString EntityImpl::notationName() const
94 {
95     return m_notationName;
96 }
97
98 DOMString EntityImpl::nodeName() const
99 {
100     return m_name;
101 }
102
103 unsigned short EntityImpl::nodeType() const
104 {
105     return Node::ENTITY_NODE;
106 }
107
108 NodeImpl *EntityImpl::cloneNode ( bool /*deep*/)
109 {
110     // Spec says cloning Document nodes is "implementation dependent"
111     // so we do not support it...
112     return 0;
113 }
114
115 // DOM Section 1.1.1
116 bool EntityImpl::childTypeAllowed( unsigned short type )
117 {
118     switch (type) {
119         case Node::ELEMENT_NODE:
120         case Node::PROCESSING_INSTRUCTION_NODE:
121         case Node::COMMENT_NODE:
122         case Node::TEXT_NODE:
123         case Node::CDATA_SECTION_NODE:
124         case Node::ENTITY_REFERENCE_NODE:
125             return true;
126             break;
127         default:
128             return false;
129     }
130 }
131
132 DOMString EntityImpl::toString() const
133 {
134     DOMString result = "<!ENTITY' ";
135
136     if (m_name && m_name->l != 0) {
137         result += " ";
138         result += m_name;
139     }
140
141     if (m_publicId && m_publicId->l != 0) {
142         result += " PUBLIC \"";
143         result += m_publicId;
144         result += "\" \"";
145         result += m_systemId;
146         result += "\"";
147     } else if (m_systemId && m_systemId->l != 0) {
148         result += " SYSTEM \"";
149         result += m_systemId;
150         result += "\"";
151     }
152
153     if (m_notationName && m_notationName->l != 0) {
154         result += " NDATA ";
155         result += m_notationName;
156     }
157
158     result += ">";
159
160     return result;
161 }
162
163
164 // -------------------------------------------------------------------------
165
166 EntityReferenceImpl::EntityReferenceImpl(DocumentPtr *doc) : NodeBaseImpl(doc)
167 {
168     m_entityName = 0;
169 }
170
171 EntityReferenceImpl::EntityReferenceImpl(DocumentPtr *doc, DOMStringImpl *_entityName) : NodeBaseImpl(doc)
172 {
173     m_entityName = _entityName;
174     if (m_entityName)
175         m_entityName->ref();
176 }
177
178 EntityReferenceImpl::~EntityReferenceImpl()
179 {
180     if (m_entityName)
181         m_entityName->deref();
182 }
183
184 DOMString EntityReferenceImpl::nodeName() const
185 {
186     return m_entityName;
187 }
188
189 unsigned short EntityReferenceImpl::nodeType() const
190 {
191     return Node::ENTITY_REFERENCE_NODE;
192 }
193
194 NodeImpl *EntityReferenceImpl::cloneNode ( bool deep )
195 {
196     EntityReferenceImpl *clone = new EntityReferenceImpl(docPtr(),m_entityName);
197     // ### make sure children are readonly
198     // ### since we are a reference, should we clone children anyway (even if not deep?)
199     if (deep)
200         cloneChildNodes(clone);
201     return clone;
202 }
203
204 // DOM Section 1.1.1
205 bool EntityReferenceImpl::childTypeAllowed( unsigned short type )
206 {
207     switch (type) {
208         case Node::ELEMENT_NODE:
209         case Node::PROCESSING_INSTRUCTION_NODE:
210         case Node::COMMENT_NODE:
211         case Node::TEXT_NODE:
212         case Node::CDATA_SECTION_NODE:
213         case Node::ENTITY_REFERENCE_NODE:
214             return true;
215             break;
216         default:
217             return false;
218     }
219 }
220
221 DOMString EntityReferenceImpl::toString() const
222 {
223     DOMString result = "&";
224     result += m_entityName;
225     result += ";";
226
227     return result;
228 }
229
230 // -------------------------------------------------------------------------
231
232 NotationImpl::NotationImpl(DocumentPtr *doc) : NodeBaseImpl(doc)
233 {
234     m_publicId = 0;
235     m_systemId = 0;
236     m_name = 0;
237 }
238
239 NotationImpl::NotationImpl(DocumentPtr *doc, DOMString _name, DOMString _publicId, DOMString _systemId) : NodeBaseImpl(doc)
240 {
241     m_name = _name.implementation();
242     if (m_name)
243         m_name->ref();
244     m_publicId = _publicId.implementation();
245     if (m_publicId)
246         m_publicId->ref();
247     m_systemId = _systemId.implementation();
248     if (m_systemId)
249         m_systemId->ref();
250 }
251
252 NotationImpl::~NotationImpl()
253 {
254     if (m_name)
255         m_name->deref();
256     if (m_publicId)
257         m_publicId->deref();
258     if (m_systemId)
259         m_systemId->deref();
260 }
261
262 DOMString NotationImpl::publicId() const
263 {
264     return m_publicId;
265 }
266
267 DOMString NotationImpl::systemId() const
268 {
269     return m_systemId;
270 }
271
272 DOMString NotationImpl::nodeName() const
273 {
274     return m_name;
275 }
276
277 unsigned short NotationImpl::nodeType() const
278 {
279     return Node::NOTATION_NODE;
280 }
281
282 NodeImpl *NotationImpl::cloneNode ( bool /*deep*/)
283 {
284     // Spec says cloning Document nodes is "implementation dependent"
285     // so we do not support it...
286     return 0;
287 }
288
289 // DOM Section 1.1.1
290 bool NotationImpl::childTypeAllowed( unsigned short /*type*/ )
291 {
292     return false;
293 }
294
295 // -------------------------------------------------------------------------
296
297 // ### need a way of updating these properly whenever child nodes of the processing instruction
298 // change or are added/removed
299
300 ProcessingInstructionImpl::ProcessingInstructionImpl(DocumentPtr *doc) : NodeBaseImpl(doc)
301 {
302     m_target = 0;
303     m_data = 0;
304     m_localHref = 0;
305     m_sheet = 0;
306     m_cachedSheet = 0;
307     m_loading = false;
308 #ifdef KHTML_XSLT
309     m_isXSL = false;
310 #endif
311 }
312
313 ProcessingInstructionImpl::ProcessingInstructionImpl(DocumentPtr *doc, DOMString _target, DOMString _data) : NodeBaseImpl(doc)
314 {
315     m_target = _target.implementation();
316     if (m_target)
317         m_target->ref();
318     m_data = _data.implementation();
319     if (m_data)
320         m_data->ref();
321     m_sheet = 0;
322     m_cachedSheet = 0;
323     m_localHref = 0;
324 #ifdef KHTML_XSLT
325     m_isXSL = false;
326 #endif
327 }
328
329 ProcessingInstructionImpl::~ProcessingInstructionImpl()
330 {
331     if (m_target)
332         m_target->deref();
333     if (m_data)
334         m_data->deref();
335     if (m_cachedSheet)
336         m_cachedSheet->deref(this);
337     if (m_sheet)
338         m_sheet->deref();
339 }
340
341 DOMString ProcessingInstructionImpl::target() const
342 {
343     return m_target;
344 }
345
346 DOMString ProcessingInstructionImpl::data() const
347 {
348     return m_data;
349 }
350
351 void ProcessingInstructionImpl::setData( const DOMString &_data, int &exceptioncode )
352 {
353     // NO_MODIFICATION_ALLOWED_ERR: Raised when the node is readonly.
354     if (isReadOnly()) {
355         exceptioncode = DOMException::NO_MODIFICATION_ALLOWED_ERR;
356         return;
357     }
358
359     if (m_data)
360         m_data->deref();
361     m_data = _data.implementation();
362     if (m_data)
363         m_data->ref();
364 }
365
366 DOMString ProcessingInstructionImpl::nodeName() const
367 {
368     return m_target;
369 }
370
371 unsigned short ProcessingInstructionImpl::nodeType() const
372 {
373     return Node::PROCESSING_INSTRUCTION_NODE;
374 }
375
376 DOMString ProcessingInstructionImpl::nodeValue() const
377 {
378     return m_data;
379 }
380
381 void ProcessingInstructionImpl::setNodeValue( const DOMString &_nodeValue, int &exceptioncode )
382 {
383     // NO_MODIFICATION_ALLOWED_ERR: taken care of by setData()
384     setData(_nodeValue, exceptioncode);
385 }
386
387 NodeImpl *ProcessingInstructionImpl::cloneNode ( bool /*deep*/)
388 {
389     // ### copy m_localHref
390     return new ProcessingInstructionImpl(docPtr(),m_target,m_data);
391 }
392
393 DOMString ProcessingInstructionImpl::localHref() const
394 {
395     return m_localHref;
396 }
397
398 // DOM Section 1.1.1
399 bool ProcessingInstructionImpl::childTypeAllowed( unsigned short /*type*/ )
400 {
401     return false;
402 }
403
404 bool ProcessingInstructionImpl::checkStyleSheet()
405 {
406     if (m_target && DOMString(m_target) == "xml-stylesheet") {
407         // see http://www.w3.org/TR/xml-stylesheet/
408         // ### check that this occurs only in the prolog
409         // ### support stylesheet included in a fragment of this (or another) document
410         // ### make sure this gets called when adding from javascript
411         bool attrsOk;
412         const QMap<QString, QString> attrs = parseAttributes(m_data, attrsOk);
413         if (!attrsOk)
414             return true;
415         QMap<QString, QString>::ConstIterator i = attrs.find("type");
416         QString type;
417         if (i != attrs.end())
418             type = *i;
419         
420         bool isCSS = type.isEmpty() || type == "text/css";
421 #ifdef KHTML_XSLT
422         m_isXSL = (type == "text/xml" || type == "text/xsl" || type == "application/xml" ||
423                    type == "application/xml+xhtml");
424         if (!isCSS && !m_isXSL)
425 #else
426         if (!isCSS)
427 #endif
428             return true;
429
430 #ifdef KHTML_XSLT
431         if (m_isXSL)
432             getDocument()->tokenizer()->setTransformSource(getDocument());
433 #endif
434
435         i = attrs.find("href");
436         QString href;
437         if (i != attrs.end())
438             href = *i;
439
440         if (href.length()>1)
441         {
442             if (href[0]=='#')
443             {
444                 DOMString newLocalHref = href.mid(1);
445                 if (m_localHref)
446                     m_localHref->deref();
447                 m_localHref = newLocalHref.implementation();
448                 if (m_localHref)
449                     m_localHref->ref();
450 #ifdef KHTML_XSLT
451                 return !m_isXSL;
452 #endif
453             }
454             else
455             {
456                 // ### some validation on the URL?
457                 // ### FIXME charset
458                 if (getDocument()->part()) {
459                     m_loading = true;
460                     getDocument()->addPendingSheet();
461                     if (m_cachedSheet) m_cachedSheet->deref(this);
462 #ifdef KHTML_XSLT
463                     if (m_isXSL)
464                         m_cachedSheet = getDocument()->docLoader()->requestXSLStyleSheet(getDocument()->completeURL(href));
465                     else
466 #endif
467                     m_cachedSheet = getDocument()->docLoader()->requestStyleSheet(getDocument()->completeURL(href), QString::null);
468                     if (m_cachedSheet)
469                         m_cachedSheet->ref( this );
470 #ifdef KHTML_XSLT
471                     return !m_isXSL;
472 #endif
473                 }
474             }
475
476         }
477     }
478     
479     return true;
480 }
481
482 StyleSheetImpl* ProcessingInstructionImpl::sheet() const
483 {
484     return m_sheet;
485 }
486
487 bool ProcessingInstructionImpl::isLoading() const
488 {
489     if (m_loading)
490         return true;
491     if (!m_sheet)
492         return false;
493     return m_sheet->isLoading();
494 }
495
496 void ProcessingInstructionImpl::sheetLoaded()
497 {
498     if (!isLoading())
499         getDocument()->stylesheetLoaded();
500 }
501
502 void ProcessingInstructionImpl::setStyleSheet(const DOM::DOMString &url, const DOM::DOMString &sheet)
503 {
504     if (m_sheet)
505         m_sheet->deref();
506 #ifdef KHTML_XSLT
507     if (m_isXSL)
508         m_sheet = new XSLStyleSheetImpl(this, url);
509     else
510 #endif
511         m_sheet = new CSSStyleSheetImpl(this, url);
512     m_sheet->ref();
513     m_sheet->parseString(sheet);
514     if (m_cachedSheet)
515         m_cachedSheet->deref(this);
516     m_cachedSheet = 0;
517
518     m_loading = false;
519
520     // Tell the doc about the sheet.
521     if (!isLoading() && m_sheet)
522         getDocument()->stylesheetLoaded();
523 }
524
525 void ProcessingInstructionImpl::setStyleSheet(CSSStyleSheetImpl* sheet)
526 {
527     if (m_sheet)
528         m_sheet->deref();
529     m_sheet = sheet;
530     if (m_sheet)
531         m_sheet->ref();
532 }
533
534 DOMString ProcessingInstructionImpl::toString() const
535 {
536     DOMString result = "<?";
537     result += m_target;
538     result += " ";
539     result += m_data;
540     result += ">";
541     return result;
542 }
543
544 } // namespace