2 * This file is part of the XSL implementation.
4 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple, Inc. All rights reserved.
5 * Copyright (C) 2005, 2006 Alexey Proskuryakov <ap@webkit.org>
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.
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.
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.
24 #include "core/xml/XSLTProcessor.h"
26 #include <libxslt/imports.h>
27 #include <libxslt/security.h>
28 #include <libxslt/variables.h>
29 #include <libxslt/xsltutils.h>
30 #include "FetchInitiatorTypeNames.h"
31 #include "core/dom/Document.h"
32 #include "core/dom/TransformSource.h"
33 #include "core/editing/markup.h"
34 #include "core/fetch/Resource.h"
35 #include "core/fetch/ResourceFetcher.h"
36 #include "core/frame/FrameConsole.h"
37 #include "core/frame/FrameHost.h"
38 #include "core/frame/LocalFrame.h"
39 #include "core/xml/XSLStyleSheet.h"
40 #include "core/xml/XSLTExtensions.h"
41 #include "core/xml/XSLTUnicodeSort.h"
42 #include "core/xml/parser/XMLDocumentParser.h"
43 #include "platform/SharedBuffer.h"
44 #include "platform/network/ResourceError.h"
45 #include "platform/network/ResourceRequest.h"
46 #include "platform/network/ResourceResponse.h"
47 #include "platform/weborigin/SecurityOrigin.h"
48 #include "wtf/Assertions.h"
49 #include "wtf/Vector.h"
50 #include "wtf/text/CString.h"
51 #include "wtf/text/StringBuffer.h"
52 #include "wtf/unicode/UTF8.h"
56 void XSLTProcessor::genericErrorFunc(void*, const char*, ...)
58 // It would be nice to do something with this error message.
61 void XSLTProcessor::parseErrorFunc(void* userData, xmlError* error)
63 FrameConsole* console = static_cast<FrameConsole*>(userData);
68 switch (error->level) {
70 level = DebugMessageLevel;
73 level = WarningMessageLevel;
78 level = ErrorMessageLevel;
82 console->addMessage(XMLMessageSource, level, error->message, error->file, error->line);
85 // FIXME: There seems to be no way to control the ctxt pointer for loading here, thus we have globals.
86 static XSLTProcessor* globalProcessor = 0;
87 static ResourceFetcher* globalResourceFetcher = 0;
88 static xmlDocPtr docLoaderFunc(const xmlChar* uri,
98 case XSLT_LOAD_DOCUMENT: {
99 xsltTransformContextPtr context = (xsltTransformContextPtr)ctxt;
100 xmlChar* base = xmlNodeGetBase(context->document->doc, context->node);
101 KURL url(KURL(ParsedURLString, reinterpret_cast<const char*>(base)), reinterpret_cast<const char*>(uri));
104 ResourceLoaderOptions fetchOptions(ResourceFetcher::defaultResourceOptions());
105 FetchRequest request(ResourceRequest(url), FetchInitiatorTypeNames::xml, fetchOptions);
106 request.setOriginRestriction(FetchRequest::RestrictToSameOrigin);
107 ResourcePtr<Resource> resource = globalResourceFetcher->fetchSynchronously(request);
108 if (!resource || !globalProcessor)
111 FrameConsole* console = 0;
112 LocalFrame* frame = globalProcessor->xslStylesheet()->ownerDocument()->frame();
114 console = &frame->console();
115 xmlSetStructuredErrorFunc(console, XSLTProcessor::parseErrorFunc);
116 xmlSetGenericErrorFunc(console, XSLTProcessor::genericErrorFunc);
118 // We don't specify an encoding here. Neither Gecko nor WinIE respects
119 // the encoding specified in the HTTP headers.
120 SharedBuffer* data = resource->resourceBuffer();
121 xmlDocPtr doc = data ? xmlReadMemory(data->data(), data->size(), (const char*)uri, 0, options) : 0;
123 xmlSetStructuredErrorFunc(0, 0);
124 xmlSetGenericErrorFunc(0, 0);
128 case XSLT_LOAD_STYLESHEET:
129 return globalProcessor->xslStylesheet()->locateStylesheetSubResource(((xsltStylesheetPtr)ctxt)->doc, uri);
137 static inline void setXSLTLoadCallBack(xsltDocLoaderFunc func, XSLTProcessor* processor, ResourceFetcher* fetcher)
139 xsltSetLoaderFunc(func);
140 globalProcessor = processor;
141 globalResourceFetcher = fetcher;
144 static int writeToStringBuilder(void* context, const char* buffer, int len)
146 StringBuilder& resultOutput = *static_cast<StringBuilder*>(context);
151 StringBuffer<UChar> stringBuffer(len);
152 UChar* bufferUChar = stringBuffer.characters();
153 UChar* bufferUCharEnd = bufferUChar + len;
155 const char* stringCurrent = buffer;
156 WTF::Unicode::ConversionResult result = WTF::Unicode::convertUTF8ToUTF16(&stringCurrent, buffer + len, &bufferUChar, bufferUCharEnd);
157 if (result != WTF::Unicode::conversionOK && result != WTF::Unicode::sourceExhausted) {
158 ASSERT_NOT_REACHED();
162 int utf16Length = bufferUChar - stringBuffer.characters();
163 resultOutput.append(stringBuffer.characters(), utf16Length);
164 return stringCurrent - buffer;
167 static bool saveResultToString(xmlDocPtr resultDoc, xsltStylesheetPtr sheet, String& resultString)
169 xmlOutputBufferPtr outputBuf = xmlAllocOutputBuffer(0);
173 StringBuilder resultBuilder;
174 outputBuf->context = &resultBuilder;
175 outputBuf->writecallback = writeToStringBuilder;
177 int retval = xsltSaveResultTo(outputBuf, resultDoc, sheet);
178 xmlOutputBufferClose(outputBuf);
182 // Workaround for <http://bugzilla.gnome.org/show_bug.cgi?id=495668>: libxslt appends an extra line feed to the result.
183 if (resultBuilder.length() > 0 && resultBuilder[resultBuilder.length() - 1] == '\n')
184 resultBuilder.resize(resultBuilder.length() - 1);
186 resultString = resultBuilder.toString();
191 static const char** xsltParamArrayFromParameterMap(XSLTProcessor::ParameterMap& parameters)
193 if (parameters.isEmpty())
196 const char** parameterArray = (const char**)fastMalloc(((parameters.size() * 2) + 1) * sizeof(char*));
198 XSLTProcessor::ParameterMap::iterator end = parameters.end();
200 for (XSLTProcessor::ParameterMap::iterator it = parameters.begin(); it != end; ++it) {
201 parameterArray[index++] = fastStrDup(it->key.utf8().data());
202 parameterArray[index++] = fastStrDup(it->value.utf8().data());
204 parameterArray[index] = 0;
206 return parameterArray;
209 static void freeXsltParamArray(const char** params)
211 const char** temp = params;
216 fastFree((void*)*(temp++));
217 fastFree((void*)*(temp++));
222 static xsltStylesheetPtr xsltStylesheetPointer(RefPtrWillBeMember<XSLStyleSheet>& cachedStylesheet, Node* stylesheetRootNode)
224 if (!cachedStylesheet && stylesheetRootNode) {
225 cachedStylesheet = XSLStyleSheet::createForXSLTProcessor(stylesheetRootNode->parentNode() ? stylesheetRootNode->parentNode() : stylesheetRootNode,
226 stylesheetRootNode->document().url().string(),
227 stylesheetRootNode->document().url()); // FIXME: Should we use baseURL here?
229 // According to Mozilla documentation, the node must be a Document node, an xsl:stylesheet or xsl:transform element.
230 // But we just use text content regardless of node type.
231 cachedStylesheet->parseString(createMarkup(stylesheetRootNode));
234 if (!cachedStylesheet || !cachedStylesheet->document())
237 return cachedStylesheet->compileStyleSheet();
240 static inline xmlDocPtr xmlDocPtrFromNode(Node* sourceNode, bool& shouldDelete)
242 RefPtr<Document> ownerDocument(sourceNode->document());
243 bool sourceIsDocument = (sourceNode == ownerDocument.get());
245 xmlDocPtr sourceDoc = 0;
246 if (sourceIsDocument && ownerDocument->transformSource())
247 sourceDoc = (xmlDocPtr)ownerDocument->transformSource()->platformSource();
249 sourceDoc = (xmlDocPtr)xmlDocPtrForString(ownerDocument->fetcher(), createMarkup(sourceNode),
250 sourceIsDocument ? ownerDocument->url().string() : String());
251 shouldDelete = sourceDoc;
256 static inline String resultMIMEType(xmlDocPtr resultDoc, xsltStylesheetPtr sheet)
258 // There are three types of output we need to be able to deal with:
259 // HTML (create an HTML document), XML (create an XML document),
260 // and text (wrap in a <pre> and create an XML document).
262 const xmlChar* resultType = 0;
263 XSLT_GET_IMPORT_PTR(resultType, sheet, method);
264 if (!resultType && resultDoc->type == XML_HTML_DOCUMENT_NODE)
265 resultType = (const xmlChar*)"html";
267 if (xmlStrEqual(resultType, (const xmlChar*)"html"))
269 if (xmlStrEqual(resultType, (const xmlChar*)"text"))
272 return "application/xml";
275 bool XSLTProcessor::transformToString(Node* sourceNode, String& mimeType, String& resultString, String& resultEncoding)
277 RefPtr<Document> ownerDocument(sourceNode->document());
279 setXSLTLoadCallBack(docLoaderFunc, this, ownerDocument->fetcher());
280 xsltStylesheetPtr sheet = xsltStylesheetPointer(m_stylesheet, m_stylesheetRootNode.get());
282 setXSLTLoadCallBack(0, 0, 0);
283 m_stylesheet = nullptr;
286 m_stylesheet->clearDocuments();
288 xmlChar* origMethod = sheet->method;
289 if (!origMethod && mimeType == "text/html")
290 sheet->method = (xmlChar*)"html";
292 bool success = false;
293 bool shouldFreeSourceDoc = false;
294 if (xmlDocPtr sourceDoc = xmlDocPtrFromNode(sourceNode, shouldFreeSourceDoc)) {
295 // The XML declaration would prevent parsing the result as a fragment, and it's not needed even for documents,
296 // as the result of this function is always immediately parsed.
297 sheet->omitXmlDeclaration = true;
299 xsltTransformContextPtr transformContext = xsltNewTransformContext(sheet, sourceDoc);
300 registerXSLTExtensions(transformContext);
302 xsltSecurityPrefsPtr securityPrefs = xsltNewSecurityPrefs();
303 // Read permissions are checked by docLoaderFunc.
304 if (0 != xsltSetSecurityPrefs(securityPrefs, XSLT_SECPREF_WRITE_FILE, xsltSecurityForbid))
306 if (0 != xsltSetSecurityPrefs(securityPrefs, XSLT_SECPREF_CREATE_DIRECTORY, xsltSecurityForbid))
308 if (0 != xsltSetSecurityPrefs(securityPrefs, XSLT_SECPREF_WRITE_NETWORK, xsltSecurityForbid))
310 if (0 != xsltSetCtxtSecurityPrefs(securityPrefs, transformContext))
313 // <http://bugs.webkit.org/show_bug.cgi?id=16077>: XSLT processor <xsl:sort> algorithm only compares by code point.
314 xsltSetCtxtSortFunc(transformContext, xsltUnicodeSortFunction);
316 // This is a workaround for a bug in libxslt.
317 // The bug has been fixed in version 1.1.13, so once we ship that this can be removed.
318 if (!transformContext->globalVars)
319 transformContext->globalVars = xmlHashCreate(20);
321 const char** params = xsltParamArrayFromParameterMap(m_parameters);
322 xsltQuoteUserParams(transformContext, params);
323 xmlDocPtr resultDoc = xsltApplyStylesheetUser(sheet, sourceDoc, 0, 0, 0, transformContext);
325 xsltFreeTransformContext(transformContext);
326 xsltFreeSecurityPrefs(securityPrefs);
327 freeXsltParamArray(params);
329 if (shouldFreeSourceDoc)
330 xmlFreeDoc(sourceDoc);
332 if ((success = saveResultToString(resultDoc, sheet, resultString))) {
333 mimeType = resultMIMEType(resultDoc, sheet);
334 resultEncoding = (char*)resultDoc->encoding;
336 xmlFreeDoc(resultDoc);
339 sheet->method = origMethod;
340 setXSLTLoadCallBack(0, 0, 0);
341 xsltFreeStylesheet(sheet);
342 m_stylesheet = nullptr;
347 } // namespace WebCore