Changeset 12181

Show
Ignore:
Timestamp:
01/27/08 22:21:23 (12 months ago)
Author:
jburke
Message:

Refs #5699. Safari 3 is now working, and the general approach is much simpler than before. No need for a third iframe in IE7 now. Still need to fix Opera, but that will be hopefully fairly straight-forward. \!strict

Location:
dojox/trunk/io/proxy
Files:
3 modified

Legend:

Unmodified
Added
Removed
  • dojox/trunk/io/proxy/xip.js

    r12124 r12181  
    1212        //Usage of XHR IFrame Proxying does not work from local disk in Safari. 
    1313 
    14         xipClientUrl: dojo.config["xipClientUrl"] || dojo.moduleUrl("dojox.io.proxy", "xip_client.html"), 
    15  
     14        /* 
     15        This code is really focused on just sending one complete request to the server, and 
     16        receiving one complete response per iframe. The code does not expect to reuse iframes for multiple XHR request/response 
     17        sequences. This might be reworked later if performance indicates a need for it. 
     18         
     19        xip fragment identifier/hash values have the form: 
     20        #id:cmd:realEncodedMessage 
     21 
     22        id: some ID that should be unique among message fragments. No inherent meaning, 
     23                just something to make sure the hash value is unique so the message 
     24                receiver knows a new message is available. 
     25                 
     26        cmd: command to the receiver. Valid values are: 
     27                 - init: message used to init the frame. Sent as the first URL when loading 
     28                         the page. Contains some config parameters. 
     29                 - loaded: the remote frame is loaded. Only sent from xip_client.html to this module. 
     30                 - ok: the message that this page sent was received OK. The next message may 
     31                       now be sent. 
     32                 - start: the start message of a block of messages (a complete message may 
     33                          need to be segmented into many messages to get around the limitiations 
     34                          of the size of an URL that a browser accepts. 
     35                 - part: indicates this is a part of a message. 
     36                 - end: the end message of a block of messages. The message can now be acted upon. 
     37                        If the message is small enough that it doesn't need to be segmented, then 
     38                        just one hash value message can be sent with "end" as the command. 
     39         
     40        To reassemble a segmented message, the realEncodedMessage parts just have to be concatenated 
     41        together. 
     42        */ 
     43 
     44        xipClientUrl: ((dojo.config || djConfig)["xipClientUrl"]) || dojo.moduleUrl("dojox.io.proxy", "xip_client.html"), 
     45 
     46 
     47        //MSIE has the lowest limit for URLs with fragment identifiers, 
     48        //at around 4K. Choosing a slightly smaller number for good measure. 
     49        urlLimit: 4000, 
     50 
     51        _callbackName: (dojox._scopeName || "dojox") + ".io.proxy.xip.fragmentReceived", 
    1652        _state: {}, 
    1753        _stateIdCounter: 0, 
    18  
    19         needFrameRecursion: function(){ 
    20                 return (dojo.isIE >= 7); 
    21         }, 
    22  
    23         send: function(facade){          
     54        _isWebKit: navigator.userAgent.indexOf("WebKit") != -1, 
     55 
     56        send: function(facade){ 
    2457                var stateId = "XhrIframeProxy" + (this._stateIdCounter++); 
    2558                facade._stateId = stateId; 
    26  
    27  
    28                 var frameUrl = this.xipClientUrl + "#0:init:id=" + stateId + "&server="  
    29                         + encodeURIComponent(facade._ifpServerUrl) + "&fr=false"; 
    30                 if(this.needFrameRecursion()){ 
    31                         //IE7 hack. Need to load server URL, and have that load the xip_client.html. 
    32                         //Also, this server URL needs to different from the one eventually loaded by xip_client.html 
    33                         //Otherwise, IE7 will not load it. Funky. 
    34                         var fullClientUrl = window.location.href.split("#")[0].split("?")[0]; 
    35                         if((this.xipClientUrl + "").charAt(0) == "/"){ 
    36                                 var endIndex = fullClientUrl.indexOf("://"); 
    37                                 endIndex = fullClientUrl.indexOf("/", endIndex + 3); 
    38                                 fullClientUrl = fullClientUrl.substring(0, endIndex); 
    39                         }else{ 
    40                                 fullClientUrl = fullClientUrl.substring(0, fullClientUrl.lastIndexOf("/") + 1); 
    41                         } 
    42                         fullClientUrl += this.xipClientUrl; 
    43                  
    44                         var serverUrl = facade._ifpServerUrl 
    45                                 + (facade._ifpServerUrl.indexOf("?") == -1 ? "?" : "&") + "dojo.fr=1"; 
    46  
    47                         frameUrl = serverUrl + "#0:init:id=" + stateId + "&client="  
    48                                 + encodeURIComponent(fullClientUrl) + "&fr=" + this.needFrameRecursion(); //fr is for Frame Recursion 
    49                 } 
    50  
    51                 this._state[stateId] = { 
    52                         facade: facade, 
    53                         stateId: stateId, 
    54                         clientFrame: dojo.io.iframe.create(stateId, "", frameUrl) 
    55                 }; 
    56                  
    57                 return stateId; 
    58         }, 
    59          
     59                var url = this.xipClientUrl; 
     60 
     61                //Make sure we are not dealing with javascript urls, just to be safe. 
     62                if(!url.split(":")[0].match(/javascript/i) && !facade._ifpServerUrl.split(":")[0].match(/javascript/i)){ 
     63                        //Make xip_client a full URL. 
     64                        var colonIndex = url.indexOf(":"); 
     65                        var slashIndex = url.indexOf("/"); 
     66                        if(colonIndex == -1 || slashIndex < colonIndex){ 
     67                                //No colon or we are starting with a / before a colon, so we need to make a full URL. 
     68                                var loc = window.location.href; 
     69                                if(slashIndex == 0){ 
     70                                        //Have a full path, just need the domain. 
     71                                        url = loc.substring(0, loc.indexOf("/", 9)) + url; //Using 9 to get past http(s):// 
     72                                }else{ 
     73                                        url = loc.substring(0, (loc.lastIndexOf("/") + 1)) + url; 
     74                                } 
     75                        } 
     76         
     77                        var frameUrl = facade._ifpServerUrl + "#0:init:id=" + stateId + "&client="  
     78                                + encodeURIComponent(url) + "&callback=" + encodeURIComponent(this._callbackName); 
     79         
     80                        this._state[stateId] = { 
     81                                facade: facade, 
     82                                stateId: stateId, 
     83                                clientFrame: dojo.io.iframe.create(stateId, "", frameUrl), 
     84                                isSending: false, 
     85                                serverUrl: facade._ifpServerUrl, 
     86                                requestData: null, 
     87                                responseMessage: "", 
     88                                requestParts: [], 
     89                                idCounter: 1, 
     90                                partIndex: 0, 
     91                                serverWindow: null 
     92                        }; 
     93         
     94                        return stateId; 
     95                } 
     96        }, 
     97 
    6098        receive: function(/*String*/stateId, /*String*/urlEncodedData){ 
    6199                /* urlEncodedData should have the following params: 
     
    93131                        if(contentType){ 
    94132                                var mimeType = contentType.split(";")[0]; 
    95                                 if(mimeType == "application/xml" || mimeType == "text/xml"){ 
     133                                if(mimeType.indexOf("application/xml") == 0 || mimeType.indexOf("text/xml") == 0){ 
    96134                                        facade.responseXML = dojox.data.dom.createDocument(response.responseText, contentType); 
    97135                                } 
     
    103141        }, 
    104142 
    105         clientFrameLoaded: function(/*String*/stateId){ 
     143        frameLoaded: function(/*String*/stateId){ 
    106144                var state = this._state[stateId]; 
    107145                var facade = state.facade; 
    108146 
    109                 var clientWindow = this.needFrameRecursion() ? window.open("", state.stateId + "_clientEndPoint") : state.clientFrame.contentWindow; 
     147                var clientWindow = state.clientFrame.contentWindow; 
    110148 
    111149                var reqHeaders = []; 
     
    127165                } 
    128166 
    129                 clientWindow.send(dojo.objectToQuery(requestData)); 
     167                this.sendRequest(stateId, dojo.objectToQuery(requestData)); 
    130168        }, 
    131169         
     
    147185                        return dojox.io.proxy.xip._xhrObjOld.apply(dojo, arguments); 
    148186                } 
     187        }, 
     188         
     189        //**** State-bound methods **** 
     190        sendRequest: function(stateId, encodedData){ 
     191                var state = this._state[stateId]; 
     192                if(!state.isSending){ 
     193                        state.isSending = true; 
     194 
     195                        state.requestData = encodedData || ""; 
     196 
     197                        //Get a handle to the server iframe. 
     198                        state.serverWindow = frames[state.stateId]; 
     199                        if (!state.serverWindow){ 
     200                                state.serverWindow = document.getElementById(state.stateId).contentWindow; 
     201                        } 
     202                         
     203                        if(state.serverWindow.contentWindow){ 
     204                                state.serverWindow = state.serverWindow.contentWindow; 
     205                        } 
     206 
     207                        this.sendRequestStart(stateId); 
     208                } 
     209        }, 
     210 
     211        sendRequestStart: function(stateId){ 
     212                //Break the message into parts, if necessary. 
     213                var state = this._state[stateId]; 
     214                state.requestParts = []; 
     215                var reqData = state.requestData; 
     216                var urlLength = state.serverUrl.length; 
     217                var partLength = this.urlLimit - urlLength; 
     218                var reqIndex = 0; 
     219 
     220                while((reqData.length - reqIndex) + urlLength > this.urlLimit){ 
     221                        var part = reqData.substring(reqIndex, reqIndex + partLength); 
     222                        //Safari will do some extra hex escaping unless we keep the original hex 
     223                        //escaping complete. 
     224                        var percentIndex = part.lastIndexOf("%"); 
     225                        if(percentIndex == part.length - 1 || percentIndex == part.length - 2){ 
     226                                part = part.substring(0, percentIndex); 
     227                        } 
     228                        state.requestParts.push(part); 
     229                        reqIndex += part.length; 
     230                } 
     231                state.requestParts.push(reqData.substring(reqIndex, reqData.length)); 
     232                 
     233                state.partIndex = 0; 
     234                this.sendRequestPart(stateId); 
     235 
     236        }, 
     237         
     238        sendRequestPart: function(stateId){ 
     239                var state = this._state[stateId]; 
     240 
     241                if(state.partIndex < state.requestParts.length){ 
     242                        //Get the message part. 
     243                        var partData = state.requestParts[state.partIndex]; 
     244 
     245                        //Get the command. 
     246                        var cmd = "part"; 
     247                        if(state.partIndex + 1 == state.requestParts.length){ 
     248                                cmd = "end"; 
     249                        }else if (state.partIndex == 0){ 
     250                                cmd = "start"; 
     251                        } 
     252                         
     253                        this.setServerUrl(stateId, cmd, partData); 
     254                        state.partIndex++; 
     255                } 
     256        }, 
     257 
     258        setServerUrl: function(stateId, cmd, message){ 
     259                var serverUrl = this.makeServerUrl(stateId, cmd, message); 
     260                var state = this._state[stateId]; 
     261 
     262                //Safari won't let us replace across domains. 
     263                if(this._isWebKit){ 
     264                        state.serverWindow.location = serverUrl; 
     265                }else{ 
     266                        state.serverWindow.location.replace(serverUrl); 
     267                } 
     268        }, 
     269 
     270        makeServerUrl: function(stateId, cmd, message){ 
     271                var state = this._state[stateId]; 
     272                var serverUrl = state.serverUrl + "#" + (state.idCounter++) + ":" + cmd; 
     273                if(message){ 
     274                        serverUrl += ":" + message; 
     275                } 
     276                return serverUrl; 
     277        }, 
     278 
     279        fragmentReceived: function(frag){ 
     280                var index = frag.indexOf("#"); 
     281                var stateId = frag.substring(0, index); 
     282                var encodedData = frag.substring(index + 1, frag.length); 
     283 
     284                var msg = this.unpackMessage(encodedData); 
     285                var state = this._state[stateId]; 
     286 
     287                switch(msg.command){ 
     288                        case "loaded": 
     289                                this.frameLoaded(stateId); 
     290                                break; 
     291                        case "ok": 
     292                                this.sendRequestPart(stateId); 
     293                                break; 
     294                        case "start": 
     295                                state.responseMessage = "" + msg.message; 
     296                                this.setServerUrl(stateId, "ok"); 
     297                                break; 
     298                        case "part": 
     299                                state.responseMessage += msg.message;                    
     300                                this.setServerUrl(stateId, "ok"); 
     301                                break; 
     302                        case "end": 
     303                                this.setServerUrl(stateId, "ok"); 
     304                                state.responseMessage += msg.message; 
     305                                this.receive(stateId, state.responseMessage); 
     306                                break; 
     307                } 
     308        }, 
     309         
     310        unpackMessage: function(encodedMessage){ 
     311                var parts = encodedMessage.split(":"); 
     312                var command = parts[1]; 
     313                encodedMessage = parts[2] || ""; 
     314 
     315                var config = null; 
     316                if(command == "init"){ 
     317                        var configParts = encodedMessage.split("&"); 
     318                        config = {}; 
     319                        for(var i = 0; i < configParts.length; i++){ 
     320                                var nameValue = configParts[i].split("="); 
     321                                config[decodeURIComponent(nameValue[0])] = decodeURIComponent(nameValue[1]); 
     322                        } 
     323                } 
     324                return {command: command, message: encodedMessage, config: config}; 
    149325        } 
    150326} 
  • dojox/trunk/io/proxy/xip_client.html

    r10855 r12181  
     1<!-- 
     2        /* 
     3                Copyright (c) 2004-2008, The Dojo Foundation 
     4                All Rights Reserved. 
     5         
     6                Licensed under the Academic Free License version 2.1 or above OR the 
     7                modified BSD license. For more information on Dojo licensing, see: 
     8         
     9                        http://dojotoolkit.org/community/licensing.shtml 
     10        */ 
     11--> 
    112<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    213        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
     
    920        <!-- script type="text/javascript" --> 
    1021        // <!-- 
    11         /* 
    12         This file is really focused on just sending one message to the server, and 
    13         receiving one response. The code does not expect to be re-used for multiple messages. 
    14         This might be reworked later if performance indicates a need for it. 
    15          
    16         xip fragment identifier/hash values have the form: 
    17         #id:cmd:realEncodedMessage 
    18  
    19         id: some ID that should be unique among messages. No inherent meaning, 
    20                 just something to make sure the hash value is unique so the message 
    21                 receiver knows a new message is available. 
    22                  
    23         cmd: command to the receiver. Valid values are: 
    24                  - init: message used to init the frame. Sent as the first URL when loading 
    25                          the page. Contains some config parameters. 
    26                  - loaded: the remote frame is loaded. Only sent from server to client. 
    27                  - ok: the message that this page sent was received OK. The next message may 
    28                        now be sent. 
    29                  - start: the start message of a block of messages (a complete message may 
    30                           need to be segmented into many messages to get around the limitiations 
    31                           of the size of an URL that a browser accepts. 
    32                  - part: indicates this is a part of a message. 
    33                  - end: the end message of a block of messages. The message can now be acted upon. 
    34                         If the message is small enough that it doesn't need to be segmented, then 
    35                         just one hash value message can be sent with "end" as the command. 
    36          
    37         To reassemble a segmented message, the realEncodedMessage parts just have to be concatenated 
    38         together. 
    39         */ 
    40  
    41         //MSIE has the lowest limit for URLs with fragment identifiers, 
    42         //at around 4K. Choosing a slightly smaller number for good measure. 
    43         xipUrlLimit = 4000; 
    44         xipIdCounter = 1; 
    45  
    46         function xipInit(){ 
    47                 xipStateId = ""; 
    48                 xipIsSending = false; 
    49                 xipServerUrl = null; 
    50                 xipStateId = null; 
    51                 xipRequestData = null; 
    52                 xipCurrentHash = ""; 
    53                 xipResponseMessage = ""; 
    54                 xipRequestParts = []; 
    55                 xipPartIndex = 0; 
    56                 xipServerWindow = null; 
    57                 xipUseFrameRecursion = false; 
    58         } 
    59         xipInit(); 
    60          
    61         function send(encodedData){ 
    62                 if(xipUseFrameRecursion == "true"){ 
    63                         var clientEndPoint = window.open(xipStateId + "_clientEndPoint"); 
    64                         clientEndPoint.send(encodedData); 
    65                 }else{ 
    66                         if(!xipIsSending){ 
    67                                 xipIsSending = true; 
    68          
    69                                 xipRequestData = encodedData || ""; 
    70  
    71                                 //Get a handle to the server iframe. 
    72                                 xipServerWindow = frames[xipStateId + "_frame"]; 
    73                                 if (!xipServerWindow){ 
    74                                         xipServerWindow = document.getElementById(xipStateId + "_frame").contentWindow; 
    75                                 } 
    76          
    77                                 sendRequestStart(); 
    78                         } 
    79                 } 
    80         } 
    81  
    82         //Modify the server URL if it is a local path and  
    83         //This is done for local/same domain testing. 
    84         function fixServerUrl(ifpServerUrl){ 
    85                 if(ifpServerUrl.indexOf("..") == 0){ 
    86                         var parts = ifpServerUrl.split("/"); 
    87                         ifpServerUrl = parts[parts.length - 1]; 
    88                 } 
    89                 return ifpServerUrl; 
    90         } 
    91          
    9222         
    9323        function pollHash(){ 
     
    9828                        if(newHash != xipCurrentHash){ 
    9929                                try{ 
    100                                         messageReceived(newHash); 
     30                                        callMaster(xipStateId, newHash); 
    10131                                }catch(e){ 
    10232                                        //Make sure to not keep processing the error hash value. 
     
    10737                        } 
    10838                } 
    109         } 
    110  
    111         function messageReceived(encodedData){ 
    112                 var msg = unpackMessage(encodedData); 
    113  
    114                 switch(msg.command){ 
    115                         case "loaded": 
    116                                 xipMasterFrame.dojox.io.proxy.xip.clientFrameLoaded(xipStateId); 
    117                                 break; 
    118                         case "ok": 
    119                                 sendRequestPart(); 
    120                                 break; 
    121                         case "start": 
    122                                 xipResponseMessage = ""; 
    123                                 xipResponseMessage += msg.message; 
    124                                 setServerUrl("ok"); 
    125                                 break; 
    126                         case "part": 
    127                                 xipResponseMessage += msg.message;                       
    128                                 setServerUrl("ok"); 
    129                                 break; 
    130                         case "end": 
    131                                 setServerUrl("ok"); 
    132                                 xipResponseMessage += msg.message; 
    133                                 xipMasterFrame.dojox.io.proxy.xip.receive(xipStateId, xipResponseMessage); 
    134                                 break; 
    135                 } 
    136         } 
    137          
    138         function sendRequestStart(){ 
    139                 //Break the message into parts, if necessary. 
    140                 xipRequestParts = []; 
    141                 var reqData = xipRequestData; 
    142                 var urlLength = xipServerUrl.length; 
    143                 var partLength = xipUrlLimit - urlLength; 
    144                 var reqIndex = 0; 
    145  
    146                 while((reqData.length - reqIndex) + urlLength > xipUrlLimit){ 
    147                         var part = reqData.substring(reqIndex, reqIndex + partLength); 
    148                         //Safari will do some extra hex escaping unless we keep the original hex 
    149                         //escaping complete. 
    150                         var percentIndex = part.lastIndexOf("%"); 
    151                         if(percentIndex == part.length - 1 || percentIndex == part.length - 2){ 
    152                                 part = part.substring(0, percentIndex); 
    153                         } 
    154                         xipRequestParts.push(part); 
    155                         reqIndex += part.length; 
    156                 } 
    157                 xipRequestParts.push(reqData.substring(reqIndex, reqData.length)); 
    158                  
    159                 xipPartIndex = 0; 
    160                 sendRequestPart(); 
    161                  
    162         } 
    163          
    164         function sendRequestPart(){ 
    165                 if(xipPartIndex < xipRequestParts.length){ 
    166                         //Get the message part. 
    167                         var partData = xipRequestParts[xipPartIndex]; 
    168  
    169                         //Get the command. 
    170                         var cmd = "part"; 
    171                         if(xipPartIndex + 1 == xipRequestParts.length){ 
    172                                 cmd = "end"; 
    173                         }else if (xipPartIndex == 0){ 
    174                                 cmd = "start"; 
    175                         } 
    176                          
    177                         setServerUrl(cmd, partData); 
    178                         xipPartIndex++; 
    179                 } 
    180         } 
    181          
    182         function setServerUrl(cmd, message){ 
    183                 var serverUrl = makeServerUrl(cmd, message); 
    184  
    185                 //Safari won't let us replace across domains. 
    186                 if(navigator.userAgent.indexOf("Safari") == -1){ 
    187                         xipServerWindow.location.replace(serverUrl); 
    188                 }else{ 
    189                         xipServerWindow.location = serverUrl; 
    190                 } 
    191         } 
    192          
    193         function makeServerUrl(cmd, message){ 
    194                 var serverUrl = xipServerUrl + "#" + (xipIdCounter++) + ":" + cmd; 
    195                 if(message){ 
    196                         serverUrl += ":" + message; 
    197                 } 
    198                 return serverUrl; 
    19939        } 
    20040 
     
    21656        } 
    21757 
    218         function onClientLoad(){ 
    219                 //Decode the init params 
    220                 var config = unpackMessage(window.location.href.split("#")[1]).config; 
     58        //************** Init ************************** 
     59        xipCurrentHash = ""; 
     60         
     61        //Decode the init params 
     62        var fragId = window.location.href.split("#")[1]; 
     63        var config = unpackMessage(fragId).config; 
    22164 
    222                 xipStateId = config.id; 
     65        xipStateId = config.id; 
     66        xipMasterFrame = parent.parent; 
    22367 
    224                 //Remove the query param for the IE7 recursive case. 
    225                 xipServerUrl = fixServerUrl(config.server).replace(/(\?|\&)dojo\.fr\=1/, ""); 
    226                  
    227                 //Make sure we don't have a javascript: url, just for good measure. 
    228                 if(xipServerUrl.split(":")[0].match(/javascript/i)){ 
    229                         throw "Invalid server URL"; 
    230                 } 
    231  
    232                 xipUseFrameRecursion = config["fr"]; 
    233                  
    234                 if(xipUseFrameRecursion == "endpoint"){ 
    235                         xipMasterFrame = parent.parent; 
    236                 }else{ 
    237                         xipMasterFrame = parent; 
    238                 } 
    239                  
    240                 //Start counter to inspect hash value. 
    241                 setInterval(pollHash, 10); 
    242  
    243                 var clientUrl = window.location.href.split("#")[0]; 
    244                 var iframeNode = document.getElementsByTagName("iframe")[0]; 
    245                 iframeNode.id = xipStateId + "_frame"; 
    246                 iframeNode.src = makeServerUrl("init", 'id=' + xipStateId + '&client=' 
    247                         + encodeURIComponent(clientUrl) + '&fr=' + xipUseFrameRecursion); 
     68        var parts = config.callback.split("."); 
     69        xipCallbackObject = xipMasterFrame; 
     70        for(var i = 0; i < parts.length - 1; i++){ 
     71                xipCallbackObject = xipCallbackObject[parts[i]]; 
     72        } 
     73        xipCallback = parts[parts.length - 1]; 
     74         
     75        callMaster = function(stateId, message){ 
     76                xipCallbackObject[xipCallback](stateId + "#" + message); 
    24877        } 
    24978 
    250         if(typeof(window.addEventListener) == "undefined"){ 
    251                 window.attachEvent("onload", onClientLoad); 
    252         }else{ 
    253                 window.addEventListener('load', onClientLoad, false); 
    254         } 
     79        //Call the master frame to let it know it is OK to start sending. 
     80        callMaster(xipStateId, "0:loaded"); 
    25581         
     82        //Start counter to inspect hash value. 
     83        setInterval(pollHash, 10); 
     84 
    25685        // --> 
    25786        <!-- </script> --> 
     
    26291        <p>This file is used for Dojo's XMLHttpRequest Iframe Proxy. This is the "client" file used 
    26392        internally by dojox.io.proxy.xip.</p> 
    264          
    265         <iframe src="javascript:false"></iframe> 
    26693</body> 
    26794</html> 
  • dojox/trunk/io/proxy/xip_server.html

    r10855 r12181  
    11<!-- 
    22        /* 
    3                 Copyright (c) 2004-2006, The Dojo Foundation 
     3                Copyright (c) 2004-2008, The Dojo Foundation 
    44                All Rights Reserved. 
    55         
     
    201201                //Safari won't let us replace across domains. 
    202202                if(navigator.userAgent.indexOf("Safari") == -1){ 
    203     &nb