• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
XSLStyleSheet(XSLImportRule * parentRule,const String & originalURL,const KURL & finalURL)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 
XSLStyleSheet(Node * parentNode,const String & originalURL,const KURL & finalURL,bool embedded)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 
~XSLStyleSheet()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 
isLoading() const80 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 
checkLoaded()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 
document()99 xmlDocPtr XSLStyleSheet::document()
100 {
101     if (m_embedded && ownerDocument() && ownerDocument()->transformSource())
102         return (xmlDocPtr)ownerDocument()->transformSource()->platformSource();
103     return m_stylesheetDoc;
104 }
105 
clearDocuments()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 
fetcher()116 ResourceFetcher* XSLStyleSheet::fetcher()
117 {
118     Document* document = ownerDocument();
119     if (!document)
120         return 0;
121     return document->fetcher();
122 }
123 
parseString(const String & source)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 
loadChildSheets()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 
loadChildSheet(const String & href)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 
compileStyleSheet()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 
setParentStyleSheet(XSLStyleSheet * parent)247 void XSLStyleSheet::setParentStyleSheet(XSLStyleSheet* parent)
248 {
249     m_parentStyleSheet = parent;
250 }
251 
ownerDocument()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 
locateStylesheetSubResource(xmlDocPtr parentDoc,const xmlChar * uri)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 
markAsProcessed()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