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 IFrame
 20  * @memberOf myfaces._impl.xhrCore.engine
 21  * @extends myfaces._impl.xhrCore.engine.BaseRequest
 22  * @description
 23  *
 24  * wrapper for an iframe transport object with all its differences
 25  * it emulates the xhr level2 api
 26  */
 27 _MF_CLS(_PFX_XHR+"engine.IFrame", myfaces._impl.xhrCore.engine.BaseRequest,
 28         /** @lends myfaces._impl.xhrCore.engine.IFrame.prototype */
 29         {
 30 
 31 
 32 
 33             /*the target frame responsible for the communication*/
 34             _frame: null,
 35             _requestHeader: null,
 36             _aborted: false,
 37 
 38 
 39             CLS_NAME: "myfaces._impl.xhrCore._IFrameRequest",
 40             _FRAME_ID: "_mf_comm_frm",
 41 
 42             /**
 43              * constructor which shifts the arguments
 44              * to the protected properties of this clas
 45              *
 46              * @param arguments
 47              */
 48             constructor_: function(arguments) {
 49                 //we fetch in the standard arguments
 50 
 51                 this._callSuper("constructor", arguments);
 52 
 53                 this._initDefaultFinalizableFields();
 54                 this._requestHeader = {};
 55 
 56                 this._XHRConst = myfaces._impl.xhrCore.engine.XhrConst;
 57 
 58                 this._Lang.applyArgs(this, arguments);
 59                 this.readyState = this._XHRConst.READY_STATE_UNSENT;
 60             },
 61 
 62             setRequestHeader: function(key, value) {
 63                 this._requestHeader[key] = value;
 64             },
 65 
 66             open: function(method, url, async) {
 67 
 68                 this.readyState = this._XHRConst.READY_STATE_OPENED;
 69 
 70                 var _RT = myfaces._impl.core._Runtime;
 71                 var _Lang = this._Lang;
 72 
 73                 this._frame = this._createTransportFrame();
 74 
 75                 //we append an onload handler to the frame
 76                 //to cover the starting and loading events,
 77                 //timeouts cannot be covered in a cross browser way
 78 
 79                 //we point our onload handler to the frame, we do not use addOnLoad
 80                 //because the frame should not have other onload handlers in place
 81                 if (!_RT.browser.isIE || this._Dom.isDomCompliant()) {
 82                     this._frame.onload = _Lang.hitch(this, this._callback);
 83                 } else {
 84                     //ie has a bug, onload is not settable outside of innerHTML on iframes
 85                     this._frame.onload_IE = _Lang.hitch(this, this._callback);
 86                 }
 87 
 88                 this.method = method || this.method;
 89                 this.url = url || this.url;
 90                 this.async = ('undefined' != typeof async) ? async : this.async;
 91 
 92                 var myevt = {};
 93                 this._addProgressAttributes(myevt, 10, 100);
 94                 this.onprogress(myevt);
 95             },
 96 
 97             send: function(formData) {
 98 
 99                 var myevt = {};
100                 this._addProgressAttributes(myevt, 20, 100);
101                 this.onloadstart(myevt);
102                 this.onprogress(myevt);
103 
104                 var oldTarget = formData.form.target;
105                 var oldMethod = formData.form.method;
106                 var oldAction = formData.form.action;
107                 var formDataForm = formData.form;
108                 
109                 try {
110                     //_t._initAjaxParams();
111                     for (var key in this._requestHeader) {
112                         formData.append(key, this._requestHeader[key]);
113                     }
114 
115                     formData.form.target = this._frame.name;
116                     formData.form.method = this.method;
117                     if (this.url) {
118                         formData.form.action = this.url;
119                     }
120                     this.readyState = this._XHRConst.READY_STATE_LOADING;
121                     this.onreadystatechange(myevt);
122                     formData.form.submit();
123                 } finally {
124                     formData.form.action = oldAction;
125                     formData.form.target = oldTarget;
126                     formData.form.method = oldMethod;
127 
128                     formData._finalize();
129                 }
130             },
131 
132             /*we can implement it, but it will work only on browsers
133              * which have asynchronous iframe loading*/
134             abort: function() {
135                 this._aborted = true;
136                 this.onabort({});
137             },
138 
139             _addProgressAttributes: function(evt, percent, total) {
140                 //http://www.w3.org/TR/progress-events/#progressevent
141                 evt.lengthComputable = true;
142                 evt.loaded = percent;
143                 evt.total = total;
144 
145             },
146 
147             _callback: function() {
148 
149                 //aborted no further processing
150                 if (this._aborted) return;
151                 try {
152                     var myevt = {};
153 
154                     this._addProgressAttributes(myevt, 100, 100);
155                     this.readyState = this._XHRConst.READY_STATE_DONE;
156                     this.onreadystatechange(myevt);
157 
158                     this.responseText = this._getFrameText();
159                     this.responseXML = this._getFrameXml();
160                     this.readyState = this._READY_STATE_DONE;
161 
162                     //TODO status and statusText
163 
164                     this.onreadystatechange(myevt);
165                     this.onloadend();
166 
167                     if (!this._Lang.isXMLParseError(this.responseXML)) {
168                         this.status = 201;
169                         this.onload();
170                     } else {
171                         this.status = 0;
172                         //we simulate the request for our xhr call
173                         this.onerror();
174                     }
175 
176                 } finally {
177                     this._frame = null;
178                 }
179             },
180 
181             /**
182              * returns the frame text in a browser independend manner
183              */
184             _getFrameDocument: function() {
185 
186                 //we cover various browsers here, because almost all browsers keep the document in a different
187                 //position
188                 return this._frame.contentWindow.document || this._frame.contentDocument || this._frame.document;
189             },
190 
191             _getFrameText: function() {
192                 var framedoc = this._getFrameDocument();
193                 //also ie keeps the body in framedoc.body the rest in documentElement
194                 var body = framedoc.body || framedoc.documentElement;
195                 return  body.innerHTML;
196             },
197 
198             _clearFrame: function() {
199 
200                 var framedoc = this._getFrameDocument();
201                 var body = framedoc.documentElement || framedoc.body;
202                 //ie8 in 7 mode chokes on the innerHTML method
203                 //direct dom removal is less flakey and works
204                 //over all browsers, but is slower
205                 if (myfaces._impl.core._Runtime.browser.isIE) {
206                     this._Dom._removeChildNodes(body, false);
207                 } else {
208                     body.innerHTML = "";
209                 }
210             },
211 
212             /**
213              * returns the processed xml from the frame
214              */
215             _getFrameXml: function() {
216                 var framedoc = this._getFrameDocument();
217                 //same situation here, the xml is hosted either in xmlDocument or
218                 //is located directly under the frame document
219                 return  framedoc.XMLDocument || framedoc;
220             },
221 
222             _createTransportFrame: function() {
223 
224                 var _FRM_ID = this._FRAME_ID;
225                 var frame = document.getElementById(_FRM_ID);
226                 if (frame) return frame;
227                 //normally this code should not be called
228                 //but just to be sure
229 
230                 if (this._Dom.isDomCompliant()) {
231                     frame = this._Dom.createElement('iframe', {
232                         "src": "about:blank",
233                         "id": _FRM_ID,
234                         "name": _FRM_ID,
235                         "type": "content",
236                         "collapsed": "true",
237                         "style": "display:none"
238                     });
239 
240                     //probably the ie method would work on all browsers
241                     //but this code is the safe bet it works on all standards
242                     //compliant browsers in a clean manner
243 
244                     document.body.appendChild(frame);
245                 } else { //Now to the non compliant browsers
246                     var node = this._Dom.createElement("div", {
247                         "style": "display:none"
248                     });
249 
250                     //we are dealing with two well known iframe ie bugs here
251                     //first the iframe has to be set via innerHTML to be present
252                     //secondly the onload handler is immutable on ie, we have to
253                     //use a dummy onload handler in this case and call this one
254                     //from the onload handler
255                     node.innerHTML = "<iframe id='" + _FRM_ID + "' name='" + _FRM_ID + "' style='display:none;' src='about:blank' type='content' onload='this.onload_IE();'  ></iframe>";
256 
257                     //avoid the ie open tag problem
258                     var body = document.body;
259                     if (body.firstChild) {
260                         body.insertBefore(node, document.body.firstChild);
261                     } else {
262                         body.appendChild(node);
263                     }
264                 }
265 
266                 //helps to for the onload handlers and innerhtml to be in sync again
267                 return document.getElementById(_FRM_ID);
268             },
269 
270             _startTimeout: function() {
271 
272                 if (this.timeout == 0) return;
273                 this._timeoutTimer = setTimeout(this._Lang.hitch(this, function() {
274                     if (this._xhrObject.readyState != this._XHRConst.READY_STATE_DONE) {
275 
276                         this._aborted = true;
277                         clearTimeout(this._timeoutTimer);
278                         //we cannot abort an iframe request
279                         this.ontimeout({});
280                         this._timeoutTimer = null;
281                     }
282                 }), this.timeout);
283             }
284         });
285