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