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 "Console.h"
     28 #include "DOMWindow.h"
     29 #include "CachedResourceLoader.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 <wtf/text/CString.h>
     39 
     40 #include <libxml/uri.h>
     41 #include <libxslt/xsltutils.h>
     42 
     43 #if PLATFORM(MAC)
     44 #include "SoftLinking.h"
     45 #endif
     46 
     47 #if PLATFORM(MAC)
     48 SOFT_LINK_LIBRARY(libxslt)
     49 SOFT_LINK(libxslt, xsltIsBlank, int, (xmlChar *str), (str))
     50 SOFT_LINK(libxslt, xsltGetNsProp, xmlChar *, (xmlNodePtr node, const xmlChar *name, const xmlChar *nameSpace), (node, name, nameSpace))
     51 SOFT_LINK(libxslt, xsltParseStylesheetDoc, xsltStylesheetPtr, (xmlDocPtr doc), (doc))
     52 SOFT_LINK(libxslt, xsltLoadStylesheetPI, xsltStylesheetPtr, (xmlDocPtr doc), (doc))
     53 #endif
     54 
     55 namespace WebCore {
     56 
     57 XSLStyleSheet::XSLStyleSheet(XSLImportRule* parentRule, const String& originalURL, const KURL& finalURL)
     58     : StyleSheet(parentRule, originalURL, finalURL)
     59     , m_embedded(false)
     60     , m_processed(false) // Child sheets get marked as processed when the libxslt engine has finally seen them.
     61     , m_stylesheetDoc(0)
     62     , m_stylesheetDocTaken(false)
     63     , m_parentStyleSheet(0)
     64 {
     65 }
     66 
     67 XSLStyleSheet::XSLStyleSheet(Node* parentNode, const String& originalURL, const KURL& finalURL,  bool embedded)
     68     : StyleSheet(parentNode, originalURL, finalURL)
     69     , m_embedded(embedded)
     70     , m_processed(true) // The root sheet starts off processed.
     71     , m_stylesheetDoc(0)
     72     , m_stylesheetDocTaken(false)
     73     , m_parentStyleSheet(0)
     74 {
     75 }
     76 
     77 XSLStyleSheet::~XSLStyleSheet()
     78 {
     79     if (!m_stylesheetDocTaken)
     80         xmlFreeDoc(m_stylesheetDoc);
     81 }
     82 
     83 bool XSLStyleSheet::isLoading()
     84 {
     85     unsigned len = length();
     86     for (unsigned i = 0; i < len; ++i) {
     87         StyleBase* rule = item(i);
     88         if (rule->isImportRule()) {
     89             XSLImportRule* import = static_cast<XSLImportRule*>(rule);
     90             if (import->isLoading())
     91                 return true;
     92         }
     93     }
     94     return false;
     95 }
     96 
     97 void XSLStyleSheet::checkLoaded()
     98 {
     99     if (isLoading())
    100         return;
    101     if (parent())
    102         parent()->checkLoaded();
    103     if (ownerNode())
    104         ownerNode()->sheetLoaded();
    105 }
    106 
    107 xmlDocPtr XSLStyleSheet::document()
    108 {
    109     if (m_embedded && ownerDocument() && ownerDocument()->transformSource())
    110         return (xmlDocPtr)ownerDocument()->transformSource()->platformSource();
    111     return m_stylesheetDoc;
    112 }
    113 
    114 void XSLStyleSheet::clearDocuments()
    115 {
    116     m_stylesheetDoc = 0;
    117     unsigned len = length();
    118     for (unsigned i = 0; i < len; ++i) {
    119         StyleBase* rule = item(i);
    120         if (rule->isImportRule()) {
    121             XSLImportRule* import = static_cast<XSLImportRule*>(rule);
    122             if (import->styleSheet())
    123                 import->styleSheet()->clearDocuments();
    124         }
    125     }
    126 }
    127 
    128 CachedResourceLoader* XSLStyleSheet::cachedResourceLoader()
    129 {
    130     Document* document = ownerDocument();
    131     if (!document)
    132         return 0;
    133     return document->cachedResourceLoader();
    134 }
    135 
    136 bool XSLStyleSheet::parseString(const String& string, bool)
    137 {
    138     // Parse in a single chunk into an xmlDocPtr
    139     const UChar BOM = 0xFEFF;
    140     const unsigned char BOMHighByte = *reinterpret_cast<const unsigned char*>(&BOM);
    141     if (!m_stylesheetDocTaken)
    142         xmlFreeDoc(m_stylesheetDoc);
    143     m_stylesheetDocTaken = false;
    144 
    145     Console* console = 0;
    146     if (Frame* frame = ownerDocument()->frame())
    147         console = frame->domWindow()->console();
    148 
    149     XMLDocumentParserScope scope(cachedResourceLoader(), XSLTProcessor::genericErrorFunc, XSLTProcessor::parseErrorFunc, console);
    150 
    151     const char* buffer = reinterpret_cast<const char*>(string.characters());
    152     int size = string.length() * sizeof(UChar);
    153 
    154     xmlParserCtxtPtr ctxt = xmlCreateMemoryParserCtxt(buffer, size);
    155     if (!ctxt)
    156         return 0;
    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 }
    260 
    261 Document* XSLStyleSheet::ownerDocument()
    262 {
    263     for (XSLStyleSheet* styleSheet = this; styleSheet; styleSheet = styleSheet->parentStyleSheet()) {
    264         Node* node = styleSheet->ownerNode();
    265         if (node)
    266             return node->document();
    267     }
    268     return 0;
    269 }
    270 
    271 xmlDocPtr XSLStyleSheet::locateStylesheetSubResource(xmlDocPtr parentDoc, const xmlChar* uri)
    272 {
    273     bool matchedParent = (parentDoc == document());
    274     unsigned len = length();
    275     for (unsigned i = 0; i < len; ++i) {
    276         StyleBase* rule = item(i);
    277         if (rule->isImportRule()) {
    278             XSLImportRule* import = static_cast<XSLImportRule*>(rule);
    279             XSLStyleSheet* child = import->styleSheet();
    280             if (!child)
    281                 continue;
    282             if (matchedParent) {
    283                 if (child->processed())
    284                     continue; // libxslt has been given this sheet already.
    285 
    286                 // Check the URI of the child stylesheet against the doc URI.
    287                 // In order to ensure that libxml canonicalized both URLs, we get the original href
    288                 // string from the import rule and canonicalize it using libxml before comparing it
    289                 // with the URI argument.
    290                 CString importHref = import->href().utf8();
    291                 xmlChar* base = xmlNodeGetBase(parentDoc, (xmlNodePtr)parentDoc);
    292                 xmlChar* childURI = xmlBuildURI((const xmlChar*)importHref.data(), base);
    293                 bool equalURIs = xmlStrEqual(uri, childURI);
    294                 xmlFree(base);
    295                 xmlFree(childURI);
    296                 if (equalURIs) {
    297                     child->markAsProcessed();
    298                     return child->document();
    299                 }
    300             } else {
    301                 xmlDocPtr result = import->styleSheet()->locateStylesheetSubResource(parentDoc, uri);
    302                 if (result)
    303                     return result;
    304             }
    305         }
    306     }
    307 
    308     return 0;
    309 }
    310 
    311 void XSLStyleSheet::markAsProcessed()
    312 {
    313     ASSERT(!m_processed);
    314     ASSERT(!m_stylesheetDocTaken);
    315     m_processed = true;
    316     m_stylesheetDocTaken = true;
    317 }
    318 
    319 } // namespace WebCore
    320 
    321 #endif // ENABLE(XSLT)
    322