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