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