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