Solved

Safari's transformToDocument() and xsl:import

Posted on 2009-04-09
18
5,378 Views
Last Modified: 2013-12-07
Is it possible to make Safari browser's XSLTProcessor properly execute the transformToDocument() method when the XSLT template has an xsl:import element? In my case the method returns nothing.  
I have attached a HTML snippet  for testing as well as  the XML and XSLT files it refers to.

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
 
<SCRIPT type='text/javascript'>
 
function testXSL() {
   try {
        var req = new XMLHttpRequest();
        req.open("GET", "empty.xsl", false);
        req.send(null);
        var  xsl = req.responseXML;
        if( !xsl ) {
            alert("Unable to load xsl!");
            return false;
        }
        alert(new XMLSerializer().serializeToString( xsl.documentElement ));
 
        var xsl_processor = new XSLTProcessor();
        xsl_processor.importStylesheet( xsl );
 
 
        req.open("GET", "empty.xml", false);
        req.send(null);
        var  xml = req.responseXML;
        if( !xml ) {
            alert("Unable to load xml!");
            return false;
        }
 
 
        var result_doc = xsl_processor.transformToDocument( xml );
        if( !result_doc ) {
            alert("Unable to transform!");
            return false;
        }
        alert(new XMLSerializer().serializeToString(result_doc.documentElement));
    }
    catch( e ) {
        alert("XSLT error: "+e.message + "(" + e + ")");
        return false;
    }
}
 
//</SCRIPT>
</HEAD>
<BODY>
<input type="button" value="test XSL" onclick="testXSL();"/>
</BODY>
</HTML>
 
--- empty.xsl ---
 
<?xml version="1.0"?>
<xsl:stylesheet 
	xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
	version="1.0">
	<xsl:import href="utility_template.xsl"/>
</xsl:stylesheet>
 
--- empty.xml ---
 
<root></root>

Open in new window

0
Comment
Question by:zc2
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 9
  • 8
18 Comments
 
LVL 39

Expert Comment

by:abel
ID: 24125952
Quite recently, a report of a bug/issue with xsl:include was posted on the Google Chrome site: http://code.google.com/p/chromium/issues/detail?id=8441. Since Chrome uses the same processor as Safari, it seems related. According to the issuer, the problem is a permissions/security issue which blocks the URL from being retrieved.

Quite a while ago I posted a fix for a similar issue for the Sarissa cross-browser XSLT library, which was related to relative paths and *.js files, where IE and other browsers interpreted the base path differently. If a path problem may be the cause of your issue, you can test that by applying an absolute path to see if that makes a difference.

-- Abel --
0
 
LVL 18

Author Comment

by:zc2
ID: 24127185
No, absolute path brings the same result.
Is there any possible workaround?
0
 
LVL 39

Expert Comment

by:abel
ID: 24127237
that's where the problem comes in. On the XSLT list this matter has been the subject of debate a couple of times. The problem is, you cannot simply create some function that combines the stylesheets and then runs the complete stylesheet. That is because of the import precedence complexities (they would fade away as soon as you combine them) and add to that the apply-imports calls, which wouldn't mean much anymore.

Unfortunately it has been found that expressing xsl:import in one XSLT is actually impossible, apart from the most trivial cases: parse your stylesheet and apply an <xsl:copy-of select="document(xls-import/@href)/*/xsl:template" /> (use an xsl ns alias) to every xsl:import statement, recursively. The resulting XSLT XML document can then be used for XSLT processing. But you'll loose much of the xsl:import functionality this way.

If I have some time tomorrow I'll try to dig up some comments of Chrome/Safari developers on the subject. Maybe they've some timeline for a fix, but so far I haven't seen much in that direction.

-- Abel --
0
Get 15 Days FREE Full-Featured Trial

Benefit from a mission critical IT monitoring with Monitis Premium or get it FREE for your entry level monitoring needs.
-Over 200,000 users
-More than 300,000 websites monitored
-Used in 197 countries
-Recommended by 98% of users

 
LVL 18

Author Comment

by:zc2
ID: 24132336
Thank you for the advise. But, not matter how hard I try, the document() function returns an empty document.
0
 
