1/*
2 * This file is part of the XSL implementation.
3 *
4 * Copyright (C) 2009 Jakub Wieczorek <faw217@gmail.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., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 */
21
22#include "config.h"
23
24#if ENABLE(XSLT)
25
26#include "XSLTProcessor.h"
27
28#include "Console.h"
29#include "DOMWindow.h"
30#include "Frame.h"
31#include "TransformSource.h"
32#include "markup.h"
33#include <wtf/Assertions.h>
34#include <wtf/Vector.h>
35
36#include <qabstractmessagehandler.h>
37#include <qabstracturiresolver.h>
38#include <qbuffer.h>
39#include <qsourcelocation.h>
40#include <qxmlquery.h>
41
42namespace WebCore {
43
44class XSLTMessageHandler : public QAbstractMessageHandler {
45
46public:
47    XSLTMessageHandler(Document* document = 0);
48    virtual void handleMessage(QtMsgType type, const QString& description,
49                               const QUrl& identifier, const QSourceLocation& sourceLocation);
50
51private:
52    Document* m_document;
53};
54
55XSLTMessageHandler::XSLTMessageHandler(Document* document)
56    : QAbstractMessageHandler()
57    , m_document(document)
58{
59}
60
61void XSLTMessageHandler::handleMessage(QtMsgType type, const QString& description,
62                                       const QUrl&, const QSourceLocation& sourceLocation)
63{
64    if (!m_document->frame())
65        return;
66
67    MessageLevel level;
68    switch (type) {
69    case QtDebugMsg:
70        level = TipMessageLevel;
71        break;
72    case QtWarningMsg:
73        level = WarningMessageLevel;
74        break;
75    case QtCriticalMsg:
76    case QtFatalMsg:
77        level = ErrorMessageLevel;
78        break;
79    default:
80        level = LogMessageLevel;
81        break;
82    }
83
84    Console* console = m_document->frame()->domWindow()->console();
85    console->addMessage(XMLMessageSource, LogMessageType, level, description,
86                        sourceLocation.line(), sourceLocation.uri().toString());
87}
88
89class XSLTUriResolver : public QAbstractUriResolver {
90
91public:
92    XSLTUriResolver(Document* document);
93    virtual QUrl resolve(const QUrl& relative, const QUrl& baseURI) const;
94
95private:
96    Document* m_document;
97};
98
99XSLTUriResolver::XSLTUriResolver(Document* document)
100    : QAbstractUriResolver()
101    , m_document(document)
102{
103}
104
105QUrl XSLTUriResolver::resolve(const QUrl& relative, const QUrl& baseURI) const
106{
107    QUrl url = baseURI.resolved(relative);
108
109    if (!m_document->frame() || !m_document->securityOrigin()->canRequest(url))
110        return QUrl();
111    return url;
112}
113
114bool XSLTProcessor::transformToString(Node* sourceNode, String&, String& resultString, String&)
115{
116    bool success = false;
117
118    RefPtr<XSLStyleSheet> stylesheet = m_stylesheet;
119    if (!stylesheet && m_stylesheetRootNode) {
120        Node* node = m_stylesheetRootNode.get();
121        stylesheet = XSLStyleSheet::createForXSLTProcessor(node->parentNode() ? node->parentNode() : node,
122            node->document()->url().string(),
123            node->document()->url()); // FIXME: Should we use baseURL here?
124
125        // According to Mozilla documentation, the node must be a Document node, an xsl:stylesheet or xsl:transform element.
126        // But we just use text content regardless of node type.
127        stylesheet->parseString(createMarkup(node));
128    }
129
130    if (!stylesheet || stylesheet->sheetString().isEmpty())
131        return success;
132
133    RefPtr<Document> ownerDocument = sourceNode->document();
134    bool sourceIsDocument = (sourceNode == ownerDocument.get());
135
136    QXmlQuery query(QXmlQuery::XSLT20);
137
138    XSLTMessageHandler messageHandler(ownerDocument.get());
139    XSLTUriResolver uriResolver(ownerDocument.get());
140    query.setMessageHandler(&messageHandler);
141
142    XSLTProcessor::ParameterMap::iterator end = m_parameters.end();
143    for (XSLTProcessor::ParameterMap::iterator it = m_parameters.begin(); it != end; ++it)
144        query.bindVariable(QString(it->first), QXmlItem(QVariant(QString(it->second))));
145
146    QString source;
147    if (sourceIsDocument && ownerDocument->transformSource())
148        source = ownerDocument->transformSource()->platformSource();
149    if (!sourceIsDocument || source.isEmpty())
150        source = createMarkup(sourceNode);
151
152    QBuffer inputBuffer;
153    QBuffer styleSheetBuffer;
154    QBuffer outputBuffer;
155
156    inputBuffer.setData(source.toUtf8());
157    styleSheetBuffer.setData(QString(stylesheet->sheetString()).toUtf8());
158
159    inputBuffer.open(QIODevice::ReadOnly);
160    styleSheetBuffer.open(QIODevice::ReadOnly);
161    outputBuffer.open(QIODevice::ReadWrite);
162
163    query.setFocus(&inputBuffer);
164    query.setQuery(&styleSheetBuffer, QUrl(stylesheet->href()));
165
166    query.setUriResolver(&uriResolver);
167
168    success = query.evaluateTo(&outputBuffer);
169    outputBuffer.reset();
170    resultString = QString::fromUtf8(outputBuffer.readAll()).trimmed();
171
172    if (m_stylesheet) {
173        m_stylesheet->clearDocuments();
174        m_stylesheet = 0;
175    }
176
177    return success;
178}
179
180} // namespace WebCore
181
182#endif // ENABLE(XSLT)
183