Recently, work has been done on writing a custom script (Firefox, Chrome and Opera browsers), in which it was necessary to refer to an XML document located in the senior level 2 domain from the 3rd level domain. The work opened the view on some features of browser behavior, especially Operas, the reasons for which are not fully understood. But, since such scripting (reading and writing to XML documents in a superdomain) is sometimes necessary, I would like to share practical results and show open questions.
Conditions of the problem.
There is an HTML page with a non-strict Doctype (transitional), which should receive data from some XML, which, moreover, we cannot change and write our scripts into it (as if it were XHTML). We cannot change the HTML page either, introducing, for example, XML, and manage all actions only through a user script that is launched at the time of onload. The first thought is that nothing is simpler, a classic task with an XMLHttpRequest and rebuilding the DOM to our needs.
Yes, if XML was on the same domain, nothing could be simpler. But the domain is different, so AJAX-request would be possible here, but it is difficult in a redundant way: 1) load some existing plain HTML from a superdomain into a frame, 2) write an AJAX script into it with a script, 3) read AJAX XML, 4 ) read the script from the subdomain received. Why do 2 readings, if you can do one reading per frame of one XML? It is not necessary to load the code into the frame, just to read it is also easier. Therefore, we exclude AJAX as a method that accompanies a complex method and try to make it simple.
Reading the JS data and the DOM document in the superdomain, as you know, can be done by specifying document.domain in the subdomain to be the highest domain:
')
document .domain = '.' ;
Indeed, in FF and Chrome, the work on this scheme occurs without features, and it would be possible not to write about it, but in the Opera there were strange unresolved problems (maybe from XML?), Which were simply bypassed, but they are experimentally observed. About them and some side effects in Firefox - this article.
Since the user scripts, IE in the study had nothing to do.
A working example of the script can be viewed in Firefox + GreeaseMonkey, Chrome or Opera, by installing the script described
in the article (latest version 1.3).
The process of the script.
Let's talk only about the essence - about the part of the script that relates to the issue of cross-domain access.
We completely abandoned AJAX on XMLHttp, and now we need to make a frame into which the XML is loaded.
if (! document .getElementsByName( 'ifr' ).length){ //
var ifr= document .createElement( 'iframe' );
ifr.setAttribute( 'name' , 'ifr' );
ifr.src = 'http://habrahabr.ru/api/profile/' +username+ '/' ;
ifr.style.display= 'none' ;
document .body.appendChild(ifr);
}
XML has the following structure:
<? xml version ="1.0" ? >
< habrauser >
< login > spmbt </ login >
< karma > 24 </ karma >
< rating > 59.3 </ rating >
< ratingPosition > 1038 </ ratingPosition >
</ habrauser >
Then we turn on the counter of periodic attempts to read the XML structure, because the onload event in a foreign frame cannot be caught. (Actually, in the Opera it is possible, the code and explanations are lower, but read samples gave even worse results, about them later.)
win.habrKarmView.ii=20; //
win.habrKarmView.ww = setInterval(showValue, 300);
This is where the fun begins. First, in order not to have errors, it is necessary, as a sapper, to check the tree step by step (of course, you can also catch errors in
try-catch ).
var f = document .getElementsByName( 'ifr' );
if (f && f[0] && f[0].contentDocument && f[0].contentDocument.getElementsByTagName( 'login' )
&& f[0].contentDocument.getElementsByTagName( 'login' )[0]
&& f[0].contentDocument.getElementsByTagName( 'karma' )[0]){
if ( (f[0].contentDocument.u == username || !f[0].contentDocument.u) && self.opera
|| !self.opera && f[0].contentDocument.getElementsByTagName( 'login' )[0].childNodes[0].nodeValue == username ){
... showValue - XML ...
}
}
Why did the second
if statement have to separate the “Opera” and the non-Opera parts? Because at Opera we were able to write the variable
u in the XML document.
if (self.opera) document .getElementsByName( 'ifr' )[0].contentDocument.u = username;
In FF, they could not do this (why is the
question open ), but this was not very desirable, because there is the second part of the condition: a long expression reading a node in the tag is the same username that was read in FF / Chrome without problems. What kind of problems were in the Opera?
The problems (
question two ) were strange and poorly explained. Trying to read the login in the second way, according to the nodes, and not recorded in advance, they got the existence of a node with login, getElementsByTagName ('login') [0], but the absence of .childNodes - text in the login. Those. as if xml was kind
< habrauser >
< login />
< karma > 24 </ karma >
< rating > 59.3 </ rating >
< ratingPosition > 1038 </ ratingPosition >
</ habrauser >
Neither delaying nor reading type dances getElementsByTagName ('habrauser') [0] .childNodes [1] ([1] - because text nodes are at the place of line breaks) did not help. <login /> seemed empty from the point of view of the Opera. Why - the second open question. (In the frame it was non-empty and was seen if not writing ifr.style.display = 'none';.)
Despite the incomprehensibility of the behavior of the first node, we had to leave the script for Opera in this form - by the coincidence of workable alternatives, we still received a substitute - the variable
u in the document. But the decision in general, if
<karma> were the first, would be an impossible task for the Opera (if solved by this way).
Finally, the
third question and feature of the Opera and FF.
In the document “Web Technologies for Opera Web Applications” I peeped a hack to connect onload to the frame. Interestingly, the look of the DOM document seen at the time of onload was even worse. Not seen the node, not only with the login, but with karma. An effect such as inaccurate onload moment selection is obtained, but not quite so - the login [0] .firstChild node does not appear for a long time, if not to say that always. What to do with it and how to avoid it? Maybe Opera is “choked” in a long line of checks of nodes and it is necessary to do them somehow differently? No one came across this situation?
How will see an arbitrary document in the Opera through nodes - a theoretical question. So far there is no desire to answer him, because the very meaning of what is happening is incomprehensible, therefore there is no place to predict behavior and probe the results.
Useful knowledge and conclusions.
1. Opera can write new variables to the superdomain in the frame via contentDocument and contentWindow. Firefox does not know how, but it does not give an error through contentWindow - just undefined. (The frame call is in the document form: document.getElementsByName ('ifr'), so it would be more correct to call through the contentDocument, but it is interesting that FF does not work, yielding
uncaught error .)
if (self.opera)
document .getElementsByName( 'ifr' )[0].contentDocument.u = username;
2. Opera knows how to make a hack for onload of someone else's document, so that Firefox does not know how to execute code in a subdomain after generating a document. (Although Opera has little use for XML.)
var ifr= document .createElement( 'iframe' );
ifr.src = 'http://habrahabr.ru/api/profile/' +username+ '/' ;
ifr.style.display= 'none' ;
ifr.onload = function (){
...;
}
document .body.appendChild(ifr);
3. With Opera or the code for checking the existence of a node, which leads to the unreadable effect of the first text node, when it exists, it is necessary to figure out how to properly address the nodes, is this the influence of cross-community, did other developers encounter similar effects?