Go Premium for a chance to win a PS4. Enter to Win

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 5466
  • Last Modified:

Safari's transformToDocument() and xsl:import

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
zc2
Asked:
zc2
  • 9
  • 8
1 Solution
 
abelCommented:
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
 
zc2Author Commented:
No, absolute path brings the same result.
Is there any possible workaround?
0
 
abelCommented:
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
Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

 
zc2Author Commented:
Thank you for the advise. But, not matter how hard I try, the document() function returns an empty document.
0
 
abelCommented:
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
 
zc2Author Commented:
<a>(((((<item>2222</item>))))</a>
xsl-document.tar.txt
0
 
zc2Author Commented:
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
 
abelCommented:
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
 
abelCommented:
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
 
zc2Author Commented:
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
 
abelCommented:
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
 
zc2Author Commented:
Thank you. But is there simpler way to detect a webkit based browser?
0
 
Michel PlungjanIT ExpertCommented:
if (navigator.userAgent.toLowerCase().indexOf('webkit')!=-1) alert('Go upgrade to an XSLT supporting browser')
0
 
zc2Author Commented:
:)
Thank you.
0
 
abelCommented:
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
 
zc2Author Commented:
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
 
abelCommented:
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
 
zc2Author Commented:
I hope they will respond and fix the bug.
0

Featured Post

Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

  • 9
  • 8
Tackle projects and never again get stuck behind a technical roadblock.
Join Now