Home | History | Annotate | Download | only in xml
      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 "CString.h"
     28 #include "Console.h"
     29 #include "DOMWindow.h"
     30 #include "DocLoader.h"
     31 #include "Document.h"
     32 #include "Frame.h"
     33 #include "Node.h"
     34 #include "TransformSource.h"
     35 #include "XMLTokenizer.h"
     36 #include "XMLTokenizerScope.h"
     37 #include "XSLImportRule.h"
     38 #include "XSLTProcessor.h"
     39 #include "loader.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     XMLTokenizerScope 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 
    158     if (m_parentStyleSheet) {
    159         // The XSL transform may leave the newly-transformed document
    160         // with references to the symbol dictionaries of the style sheet
    161         // and any of its children. XML document disposal can corrupt memory
    162         // if a document uses more than one symbol dictionary, so we
    163         // ensure that all child stylesheets use the same dictionaries as their
    164         // parents.
    165         xmlDictFree(ctxt->dict);
    166         ctxt->dict = m_parentStyleSheet->m_stylesheetDoc->dict;
    167         xmlDictReference(ctxt->dict);
    168     }
    169 
    170     m_stylesheetDoc = xmlCtxtReadMemory(ctxt, buffer, size,
    171         finalURL().string().utf8().data(),
    172         BOMHighByte == 0xFF ? "UTF-16LE" : "UTF-16BE",
    173         XML_PARSE_NOENT | XML_PARSE_DTDATTR | XML_PARSE_NOWARNING | XML_PARSE_NOCDATA);
    174     xmlFreeParserCtxt(ctxt);
    175 
    176     loadChildSheets();
    177 
    178     return m_stylesheetDoc;
    179 }
    180 
    181 void XSLStyleSheet::loadChildSheets()
    182 {
    183     if (!document())
    184         return;
    185 
    186     xmlNodePtr stylesheetRoot = document()->children;
    187 
    188     // Top level children may include other things such as DTD nodes, we ignore those.
    189     while (stylesheetRoot && stylesheetRoot->type != XML_ELEMENT_NODE)
    190         stylesheetRoot = stylesheetRoot->next;
    191 
    192     if (m_embedded) {
    193         // We have to locate (by ID) the appropriate embedded stylesheet element, so that we can walk the
    194         // import/include list.
    195         xmlAttrPtr idNode = xmlGetID(document(), (const xmlChar*)(finalURL().string().utf8().data()));
    196         if (!idNode)
    197             return;
    198         stylesheetRoot = idNode->parent;
    199     } else {
    200         // FIXME: Need to handle an external URI with a # in it.  This is a pretty minor edge case, so we'll deal
    201         // with it later.
    202     }
    203 
    204     if (stylesheetRoot) {
    205         // Walk the children of the root element and look for import/include elements.
    206         // Imports must occur first.
    207         xmlNodePtr curr = stylesheetRoot->children;
    208         while (curr) {
    209             if (curr->type != XML_ELEMENT_NODE) {
    210                 curr = curr->next;
    211                 continue;
    212             }
    213             if (IS_XSLT_ELEM(curr) && IS_XSLT_NAME(curr, "import")) {
    214                 xmlChar* uriRef = xsltGetNsProp(curr, (const xmlChar*)"href", XSLT_NAMESPACE);
    215                 loadChildSheet(String::fromUTF8((const char*)uriRef));
    216                 xmlFree(uriRef);
    217             } else
    218                 break;
    219             curr = curr->next;
    220         }
    221 
    222         // Now handle includes.
    223         while (curr) {
    224             if (curr->type == XML_ELEMENT_NODE && IS_XSLT_ELEM(curr) && IS_XSLT_NAME(curr, "include")) {
    225                 xmlChar* uriRef = xsltGetNsProp(curr, (const xmlChar*)"href", XSLT_NAMESPACE);
    226                 loadChildSheet(String::fromUTF8((const char*)uriRef));
    227                 xmlFree(uriRef);
    228             }
    229             curr = curr->next;
    230         }
    231     }
    232 }
    233 
    234 void XSLStyleSheet::loadChildSheet(const String& href)
    235 {
    236     RefPtr<XSLImportRule> childRule = XSLImportRule::create(this, href);
    237     append(childRule);
    238     childRule->loadSheet();
    239 }
    240 
    241 xsltStylesheetPtr XSLStyleSheet::compileStyleSheet()
    242 {
    243     // FIXME: Hook up error reporting for the stylesheet compilation process.
    244     if (m_embedded)
    245         return xsltLoadStylesheetPI(document());
    246 
    247     // xsltParseStylesheetDoc makes the document part of the stylesheet
    248     // so we have to release our pointer to it.
    249     ASSERT(!m_stylesheetDocTaken);
    250     xsltStylesheetPtr result = xsltParseStylesheetDoc(m_stylesheetDoc);
    251     if (result)
    252         m_stylesheetDocTaken = true;
    253     return result;
    254 }
    255 
    256 void XSLStyleSheet::setParentStyleSheet(XSLStyleSheet* parent)
    257 {
    258     m_parentStyleSheet = parent;
    259     if (parent)
    260         m_ownerDocument = parent->ownerDocument();
    261 }
    262 
    263 xmlDocPtr XSLStyleSheet::locateStylesheetSubResource(xmlDocPtr parentDoc, const xmlChar* uri)
    264 {
    265     bool matchedParent = (parentDoc == document());
    266     unsigned len = length();
    267     for (unsigned i = 0; i < len; ++i) {
    268         StyleBase* rule = item(i);
    269         if (rule->isImportRule()) {
    270             XSLImportRule* import = static_cast<XSLImportRule*>(rule);
    271             XSLStyleSheet* child = import->styleSheet();
    272             if (!child)
    273                 continue;
    274             if (matchedParent) {
    275                 if (child->processed())
    276                     continue; // libxslt has been given this sheet already.
    277 
    278                 // Check the URI of the child stylesheet against the doc URI.
    279                 // In order to ensure that libxml canonicalized both URLs, we get the original href
    280                 // string from the import rule and canonicalize it using libxml before comparing it
    281                 // with the URI argument.
    282                 CString importHref = import->href().utf8();
    283                 xmlChar* base = xmlNodeGetBase(parentDoc, (xmlNodePtr)parentDoc);
    284                 xmlChar* childURI = xmlBuildURI((const xmlChar*)importHref.data(), base);
    285                 bool equalURIs = xmlStrEqual(uri, childURI);
    286                 xmlFree(base);
    287                 xmlFree(childURI);
    288                 if (equalURIs) {
    289                     child->markAsProcessed();
    290                     return child->document();
    291                 }
    292             } else {
    293                 xmlDocPtr result = import->styleSheet()->locateStylesheetSubResource(parentDoc, uri);
    294                 if (result)
    295                     return result;
    296             }
    297         }
    298     }
    299 
    300     return 0;
    301 }
    302 
    303 void XSLStyleSheet::markAsProcessed()
    304 {
    305     ASSERT(!m_processed);
    306     ASSERT(!m_stylesheetDocTaken);
    307     m_processed = true;
    308     m_stylesheetDocTaken = true;
    309 }
    310 
    311 } // namespace WebCore
    312 
    313 #endif // ENABLE(XSLT)
    314