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