1 /* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to you under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 /** 18 * @class 19 * @name _IFrameRequest 20 * @memberOf myfaces._impl.xhrCore 21 * @extends myfaces._impl.xhrCore._BaseRequest 22 * @description 23 * iframe transport for an alternative way to do ajax communication 24 * <p /> 25 * The idea to make a frame a protocol transport is, to make a form submit 26 * with the iframe as target, and once done use the result in the iframe 27 * as result for the request. 28 * 29 * <p /> 30 * This method can be used by older browsers and if you have 31 * a multipart request which includes 32 * a fileupload element, fileuploads cannot be handled by 33 * normal xhr requests. The standard html 4+ compliant way to do this 34 * is to use an iframe as submit target for a form. 35 * <p /> 36 * Note on almost all browsers this method induces a real asynchronity, the only 37 * exception is firefox 3.6- which blocks the ui, this is resolved in Firefox 4 38 */ 39 myfaces._impl.core._Runtime.extendClass("myfaces._impl.xhrCore._IFrameRequest", myfaces._impl.xhrCore._BaseRequest, 40 /** @lends myfaces._impl.xhrCore._IFrameRequest */ 41 { 42 43 _FRAME_ID: "_mf_comm_frm", 44 _frame: null, 45 _RT: myfaces._impl.core._Runtime, 46 CLS_NAME: "myfaces._impl.xhrCore._IFrameRequest", 47 48 /** 49 * @constant 50 * @description request marker that the request is an iframe based request 51 */ 52 JX_PART_IFRAME: "javax.faces.partial.iframe", 53 /** 54 * @constant 55 * @description request marker that the request is an apache myfaces iframe request based request 56 */ 57 MF_PART_IFRAME: "org.apache.myfaces.partial.iframe", 58 59 /** 60 * constructor which shifts the arguments 61 * to the protected properties of this clas 62 * 63 * @param arguments 64 */ 65 constructor_: function(arguments) { 66 try { 67 //we fetch in the standard arguments 68 this._callSuper("constructor", arguments); 69 this._Lang.applyArgs(this, arguments); 70 71 if (!this._response) { 72 this._response = new myfaces._impl.xhrCore._AjaxResponse(this._onException, this._onWarning); 73 } 74 this._ajaxUtil = new myfaces._impl.xhrCore._AjaxUtils(this._onException, this._onWarning); 75 } catch (e) { 76 //_onError 77 this._onException(null, this._context, this.CLS_NAME, "constructor", e); 78 } 79 }, 80 81 /** 82 * send method, central callback which sends the 83 * request 84 */ 85 send: function() { 86 var _Impl = this._getImpl(); 87 var _RT = myfaces._impl.core._Runtime; 88 89 this._frame = this._createTransportFrame(); 90 91 //we append an onload handler to the frame 92 //to cover the starting and loading events, 93 //timeouts cannot be covered in a cross browser way 94 95 //we point our onload handler to the frame, we do not use addOnLoad 96 //because the frame should not have other onload handlers in place 97 if (!_RT.browser.isIE) { 98 this._frame.onload = this._Lang.hitch(this, this.callback); 99 } else { 100 //ie has a bug, onload is not settable outside of innerHTML on iframes 101 this._frame.onload_IE = this._Lang.hitch(this, this.callback); 102 } 103 104 //now to the parameter passing: 105 _Impl.sendEvent(this._xhr, this._context, _Impl.BEGIN); 106 107 //viewstate should be in our parent form which we will isse we however have to add the execute and 108 //render parameters as well as the usual javax.faces.request params to our target 109 110 var oldTarget = this._sourceForm.target; 111 var oldMethod = this._sourceForm.method; 112 var _progress = 0; 113 var _srcFrm = this._sourceForm; 114 try { 115 this._initAjaxParams(); 116 _srcFrm.target = this._frame.name; 117 _srcFrm.method = this._ajaxType; 118 _srcFrm.submit(); 119 } finally { 120 this._removeAjaxParams(oldTarget); 121 _srcFrm.target = oldTarget; 122 _srcFrm.method = oldMethod; 123 } 124 }, 125 126 /** 127 * the callback function after the request is done 128 */ 129 callback: function() { 130 //now we have to do the processing, for that we have to parse the result, if it is a http 404 then 131 //nothing could be delivered and we bomb out with an error anything else has to be parsed 132 //via our xml parser 133 var request = {}; 134 try { 135 request.responseText = this._getFrameText(); 136 request.responseXML = this._getFrameXml(); 137 request.readyState = this._READY_STATE_DONE; 138 this._xhr = request; 139 this._onDone(request, this._context); 140 141 if (!this._Lang.isXMLParseError(request.responseXML)) { 142 request.status = 201; 143 this._onSuccess(); 144 } else { 145 request.status = 0; 146 //we simulate the request for our xhr call 147 this._onError(); 148 149 } 150 } catch (e) { 151 //_onError 152 this._onException(request, this._context, this.CLS_NAME, "constructor", e); 153 } finally { 154 //this closes any hanging or pending comm channel caused by the iframe 155 this._clearFrame(); 156 this._frame = null; 157 this._xhr = null; 158 } 159 }, 160 161 /** 162 * returns the frame text in a browser independend manner 163 */ 164 _getFrameDocument: function() { 165 //we cover various browsers here, because almost all browsers keep the document in a different 166 //position 167 return this._frame.contentWindow.document || this._frame.contentDocument || this._frame.document ; 168 }, 169 170 _getFrameText: function() { 171 var framedoc = this._getFrameDocument(); 172 //also ie keeps the body in framedoc.body the rest in documentElement 173 var body = framedoc.body || framedoc.documentElement ; 174 return body.innerHTML; 175 }, 176 177 _clearFrame: function() { 178 var framedoc = this._getFrameDocument(); 179 var body = framedoc.documentElement || framedoc.body; 180 //ie8 in 7 mode chokes on the innerHTML method 181 //direct dom removal is less flakey and works 182 //over all browsers, but is slower 183 this._Dom._removeChildNodes(body, false); 184 }, 185 186 /** 187 * returns the processed xml from the frame 188 */ 189 _getFrameXml: function() { 190 var framedoc = this._getFrameDocument(); 191 //same situation here, the xml is hosted either in xmlDocument or 192 //is located directly under the frame document 193 return framedoc.XMLDocument || framedoc; 194 }, 195 196 197 _initAjaxParams: function() { 198 var _Impl = this._getImpl(); 199 //this._appendHiddenValue(_Impl.P_AJAX, ""); 200 var appendHiddenValue = this._Lang.hitch(this, this._appendHiddenValue); 201 for (var key in this._passThrough) { 202 203 appendHiddenValue(key, this._passThrough[key]); 204 } 205 //marker that this is an ajax iframe request 206 appendHiddenValue(this.JX_PART_IFRAME, "true"); 207 appendHiddenValue(this.MF_PART_IFRAME, "true"); 208 209 }, 210 211 212 _removeAjaxParams: function(oldTarget) { 213 var _Impl = this._getImpl(); 214 this._sourceForm.target = oldTarget; 215 //some browsers optimize this and loose their scope that way, 216 //I am still not sure why, but probably because the function itself 217 //was called under finally and I ran into a bug in the fox 4 218 //scripting engine 219 var toDelete = []; 220 var possibleKeys = {}; 221 for(var key in this._passThrough) { 222 possibleKeys[key] = true; 223 } 224 possibleKeys[this.JX_PART_IFRAME] = true; 225 possibleKeys[this.MF_PART_IFRAME] = true; 226 (possibleKeys["javax.faces.ViewState"])? delete possibleKeys["javax.faces.ViewState"]:null; 227 228 for(var cnt = this._sourceForm.elements.length -1; cnt >= 0; cnt--) { 229 var elem = this._sourceForm.elements[cnt]; 230 if(possibleKeys[elem.name] && elem.type == "hidden") { 231 elem.parentNode.removeChild(elem); 232 delete elem; 233 } 234 } 235 }, 236 237 _appendHiddenValue: function(key, value) { 238 if ('undefined' == typeof value) { 239 return; 240 } 241 var input = document.createElement("input"); 242 //the dom is a singleton nothing can happen by remapping 243 this._Dom.setAttribute(input, "type", "hidden"); 244 this._Dom.setAttribute(input, "name", key); 245 this._Dom.setAttribute(input, "style", "display:none"); 246 this._Dom.setAttribute(input, "value", value); 247 this._sourceForm.appendChild(input); 248 }, 249 250 _removeHiddenValue: function(key) { 251 var elem = this._Dom.findByName(this._sourceForm, key, true); 252 if (elem.length) { 253 254 elem[0].parentNode.removeChild(elem[0]); 255 delete elem[0]; 256 } 257 }, 258 259 _createTransportFrame: function() { 260 var _RT = this._RT; 261 var frame = document.getElementById(this._FRAME_ID); 262 //normally this code should not be called 263 //but just to be sure 264 if (!frame) { 265 if (!_RT.browser.isIE) { 266 frame = document.createElement('iframe'); 267 268 //probably the ie method would work on all browsers 269 //but this code is the safe bet it works on all standards 270 //compliant browsers in a clean manner 271 272 this._Dom.setAttribute(frame, "src", "about:blank"); 273 this._Dom.setAttribute(frame, "id", this._FRAME_ID); 274 this._Dom.setAttribute(frame, "name", this._FRAME_ID); 275 this._Dom.setAttribute(frame, "type", "content"); 276 this._Dom.setAttribute(frame, "collapsed", "true"); 277 this._Dom.setAttribute(frame, "style", "display:none"); 278 279 document.body.appendChild(frame); 280 } else { //Now to the non compliant browsers 281 var node = document.createElement("div"); 282 this._Dom.setAttribute(node, "style", "display:none"); 283 //we are dealing with two well known iframe ie bugs here 284 //first the iframe has to be set via innerHTML to be present 285 //secondly the onload handler is immutable on ie, we have to 286 //use a dummy onload handler in this case and call that one 287 //from the onload handler 288 node.innerHTML = "<iframe id='" + this._FRAME_ID + "' name='" + this._FRAME_ID + "' style='display:none;' src='about:blank' type='content' onload='this.onload_IE();' ></iframe>"; 289 290 //avoid the ie open tag problem 291 var body = document.body; 292 if (body.firstChild) { 293 body.insertBefore(node, document.body.firstChild); 294 } else { 295 body.appendChild(node); 296 } 297 } 298 299 } 300 //helps to for the onload handlers and innerhtml to be in sync again 301 return document.getElementById(this._FRAME_ID); 302 303 } 304 305 //TODO pps, the idea behind pps is to generate another form 306 // and temporarily shift the elements over which have to be 307 // ppsed, but it is up for discussion if we do pps at all in case of 308 // an iframe, so I wont implement anything for now 309 });