blob: 87f35984a7dacb9a6560903cc7d755a040fe0672 [file] [log] [blame]
darinb9481ed2006-03-20 02:57:59 +00001/**
2 * This file is part of the XSL implementation.
3 *
4 * Copyright (C) 2004, 2005, 2006 Apple Computer, Inc.
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 "config.h"
23
hyatt5cc8ab82006-09-13 22:10:25 +000024#ifdef XSLT_SUPPORT
darinb9481ed2006-03-20 02:57:59 +000025
darin91298e52006-06-12 01:10:17 +000026#include "XSLTProcessor.h"
27
darinb9481ed2006-03-20 02:57:59 +000028#include "Cache.h"
andersca9cf539a2006-09-17 18:36:23 +000029#include "CString.h"
darin6f2a3ef2006-04-05 21:19:57 +000030#include "DOMImplementation.h"
darinb4483822006-06-02 06:30:49 +000031#include "Decoder.h"
darinb9481ed2006-03-20 02:57:59 +000032#include "DocLoader.h"
darin36d11362006-04-11 16:30:21 +000033#include "DocumentFragment.h"
darinb9481ed2006-03-20 02:57:59 +000034#include "Frame.h"
darin6f2a3ef2006-04-05 21:19:57 +000035#include "HTMLDocument.h"
36#include "HTMLTokenizer.h"
darinb53ebdc2006-07-09 15:10:21 +000037#include "LoaderFunctions.h"
mjs63474f32006-08-01 05:15:40 +000038#include "ResourceLoader.h"
weinigf18aae32006-08-03 21:55:57 +000039#include "Text.h"
darin6f2a3ef2006-04-05 21:19:57 +000040#include "loader.h"
41#include "markup.h"
darinb9481ed2006-03-20 02:57:59 +000042#include <libxslt/imports.h>
anderscacae8bef2006-07-21 07:52:10 +000043#include <libxslt/variables.h>
darin6f2a3ef2006-04-05 21:19:57 +000044#include <libxslt/xsltutils.h>
weinigf18aae32006-08-03 21:55:57 +000045#include <wtf/Assertions.h>
46#include <wtf/Platform.h>
47#include <wtf/Vector.h>
darinb9481ed2006-03-20 02:57:59 +000048
49namespace WebCore {
50
51static void parseErrorFunc(void *ctxt, const char *msg, ...)
52{
53 // FIXME: It would be nice to display error messages somewhere.
weinigf18aae32006-08-03 21:55:57 +000054#if !PLATFORM(WIN_OS)
darinb9481ed2006-03-20 02:57:59 +000055 // FIXME: No vasprintf support.
weinigf18aae32006-08-03 21:55:57 +000056#ifndef ERROR_DISABLED
darinb9481ed2006-03-20 02:57:59 +000057 char *errorMessage = 0;
58 va_list args;
59 va_start(args, msg);
60 vasprintf(&errorMessage, msg, args);
61 LOG_ERROR("%s", errorMessage);
62 if (errorMessage)
63 free(errorMessage);
64 va_end(args);
65#endif
66#endif
67}
68
69// FIXME: There seems to be no way to control the ctxt pointer for loading here, thus we have globals.
70static XSLTProcessor *globalProcessor = 0;
darinf4b05b22006-07-10 05:20:17 +000071static DocLoader *globalDocLoader = 0;
darinb9481ed2006-03-20 02:57:59 +000072static xmlDocPtr docLoaderFunc(const xmlChar *uri,
73 xmlDictPtr dict,
74 int options,
75 void* ctxt,
76 xsltLoadType type)
77{
78 if (!globalProcessor)
79 return 0;
80
81 switch (type) {
82 case XSLT_LOAD_DOCUMENT: {
83 xsltTransformContextPtr context = (xsltTransformContextPtr)ctxt;
84 xmlChar *base = xmlNodeGetBase(context->document->doc, context->node);
85 KURL url((const char*)base, (const char*)uri);
86 xmlFree(base);
87 KURL finalURL;
mjs76169ca2006-10-05 13:15:04 +000088 RefPtr<ResourceLoader> loader = ResourceLoader::create(0, "GET", url);
darinb9481ed2006-03-20 02:57:59 +000089 DeprecatedString headers;
90 xmlGenericErrorFunc oldErrorFunc = xmlGenericError;
91 void *oldErrorContext = xmlGenericErrorContext;
92
mjs76169ca2006-10-05 13:15:04 +000093 Vector<char> data = ServeSynchronousRequest(Cache::loader(), globalDocLoader, loader.get(), finalURL, headers);
darinb9481ed2006-03-20 02:57:59 +000094
95 xmlSetGenericErrorFunc(0, parseErrorFunc);
96 // We don't specify an encoding here. Neither Gecko nor WinIE respects
97 // the encoding specified in the HTTP headers.
98 xmlDocPtr doc = xmlReadMemory(data.data(), data.size(), (const char*)uri, 0, options);
99 xmlSetGenericErrorFunc(oldErrorContext, oldErrorFunc);
100 return doc;
101 }
102 case XSLT_LOAD_STYLESHEET:
103 return globalProcessor->xslStylesheet()->locateStylesheetSubResource(((xsltStylesheetPtr)ctxt)->doc, uri);
104 default:
105 break;
106 }
107
108 return 0;
109}
110
darinf4b05b22006-07-10 05:20:17 +0000111static inline void setXSLTLoadCallBack(xsltDocLoaderFunc func, XSLTProcessor *processor, DocLoader *loader)
darinb9481ed2006-03-20 02:57:59 +0000112{
113 xsltSetLoaderFunc(func);
114 globalProcessor = processor;
115 globalDocLoader = loader;
116}
117
118static int writeToQString(void *context, const char *buffer, int len)
119{
120 DeprecatedString &resultOutput = *static_cast<DeprecatedString *>(context);
121 resultOutput += DeprecatedString::fromUtf8(buffer, len);
122 return len;
123}
124
125static bool saveResultToString(xmlDocPtr resultDoc, xsltStylesheetPtr sheet, DeprecatedString &resultString)
126{
127 xmlOutputBufferPtr outputBuf = xmlAllocOutputBuffer(0);
128 if (!outputBuf)
129 return false;
130 outputBuf->context = &resultString;
131 outputBuf->writecallback = writeToQString;
132
133 int retval = xsltSaveResultTo(outputBuf, resultDoc, sheet);
134 xmlOutputBufferClose(outputBuf);
135
136 return (retval >= 0);
137}
138
darinb3547a32006-09-06 04:40:44 +0000139static inline void transformTextStringToXHTMLDocumentString(String &text)
darinb9481ed2006-03-20 02:57:59 +0000140{
141 // Modify the output so that it is a well-formed XHTML document with a <pre> tag enclosing the text.
142 text.replace('&', "&amp;");
143 text.replace('<', "&lt;");
144 text = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
145 "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"
146 "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
147 "<head><title/></head>\n"
148 "<body>\n"
149 "<pre>" + text + "</pre>\n"
150 "</body>\n"
151 "</html>\n";
152}
153
154static const char **xsltParamArrayFromParameterMap(XSLTProcessor::ParameterMap& parameters)
155{
156 if (parameters.isEmpty())
157 return 0;
158
159 const char **parameterArray = (const char **)fastMalloc(((parameters.size() * 2) + 1) * sizeof(char *));
160
161 XSLTProcessor::ParameterMap::iterator end = parameters.end();
162 unsigned index = 0;
163 for (XSLTProcessor::ParameterMap::iterator it = parameters.begin(); it != end; ++it) {
andersca9cf539a2006-09-17 18:36:23 +0000164 parameterArray[index++] = strdup(it->first.utf8());
165 parameterArray[index++] = strdup(it->second.utf8());
darinb9481ed2006-03-20 02:57:59 +0000166 }
167 parameterArray[index] = 0;
168
169 return parameterArray;
170}
171
172static void freeXsltParamArray(const char **params)
173{
174 const char **temp = params;
175 if (!params)
176 return;
177
178 while (*temp) {
179 free((void *)*(temp++)); // strdup returns malloc'd blocks, so we have to use free() here
180 free((void *)*(temp++));
181 }
182 fastFree(params);
183}
184
185
darinb3547a32006-09-06 04:40:44 +0000186RefPtr<Document> XSLTProcessor::createDocumentFromSource(const DeprecatedString& sourceString,
187 const DeprecatedString& sourceEncoding, const DeprecatedString& sourceMIMEType, Node* sourceNode, FrameView* view)
darinb9481ed2006-03-20 02:57:59 +0000188{
ggarenaa01dc02006-03-29 00:21:31 +0000189 RefPtr<Document> ownerDocument = sourceNode->document();
darinb9481ed2006-03-20 02:57:59 +0000190 bool sourceIsDocument = (sourceNode == ownerDocument.get());
darinb3547a32006-09-06 04:40:44 +0000191 String documentSource = sourceString;
darinb9481ed2006-03-20 02:57:59 +0000192
193 RefPtr<Document> result;
194 if (sourceMIMEType == "text/html")
195 result = ownerDocument->implementation()->createHTMLDocument(view);
196 else {
197 result = ownerDocument->implementation()->createDocument(view);
198 if (sourceMIMEType == "text/plain")
199 transformTextStringToXHTMLDocumentString(documentSource);
200 }
201
darinb9481ed2006-03-20 02:57:59 +0000202 // Before parsing, we need to save & detach the old document and get the new document
203 // in place. We have to do this only if we're rendering the result document.
204 if (view) {
205 view->clear();
206 result->setTransformSourceDocument(view->frame()->document());
207 view->frame()->setDocument(result.get());
208 }
209
210 result->open();
211 if (sourceIsDocument) {
212 result->setURL(ownerDocument->URL());
213 result->setBaseURL(ownerDocument->baseURL());
214 }
215 result->determineParseMode(documentSource); // Make sure we parse in the correct mode.
216
ap41942e02006-09-01 19:36:06 +0000217 RefPtr<Decoder> decoder = new Decoder(sourceMIMEType);
darinb3547a32006-09-06 04:40:44 +0000218 decoder->setEncoding(sourceEncoding.isEmpty() ? UTF8Encoding() : TextEncoding(sourceEncoding), Decoder::EncodingFromXMLHeader);
darinb9481ed2006-03-20 02:57:59 +0000219 result->setDecoder(decoder.get());
220
221 result->write(documentSource);
222 result->finishParsing();
223 if (view)
224 view->frame()->checkCompleted();
225 else
226 result->close(); // FIXME: Even viewless docs can load subresources. onload will fire too early.
227 // This is probably a bug in XMLHttpRequestObjects as well.
228 return result;
229}
230
231static inline RefPtr<DocumentFragment> createFragmentFromSource(DeprecatedString sourceString, DeprecatedString sourceMIMEType, Node *sourceNode, Document *outputDoc)
232{
233 RefPtr<DocumentFragment> fragment = new DocumentFragment(outputDoc);
234
235 if (sourceMIMEType == "text/html")
236 parseHTMLDocumentFragment(sourceString, fragment.get());
237 else if (sourceMIMEType == "text/plain")
238 fragment->addChild(new Text(outputDoc, sourceString));
239 else {
240 bool successfulParse = parseXMLDocumentFragment(sourceString, fragment.get(), outputDoc->documentElement());
241 if (!successfulParse)
242 return 0;
243 }
244
245 // FIXME: Do we need to mess with URLs here?
246
247 return fragment;
248}
249
250static xsltStylesheetPtr xsltStylesheetPointer(RefPtr<XSLStyleSheet> &cachedStylesheet, Node *stylesheetRootNode)
251{
252 if (!cachedStylesheet && stylesheetRootNode) {
253 cachedStylesheet = new XSLStyleSheet(stylesheetRootNode->parent() ? stylesheetRootNode->parent() : stylesheetRootNode);
254 cachedStylesheet->parseString(createMarkup(stylesheetRootNode));
255 }
256
257 if (!cachedStylesheet || !cachedStylesheet->document())
258 return 0;
259
260 return cachedStylesheet->compileStyleSheet();
261}
262
263static inline xmlDocPtr xmlDocPtrFromNode(Node *sourceNode, bool &shouldDelete)
264{
ggarenaa01dc02006-03-29 00:21:31 +0000265 RefPtr<Document> ownerDocument = sourceNode->document();
darinb9481ed2006-03-20 02:57:59 +0000266 bool sourceIsDocument = (sourceNode == ownerDocument.get());
267
268 xmlDocPtr sourceDoc = 0;
269 if (sourceIsDocument)
270 sourceDoc = (xmlDocPtr)ownerDocument->transformSource();
271 if (!sourceDoc) {
andersca06c0afdc2006-06-22 17:46:25 +0000272 sourceDoc = (xmlDocPtr)xmlDocPtrForString(ownerDocument->docLoader(), createMarkup(sourceNode), sourceIsDocument ? ownerDocument->URL() : DeprecatedString());
darinb9481ed2006-03-20 02:57:59 +0000273 shouldDelete = (sourceDoc != 0);
274 }
275 return sourceDoc;
276}
277
278static inline DeprecatedString resultMIMEType(xmlDocPtr resultDoc, xsltStylesheetPtr sheet)
279{
280 // There are three types of output we need to be able to deal with:
281 // HTML (create an HTML document), XML (create an XML document),
282 // and text (wrap in a <pre> and create an XML document).
283
284 const xmlChar *resultType = 0;
285 XSLT_GET_IMPORT_PTR(resultType, sheet, method);
286 if (resultType == 0 && resultDoc->type == XML_HTML_DOCUMENT_NODE)
287 resultType = (const xmlChar *)"html";
288
289 if (xmlStrEqual(resultType, (const xmlChar *)"html"))
290 return DeprecatedString("text/html");
291 else if (xmlStrEqual(resultType, (const xmlChar *)"text"))
292 return DeprecatedString("text/plain");
293
294 return DeprecatedString("application/xml");
295}
296
297bool XSLTProcessor::transformToString(Node *sourceNode, DeprecatedString &mimeType, DeprecatedString &resultString, DeprecatedString &resultEncoding)
298{
ggarenaa01dc02006-03-29 00:21:31 +0000299 RefPtr<Document> ownerDocument = sourceNode->document();
darinb9481ed2006-03-20 02:57:59 +0000300 RefPtr<XSLStyleSheet> cachedStylesheet = m_stylesheet;
301
302 setXSLTLoadCallBack(docLoaderFunc, this, ownerDocument->docLoader());
303 xsltStylesheetPtr sheet = xsltStylesheetPointer(cachedStylesheet, m_stylesheetRootNode.get());
304 if (!sheet) {
305 setXSLTLoadCallBack(0, 0, 0);
306 return false;
307 }
308 cachedStylesheet->clearDocuments();
309
310 bool success = false;
311 bool shouldFreeSourceDoc = false;
312 if (xmlDocPtr sourceDoc = xmlDocPtrFromNode(sourceNode, shouldFreeSourceDoc)) {
ap3bc61e92006-08-22 04:32:02 +0000313 // The XML declaration would prevent parsing the result as a fragment, and it's not needed even for documents,
314 // as the result of this function is always immediately parsed.
315 sheet->omitXmlDeclaration = true;
316
anderscacae8bef2006-07-21 07:52:10 +0000317 xsltTransformContextPtr transformContext = xsltNewTransformContext(sheet, sourceDoc);
318
319 // This is a workaround for a bug in libxslt.
320 // The bug has been fixed in version 1.1.13, so once we ship that this can be removed.
321 if (transformContext->globalVars == NULL)
322 transformContext->globalVars = xmlHashCreate(20);
323
darinb9481ed2006-03-20 02:57:59 +0000324 const char **params = xsltParamArrayFromParameterMap(m_parameters);
anderscacae8bef2006-07-21 07:52:10 +0000325 xsltQuoteUserParams(transformContext, params);
326 xmlDocPtr resultDoc = xsltApplyStylesheetUser(sheet, sourceDoc, 0, 0, 0, transformContext);
327
328 xsltFreeTransformContext(transformContext);
darinb9481ed2006-03-20 02:57:59 +0000329 freeXsltParamArray(params);
anderscacae8bef2006-07-21 07:52:10 +0000330
darinb9481ed2006-03-20 02:57:59 +0000331 if (shouldFreeSourceDoc)
332 xmlFreeDoc(sourceDoc);
333
334 if (success = saveResultToString(resultDoc, sheet, resultString)) {
335 mimeType = resultMIMEType(resultDoc, sheet);
336 resultEncoding = (char *)resultDoc->encoding;
337 }
338 xmlFreeDoc(resultDoc);
339 }
340
341 setXSLTLoadCallBack(0, 0, 0);
342 xsltFreeStylesheet(sheet);
343
344 return success;
345}
346
347RefPtr<Document> XSLTProcessor::transformToDocument(Node *sourceNode)
348{
349 DeprecatedString resultMIMEType;
350 DeprecatedString resultString;
351 DeprecatedString resultEncoding;
352 if (!transformToString(sourceNode, resultMIMEType, resultString, resultEncoding))
353 return 0;
354 return createDocumentFromSource(resultString, resultEncoding, resultMIMEType, sourceNode);
355}
356
357RefPtr<DocumentFragment> XSLTProcessor::transformToFragment(Node *sourceNode, Document *outputDoc)
358{
359 DeprecatedString resultMIMEType;
360 DeprecatedString resultString;
361 DeprecatedString resultEncoding;
362 if (!transformToString(sourceNode, resultMIMEType, resultString, resultEncoding))
363 return 0;
364 return createFragmentFromSource(resultString, resultMIMEType, sourceNode, outputDoc);
365}
366
andersca9cf539a2006-09-17 18:36:23 +0000367void XSLTProcessor::setParameter(const String& namespaceURI, const String& localName, const String& value)
darinb9481ed2006-03-20 02:57:59 +0000368{
369 // FIXME: namespace support?
370 // should make a QualifiedName here but we'd have to expose the impl
371 m_parameters.set(localName, value);
372}
373
andersca9cf539a2006-09-17 18:36:23 +0000374String XSLTProcessor::getParameter(const String& namespaceURI, const String& localName) const
darinb9481ed2006-03-20 02:57:59 +0000375{
376 // FIXME: namespace support?
377 // should make a QualifiedName here but we'd have to expose the impl
378 return m_parameters.get(localName);
379}
380
andersca9cf539a2006-09-17 18:36:23 +0000381void XSLTProcessor::removeParameter(const String& namespaceURI, const String& localName)
darinb9481ed2006-03-20 02:57:59 +0000382{
383 // FIXME: namespace support?
384 m_parameters.remove(localName);
385}
386
weinigf18aae32006-08-03 21:55:57 +0000387} // namespace WebCore
darinb9481ed2006-03-20 02:57:59 +0000388
hyatt5cc8ab82006-09-13 22:10:25 +0000389#endif // XSLT_SUPPORT