WebCore/xml/XSLStyleSheetLibxslt.cpp
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     1 /*
       
     2  * This file is part of the XSL implementation.
       
     3  *
       
     4  * Copyright (C) 2004, 2005, 2006, 2008 Apple Inc. All rights reserved.
       
     5  *
       
     6  * This library is free software; you can redistribute it and/or
       
     7  * modify it under the terms of the GNU Library General Public
       
     8  * License as published by the Free Software Foundation; either
       
     9  * version 2 of the License, or (at your option) any later version.
       
    10  *
       
    11  * This library is distributed in the hope that it will be useful,
       
    12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    14  * Library General Public License for more details.
       
    15  *
       
    16  * You should have received a copy of the GNU Library General Public License
       
    17  * along with this library; see the file COPYING.LIB.  If not, write to
       
    18  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
       
    19  * Boston, MA 02110-1301, USA.
       
    20  */
       
    21 
       
    22 #include "config.h"
       
    23 #include "XSLStyleSheet.h"
       
    24 
       
    25 #if ENABLE(XSLT)
       
    26 
       
    27 #include "Console.h"
       
    28 #include "DOMWindow.h"
       
    29 #include "DocLoader.h"
       
    30 #include "Document.h"
       
    31 #include "Frame.h"
       
    32 #include "Node.h"
       
    33 #include "TransformSource.h"
       
    34 #include "XMLDocumentParser.h"
       
    35 #include "XMLDocumentParserScope.h"
       
    36 #include "XSLImportRule.h"
       
    37 #include "XSLTProcessor.h"
       
    38 #include "loader.h"
       
    39 #include <wtf/text/CString.h>
       
    40 
       
    41 #include <libxml/uri.h>
       
    42 #include <libxslt/xsltutils.h>
       
    43 
       
    44 #if PLATFORM(MAC)
       
    45 #include "SoftLinking.h"
       
    46 #endif
       
    47 
       
    48 #if PLATFORM(MAC)
       
    49 SOFT_LINK_LIBRARY(libxslt)
       
    50 SOFT_LINK(libxslt, xsltIsBlank, int, (xmlChar *str), (str))
       
    51 SOFT_LINK(libxslt, xsltGetNsProp, xmlChar *, (xmlNodePtr node, const xmlChar *name, const xmlChar *nameSpace), (node, name, nameSpace))
       
    52 SOFT_LINK(libxslt, xsltParseStylesheetDoc, xsltStylesheetPtr, (xmlDocPtr doc), (doc))
       
    53 SOFT_LINK(libxslt, xsltLoadStylesheetPI, xsltStylesheetPtr, (xmlDocPtr doc), (doc))
       
    54 #endif
       
    55 
       
    56 namespace WebCore {
       
    57 
       
    58 XSLStyleSheet::XSLStyleSheet(XSLImportRule* parentRule, const String& originalURL, const KURL& finalURL)
       
    59     : StyleSheet(parentRule, originalURL, finalURL)
       
    60     , m_ownerDocument(0)
       
    61     , m_embedded(false)
       
    62     , m_processed(false) // Child sheets get marked as processed when the libxslt engine has finally seen them.
       
    63     , m_stylesheetDoc(0)
       
    64     , m_stylesheetDocTaken(false)
       
    65     , m_parentStyleSheet(0)
       
    66 {
       
    67 }
       
    68 
       
    69 XSLStyleSheet::XSLStyleSheet(Node* parentNode, const String& originalURL, const KURL& finalURL,  bool embedded)
       
    70     : StyleSheet(parentNode, originalURL, finalURL)
       
    71     , m_ownerDocument(parentNode->document())
       
    72     , m_embedded(embedded)
       
    73     , m_processed(true) // The root sheet starts off processed.
       
    74     , m_stylesheetDoc(0)
       
    75     , m_stylesheetDocTaken(false)
       
    76     , m_parentStyleSheet(0)
       
    77 {
       
    78 }
       
    79 
       
    80 XSLStyleSheet::~XSLStyleSheet()
       
    81 {
       
    82     if (!m_stylesheetDocTaken)
       
    83         xmlFreeDoc(m_stylesheetDoc);
       
    84 }
       
    85 
       
    86 bool XSLStyleSheet::isLoading()
       
    87 {
       
    88     unsigned len = length();
       
    89     for (unsigned i = 0; i < len; ++i) {
       
    90         StyleBase* rule = item(i);
       
    91         if (rule->isImportRule()) {
       
    92             XSLImportRule* import = static_cast<XSLImportRule*>(rule);
       
    93             if (import->isLoading())
       
    94                 return true;
       
    95         }
       
    96     }
       
    97     return false;
       
    98 }
       
    99 
       
   100 void XSLStyleSheet::checkLoaded()
       
   101 {
       
   102     if (isLoading())
       
   103         return;
       
   104     if (parent())
       
   105         parent()->checkLoaded();
       
   106     if (ownerNode())
       
   107         ownerNode()->sheetLoaded();
       
   108 }
       
   109 
       
   110 xmlDocPtr XSLStyleSheet::document()
       
   111 {
       
   112     if (m_embedded && ownerDocument() && ownerDocument()->transformSource())
       
   113         return (xmlDocPtr)ownerDocument()->transformSource()->platformSource();
       
   114     return m_stylesheetDoc;
       
   115 }
       
   116 
       
   117 void XSLStyleSheet::clearDocuments()
       
   118 {
       
   119     m_stylesheetDoc = 0;
       
   120     unsigned len = length();
       
   121     for (unsigned i = 0; i < len; ++i) {
       
   122         StyleBase* rule = item(i);
       
   123         if (rule->isImportRule()) {
       
   124             XSLImportRule* import = static_cast<XSLImportRule*>(rule);
       
   125             if (import->styleSheet())
       
   126                 import->styleSheet()->clearDocuments();
       
   127         }
       
   128     }
       
   129 }
       
   130 
       
   131 DocLoader* XSLStyleSheet::docLoader()
       
   132 {
       
   133     if (!m_ownerDocument)
       
   134         return 0;
       
   135     return m_ownerDocument->docLoader();
       
   136 }
       
   137 
       
   138 bool XSLStyleSheet::parseString(const String& string, bool)
       
   139 {
       
   140     // Parse in a single chunk into an xmlDocPtr
       
   141     const UChar BOM = 0xFEFF;
       
   142     const unsigned char BOMHighByte = *reinterpret_cast<const unsigned char*>(&BOM);
       
   143     if (!m_stylesheetDocTaken)
       
   144         xmlFreeDoc(m_stylesheetDoc);
       
   145     m_stylesheetDocTaken = false;
       
   146 
       
   147     Console* console = 0;
       
   148     if (Frame* frame = ownerDocument()->frame())
       
   149         console = frame->domWindow()->console();
       
   150 
       
   151     XMLDocumentParserScope scope(docLoader(), XSLTProcessor::genericErrorFunc, XSLTProcessor::parseErrorFunc, console);
       
   152 
       
   153     const char* buffer = reinterpret_cast<const char*>(string.characters());
       
   154     int size = string.length() * sizeof(UChar);
       
   155 
       
   156     xmlParserCtxtPtr ctxt = xmlCreateMemoryParserCtxt(buffer, size);
       
   157     if (!ctxt)
       
   158         return 0;
       
   159 
       
   160     if (m_parentStyleSheet) {
       
   161         // The XSL transform may leave the newly-transformed document
       
   162         // with references to the symbol dictionaries of the style sheet
       
   163         // and any of its children. XML document disposal can corrupt memory
       
   164         // if a document uses more than one symbol dictionary, so we
       
   165         // ensure that all child stylesheets use the same dictionaries as their
       
   166         // parents.
       
   167         xmlDictFree(ctxt->dict);
       
   168         ctxt->dict = m_parentStyleSheet->m_stylesheetDoc->dict;
       
   169         xmlDictReference(ctxt->dict);
       
   170     }
       
   171 
       
   172     m_stylesheetDoc = xmlCtxtReadMemory(ctxt, buffer, size,
       
   173         finalURL().string().utf8().data(),
       
   174         BOMHighByte == 0xFF ? "UTF-16LE" : "UTF-16BE",
       
   175         XML_PARSE_NOENT | XML_PARSE_DTDATTR | XML_PARSE_NOWARNING | XML_PARSE_NOCDATA);
       
   176     xmlFreeParserCtxt(ctxt);
       
   177 
       
   178     loadChildSheets();
       
   179 
       
   180     return m_stylesheetDoc;
       
   181 }
       
   182 
       
   183 void XSLStyleSheet::loadChildSheets()
       
   184 {
       
   185     if (!document())
       
   186         return;
       
   187 
       
   188     xmlNodePtr stylesheetRoot = document()->children;
       
   189 
       
   190     // Top level children may include other things such as DTD nodes, we ignore those.
       
   191     while (stylesheetRoot && stylesheetRoot->type != XML_ELEMENT_NODE)
       
   192         stylesheetRoot = stylesheetRoot->next;
       
   193 
       
   194     if (m_embedded) {
       
   195         // We have to locate (by ID) the appropriate embedded stylesheet element, so that we can walk the
       
   196         // import/include list.
       
   197         xmlAttrPtr idNode = xmlGetID(document(), (const xmlChar*)(finalURL().string().utf8().data()));
       
   198         if (!idNode)
       
   199             return;
       
   200         stylesheetRoot = idNode->parent;
       
   201     } else {
       
   202         // FIXME: Need to handle an external URI with a # in it.  This is a pretty minor edge case, so we'll deal
       
   203         // with it later.
       
   204     }
       
   205 
       
   206     if (stylesheetRoot) {
       
   207         // Walk the children of the root element and look for import/include elements.
       
   208         // Imports must occur first.
       
   209         xmlNodePtr curr = stylesheetRoot->children;
       
   210         while (curr) {
       
   211             if (curr->type != XML_ELEMENT_NODE) {
       
   212                 curr = curr->next;
       
   213                 continue;
       
   214             }
       
   215             if (IS_XSLT_ELEM(curr) && IS_XSLT_NAME(curr, "import")) {
       
   216                 xmlChar* uriRef = xsltGetNsProp(curr, (const xmlChar*)"href", XSLT_NAMESPACE);
       
   217                 loadChildSheet(String::fromUTF8((const char*)uriRef));
       
   218                 xmlFree(uriRef);
       
   219             } else
       
   220                 break;
       
   221             curr = curr->next;
       
   222         }
       
   223 
       
   224         // Now handle includes.
       
   225         while (curr) {
       
   226             if (curr->type == XML_ELEMENT_NODE && IS_XSLT_ELEM(curr) && IS_XSLT_NAME(curr, "include")) {
       
   227                 xmlChar* uriRef = xsltGetNsProp(curr, (const xmlChar*)"href", XSLT_NAMESPACE);
       
   228                 loadChildSheet(String::fromUTF8((const char*)uriRef));
       
   229                 xmlFree(uriRef);
       
   230             }
       
   231             curr = curr->next;
       
   232         }
       
   233     }
       
   234 }
       
   235 
       
   236 void XSLStyleSheet::loadChildSheet(const String& href)
       
   237 {
       
   238     RefPtr<XSLImportRule> childRule = XSLImportRule::create(this, href);
       
   239     append(childRule);
       
   240     childRule->loadSheet();
       
   241 }
       
   242 
       
   243 xsltStylesheetPtr XSLStyleSheet::compileStyleSheet()
       
   244 {
       
   245     // FIXME: Hook up error reporting for the stylesheet compilation process.
       
   246     if (m_embedded)
       
   247         return xsltLoadStylesheetPI(document());
       
   248 
       
   249     // xsltParseStylesheetDoc makes the document part of the stylesheet
       
   250     // so we have to release our pointer to it.
       
   251     ASSERT(!m_stylesheetDocTaken);
       
   252     xsltStylesheetPtr result = xsltParseStylesheetDoc(m_stylesheetDoc);
       
   253     if (result)
       
   254         m_stylesheetDocTaken = true;
       
   255     return result;
       
   256 }
       
   257 
       
   258 void XSLStyleSheet::setParentStyleSheet(XSLStyleSheet* parent)
       
   259 {
       
   260     m_parentStyleSheet = parent;
       
   261     if (parent)
       
   262         m_ownerDocument = parent->ownerDocument();
       
   263 }
       
   264 
       
   265 xmlDocPtr XSLStyleSheet::locateStylesheetSubResource(xmlDocPtr parentDoc, const xmlChar* uri)
       
   266 {
       
   267     bool matchedParent = (parentDoc == document());
       
   268     unsigned len = length();
       
   269     for (unsigned i = 0; i < len; ++i) {
       
   270         StyleBase* rule = item(i);
       
   271         if (rule->isImportRule()) {
       
   272             XSLImportRule* import = static_cast<XSLImportRule*>(rule);
       
   273             XSLStyleSheet* child = import->styleSheet();
       
   274             if (!child)
       
   275                 continue;
       
   276             if (matchedParent) {
       
   277                 if (child->processed())
       
   278                     continue; // libxslt has been given this sheet already.
       
   279 
       
   280                 // Check the URI of the child stylesheet against the doc URI.
       
   281                 // In order to ensure that libxml canonicalized both URLs, we get the original href
       
   282                 // string from the import rule and canonicalize it using libxml before comparing it
       
   283                 // with the URI argument.
       
   284                 CString importHref = import->href().utf8();
       
   285                 xmlChar* base = xmlNodeGetBase(parentDoc, (xmlNodePtr)parentDoc);
       
   286                 xmlChar* childURI = xmlBuildURI((const xmlChar*)importHref.data(), base);
       
   287                 bool equalURIs = xmlStrEqual(uri, childURI);
       
   288                 xmlFree(base);
       
   289                 xmlFree(childURI);
       
   290                 if (equalURIs) {
       
   291                     child->markAsProcessed();
       
   292                     return child->document();
       
   293                 }
       
   294             } else {
       
   295                 xmlDocPtr result = import->styleSheet()->locateStylesheetSubResource(parentDoc, uri);
       
   296                 if (result)
       
   297                     return result;
       
   298             }
       
   299         }
       
   300     }
       
   301 
       
   302     return 0;
       
   303 }
       
   304 
       
   305 void XSLStyleSheet::markAsProcessed()
       
   306 {
       
   307     ASSERT(!m_processed);
       
   308     ASSERT(!m_stylesheetDocTaken);
       
   309     m_processed = true;
       
   310     m_stylesheetDocTaken = true;
       
   311 }
       
   312 
       
   313 } // namespace WebCore
       
   314 
       
   315 #endif // ENABLE(XSLT)