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, 2012 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 "core/xml/XSLStyleSheet.h"
     24 
     25 #include "core/dom/Document.h"
     26 #include "core/dom/Node.h"
     27 #include "core/dom/TransformSource.h"
     28 #include "core/frame/Frame.h"
     29 #include "core/page/Page.h"
     30 #include "core/xml/XSLImportRule.h"
     31 #include "core/xml/XSLTProcessor.h"
     32 #include "core/xml/parser/XMLDocumentParserScope.h"
     33 #include "core/xml/parser/XMLParserInput.h"
     34 #include "wtf/text/CString.h"
     35 
     36 #include <libxml/uri.h>
     37 #include <libxslt/xsltutils.h>
     38 
     39 namespace WebCore {
     40 
     41 XSLStyleSheet::XSLStyleSheet(XSLImportRule* parentRule, const String& originalURL, const KURL& finalURL)
     42     : m_ownerNode(0)
     43     , m_originalURL(originalURL)
     44     , m_finalURL(finalURL)
     45     , m_isDisabled(false)
     46     , m_embedded(false)
     47     , m_processed(false) // Child sheets get marked as processed when the libxslt engine has finally seen them.
     48     , m_stylesheetDoc(0)
     49     , m_stylesheetDocTaken(false)
     50     , m_compilationFailed(false)
     51     , m_parentStyleSheet(parentRule ? parentRule->parentStyleSheet() : 0)
     52 {
     53 }
     54 
     55 XSLStyleSheet::XSLStyleSheet(Node* parentNode, const String& originalURL, const KURL& finalURL,  bool embedded)
     56     : m_ownerNode(parentNode)
     57     , m_originalURL(originalURL)
     58     , m_finalURL(finalURL)
     59     , m_isDisabled(false)
     60     , m_embedded(embedded)
     61     , m_processed(true) // The root sheet starts off processed.
     62     , m_stylesheetDoc(0)
     63     , m_stylesheetDocTaken(false)
     64     , m_compilationFailed(false)
     65     , m_parentStyleSheet(0)
     66 {
     67 }
     68 
     69 XSLStyleSheet::~XSLStyleSheet()
     70 {
     71     if (!m_stylesheetDocTaken)
     72         xmlFreeDoc(m_stylesheetDoc);
     73 
     74     for (unsigned i = 0; i < m_children.size(); ++i) {
     75         ASSERT(m_children.at(i)->parentStyleSheet() == this);
     76         m_children.at(i)->setParentStyleSheet(0);
     77     }
     78 }
     79 
     80 bool XSLStyleSheet::isLoading() const
     81 {
     82     for (unsigned i = 0; i < m_children.size(); ++i) {
     83         if (m_children.at(i)->isLoading())
     84             return true;
     85     }
     86     return false;
     87 }
     88 
     89 void XSLStyleSheet::checkLoaded()
     90 {
     91     if (isLoading())
     92         return;
     93     if (XSLStyleSheet* styleSheet = parentStyleSheet())
     94         styleSheet->checkLoaded();
     95     if (ownerNode())
     96         ownerNode()->sheetLoaded();
     97 }
     98 
     99 xmlDocPtr XSLStyleSheet::document()
    100 {
    101     if (m_embedded && ownerDocument() && ownerDocument()->transformSource())
    102         return (xmlDocPtr)ownerDocument()->transformSource()->platformSource();
    103     return m_stylesheetDoc;
    104 }
    105 
    106 void XSLStyleSheet::clearDocuments()
    107 {
    108     m_stylesheetDoc = 0;
    109     for (unsigned i = 0; i < m_children.size(); ++i) {
    110         XSLImportRule* import = m_children.at(i).get();
    111         if (import->styleSheet())
    112             import->styleSheet()->clearDocuments();
    113     }
    114 }
    115 
    116 ResourceFetcher* XSLStyleSheet::fetcher()
    117 {
    118     Document* document = ownerDocument();
    119     if (!document)
    120         return 0;
    121     return document->fetcher();
    122 }
    123 
    124 bool XSLStyleSheet::parseString(const String& source)
    125 {
    126     // Parse in a single chunk into an xmlDocPtr
    127     if (!m_stylesheetDocTaken)
    128         xmlFreeDoc(m_stylesheetDoc);
    129     m_stylesheetDocTaken = false;
    130 
    131     PageConsole* console = 0;
    132     Frame* frame = ownerDocument()->frame();
    133     if (frame && frame->page())
    134         console = &frame->page()->console();
    135 
    136     XMLDocumentParserScope scope(fetcher(), XSLTProcessor::genericErrorFunc, XSLTProcessor::parseErrorFunc, console);
    137     XMLParserInput input(source);
    138 
    139     xmlParserCtxtPtr ctxt = xmlCreateMemoryParserCtxt(input.data(), input.size());
    140     if (!ctxt)
    141         return 0;
    142 
    143     if (m_parentStyleSheet) {
    144         // The XSL transform may leave the newly-transformed document
    145         // with references to the symbol dictionaries of the style sheet
    146         // and any of its children. XML document disposal can corrupt memory
    147         // if a document uses more than one symbol dictionary, so we
    148         // ensure that all child stylesheets use the same dictionaries as their
    149         // parents.
    150         xmlDictFree(ctxt->dict);
    151         ctxt->dict = m_parentStyleSheet->m_stylesheetDoc->dict;
    152         xmlDictReference(ctxt->dict);
    153     }
    154 
    155     m_stylesheetDoc = xmlCtxtReadMemory(ctxt, input.data(), input.size(),
    156         finalURL().string().utf8().data(), input.encoding(),
    157         XML_PARSE_NOENT | XML_PARSE_DTDATTR | XML_PARSE_NOWARNING | XML_PARSE_NOCDATA);
    158 
    159     xmlFreeParserCtxt(ctxt);
    160     loadChildSheets();
    161     return m_stylesheetDoc;
    162 }
    163 
    164 void XSLStyleSheet::loadChildSheets()
    165 {
    166     if (!document())
    167         return;
    168 
    169     xmlNodePtr stylesheetRoot = document()->children;
    170 
    171     // Top level children may include other things such as DTD nodes, we ignore those.
    172     while (stylesheetRoot && stylesheetRoot->type != XML_ELEMENT_NODE)
    173         stylesheetRoot = stylesheetRoot->next;
    174 
    175     if (m_embedded) {
    176         // We have to locate (by ID) the appropriate embedded stylesheet element, so that we can walk the
    177         // import/include list.
    178         xmlAttrPtr idNode = xmlGetID(document(), (const xmlChar*)(finalURL().string().utf8().data()));
    179         if (!idNode)
    180             return;
    181         stylesheetRoot = idNode->parent;
    182     } else {
    183         // FIXME: Need to handle an external URI with a # in it.  This is a pretty minor edge case, so we'll deal
    184         // with it later.
    185     }
    186 
    187     if (stylesheetRoot) {
    188         // Walk the children of the root element and look for import/include elements.
    189         // Imports must occur first.
    190         xmlNodePtr curr = stylesheetRoot->children;
    191         while (curr) {
    192             if (curr->type != XML_ELEMENT_NODE) {
    193                 curr = curr->next;
    194                 continue;
    195             }
    196             if (IS_XSLT_ELEM(curr) && IS_XSLT_NAME(curr, "import")) {
    197                 xmlChar* uriRef = xsltGetNsProp(curr, (const xmlChar*)"href", XSLT_NAMESPACE);
    198                 loadChildSheet(String::fromUTF8((const char*)uriRef));
    199                 xmlFree(uriRef);
    200             } else
    201                 break;
    202             curr = curr->next;
    203         }
    204 
    205         // Now handle includes.
    206         while (curr) {
    207             if (curr->type == XML_ELEMENT_NODE && IS_XSLT_ELEM(curr) && IS_XSLT_NAME(curr, "include")) {
    208                 xmlChar* uriRef = xsltGetNsProp(curr, (const xmlChar*)"href", XSLT_NAMESPACE);
    209                 loadChildSheet(String::fromUTF8((const char*)uriRef));
    210                 xmlFree(uriRef);
    211             }
    212             curr = curr->next;
    213         }
    214     }
    215 }
    216 
    217 void XSLStyleSheet::loadChildSheet(const String& href)
    218 {
    219     OwnPtr<XSLImportRule> childRule = XSLImportRule::create(this, href);
    220     XSLImportRule* c = childRule.get();
    221     m_children.append(childRule.release());
    222     c->loadSheet();
    223 }
    224 
    225 xsltStylesheetPtr XSLStyleSheet::compileStyleSheet()
    226 {
    227     // FIXME: Hook up error reporting for the stylesheet compilation process.
    228     if (m_embedded)
    229         return xsltLoadStylesheetPI(document());
    230 
    231     // Certain libxslt versions are corrupting the xmlDoc on compilation failures -
    232     // hence attempting to recompile after a failure is unsafe.
    233     if (m_compilationFailed)
    234         return 0;
    235 
    236     // xsltParseStylesheetDoc makes the document part of the stylesheet
    237     // so we have to release our pointer to it.
    238     ASSERT(!m_stylesheetDocTaken);
    239     xsltStylesheetPtr result = xsltParseStylesheetDoc(m_stylesheetDoc);
    240     if (result)
    241         m_stylesheetDocTaken = true;
    242     else
    243         m_compilationFailed = true;
    244     return result;
    245 }
    246 
    247 void XSLStyleSheet::setParentStyleSheet(XSLStyleSheet* parent)
    248 {
    249     m_parentStyleSheet = parent;
    250 }
    251 
    252 Document* XSLStyleSheet::ownerDocument()
    253 {
    254     for (XSLStyleSheet* styleSheet = this; styleSheet; styleSheet = styleSheet->parentStyleSheet()) {
    255         Node* node = styleSheet->ownerNode();
    256         if (node)
    257             return &node->document();
    258     }
    259     return 0;
    260 }
    261 
    262 xmlDocPtr XSLStyleSheet::locateStylesheetSubResource(xmlDocPtr parentDoc, const xmlChar* uri)
    263 {
    264     bool matchedParent = (parentDoc == document());
    265     for (unsigned i = 0; i < m_children.size(); ++i) {
    266         XSLImportRule* import = m_children.at(i).get();
    267         XSLStyleSheet* child = import->styleSheet();
    268         if (!child)
    269             continue;
    270         if (matchedParent) {
    271             if (child->processed())
    272                 continue; // libxslt has been given this sheet already.
    273 
    274             // Check the URI of the child stylesheet against the doc URI.
    275             // In order to ensure that libxml canonicalized both URLs, we get the original href
    276             // string from the import rule and canonicalize it using libxml before comparing it
    277             // with the URI argument.
    278             CString importHref = import->href().utf8();
    279             xmlChar* base = xmlNodeGetBase(parentDoc, (xmlNodePtr)parentDoc);
    280             xmlChar* childURI = xmlBuildURI((const xmlChar*)importHref.data(), base);
    281             bool equalURIs = xmlStrEqual(uri, childURI);
    282             xmlFree(base);
    283             xmlFree(childURI);
    284             if (equalURIs) {
    285                 child->markAsProcessed();
    286                 return child->document();
    287             }
    288             continue;
    289         }
    290         xmlDocPtr result = import->styleSheet()->locateStylesheetSubResource(parentDoc, uri);
    291         if (result)
    292             return result;
    293     }
    294 
    295     return 0;
    296 }
    297 
    298 void XSLStyleSheet::markAsProcessed()
    299 {
    300     ASSERT(!m_processed);
    301     ASSERT(!m_stylesheetDocTaken);
    302     m_processed = true;
    303     m_stylesheetDocTaken = true;
    304 }
    305 
    306 } // namespace WebCore
    307