Solved

Safari's transformToDocument() and xsl:import

Posted on 2009-04-09
18
5,158 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
  • 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
 
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
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
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

6 Surprising Benefits of Threat Intelligence

All sorts of threat intelligence is available on the web. Intelligence you can learn from, and use to anticipate and prepare for future attacks.

Join & Write a Comment

Introduction Knockoutjs (Knockout) is a JavaScript framework (Model View ViewModel or MVVM framework).   The main ideology behind Knockout is to control from JavaScript how a page looks whilst creating an engaging user experience in the least …
SSL stands for “Secure Sockets Layer” and an SSL certificate is a critical component to keeping your website safe, secured, and compliant. Any ecommerce website must have an SSL certificate to ensure the safe handling of sensitive information like…
Explain concepts important to validation of email addresses with regular expressions. Applies to most languages/tools that uses regular expressions. Consider email address RFCs: Look at HTML5 form input element (with type=email) regex pattern: T…
The viewer will learn how to dynamically set the form action using jQuery.

707 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

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now