LVL 39

Expert Comment

by:abel
ID: 24133436
just a question intermittently, but are the urls relative to the current url? Are they higher or lower up the path hierarchy? Can you give an example of your urls from the main .html (or .jsp/.aspx), the .js holding the XSLT transform code (if any) and the *.xslt files (incl the imported ones)?

It looks very much like a similar issue that I had before, it would help if I knew how exactly you are building the xslt and from where (and yes, I saw the code above).
0
 
LVL 18

Author Comment

by:zc2
ID: 24139013
<a>(((((<item>2222</item>))))</a>
xsl-document.tar.txt
0
 
LVL 18

Author Comment

by:zc2
ID: 24139080
I'm sorry, sent the last comment before actually write it.

What you see in the previous comment is the result I see in a firefox browser.
In a webkit based browser (Chrome, Safari 4) the result is only

<a>((((())))</a>

The attached file in the previous comment is a .tar archive with the files I use for the testing.
testxsl_doc.htm -  main file, open it in the browser, then click the button.
doc.xsl - an XSLT using the document() function
empty.xml - empty xml which document() tries to access
0.xml - another empty xml to make the transformation with.
0
 
LVL 39

Expert Comment

by:abel
ID: 24147076
I started out getting other results, but now I'm on the same page as you. Just for argument's sake, I put some screenshots here to make sure we all agree on the differences.

ScreenShot209.png
ScreenShot210.png
ScreenShot208.png
ScreenShot207.png
ScreenShot206.png
0
 
LVL 39

Accepted Solution

by:
abel earned 500 total points
ID: 24147710
The end-result of my research is not really going to make you happy. Apparently, Chrome and Safari are doing the same as Opera did until version 9.5: saying they support XSLT, but meanwhile not supporting document() or xsl:import/include. It is not a rights issue. While Opera finally made it with version 9.5 to include external documents and xsl:import/include, Chrome and Safari (by means of Webkit) have not.

The code below is part of WebKit and was checked in in October 2008 and clearly shows that there has been support planned for xsl:import/include. So far I still hope it is some dark security setting somewhere, but no reference thereof anywhere. Remember that libxslt is the processor used by Webkit, and that libxslt supports xsl:import/include and the document function.

Sorry to give you bad news :S.
Here's the piece of the code where the imports are (should be) handled (from http://src.chromium.org/viewvc/chrome/trunk/deps/third_party/WebKit/WebCore/xml/XSLStyleSheet.cpp):

-- Abel --

void XSLStyleSheet::loadChildSheets()
{
    if (!document())
        return;
    
    xmlNodePtr stylesheetRoot = document()->children;
    
    // Top level children may include other things such as DTD nodes, we ignore those.
    while (stylesheetRoot && stylesheetRoot->type != XML_ELEMENT_NODE)
        stylesheetRoot = stylesheetRoot->next;
    
    if (m_embedded) {
        // We have to locate (by ID) the appropriate embedded stylesheet element, so that we can walk the 
        // import/include list.
        xmlAttrPtr idNode = xmlGetID(document(), (const xmlChar*)(href().utf8().data()));
        if (!idNode)
            return;
        stylesheetRoot = idNode->parent;
    } else {
        // FIXME: Need to handle an external URI with a # in it.  This is a pretty minor edge case, so we'll deal
        // with it later.
    }
    
    if (stylesheetRoot) {
        // Walk the children of the root element and look for import/include elements.
        // Imports must occur first.
        xmlNodePtr curr = stylesheetRoot->children;
        while (curr) {
            if (curr->type != XML_ELEMENT_NODE) {
                curr = curr->next;
                continue;
            }
            if (IS_XSLT_ELEM(curr) && IS_XSLT_NAME(curr, "import")) {
                xmlChar* uriRef = xsltGetNsProp(curr, (const xmlChar*)"href", XSLT_NAMESPACE);                
                loadChildSheet(DeprecatedString::fromUtf8((const char*)uriRef));
                xmlFree(uriRef);
            } else
                break;
            curr = curr->next;
        }
 
        // Now handle includes.
        while (curr) {
            if (curr->type == XML_ELEMENT_NODE && IS_XSLT_ELEM(curr) && IS_XSLT_NAME(curr, "include")) {
                xmlChar* uriRef = xsltGetNsProp(curr, (const xmlChar*)"href", XSLT_NAMESPACE);
                loadChildSheet(DeprecatedString::fromUtf8((const char*)uriRef));
                xmlFree(uriRef);
            }
            curr = curr->next;
        }
    }
}

Open in new window

0
 
LVL 18

Author Comment

by:zc2
ID: 24148215
Thank you for the great work you did. It seems I need just to disable the ajax functionality for that kind of browsers at least for a while.
If there any news how to make the xsl:import work, please let me know, I'll very appreciate that.
0
 
LVL 39

Expert Comment

by:abel
ID: 24148533
glad I could be of some help :)

See the second post here: http://ajaxandxml.blogspot.com/search/label/Chrome which is about disabling XSLT for chrome.

I just downloaded the Chrome source to see why it is not executing the code above. I'll let you know if I find something useful. I checked the (advanced) security settings and widened them, but that did not yield any results.
0
 
LVL 18

Author Comment

by:zc2
ID: 24148688
Thank you. But is there simpler way to detect a webkit based browser?
0
 
LVL 75

Expert Comment

by:Michel Plungjan
ID: 24148742
if (navigator.userAgent.toLowerCase().indexOf('webkit')!=-1) alert('Go upgrade to an XSLT supporting browser')
0
 
LVL 18

Author Comment

by:zc2
ID: 24148938
:)
Thank you.
0
 
