1/*
2 * This file is part of the XSL implementation.
3 *
4 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple, Inc. All rights reserved.
5 * Copyright (C) 2005, 2006 Alexey Proskuryakov <ap@webkit.org>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB.  If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23#include "config.h"
24
25#if ENABLE(XSLT)
26
27#include "XSLTProcessor.h"
28
29#include "DOMImplementation.h"
30#include "CachedResourceLoader.h"
31#include "DocumentFragment.h"
32#include "Frame.h"
33#include "FrameLoader.h"
34#include "FrameView.h"
35#include "HTMLBodyElement.h"
36#include "HTMLDocument.h"
37#include "Page.h"
38#include "Text.h"
39#include "TextResourceDecoder.h"
40#include "markup.h"
41
42#include <wtf/Assertions.h>
43#include <wtf/Vector.h>
44
45namespace WebCore {
46
47static inline void transformTextStringToXHTMLDocumentString(String& text)
48{
49    // Modify the output so that it is a well-formed XHTML document with a <pre> tag enclosing the text.
50    text.replace('&', "&amp;");
51    text.replace('<', "&lt;");
52    text = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
53        "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
54        "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
55        "<head><title/></head>\n"
56        "<body>\n"
57        "<pre>" + text + "</pre>\n"
58        "</body>\n"
59        "</html>\n";
60}
61
62XSLTProcessor::~XSLTProcessor()
63{
64    // Stylesheet shouldn't outlive its root node.
65    ASSERT(!m_stylesheetRootNode || !m_stylesheet || m_stylesheet->hasOneRef());
66}
67
68PassRefPtr<Document> XSLTProcessor::createDocumentFromSource(const String& sourceString,
69    const String& sourceEncoding, const String& sourceMIMEType, Node* sourceNode, Frame* frame)
70{
71    RefPtr<Document> ownerDocument = sourceNode->document();
72    bool sourceIsDocument = (sourceNode == ownerDocument.get());
73    String documentSource = sourceString;
74
75    RefPtr<Document> result;
76    if (sourceMIMEType == "text/plain") {
77        result = Document::create(frame, sourceIsDocument ? ownerDocument->url() : KURL());
78        transformTextStringToXHTMLDocumentString(documentSource);
79    } else
80        result = DOMImplementation::createDocument(sourceMIMEType, frame, sourceIsDocument ? ownerDocument->url() : KURL(), false);
81
82    // Before parsing, we need to save & detach the old document and get the new document
83    // in place. We have to do this only if we're rendering the result document.
84    if (frame) {
85        if (FrameView* view = frame->view())
86            view->clear();
87        result->setTransformSourceDocument(frame->document());
88        frame->setDocument(result);
89    }
90
91    RefPtr<TextResourceDecoder> decoder = TextResourceDecoder::create(sourceMIMEType);
92    decoder->setEncoding(sourceEncoding.isEmpty() ? UTF8Encoding() : TextEncoding(sourceEncoding), TextResourceDecoder::EncodingFromXMLHeader);
93    result->setDecoder(decoder.release());
94
95    result->setContent(documentSource);
96
97    return result.release();
98}
99
100static inline RefPtr<DocumentFragment> createFragmentFromSource(const String& sourceString, const String& sourceMIMEType, Document* outputDoc)
101{
102    RefPtr<DocumentFragment> fragment = outputDoc->createDocumentFragment();
103
104    if (sourceMIMEType == "text/html") {
105        // As far as I can tell, there isn't a spec for how transformToFragment
106        // is supposed to work.  Based on the documentation I can find, it looks
107        // like we want to start parsing the fragment in the InBody insertion
108        // mode.  Unfortunately, that's an implementation detail of the parser.
109        // We achieve that effect here by passing in a fake body element as
110        // context for the fragment.
111        RefPtr<HTMLBodyElement> fakeBody = HTMLBodyElement::create(outputDoc);
112        fragment->parseHTML(sourceString, fakeBody.get());
113    } else if (sourceMIMEType == "text/plain")
114        fragment->parserAddChild(Text::create(outputDoc, sourceString));
115    else {
116        bool successfulParse = fragment->parseXML(sourceString, 0);
117        if (!successfulParse)
118            return 0;
119    }
120
121    // FIXME: Do we need to mess with URLs here?
122
123    return fragment;
124}
125
126PassRefPtr<Document> XSLTProcessor::transformToDocument(Node* sourceNode)
127{
128    String resultMIMEType;
129    String resultString;
130    String resultEncoding;
131    if (!transformToString(sourceNode, resultMIMEType, resultString, resultEncoding))
132        return 0;
133    return createDocumentFromSource(resultString, resultEncoding, resultMIMEType, sourceNode, 0);
134}
135
136PassRefPtr<DocumentFragment> XSLTProcessor::transformToFragment(Node* sourceNode, Document* outputDoc)
137{
138    String resultMIMEType;
139    String resultString;
140    String resultEncoding;
141
142    // If the output document is HTML, default to HTML method.
143    if (outputDoc->isHTMLDocument())
144        resultMIMEType = "text/html";
145
146    if (!transformToString(sourceNode, resultMIMEType, resultString, resultEncoding))
147        return 0;
148    return createFragmentFromSource(resultString, resultMIMEType, outputDoc);
149}
150
151void XSLTProcessor::setParameter(const String& /*namespaceURI*/, const String& localName, const String& value)
152{
153    // FIXME: namespace support?
154    // should make a QualifiedName here but we'd have to expose the impl
155    m_parameters.set(localName, value);
156}
157
158String XSLTProcessor::getParameter(const String& /*namespaceURI*/, const String& localName) const
159{
160    // FIXME: namespace support?
161    // should make a QualifiedName here but we'd have to expose the impl
162    return m_parameters.get(localName);
163}
164
165void XSLTProcessor::removeParameter(const String& /*namespaceURI*/, const String& localName)
166{
167    // FIXME: namespace support?
168    m_parameters.remove(localName);
169}
170
171void XSLTProcessor::reset()
172{
173    m_stylesheet.clear();
174    m_stylesheetRootNode.clear();
175    m_parameters.clear();
176}
177
178} // namespace WebCore
179
180#endif // ENABLE(XSLT)
181