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