| 1 | dojo.provide("dojox.io.windowName"); |
|---|
| 2 | // Implements the window.name transport |
|---|
| 3 | |
|---|
| 4 | dojox.io.windowName = { |
|---|
| 5 | send: function(/*String*/ method, /*dojo.__IoArgs*/ args){ |
|---|
| 6 | // summary: |
|---|
| 7 | // Provides secure cross-domain request capability. |
|---|
| 8 | // Sends a request using an iframe (POST or GET) and reads the response through the |
|---|
| 9 | // frame's window.name. |
|---|
| 10 | // |
|---|
| 11 | // method: |
|---|
| 12 | // The method to use to send the request, GET or POST |
|---|
| 13 | // |
|---|
| 14 | // args: |
|---|
| 15 | // See dojo.xhr |
|---|
| 16 | // |
|---|
| 17 | // args.authElement: DOMNode? |
|---|
| 18 | // By providing an authElement, this indicates that windowName should use the |
|---|
| 19 | // authorized window.name protocol, relying on |
|---|
| 20 | // the loaded XD resource to return to the provided return URL on completion |
|---|
| 21 | // of authorization/authentication. The provided authElement will be used to place |
|---|
| 22 | // the iframe in, so the user can interact with the server resource for authentication |
|---|
| 23 | // and/or authorization to access the resource. |
|---|
| 24 | // |
|---|
| 25 | // args.onAuthLoad: Function? |
|---|
| 26 | // When using authorized access to resources, this function will be called when the |
|---|
| 27 | // authorization page has been loaded. (When authorization is actually completed, |
|---|
| 28 | // the deferred callback function is called with the result). The primary use for this |
|---|
| 29 | // is to make the authElement visible to the user once the resource has loaded |
|---|
| 30 | // (this can be preferable to showing the iframe while the resource is loading |
|---|
| 31 | // since it may not require authorization, it may simply return the resource). |
|---|
| 32 | // |
|---|
| 33 | // description: |
|---|
| 34 | // In order to provide a windowname transport accessible resources/web services, a server |
|---|
| 35 | // should check for the presence of a parameter window.name=true and if a request includes |
|---|
| 36 | // such a parameter, it should respond to the request with an HTML |
|---|
| 37 | // document that sets it's window.name to the string that is to be |
|---|
| 38 | // delivered to the client. For example, if a client makes a window.name request like: |
|---|
| 39 | // | http://othersite.com/greeting?windowname=true |
|---|
| 40 | // And server wants to respond to the client with "Hello", it should return an html page: |
|---|
| 41 | // | <html><script type="text/javascript"> |
|---|
| 42 | // | window.name="Hello"; |
|---|
| 43 | // | </script></html> |
|---|
| 44 | // One can provide XML or JSON data by simply quoting the data as a string, and parsing the data |
|---|
| 45 | // on the client. |
|---|
| 46 | // If you use the authorization window.name protocol, the requester should include an |
|---|
| 47 | // authElement element in the args, and a request will be created like: |
|---|
| 48 | // | http://othersite.com/greeting?windowname=auth |
|---|
| 49 | // And the server can respond like this: |
|---|
| 50 | // | <html><script type="text/javascript"> |
|---|
| 51 | // | var loc = window.name; |
|---|
| 52 | // | authorizationButton.onclick = function(){ |
|---|
| 53 | // | window.name="Hello"; |
|---|
| 54 | // | location = loc; |
|---|
| 55 | // | }; |
|---|
| 56 | // | </script></html> |
|---|
| 57 | // When using windowName from a XD Dojo build, make sure to set the |
|---|
| 58 | // dojo.dojoBlankHtmlUrl property to a local URL. |
|---|
| 59 | args.url += (args.url.match(/\?/) ? '&' : '?') + "windowname=" + (args.authElement ? "auth" : true); // indicate our desire for window.name communication |
|---|
| 60 | var authElement = args.authElement; |
|---|
| 61 | var cleanup = function(result){ |
|---|
| 62 | try{ |
|---|
| 63 | // we have to do this to stop the wait cursor in FF |
|---|
| 64 | var innerDoc = dfd.ioArgs.frame.contentWindow.document; |
|---|
| 65 | innerDoc.write(" "); |
|---|
| 66 | innerDoc.close(); |
|---|
| 67 | }catch(e){} |
|---|
| 68 | (authElement || dojo.body()).removeChild(dfd.ioArgs.outerFrame); // clean up |
|---|
| 69 | return result; |
|---|
| 70 | } |
|---|
| 71 | var dfd = dojo._ioSetArgs(args,cleanup,cleanup,cleanup); |
|---|
| 72 | if(args.timeout){ |
|---|
| 73 | setTimeout(function(){ |
|---|
| 74 | if(dfd.fired == -1){ |
|---|
| 75 | dfd.callback(new Error("Timeout")); |
|---|
| 76 | } |
|---|
| 77 | }, |
|---|
| 78 | args.timeout |
|---|
| 79 | ); |
|---|
| 80 | } |
|---|
| 81 | var self = dojox.io.windowName; |
|---|
| 82 | if(dojo.body()){ |
|---|
| 83 | // the DOM is ready |
|---|
| 84 | self._send(dfd, method, authElement, args.onAuthLoad); |
|---|
| 85 | }else{ |
|---|
| 86 | // we will wait for the DOM to be ready to proceed |
|---|
| 87 | dojo.addOnLoad(function(){ |
|---|
| 88 | self._send(dfd, method, authElement, args.onAuthLoad); |
|---|
| 89 | }); |
|---|
| 90 | } |
|---|
| 91 | return dfd; |
|---|
| 92 | }, |
|---|
| 93 | _send: function(dfd, method, authTarget, onAuthLoad){ |
|---|
| 94 | |
|---|
| 95 | var ioArgs = dfd.ioArgs; |
|---|
| 96 | var frameNum = dojox.io.windowName._frameNum++; |
|---|
| 97 | var sameDomainUrl = (dojo.config.dojoBlankHtmlUrl||dojo.config.dojoCallbackUrl||dojo.moduleUrl("dojo", "resources/blank.html")) + "#" + frameNum; |
|---|
| 98 | var frameName = new dojo._Url(window.location, sameDomainUrl); |
|---|
| 99 | var doc = dojo.doc; |
|---|
| 100 | var frameContainer = authTarget || dojo.body(); |
|---|
| 101 | function styleFrame(frame){ |
|---|
| 102 | frame.style.width="100%"; |
|---|
| 103 | frame.style.height="100%"; |
|---|
| 104 | frame.style.border="0px"; |
|---|
| 105 | } |
|---|
| 106 | if(dojo.isMoz && ![].reduce){ |
|---|
| 107 | // FF2 allows unsafe sibling frame modification, |
|---|
| 108 | // the fix for this is to create nested frames with getters and setters to protect access |
|---|
| 109 | var outerFrame = doc.createElement("iframe"); |
|---|
| 110 | styleFrame(outerFrame); |
|---|
| 111 | if(!authTarget){ |
|---|
| 112 | outerFrame.style.display='none'; |
|---|
| 113 | } |
|---|
| 114 | frameContainer.appendChild(outerFrame); |
|---|
| 115 | |
|---|
| 116 | var firstWindow = outerFrame.contentWindow; |
|---|
| 117 | doc = firstWindow.document; |
|---|
| 118 | doc.write("<html><body margin='0px'><iframe style='width:100%;height:100%;border:0px' name='protectedFrame'></iframe></body></html>"); |
|---|
| 119 | doc.close(); |
|---|
| 120 | var secondWindow = firstWindow[0]; |
|---|
| 121 | firstWindow.__defineGetter__(0,function(){}); |
|---|
| 122 | firstWindow.__defineGetter__("protectedFrame",function(){}); |
|---|
| 123 | doc = secondWindow.document; |
|---|
| 124 | doc.write("<html><body margin='0px'></body></html>"); |
|---|
| 125 | doc.close(); |
|---|
| 126 | frameContainer = doc.body; |
|---|
| 127 | } |
|---|
| 128 | |
|---|
| 129 | var frame = ioArgs.frame = frame = doc.createElement(dojo.isIE ? '<iframe name="' + frameName + '" onload="dojox.io.windowName['+frameNum+']()">' : 'iframe'); |
|---|
| 130 | styleFrame(frame); |
|---|
| 131 | ioArgs.outerFrame = outerFrame = outerFrame || frame; |
|---|
| 132 | if(!authTarget){ |
|---|
| 133 | outerFrame.style.display='none'; |
|---|
| 134 | } |
|---|
| 135 | var state = 0; |
|---|
| 136 | function getData(){ |
|---|
| 137 | var data = frame.contentWindow.name; |
|---|
| 138 | if(typeof data == 'string'){ |
|---|
| 139 | if(data != frameName){ |
|---|
| 140 | state = 2; // we are done now |
|---|
| 141 | dfd.ioArgs.hash = frame.contentWindow.location.hash; |
|---|
| 142 | dfd.callback(data); |
|---|
| 143 | } |
|---|
| 144 | } |
|---|
| 145 | } |
|---|
| 146 | dojox.io.windowName[frameNum] = frame.onload = function(){ |
|---|
| 147 | try{ |
|---|
| 148 | if(!dojo.isMoz && frame.contentWindow.location =='about:blank'){ |
|---|
| 149 | // opera and safari will do an onload for about:blank first, we can ignore this first onload |
|---|
| 150 | return; |
|---|
| 151 | } |
|---|
| 152 | }catch(e){ |
|---|
| 153 | // if we are in the target domain, frame.contentWindow.location will throw an ignorable error |
|---|
| 154 | } |
|---|
| 155 | if(!state){ |
|---|
| 156 | // we have loaded the target resource, now time to navigate back to our domain so we can read the frame name |
|---|
| 157 | state=1; |
|---|
| 158 | if(authTarget){ |
|---|
| 159 | // call the callback so it can make it visible |
|---|
| 160 | if(onAuthLoad){ |
|---|
| 161 | onAuthLoad(); |
|---|
| 162 | } |
|---|
| 163 | }else{ |
|---|
| 164 | // we are doing a synchronous capture, go directly to our same domain URL and retrieve the resource |
|---|
| 165 | frame.contentWindow.location = sameDomainUrl; |
|---|
| 166 | } |
|---|
| 167 | } |
|---|
| 168 | // back to our domain, we should be able to access the frame name now |
|---|
| 169 | try{ |
|---|
| 170 | if(state<2){ |
|---|
| 171 | getData(); |
|---|
| 172 | } |
|---|
| 173 | } |
|---|
| 174 | catch(e){ |
|---|
| 175 | } |
|---|
| 176 | |
|---|
| 177 | }; |
|---|
| 178 | frame.name = frameName; |
|---|
| 179 | if(method.match(/GET/i)){ |
|---|
| 180 | // if it is a GET we can just the iframe our src url |
|---|
| 181 | dojo._ioAddQueryToUrl(ioArgs); |
|---|
| 182 | frame.src = ioArgs.url; |
|---|
| 183 | frameContainer.appendChild(frame); |
|---|
| 184 | if(frame.contentWindow){ |
|---|
| 185 | frame.contentWindow.location.replace(ioArgs.url); |
|---|
| 186 | } |
|---|
| 187 | }else if(method.match(/POST/i)){ |
|---|
| 188 | // if it is a POST we will build a form to post it |
|---|
| 189 | frameContainer.appendChild(frame); |
|---|
| 190 | var form = dojo.doc.createElement("form"); |
|---|
| 191 | dojo.body().appendChild(form); |
|---|
| 192 | var query = dojo.queryToObject(ioArgs.query); |
|---|
| 193 | for(var i in query){ |
|---|
| 194 | var values = query[i]; |
|---|
| 195 | values = values instanceof Array ? values : [values]; |
|---|
| 196 | for(var j = 0; j < values.length; j++){ |
|---|
| 197 | // create hidden inputs for all the parameters |
|---|
| 198 | var input = doc.createElement("input"); |
|---|
| 199 | input.type = 'hidden'; |
|---|
| 200 | input.name = i; |
|---|
| 201 | input.value = values[j]; |
|---|
| 202 | form.appendChild(input); |
|---|
| 203 | } |
|---|
| 204 | } |
|---|
| 205 | form.method = 'POST'; |
|---|
| 206 | form.action = ioArgs.url; |
|---|
| 207 | form.target = frameName;// connect the form to the iframe |
|---|
| 208 | |
|---|
| 209 | form.submit(); |
|---|
| 210 | form.parentNode.removeChild(form); |
|---|
| 211 | }else{ |
|---|
| 212 | throw new Error("Method " + method + " not supported with the windowName transport"); |
|---|
| 213 | } |
|---|
| 214 | if(frame.contentWindow){ |
|---|
| 215 | frame.contentWindow.name = frameName; // IE likes it afterwards |
|---|
| 216 | } |
|---|
| 217 | }, |
|---|
| 218 | _frameNum: 0 |
|---|
| 219 | |
|---|
| 220 | } |
|---|