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 * An implementation of an xhr request object 19 * with partial page submit functionality, and jsf 20 * ppr request and timeout handling capabilities 21 * 22 * Author: Werner Punz (latest modification by $Author: ganeshpuri $) 23 * Version: $Revision: 1.4 $ $Date: 2009/05/31 09:16:44 $ 24 */ 25 26 /** 27 * @class 28 * @name _AjaxRequest 29 * @memberOf myfaces._impl.xhrCore 30 * @extends myfaces._impl.core.Object 31 */ 32 _MF_CLS(_PFX_XHR + "_AjaxRequest", _MF_OBJECT, /** @lends myfaces._impl.xhrCore._AjaxRequest.prototype */ { 33 34 _contentType: "application/x-www-form-urlencoded", 35 /** source element issuing the request */ 36 _source: null, 37 /** context passed down from the caller */ 38 _context:null, 39 /** source form issuing the request */ 40 _sourceForm: null, 41 /** passthrough parameters */ 42 _passThrough: null, 43 44 /** queue control */ 45 _timeout: null, 46 /** enqueuing delay */ 47 //_delay:null, 48 /** queue size */ 49 _queueSize:-1, 50 51 /** 52 back reference to the xhr queue, 53 only set if the object really is queued 54 */ 55 _xhrQueue: null, 56 57 /** pps an array of identifiers which should be part of the submit, the form is ignored */ 58 _partialIdsArray : null, 59 60 /** xhr object, internal param */ 61 _xhr: null, 62 63 /** predefined method */ 64 _ajaxType:"POST", 65 66 //CONSTANTS 67 ENCODED_URL:"javax.faces.encodedURL", 68 /* 69 * constants used internally 70 */ 71 _CONTENT_TYPE:"Content-Type", 72 _HEAD_FACES_REQ:"Faces-Request", 73 _VAL_AJAX: "partial/ajax", 74 _XHR_CONST: myfaces._impl.xhrCore.engine.XhrConst, 75 76 // _exception: null, 77 // _requestParameters: null, 78 /** 79 * Constructor 80 * <p /> 81 * note there is a load of common properties 82 * inherited by the base class which define the corner 83 * parameters and the general internal behavior 84 * like _onError etc... 85 * @param {Object} args an arguments map which an override any of the given protected 86 * instance variables, by a simple name value pair combination 87 */ 88 constructor_: function(args) { 89 90 try { 91 this._callSuper("constructor_", args); 92 93 this._initDefaultFinalizableFields(); 94 delete this._resettableContent["_xhrQueue"]; 95 96 this.applyArgs(args); 97 98 /*namespace remapping for readability*/ 99 //we fetch in the standard arguments 100 //and apply them to our protected attributes 101 //we do not gc the entry hence it is not defined on top 102 var xhrCore = myfaces._impl.xhrCore; 103 this._AJAXUTIL = xhrCore._AjaxUtils; 104 105 } catch (e) { 106 //_onError 107 this._stdErrorHandler(this._xhr, this._context, e); 108 } 109 }, 110 111 /** 112 * Sends an Ajax request 113 */ 114 send : function() { 115 116 var _Lang = this._Lang; 117 var _RT = this._RT; 118 119 try { 120 121 var scopeThis = _Lang.hitch(this, function(functionName) { 122 return _Lang.hitch(this, this[functionName]); 123 }); 124 this._xhr = _Lang.mixMaps(this._getTransport(), { 125 onprogress: scopeThis("onprogress"), 126 ontimeout: scopeThis("ontimeout"), 127 //remove for xhr level2 support (chrome has problems with it) 128 onloadend: scopeThis("ondone"), 129 onload: scopeThis("onsuccess"), 130 onerror: scopeThis("onerror") 131 132 }, true); 133 var xhr = this._xhr, 134 sourceForm = this._sourceForm, 135 targetURL = (typeof sourceForm.elements[this.ENCODED_URL] == 'undefined') ? 136 sourceForm.action : 137 sourceForm.elements[this.ENCODED_URL].value, 138 formData = this.getFormData(); 139 140 for (var key in this._passThrough) { 141 if(!this._passThrough.hasOwnProperty(key)) continue; 142 formData.append(key, this._passThrough[key]); 143 } 144 145 xhr.open(this._ajaxType, targetURL + 146 ((this._ajaxType == "GET") ? "?" + this._formDataToURI(formData) : "") 147 , true); 148 149 xhr.timeout = this._timeout || 0; 150 151 this._applyContentType(xhr); 152 xhr.setRequestHeader(this._HEAD_FACES_REQ, this._VAL_AJAX); 153 154 //some webkit based mobile browsers do not follow the w3c spec of 155 // setting the accept headers automatically 156 if(this._RT.browser.isWebKit) { 157 xhr.setRequestHeader("Accept","text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"); 158 } 159 this._sendEvent("BEGIN"); 160 //Check if it is a custom form data object 161 //if yes we use makefinal for the final handling 162 if (formData && formData.makeFinal) { 163 formData = formData.makeFinal() 164 } 165 xhr.send((this._ajaxType != "GET") ? formData : null); 166 167 } catch (e) { 168 //_onError//_onError 169 e = (e._mfInternal)? e: this._Lang.makeException(new Error(), "sendError","sendError", this._nameSpace, "send", e.message); 170 this._stdErrorHandler(this._xhr, this._context, e); 171 } 172 }, 173 174 /** 175 * helper, in multipart situations we might alter the content type 176 * from the urlencoded one 177 */ 178 _applyContentType: function(xhr) { 179 var contentType = this._contentType+"; charset=utf-8"; 180 181 xhr.setRequestHeader(this._CONTENT_TYPE, contentType); 182 }, 183 184 ondone: function() { 185 this._requestDone(); 186 }, 187 188 189 onsuccess: function(/*evt*/) { 190 191 var context = this._context; 192 var xhr = this._xhr; 193 try { 194 this._sendEvent("COMPLETE"); 195 //now we have to reroute into our official api 196 //because users might want to decorate it, we will split it apart afterwards 197 198 context._mfInternal = context._mfInternal || {}; 199 jsf.ajax.response((xhr.getXHRObject) ? xhr.getXHRObject() : xhr, context); 200 201 202 203 } catch (e) { 204 this._stdErrorHandler(this._xhr, this._context, e); 205 } 206 //add for xhr level2 support 207 //} finally { 208 //W3C spec onloadend must be called no matter if success or not 209 // this.ondone(); 210 //} 211 }, 212 213 onerror: function(/*evt*/) { 214 //TODO improve the error code detection here regarding server errors etc... 215 //and push it into our general error handling subframework 216 var context = this._context; 217 var xhr = this._xhr; 218 var _Lang = this._Lang; 219 220 var errorText = ""; 221 this._sendEvent("COMPLETE"); 222 try { 223 var UNKNOWN = _Lang.getMessage("UNKNOWN"); 224 //status can be 0 and statusText can be "" 225 var status = ('undefined' != xhr.status && null != xhr.status)? xhr.status : UNKNOWN; 226 var statusText = ('undefined' != xhr.statusText && null != xhr.statusText)? xhr.statusText : UNKNOWN; 227 errorText = _Lang.getMessage("ERR_REQU_FAILED", null,status,statusText); 228 229 } catch (e) { 230 errorText = _Lang.getMessage("ERR_REQ_FAILED_UNKNOWN", null); 231 } finally { 232 var _Impl = this.attr("impl"); 233 _Impl.sendError(xhr, context, _Impl.HTTPERROR, 234 _Impl.HTTPERROR, errorText,"","myfaces._impl.xhrCore._AjaxRequest","onerror"); 235 //add for xhr level2 support 236 //since chrome does not call properly the onloadend we have to do it manually 237 //to eliminate xhr level1 for the compile profile modern 238 //W3C spec onloadend must be called no matter if success or not 239 //this.ondone(); 240 } 241 //_onError 242 }, 243 244 onprogress: function(/*evt*/) { 245 //do nothing for now 246 }, 247 248 ontimeout: function(/*evt*/) { 249 try { 250 //we issue an event not an error here before killing the xhr process 251 this._sendEvent("TIMEOUT_EVENT"); 252 //timeout done we process the next in the queue 253 } finally { 254 this._requestDone(); 255 } 256 }, 257 258 _formDataToURI: function(formData) { 259 if (formData && formData.makeFinal) { 260 formData = formData.makeFinal() 261 } 262 return formData; 263 }, 264 265 /** 266 * change for jsf 2.3 since we drop legacy browser support 267 * there is no need anymore to support xhr level 1. 268 * @returns {XMLHttpRequest} the transport object 269 * @private 270 */ 271 _getTransport: function() { 272 return new XMLHttpRequest(); 273 }, 274 275 276 //----------------- backported from the base request -------------------------------- 277 //non abstract ones 278 279 280 /** 281 * Spec. 13.3.1 282 * Collect and encode input elements. 283 * Additionally the hidden element javax.faces.ViewState 284 * Enhancement partial page submit 285 * 286 * @return an element of formDataWrapper 287 * which keeps the final Send Representation of the 288 */ 289 getFormData : function() { 290 var _AJAXUTIL = this._AJAXUTIL, myfacesOptions = this._context.myfaces, ret = null; 291 292 293 294 if (!this._partialIdsArray || !this._partialIdsArray.length) { 295 var _AJAXUTIL = this._AJAXUTIL, myfacesOptions = this._context.myfaces; 296 return this._Lang.createFormDataDecorator(jsf.getViewState(this._sourceForm)); 297 } else { 298 //now this is less performant but we have to call it to allow viewstate decoration 299 ret = this._Lang.createFormDataDecorator(new Array()); 300 _AJAXUTIL.encodeSubmittableFields(ret, this._sourceForm, this._partialIdsArray); 301 if (this._source && myfacesOptions && myfacesOptions.form) 302 _AJAXUTIL.appendIssuingItem(this._source, ret); 303 304 } 305 return ret; 306 307 }, 308 309 310 311 /** 312 * Client error handlers which also in the long run route into our error queue 313 * but also are able to deliver more meaningful messages 314 * note, in case of an error all subsequent xhr requests are dropped 315 * to get a clean state on things 316 * 317 * @param request the xhr request object 318 * @param context the context holding all values for further processing 319 * @param exception the embedded exception 320 */ 321 _stdErrorHandler: function(request, context, exception) { 322 var xhrQueue = this._xhrQueue; 323 try { 324 this.attr("impl").stdErrorHandler(request, context, exception); 325 } finally { 326 if (xhrQueue) { 327 xhrQueue.cleanup(); 328 } 329 } 330 }, 331 332 _sendEvent: function(evtType) { 333 var _Impl = this.attr("impl"); 334 _Impl.sendEvent(this._xhr, this._context, _Impl[evtType]); 335 }, 336 337 _requestDone: function() { 338 var queue = this._xhrQueue; 339 if (queue) { 340 queue.processQueue(); 341 } 342 //ie6 helper cleanup 343 delete this._context.source; 344 345 } 346 }); 347 348