LVL 39

Expert Comment

by:abel
ID: 24168688
Hi zc2,

I know you closed this question, but you asked me to update if I knew something more. These days I played a bit with the source code of Chrome (quite a bit of a piece of code, btw) and tried to find the point of error and/or a workaround.

Unfortunately, the place in the code where XSLT is resolving external documents (id. for xsl:import/include) does never run. It uses an if-statement that is supposed to check the validity of the URL, but because of negligence of the programmers, they forgot to give it a context-frame, which is necessary for that piece of code to return true (i.e., valid).

End of the story: it is a bug, and quite a major one. They do have all the code there to make it work, but due to this bug it doesn't work. I changed the code a bit and managed to get it working without too much efforts. Here's a screenshot of a beta-build with modified source that runs your code correctly:

ScreenShot213.png
0
 
LVL 18

Author Comment

by:zc2
ID: 24168863
Great job! You're really a genius!!
Did you submit the issue to developers?
If they going to fix it both in Safari and Chromium, I'm willing to wait until the new version will come.
Thank you again very much!
0
 
LVL 39

Expert Comment

by:abel
ID: 24168976
I don't know enough of the intricacies involved to know whether bullying with these settings and code might have some waterfall effect on something else. Of course I can point them to the problem (and I will) and a possible direction for the solution. The only related bug issue that I could find so far is currently this one, and it only touches at the issue.

-- Abel --
0
 
LVL 18

Author Comment

by:zc2
ID: 24169180
I hope they will respond and fix the bug.
0

Featured Post

[Webinar] How Hackers Steal Your Credentials

Do You Know How Hackers Steal Your Credentials? Join us and Skyport Systems to learn how hackers steal your credentials and why Active Directory must be secure to stop them. Thursday, July 13, 2017 10:00 A.M. PDT

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Browsers only know CSS so your awesome SASS code needs to be translated into normal CSS. Here I'll try to explain what you should aim for in order to take full advantage of SASS.
This article offers some helpful and general tips for safe browsing and online shopping. It offers simple and manageable procedures that help to ensure the safety of one's personal information and the security of any devices.
The viewer will learn how to create and use a small PHP class to apply a watermark to an image. This video shows the viewer the setup for the PHP watermark as well as important coding language. Continue to Part 2 to learn the core code used in creat…
Shows how to create a shortcut to site-search Experts Exchange using Google in the Chrome browser. This eliminates the need to type out site:experts-exchange.com whenever you want to search the site. Launch the Search Engine Menu: In chrome, via you…
Suggested Courses

636 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question