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