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 "Console.h" 30#include "DOMWindow.h" 31#include "CachedResourceLoader.h" 32#include "Frame.h" 33#include "ResourceError.h" 34#include "ResourceHandle.h" 35#include "ResourceRequest.h" 36#include "ResourceResponse.h" 37#include "TransformSource.h" 38#include "XMLDocumentParser.h" 39#include "XSLStyleSheet.h" 40#include "XSLTExtensions.h" 41#include "XSLTUnicodeSort.h" 42#include "markup.h" 43#include <libxslt/imports.h> 44#include <libxslt/security.h> 45#include <libxslt/variables.h> 46#include <libxslt/xsltutils.h> 47#include <wtf/Assertions.h> 48#include <wtf/Vector.h> 49#include <wtf/text/CString.h> 50#include <wtf/text/StringBuffer.h> 51#include <wtf/unicode/UTF8.h> 52 53#if PLATFORM(MAC) 54#include "SoftLinking.h" 55 56SOFT_LINK_LIBRARY(libxslt); 57SOFT_LINK(libxslt, xsltFreeStylesheet, void, (xsltStylesheetPtr sheet), (sheet)) 58SOFT_LINK(libxslt, xsltFreeTransformContext, void, (xsltTransformContextPtr ctxt), (ctxt)) 59SOFT_LINK(libxslt, xsltNewTransformContext, xsltTransformContextPtr, (xsltStylesheetPtr style, xmlDocPtr doc), (style, doc)) 60SOFT_LINK(libxslt, xsltApplyStylesheetUser, xmlDocPtr, (xsltStylesheetPtr style, xmlDocPtr doc, const char** params, const char* output, FILE* profile, xsltTransformContextPtr userCtxt), (style, doc, params, output, profile, userCtxt)) 61SOFT_LINK(libxslt, xsltQuoteUserParams, int, (xsltTransformContextPtr ctxt, const char** params), (ctxt, params)) 62SOFT_LINK(libxslt, xsltSetCtxtSortFunc, void, (xsltTransformContextPtr ctxt, xsltSortFunc handler), (ctxt, handler)) 63SOFT_LINK(libxslt, xsltSetLoaderFunc, void, (xsltDocLoaderFunc f), (f)) 64SOFT_LINK(libxslt, xsltSaveResultTo, int, (xmlOutputBufferPtr buf, xmlDocPtr result, xsltStylesheetPtr style), (buf, result, style)) 65SOFT_LINK(libxslt, xsltNextImport, xsltStylesheetPtr, (xsltStylesheetPtr style), (style)) 66SOFT_LINK(libxslt, xsltNewSecurityPrefs, xsltSecurityPrefsPtr, (), ()) 67SOFT_LINK(libxslt, xsltFreeSecurityPrefs, void, (xsltSecurityPrefsPtr sec), (sec)) 68SOFT_LINK(libxslt, xsltSetSecurityPrefs, int, (xsltSecurityPrefsPtr sec, xsltSecurityOption option, xsltSecurityCheck func), (sec, option, func)) 69SOFT_LINK(libxslt, xsltSetCtxtSecurityPrefs, int, (xsltSecurityPrefsPtr sec, xsltTransformContextPtr ctxt), (sec, ctxt)) 70SOFT_LINK(libxslt, xsltSecurityForbid, int, (xsltSecurityPrefsPtr sec, xsltTransformContextPtr ctxt, const char* value), (sec, ctxt, value)) 71 72#endif 73 74namespace WebCore { 75 76void XSLTProcessor::genericErrorFunc(void*, const char*, ...) 77{ 78 // It would be nice to do something with this error message. 79} 80 81void XSLTProcessor::parseErrorFunc(void* userData, xmlError* error) 82{ 83 Console* console = static_cast<Console*>(userData); 84 if (!console) 85 return; 86 87 MessageLevel level; 88 switch (error->level) { 89 case XML_ERR_NONE: 90 level = TipMessageLevel; 91 break; 92 case XML_ERR_WARNING: 93 level = WarningMessageLevel; 94 break; 95 case XML_ERR_ERROR: 96 case XML_ERR_FATAL: 97 default: 98 level = ErrorMessageLevel; 99 break; 100 } 101 102 console->addMessage(XMLMessageSource, LogMessageType, level, error->message, error->line, error->file); 103} 104 105// FIXME: There seems to be no way to control the ctxt pointer for loading here, thus we have globals. 106static XSLTProcessor* globalProcessor = 0; 107static CachedResourceLoader* globalCachedResourceLoader = 0; 108static xmlDocPtr docLoaderFunc(const xmlChar* uri, 109 xmlDictPtr, 110 int options, 111 void* ctxt, 112 xsltLoadType type) 113{ 114 if (!globalProcessor) 115 return 0; 116 117 switch (type) { 118 case XSLT_LOAD_DOCUMENT: { 119 xsltTransformContextPtr context = (xsltTransformContextPtr)ctxt; 120 xmlChar* base = xmlNodeGetBase(context->document->doc, context->node); 121 KURL url(KURL(ParsedURLString, reinterpret_cast<const char*>(base)), reinterpret_cast<const char*>(uri)); 122 xmlFree(base); 123 ResourceError error; 124 ResourceResponse response; 125 126 Vector<char> data; 127 128 bool requestAllowed = globalCachedResourceLoader->frame() && globalCachedResourceLoader->document()->securityOrigin()->canRequest(url); 129 if (requestAllowed) { 130 globalCachedResourceLoader->frame()->loader()->loadResourceSynchronously(url, AllowStoredCredentials, error, response, data); 131 requestAllowed = globalCachedResourceLoader->document()->securityOrigin()->canRequest(response.url()); 132 } 133 if (!requestAllowed) { 134 data.clear(); 135 globalCachedResourceLoader->printAccessDeniedMessage(url); 136 } 137 138 Console* console = 0; 139 if (Frame* frame = globalProcessor->xslStylesheet()->ownerDocument()->frame()) 140 console = frame->domWindow()->console(); 141 xmlSetStructuredErrorFunc(console, XSLTProcessor::parseErrorFunc); 142 xmlSetGenericErrorFunc(console, XSLTProcessor::genericErrorFunc); 143 144 // We don't specify an encoding here. Neither Gecko nor WinIE respects 145 // the encoding specified in the HTTP headers. 146 xmlDocPtr doc = xmlReadMemory(data.data(), data.size(), (const char*)uri, 0, options); 147 148 xmlSetStructuredErrorFunc(0, 0); 149 xmlSetGenericErrorFunc(0, 0); 150 151 return doc; 152 } 153 case XSLT_LOAD_STYLESHEET: 154 return globalProcessor->xslStylesheet()->locateStylesheetSubResource(((xsltStylesheetPtr)ctxt)->doc, uri); 155 default: 156 break; 157 } 158 159 return 0; 160} 161 162static inline void setXSLTLoadCallBack(xsltDocLoaderFunc func, XSLTProcessor* processor, CachedResourceLoader* cachedResourceLoader) 163{ 164 xsltSetLoaderFunc(func); 165 globalProcessor = processor; 166 globalCachedResourceLoader = cachedResourceLoader; 167} 168 169static int writeToVector(void* context, const char* buffer, int len) 170{ 171 Vector<UChar>& resultOutput = *static_cast<Vector<UChar>*>(context); 172 173 if (!len) 174 return 0; 175 176 StringBuffer stringBuffer(len); 177 UChar* bufferUChar = stringBuffer.characters(); 178 UChar* bufferUCharEnd = bufferUChar + len; 179 180 const char* stringCurrent = buffer; 181 WTF::Unicode::ConversionResult result = WTF::Unicode::convertUTF8ToUTF16(&stringCurrent, buffer + len, &bufferUChar, bufferUCharEnd); 182 if (result != WTF::Unicode::conversionOK && result != WTF::Unicode::sourceExhausted) { 183 ASSERT_NOT_REACHED(); 184 return -1; 185 } 186 187 int utf16Length = bufferUChar - stringBuffer.characters(); 188 resultOutput.append(stringBuffer.characters(), utf16Length); 189 return stringCurrent - buffer; 190} 191 192static bool saveResultToString(xmlDocPtr resultDoc, xsltStylesheetPtr sheet, String& resultString) 193{ 194 xmlOutputBufferPtr outputBuf = xmlAllocOutputBuffer(0); 195 if (!outputBuf) 196 return false; 197 198 Vector<UChar> resultVector; 199 outputBuf->context = &resultVector; 200 outputBuf->writecallback = writeToVector; 201 202 int retval = xsltSaveResultTo(outputBuf, resultDoc, sheet); 203 xmlOutputBufferClose(outputBuf); 204 if (retval < 0) 205 return false; 206 207 // Workaround for <http://bugzilla.gnome.org/show_bug.cgi?id=495668>: libxslt appends an extra line feed to the result. 208 if (resultVector.size() > 0 && resultVector[resultVector.size() - 1] == '\n') 209 resultVector.removeLast(); 210 211 resultString = String::adopt(resultVector); 212 213 return true; 214} 215 216static const char** xsltParamArrayFromParameterMap(XSLTProcessor::ParameterMap& parameters) 217{ 218 if (parameters.isEmpty()) 219 return 0; 220 221 const char** parameterArray = (const char**)fastMalloc(((parameters.size() * 2) + 1) * sizeof(char*)); 222 223 XSLTProcessor::ParameterMap::iterator end = parameters.end(); 224 unsigned index = 0; 225 for (XSLTProcessor::ParameterMap::iterator it = parameters.begin(); it != end; ++it) { 226 parameterArray[index++] = fastStrDup(it->first.utf8().data()); 227 parameterArray[index++] = fastStrDup(it->second.utf8().data()); 228 } 229 parameterArray[index] = 0; 230 231 return parameterArray; 232} 233 234static void freeXsltParamArray(const char** params) 235{ 236 const char** temp = params; 237 if (!params) 238 return; 239 240 while (*temp) { 241 fastFree((void*)*(temp++)); 242 fastFree((void*)*(temp++)); 243 } 244 fastFree(params); 245} 246 247static xsltStylesheetPtr xsltStylesheetPointer(RefPtr<XSLStyleSheet>& cachedStylesheet, Node* stylesheetRootNode) 248{ 249 if (!cachedStylesheet && stylesheetRootNode) { 250 cachedStylesheet = XSLStyleSheet::createForXSLTProcessor(stylesheetRootNode->parentNode() ? stylesheetRootNode->parentNode() : stylesheetRootNode, 251 stylesheetRootNode->document()->url().string(), 252 stylesheetRootNode->document()->url()); // FIXME: Should we use baseURL here? 253 254 // According to Mozilla documentation, the node must be a Document node, an xsl:stylesheet or xsl:transform element. 255 // But we just use text content regardless of node type. 256 cachedStylesheet->parseString(createMarkup(stylesheetRootNode)); 257 } 258 259 if (!cachedStylesheet || !cachedStylesheet->document()) 260 return 0; 261 262 return cachedStylesheet->compileStyleSheet(); 263} 264 265static inline xmlDocPtr xmlDocPtrFromNode(Node* sourceNode, bool& shouldDelete) 266{ 267 RefPtr<Document> ownerDocument = sourceNode->document(); 268 bool sourceIsDocument = (sourceNode == ownerDocument.get()); 269 270 xmlDocPtr sourceDoc = 0; 271 if (sourceIsDocument && ownerDocument->transformSource()) 272 sourceDoc = (xmlDocPtr)ownerDocument->transformSource()->platformSource(); 273 if (!sourceDoc) { 274 sourceDoc = (xmlDocPtr)xmlDocPtrForString(ownerDocument->cachedResourceLoader(), createMarkup(sourceNode), 275 sourceIsDocument ? ownerDocument->url().string() : String()); 276 shouldDelete = sourceDoc; 277 } 278 return sourceDoc; 279} 280 281static inline String resultMIMEType(xmlDocPtr resultDoc, xsltStylesheetPtr sheet) 282{ 283 // There are three types of output we need to be able to deal with: 284 // HTML (create an HTML document), XML (create an XML document), 285 // and text (wrap in a <pre> and create an XML document). 286 287 const xmlChar* resultType = 0; 288 XSLT_GET_IMPORT_PTR(resultType, sheet, method); 289 if (!resultType && resultDoc->type == XML_HTML_DOCUMENT_NODE) 290 resultType = (const xmlChar*)"html"; 291 292 if (xmlStrEqual(resultType, (const xmlChar*)"html")) 293 return "text/html"; 294 if (xmlStrEqual(resultType, (const xmlChar*)"text")) 295 return "text/plain"; 296 297 return "application/xml"; 298} 299 300bool XSLTProcessor::transformToString(Node* sourceNode, String& mimeType, String& resultString, String& resultEncoding) 301{ 302 RefPtr<Document> ownerDocument = sourceNode->document(); 303 304 setXSLTLoadCallBack(docLoaderFunc, this, ownerDocument->cachedResourceLoader()); 305 xsltStylesheetPtr sheet = xsltStylesheetPointer(m_stylesheet, m_stylesheetRootNode.get()); 306 if (!sheet) { 307 setXSLTLoadCallBack(0, 0, 0); 308 return false; 309 } 310 m_stylesheet->clearDocuments(); 311 312 xmlChar* origMethod = sheet->method; 313 if (!origMethod && mimeType == "text/html") 314 sheet->method = (xmlChar*)"html"; 315 316 bool success = false; 317 bool shouldFreeSourceDoc = false; 318 if (xmlDocPtr sourceDoc = xmlDocPtrFromNode(sourceNode, shouldFreeSourceDoc)) { 319 // The XML declaration would prevent parsing the result as a fragment, and it's not needed even for documents, 320 // as the result of this function is always immediately parsed. 321 sheet->omitXmlDeclaration = true; 322 323 xsltTransformContextPtr transformContext = xsltNewTransformContext(sheet, sourceDoc); 324 registerXSLTExtensions(transformContext); 325 326 xsltSecurityPrefsPtr securityPrefs = xsltNewSecurityPrefs(); 327 // Read permissions are checked by docLoaderFunc. 328 if (0 != xsltSetSecurityPrefs(securityPrefs, XSLT_SECPREF_WRITE_FILE, xsltSecurityForbid)) 329 CRASH(); 330 if (0 != xsltSetSecurityPrefs(securityPrefs, XSLT_SECPREF_CREATE_DIRECTORY, xsltSecurityForbid)) 331 CRASH(); 332 if (0 != xsltSetSecurityPrefs(securityPrefs, XSLT_SECPREF_WRITE_NETWORK, xsltSecurityForbid)) 333 CRASH(); 334 if (0 != xsltSetCtxtSecurityPrefs(securityPrefs, transformContext)) 335 CRASH(); 336 337 // <http://bugs.webkit.org/show_bug.cgi?id=16077>: XSLT processor <xsl:sort> algorithm only compares by code point. 338 xsltSetCtxtSortFunc(transformContext, xsltUnicodeSortFunction); 339 340 // This is a workaround for a bug in libxslt. 341 // The bug has been fixed in version 1.1.13, so once we ship that this can be removed. 342 if (!transformContext->globalVars) 343 transformContext->globalVars = xmlHashCreate(20); 344 345 const char** params = xsltParamArrayFromParameterMap(m_parameters); 346 xsltQuoteUserParams(transformContext, params); 347 xmlDocPtr resultDoc = xsltApplyStylesheetUser(sheet, sourceDoc, 0, 0, 0, transformContext); 348 349 xsltFreeTransformContext(transformContext); 350 xsltFreeSecurityPrefs(securityPrefs); 351 freeXsltParamArray(params); 352 353 if (shouldFreeSourceDoc) 354 xmlFreeDoc(sourceDoc); 355 356 if ((success = saveResultToString(resultDoc, sheet, resultString))) { 357 mimeType = resultMIMEType(resultDoc, sheet); 358 resultEncoding = (char*)resultDoc->encoding; 359 } 360 xmlFreeDoc(resultDoc); 361 } 362 363 sheet->method = origMethod; 364 setXSLTLoadCallBack(0, 0, 0); 365 xsltFreeStylesheet(sheet); 366 m_stylesheet = 0; 367 368 return success; 369} 370 371} // namespace WebCore 372 373#endif // ENABLE(XSLT) 374