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 _AjaxResponse
 20  * @memberOf myfaces._impl.xhrCore
 21  * @extends myfaces._impl.core.Object
 22  * @description
 23  * This singleton is responsible for handling the standardized xml ajax response
 24  * Note: since the semantic processing can be handled about 90% in a functional
 25  * style we make this class stateless. Every state information is stored
 26  * temporarily in the context.
 27  *
 28  * The singleton approach also improves performance
 29  * due to less object gc compared to the old instance approach.
 30  *
 31  */
 32 _MF_SINGLTN(_PFX_XHR + "_AjaxResponse", _MF_OBJECT, /** @lends myfaces._impl.xhrCore._AjaxResponse.prototype */ {
 33 
 34     /*partial response types*/
 35     RESP_PARTIAL: "partial-response",
 36     RESP_TYPE_ERROR: "error",
 37     RESP_TYPE_REDIRECT: "redirect",
 38     RESP_TYPE_CHANGES: "changes",
 39 
 40     /*partial commands*/
 41     CMD_CHANGES: "changes",
 42     CMD_UPDATE: "update",
 43     CMD_DELETE: "delete",
 44     CMD_INSERT: "insert",
 45     CMD_EVAL: "eval",
 46     CMD_ERROR: "error",
 47     CMD_ATTRIBUTES: "attributes",
 48     CMD_EXTENSION: "extension",
 49     CMD_REDIRECT: "redirect",
 50 
 51     /*other constants*/
 52     P_VIEWSTATE: "javax.faces.ViewState",
 53     P_CLIENTWINDOW: "javax.faces.ClientWindow",
 54     P_VIEWROOT: "javax.faces.ViewRoot",
 55     P_VIEWHEAD: "javax.faces.ViewHead",
 56     P_VIEWBODY: "javax.faces.ViewBody",
 57     P_RESOURCE: "javax.faces.Resource",
 58 
 59     /**
 60      * uses response to start Html element replacement
 61      *
 62      * @param {Object} request (xhrRequest) - xhr request object
 63      * @param {Object} context (Map) - AJAX context
 64      *
 65      * A special handling has to be added to the update cycle
 66      * according to the JSDoc specs if the CDATA block contains html tags the outer rim must be stripped
 67      * if the CDATA block contains a head section the document head must be replaced
 68      * and if the CDATA block contains a body section the document body must be replaced!
 69      *
 70      */
 71     processResponse: function (request, context) {
 72         //mfinternal handling, note, the mfinternal is only optional
 73         //according to the spec
 74         context._mfInternal = context._mfInternal || {};
 75         var mfInternal = context._mfInternal;
 76 
 77         //the temporary data is hosted here
 78         mfInternal._updateElems = [];
 79         mfInternal._updateForms = [];
 80         mfInternal.appliedViewState = null;
 81         mfInternal.appliedClientWindow = null;
 82         mfInternal.namingModeId = null;
 83 
 84 
 85         try {
 86             var _Impl = this.attr("impl"), _Lang = this._Lang;
 87             // TODO:
 88             // Solution from
 89             // http://www.codingforums.com/archive/index.php/t-47018.html
 90             // to solve IE error 1072896658 when a Java server sends iso88591
 91             // istead of ISO-8859-1
 92 
 93             if (!request || !_Lang.exists(request, "responseXML")) {
 94                 throw this.makeException(new Error(), _Impl.EMPTY_RESPONSE, _Impl.EMPTY_RESPONSE, this._nameSpace, "processResponse", "");
 95             }
 96             //check for a parseError under certain browsers
 97 
 98             var xmlContent = request.responseXML;
 99             //ie6+ keeps the parsing response under xmlContent.parserError
100             //while the rest of the world keeps it as element under the first node
101             var xmlErr = _Lang.fetchXMLErrorMessage(request.responseText || request.response, xmlContent)
102             if (xmlErr) {
103                 throw this._raiseError(new Error(), xmlErr.errorMessage + "\n" + xmlErr.sourceText + "\n" + xmlErr.visualError + "\n", "processResponse");
104             }
105             var partials = xmlContent.childNodes[0];
106             if ('undefined' == typeof partials || partials == null) {
107                 throw this._raiseError(new Error(), "No child nodes for response", "processResponse");
108 
109             } else {
110                 if (partials.tagName != this.RESP_PARTIAL) {
111                     // IE 8 sees XML Header as first sibling ...
112                     partials = partials.nextSibling;
113                     if (!partials || partials.tagName != this.RESP_PARTIAL) {
114                         throw this._raiseError(new Error(), "Partial response not set", "processResponse");
115                     }
116                 }
117             }
118 
119 
120             /**
121              * jsf 2.3 naming mode partial response,
122              * we either viewstate all forms (non id mode)
123              * or the forms under the viewroot defined by id
124              *
125              * @type {string} ... the naming mode id is set or an empty string
126              * definitely not a null value to avoid type confusions later on
127              */
128             mfInternal.namingModeId = (partials.id || "");
129 
130 
131             var childNodesLength = partials.childNodes.length;
132 
133             for (var loop = 0; loop < childNodesLength; loop++) {
134                 var childNode = partials.childNodes[loop];
135                 var tagName = childNode.tagName;
136                 /**
137                  * <eval>
138                  *      <![CDATA[javascript]]>
139                  * </eval>
140                  */
141 
142                 //this ought to be enough for eval
143                 //however the run scripts still makes sense
144                 //in the update and insert area for components
145                 //which do not use the response writer properly
146                 //we might add this one as custom option in update and
147                 //insert!
148                 if (tagName == this.CMD_ERROR) {
149                     this.processError(request, context, childNode);
150                 } else if (tagName == this.CMD_REDIRECT) {
151                     this.processRedirect(request, context, childNode);
152                 } else if (tagName == this.CMD_CHANGES) {
153                     this.processChanges(request, context, childNode);
154                 }
155             }
156 
157             //fixup missing viewStates due to spec deficiencies
158             if (mfInternal.appliedViewState) {
159                 this.fixViewStates(context);
160             }
161             if (mfInternal.appliedClientWindow) {
162                 this.fixClientWindows(context);
163             }
164 
165             //spec jsdoc, the success event must be sent from response
166             _Impl.sendEvent(request, context, _Impl["SUCCESS"]);
167         } catch (e) {
168 
169             if (window.console && window.console.error) {
170                 //any error should be logged
171                 console.error(e);
172             }
173             throw e;
174         } finally {
175             delete mfInternal._updateElems;
176             delete mfInternal._updateForms;
177             delete mfInternal.appliedViewState;
178             delete mfInternal.appliedClientWindow;
179             delete mfInternal.namingModeId;
180         }
181     },
182 
183     /**
184      * fixes the viewstates in the current page
185      *
186      * @param context
187      */
188     fixViewStates: function (context) {
189         var _Lang = this._Lang;
190         var mfInternal = context._mfInternal;
191 
192         if (null == mfInternal.appliedViewState) {
193             return;
194         }
195 
196         /**
197          * JSF 2.3 we set all the viewstates under a given declared viewRoot or all forms
198          * if none is given
199          */
200         this._updateJSFClientArtifacts(context,  mfInternal.appliedViewState, this.P_VIEWSTATE);
201     },
202 
203 
204     fixClientWindows: function (context, theForm) {
205         var _Lang = this._Lang;
206         var mfInternal = context._mfInternal;
207 
208         if (null == mfInternal.appliedClientWindow) {
209             return;
210         }
211 
212         /**
213          * JSF 2.3 we set all the viewstates under a given declared viewRoot or all forms
214          * if none is given
215          */
216 
217         this._updateJSFClientArtifacts(context, mfInternal.appliedClientWindow, this.P_CLIENTWINDOW);
218 
219     },
220 
221 
222     /**
223      * sets the a jsf artifact element with a given identifier to a new value or adds this element
224      *
225      * @param theForm {Node} the form to which the element has to be set to
226      * @param context the current request context
227      */
228     _applyJSFArtifactValueToForm: function (context, theForm, value, identifier) {
229 
230         if (!theForm) return;
231         var _Lang = this._Lang;
232         var _Dom = this._Dom;
233         var prefix = this._getPrefix(context);
234 
235         //in IE7 looking up form elements with complex names (such as 'javax.faces.ViewState') fails in certain cases
236         //iterate through the form elements to find the element, instead
237         var fieldsFound = [];
238 
239         var elements = theForm.elements;
240         for (var i = 0, l = elements.length; i < l; i++) {
241             var e = elements[i];
242             if (e.name.indexOf(identifier) != -1) {
243                 fieldsFound.push(e);
244             }
245         }
246 
247         if (fieldsFound.length) {
248             _Lang.arrForEach(fieldsFound, function (fieldFound) {
249                 _Dom.setAttribute(fieldFound, "value", value);
250             });
251         } else {
252             var element = this._Dom.getDummyPlaceHolder();
253 
254             //per JSF 2.3 spec the identifier of the element must be unique in the dom tree
255             //otherwise we will break the html spec here
256             element.innerHTML = ["<input type='hidden'", "id='", this._fetchUniqueId(prefix, identifier), "' name='", identifier, "' value='", value, "' />"].join("");
257             //now we go to proper dom handling after having to deal with another ie screwup
258             try {
259                 theForm.appendChild(element.childNodes[0]);
260             } finally {
261                 element.innerHTML = "";
262             }
263         }
264     },
265 
266     _fetchUniqueId: function(prefix, identifier) {
267         var cnt = 0;
268         var retVal = prefix + identifier + jsf.separatorchar + cnt;
269         while(this._Dom.byId(retVal) != null) {
270             cnt++;
271             retVal = prefix + identifier + jsf.separatorchar + cnt;
272         }
273         return retVal;
274     },
275 
276     /**
277      * updates/inserts the jsf client artifacts under a given viewroot element
278      *
279      * @param context the client context holding all request context data and some internal data
280      * @param elem the root to start with, must be a dom node not an identifier
281      * @param value the new value
282      * @param identifier the identifier for the client artifact aka javax.faces.ViewState, ClientWindowId etc...
283      *
284      * @private
285      */
286     _updateJSFClientArtifacts: function (context, value, identifier) {
287 
288         //elem not found for whatever reason
289         //https://issues.apache.org/jira/browse/MYFACES-3544
290 
291         var prefix = this._getPrefix(context);
292 
293         //do we still need the issuing form update? I guess it is needed.
294         var sourceForm = (context._mfInternal._mfSourceFormId) ? this._Dom.byId(context._mfInternal._mfSourceFormId) : null;
295         if (sourceForm) {
296             sourceForm = this._Dom.byId(sourceForm);
297             if (sourceForm) {
298                 //some cases where the source form cannot be updated
299                 //because it is gone
300                 this._applyJSFArtifactValueToForm(context, sourceForm, value, identifier);
301             }
302         }
303 
304 
305         var viewRoot = this._getViewRoot(context);
306         var forms = this._Dom.findByTagNames(viewRoot, {"form": 1}) || [];
307 
308         //since the spec thanks to the over intrusive portlet api still is broken
309         //we need our old fallback hack for proper handling without having
310         //to deal with multiple render targets.
311         if(this._RT.getLocalOrGlobalConfig(context, "no_portlet_env", false)) {
312 
313             //We update all elements under viewroot
314             //this clearly violates the jsf 2.3 jsdocs
315             //however I think that the jsdocs were sloppily updated
316             //because just updating the render targets under one viewroot and the issuing form
317             //again would leave broken viewstates, in the end the portlet spec is at fault here
318             //which came late to the game and expected all frameworks to adapt to their needs.
319             //instead of properly adapting to the frameworks
320             //now the viewroot mechanism per se would work, but people are dropping
321             //jsf 2.3 into old portlet containers which then expose the legacy behavior
322             //of having just one view root.
323             this._Lang.arrForEach(forms, this._Lang.hitch(this, function (elem) {
324                 //update all forms which start with prefix (all render and execute targets
325                 this._applyJSFArtifactValueToForm(context, elem, value, identifier);
326             }));
327         } else {
328 
329             //follow the spec 2.3 path 1:1 we update the forms hosting the render targets which start
330             //with the viewroot
331             for(var cnt = 0; cnt < context._mfInternal._updateForms.length; cnt++) {
332                 var updateForm = context._mfInternal._updateForms[cnt];
333                 var viewRootId = viewRoot.id || "";
334                 if(updateForm.indexOf(viewRootId) != 0) {
335                     continue;
336                 } else {
337                     this._applyJSFArtifactValueToForm(context, this._Dom.byId(updateForm), value, identifier);
338                 }
339             }
340 
341         }
342 
343     },
344 
345     _getViewRoot: function (context) {
346         var prefix = this._getPrefix(context);
347         if (prefix == "") {
348             return document.getElementsByTagName("body")[0];
349         }
350         prefix = prefix.substr(0, prefix.length - 1);
351         var viewRoot = document.getElementById(prefix);
352         if (viewRoot) {
353             return viewRoot;
354         }
355         return document.getElementsByTagName("body")[0];
356     },
357 
358 
359     _getPrefix: function (context) {
360         var mfInternal = context._mfInternal;
361         var prefix = mfInternal.namingModeId;
362         if (prefix != "") {
363             prefix = prefix + jsf.separatorchar;
364         }
365         return prefix;
366     },
367 
368     /**
369      * processes an incoming error from the response
370      * which is hosted under the <error> tag
371      * @param request the current request
372      * @param context the contect object
373      * @param node the node in the xml hosting the error message
374      */
375     processError: function (request, context, node) {
376         /**
377          * <error>
378          *      <error-name>String</error-name>
379          *      <error-message><![CDATA[message]]></error-message>
380          * <error>
381          */
382         var errorName = node.firstChild.textContent || node.firstChild.text || "",
383             errorMessage = node.childNodes[1].firstChild.data || "";
384 
385         this.attr("impl").sendError(request, context, this.attr("impl").SERVER_ERROR, errorName, errorMessage, "myfaces._impl.xhrCore._AjaxResponse", "processError");
386     },
387 
388     /**
389      * processes an incoming xml redirect directive from the ajax response
390      * @param request the request object
391      * @param context the context
392      * @param node the node hosting the redirect data
393      */
394     processRedirect: function (request, context, node) {
395         /**
396          * <redirect url="url to redirect" />
397          */
398         var _Lang = this._Lang;
399         var redirectUrl = node.getAttribute("url");
400         if (!redirectUrl) {
401             throw this._raiseError(new Error(), _Lang.getMessage("ERR_RED_URL", null, "_AjaxResponse.processRedirect"), "processRedirect");
402         }
403         redirectUrl = _Lang.trim(redirectUrl);
404         if (redirectUrl == "") {
405             return false;
406         }
407         window.location = redirectUrl;
408         return true;
409     }
410     ,
411 
412     /**
413      * main entry point for processing the changes
414      * it deals with the <changes> node of the
415      * response
416      *
417      * @param request the xhr request object
418      * @param context the context map
419      * @param node the changes node to be processed
420      */
421     processChanges: function (request, context, node) {
422         var changes = node.childNodes;
423         var _Lang = this._Lang;
424         //note we need to trace the changes which could affect our insert update or delete
425         //se that we can realign our ViewStates afterwards
426         //the realignment must happen post change processing
427 
428         for (var i = 0; i < changes.length; i++) {
429 
430             switch (changes[i].tagName) {
431 
432                 case this.CMD_UPDATE:
433                     this.processUpdate(request, context, changes[i]);
434                     break;
435                 case this.CMD_EVAL:
436                     _Lang.globalEval(changes[i].firstChild.data);
437                     break;
438                 case this.CMD_INSERT:
439                     this.processInsert(request, context, changes[i]);
440                     break;
441                 case this.CMD_DELETE:
442                     this.processDelete(request, context, changes[i]);
443                     break;
444                 case this.CMD_ATTRIBUTES:
445                     this.processAttributes(request, context, changes[i]);
446                     break;
447                 case this.CMD_EXTENSION:
448                     break;
449                 case undefined:
450                     // ignoring white spaces
451                     break;
452                 default:
453                     throw this._raiseError(new Error(), "_AjaxResponse.processChanges: Illegal Command Issued", "processChanges");
454             }
455         }
456 
457         return true;
458     },
459 
460     /**
461      * First sub-step process a pending update tag
462      *
463      * @param request the xhr request object
464      * @param context the context map
465      * @param node the changes node to be processed
466      */
467     processUpdate: function (request, context, node) {
468         var mfInternal = context._mfInternal;
469         if ((node.getAttribute('id').indexOf(this.P_VIEWSTATE) != -1) || (node.getAttribute('id').indexOf(this.P_CLIENTWINDOW) != -1)) {
470             if (node.getAttribute('id').indexOf(this.P_VIEWSTATE) != -1) {
471                 mfInternal.appliedViewState = this._Dom.concatCDATABlocks(node);//node.firstChild.nodeValue;
472             } else if (node.getAttribute('id').indexOf(this.P_CLIENTWINDOW) != -1) {
473                 mfInternal.appliedClientWindow = node.firstChild.nodeValue;
474             }
475         }
476         else {
477             // response may contain several blocks
478             var cDataBlock = this._Dom.concatCDATABlocks(node),
479                 resultNode = null,
480                 pushOpRes = this._Lang.hitch(this, this._pushOperationResult);
481 
482             switch (node.getAttribute('id')) {
483                 case this.P_VIEWROOT:
484 
485                     cDataBlock = cDataBlock.substring(cDataBlock.indexOf("<html"));
486 
487                     var parsedData = this._replaceHead(request, context, cDataBlock);
488 
489                     ('undefined' != typeof parsedData && null != parsedData) ? this._replaceBody(request, context, cDataBlock, parsedData) : this._replaceBody(request, context, cDataBlock);
490 
491                     break;
492                 case this.P_VIEWHEAD:
493                     //we cannot replace the head, almost no browser allows this, some of them throw errors
494                     //others simply ignore it or replace it and destroy the dom that way!
495                     this._replaceHead(request, context, cDataBlock);
496 
497                     break;
498                 case this.P_VIEWBODY:
499                     //we assume the cdata block is our body including the tag
500                     resultNode = this._replaceBody(request, context, cDataBlock);
501                     if (resultNode) {
502                         pushOpRes(context, resultNode);
503                     }
504                     break;
505                 case this.P_RESOURCE:
506 
507                     this._addResourceToHead(request, context, cDataBlock);
508                     break;
509                 default:
510 
511                     resultNode = this.replaceHtmlItem(request, context, node.getAttribute('id'), cDataBlock);
512                     if (resultNode) {
513                         pushOpRes(context, resultNode);
514                     }
515                     break;
516             }
517         }
518 
519         return true;
520     },
521 
522     _pushOperationResult: function(context, resultNode) {
523         var mfInternal = context._mfInternal;
524         var pushSubnode = this._Lang.hitch(this, function(currNode) {
525             var parentForm = this._Dom.getParent(currNode, "form");
526             //if possible we work over the ids
527             //so that elements later replaced are referenced
528             //at the latest possibility
529             if (null != parentForm) {
530                 mfInternal._updateForms.push(parentForm.id || parentForm);
531             }
532             else {
533                 mfInternal._updateElems.push(currNode.id || currNode);
534             }
535         });
536 
537         var pushEmbedded = this._Lang.hitch(this, function(currNode) {
538             if(currNode.tagName && currNode.tagName == "form") {
539                 mfInternal._updateForms.push(currNode);
540             } else {
541                 var childForms = this._Dom.findByTagName(currNode, "form");
542                 if(childForms && childForms.length) {
543                     for(var cnt = 0; cnt < childForms.lenght; cnt++) {
544                         mfInternal._updateForms.push(childForms[cnt]);
545                     }
546                 }
547             }
548 
549         });
550 
551 
552         var isArr = 'undefined' != typeof resultNode.length && 'undefined' == typeof resultNode.nodeType;
553         if (isArr && resultNode.length) {
554             for (var cnt = 0; cnt < resultNode.length; cnt++) {
555                 pushSubnode(resultNode[cnt]);
556                 pushEmbedded(resultNode[cnt]);
557             }
558         } else if (!isArr) {
559             pushSubnode(resultNode);
560             pushEmbedded(resultNode);
561         }
562 
563     },
564 
565 
566     /**
567      * replaces a current head theoretically,
568      * pratically only the scripts are evaled anew since nothing else
569      * can be changed.
570      *
571      * @param request the current request
572      * @param context the ajax context
573      * @param newData the data to be processed
574      *
575      * @return an xml representation of the page for further processing if possible
576      */
577     _replaceHead: function (request, context, newData) {
578 
579         var _Lang = this._Lang,
580             _Dom = this._Dom,
581             isWebkit = this._RT.browser.isWebKit,
582             //we have to work around an xml parsing bug in Webkit
583             //see https://issues.apache.org/jira/browse/MYFACES-3061
584             doc = (!isWebkit) ? _Lang.parseXML(newData) : null,
585             newHead = null;
586 
587         if (!isWebkit && _Lang.isXMLParseError(doc)) {
588             doc = _Lang.parseXML(newData.replace(/<!\-\-[\s\n]*<!\-\-/g, "<!--").replace(/\/\/-->[\s\n]*\/\/-->/g, "//-->"));
589         }
590 
591         if (isWebkit || _Lang.isXMLParseError(doc)) {
592             //the standard xml parser failed we retry with the stripper
593             var parser = new (this._RT.getGlobalConfig("updateParser", myfaces._impl._util._HtmlStripper))();
594             var headData = parser.parse(newData, "head");
595             //We cannot avoid it here, but we have reduced the parsing now down to the bare minimum
596             //for further processing
597             newHead = _Lang.parseXML("<head>" + headData + "</head>");
598             //last and slowest option create a new head element and let the browser
599             //do its slow job
600             if (_Lang.isXMLParseError(newHead)) {
601                 try {
602                     newHead = _Dom.createElement("head");
603                     newHead.innerHTML = headData;
604                 } catch (e) {
605                     //we give up no further fallbacks
606                     throw this._raiseError(new Error(), "Error head replacement failed reason:" + e.toString(), "_replaceHead");
607                 }
608             }
609         } else {
610             //parser worked we go on
611             newHead = doc.getElementsByTagName("head")[0];
612         }
613 
614         var oldTags = _Dom.findByTagNames(document.getElementsByTagName("head")[0], {"link": true, "style": true});
615         _Dom.runCss(newHead, true);
616         _Dom.deleteItems(oldTags);
617 
618         //var oldTags = _Dom.findByTagNames(document.getElementsByTagName("head")[0], {"script": true});
619         //_Dom.deleteScripts(oldTags);
620         _Dom.runScripts(newHead, true);
621 
622         return doc;
623     },
624 
625     _addResourceToHead: function (request, context, newData) {
626         var lastHeadChildTag = document.getElementsByTagName("head")[0].lastChild;
627 
628         this._Dom.insertAfter(lastHeadChildTag, newData);
629 
630     },
631 
632     /**
633      * special method to handle the body dom manipulation,
634      * replacing the entire body does not work fully by simply adding a second body
635      * and by creating a range instead we have to work around that by dom creating a second
636      * body and then filling it properly!
637      *
638      * @param {Object} request our request object
639      * @param {Object} context (Map) the response context
640      * @param {String} newData the markup which replaces the old dom node!
641      * @param {Node} parsedData (optional) preparsed XML representation data of the current document
642      */
643     _replaceBody: function (request, context, newData /*varargs*/) {
644         var _RT = this._RT,
645             _Dom = this._Dom,
646             _Lang = this._Lang,
647 
648             oldBody = document.getElementsByTagName("body")[0],
649             placeHolder = document.createElement("div"),
650             isWebkit = _RT.browser.isWebKit;
651 
652         placeHolder.id = "myfaces_bodyplaceholder";
653 
654         _Dom._removeChildNodes(oldBody);
655         oldBody.innerHTML = "";
656         oldBody.appendChild(placeHolder);
657 
658         var bodyData, doc = null, parser;
659 
660         //we have to work around an xml parsing bug in Webkit
661         //see https://issues.apache.org/jira/browse/MYFACES-3061
662         if (!isWebkit) {
663             doc = (arguments.length > 3) ? arguments[3] : _Lang.parseXML(newData);
664         }
665 
666         if (!isWebkit && _Lang.isXMLParseError(doc)) {
667             doc = _Lang.parseXML(newData.replace(/<!\-\-[\s\n]*<!\-\-/g, "<!--").replace(/\/\/-->[\s\n]*\/\/-->/g, "//-->"));
668         }
669 
670         if (isWebkit || _Lang.isXMLParseError(doc)) {
671             //the standard xml parser failed we retry with the stripper
672 
673             parser = new (_RT.getGlobalConfig("updateParser", myfaces._impl._util._HtmlStripper))();
674 
675             bodyData = parser.parse(newData, "body");
676         } else {
677             //parser worked we go on
678             var newBodyData = doc.getElementsByTagName("body")[0];
679 
680             //speedwise we serialize back into the code
681             //for code reduction, speedwise we will take a small hit
682             //there which we will clean up in the future, but for now
683             //this is ok, I guess, since replace body only is a small subcase
684             //bodyData = _Lang.serializeChilds(newBodyData);
685             var browser = _RT.browser;
686             if (!browser.isIEMobile || browser.isIEMobile >= 7) {
687                 //TODO check what is failing there
688                 for (var cnt = 0; cnt < newBodyData.attributes.length; cnt++) {
689                     var value = newBodyData.attributes[cnt].value;
690                     if (value)
691                         _Dom.setAttribute(oldBody, newBodyData.attributes[cnt].name, value);
692                 }
693             }
694         }
695         //we cannot serialize here, due to escape problems
696         //we must parse, this is somewhat unsafe but should be safe enough
697         parser = new (_RT.getGlobalConfig("updateParser", myfaces._impl._util._HtmlStripper))();
698         bodyData = parser.parse(newData, "body");
699 
700         var returnedElement = this.replaceHtmlItem(request, context, placeHolder, bodyData);
701 
702         if (returnedElement) {
703             this._pushOperationResult(context, returnedElement);
704         }
705         return returnedElement;
706     },
707 
708     /**
709      * Replaces HTML elements through others and handle errors if the occur in the replacement part
710      *
711      * @param {Object} request (xhrRequest)
712      * @param {Object} context (Map)
713      * @param {Object} itemIdToReplace (String|Node) - ID of the element to replace
714      * @param {String} markup - the new tag
715      */
716     replaceHtmlItem: function (request, context, itemIdToReplace, markup) {
717         var _Lang = this._Lang, _Dom = this._Dom;
718 
719         var item = (!_Lang.isString(itemIdToReplace)) ? itemIdToReplace :
720             _Dom.byIdOrName(itemIdToReplace);
721 
722         if (!item) {
723             throw this._raiseError(new Error(), _Lang.getMessage("ERR_ITEM_ID_NOTFOUND", null, "_AjaxResponse.replaceHtmlItem", (itemIdToReplace) ? itemIdToReplace.toString() : "undefined"), "replaceHtmlItem");
724         }
725         return _Dom.outerHTML(item, markup, this._RT.getLocalOrGlobalConfig(context, "preserveFocus", false));
726     },
727 
728     /**
729      * xml insert command handler
730      *
731      * @param request the ajax request element
732      * @param context the context element holding the data
733      * @param node the xml node holding the insert data
734      * @return true upon successful completion, false otherwise
735      *
736      **/
737     processInsert: function (request, context, node) {
738         /*remapping global namespaces for speed and readability reasons*/
739         var _Dom = this._Dom,
740             _Lang = this._Lang,
741             //determine which path to go:
742             insertData = this._parseInsertData(request, context, node);
743 
744         if (!insertData) return false;
745 
746         var opNode = _Dom.byIdOrName(insertData.opId);
747         if (!opNode) {
748             throw this._raiseError(new Error(), _Lang.getMessage("ERR_PPR_INSERTBEFID_1", null, "_AjaxResponse.processInsert", insertData.opId), "processInsert");
749         }
750 
751         //call insertBefore or insertAfter in our dom routines
752         var replacementFragment = _Dom[insertData.insertType](opNode, insertData.cDataBlock);
753         if (replacementFragment) {
754             this._pushOperationResult(context, replacementFragment);
755         }
756         return true;
757     },
758 
759     /**
760      * determines the corner data from the insert tag parsing process
761      *
762      *
763      * @param request request
764      * @param context context
765      * @param node the current node pointing to the insert tag
766      * @return false if the parsing failed, otherwise a map with follwing attributes
767      * <ul>
768      *     <li>inserType - a ponter to a constant which maps the direct function name for the insert operation </li>
769      *     <li>opId - the before or after id </li>
770      *     <li>cDataBlock - the html cdata block which needs replacement </li>
771      * </ul>
772      *
773      * TODO we have to find a mechanism to replace the direct sendError calls with a javascript exception
774      * which we then can use for cleaner error code handling
775      */
776     _parseInsertData: function (request, context, node) {
777         var _Lang = this._Lang,
778             _Dom = this._Dom,
779             concatCDATA = _Dom.concatCDATABlocks,
780 
781             INSERT_TYPE_BEFORE = "insertBefore",
782             INSERT_TYPE_AFTER = "insertAfter",
783 
784             id = node.getAttribute("id"),
785             beforeId = node.getAttribute("before"),
786             afterId = node.getAttribute("after"),
787             ret = {};
788 
789         //now we have to make a distinction between two different parsing paths
790         //due to a spec malalignment
791         //a <insert id="... beforeId|AfterId ="...
792         //b <insert><before id="..., <insert> <after id="....
793         //see https://issues.apache.org/jira/browse/MYFACES-3318
794         //simple id, case1
795         if (id && beforeId && !afterId) {
796             ret.insertType = INSERT_TYPE_BEFORE;
797             ret.opId = beforeId;
798             ret.cDataBlock = concatCDATA(node);
799 
800             //<insert id=".. afterId="..
801         } else if (id && !beforeId && afterId) {
802             ret.insertType = INSERT_TYPE_AFTER;
803             ret.opId = afterId;
804             ret.cDataBlock = concatCDATA(node);
805 
806             //<insert><before id="... <insert><after id="...
807         } else if (!id) {
808             var opType = node.childNodes[0].tagName;
809 
810             if (opType != "before" && opType != "after") {
811                 throw this._raiseError(new Error(), _Lang.getMessage("ERR_PPR_INSERTBEFID"), "_parseInsertData");
812             }
813             opType = opType.toLowerCase();
814             var beforeAfterId = node.childNodes[0].getAttribute("id");
815             ret.insertType = (opType == "before") ? INSERT_TYPE_BEFORE : INSERT_TYPE_AFTER;
816             ret.opId = beforeAfterId;
817             ret.cDataBlock = concatCDATA(node.childNodes[0]);
818         } else {
819             throw this._raiseError(new Error(), [_Lang.getMessage("ERR_PPR_IDREQ"),
820                 "\n ",
821                 _Lang.getMessage("ERR_PPR_INSERTBEFID")].join(""), "_parseInsertData");
822         }
823         ret.opId = _Lang.trim(ret.opId);
824         return ret;
825     },
826 
827     processDelete: function (request, context, node) {
828 
829         var _Lang = this._Lang,
830             _Dom = this._Dom,
831             deleteId = node.getAttribute('id');
832 
833         if (!deleteId) {
834             throw this._raiseError(new Error(), _Lang.getMessage("ERR_PPR_UNKNOWNCID", null, "_AjaxResponse.processDelete", ""), "processDelete");
835         }
836 
837         var item = _Dom.byIdOrName(deleteId);
838         if (!item) {
839             throw this._raiseError(new Error(), _Lang.getMessage("ERR_PPR_UNKNOWNCID", null, "_AjaxResponse.processDelete", deleteId), "processDelete");
840         }
841 
842         var parentForm = this._Dom.getParent(item, "form");
843         if (null != parentForm) {
844             context._mfInternal._updateForms.push(parentForm);
845         }
846         _Dom.deleteItem(item);
847 
848         return true;
849     },
850 
851     processAttributes: function (request, context, node) {
852         //we now route into our attributes function to bypass
853         //IE quirks mode incompatibilities to the biggest possible extent
854         //most browsers just have to do a setAttributes but IE
855         //behaves as usual not like the official standard
856         //myfaces._impl._util.this._Dom.setAttribute(domNode, attribute, value;
857 
858         var _Lang = this._Lang,
859             //<attributes id="id of element"> <attribute name="attribute name" value="attribute value" />* </attributes>
860             elemId = node.getAttribute('id');
861 
862         if (!elemId) {
863             throw this._raiseError(new Error(), "Error in attributes, id not in xml markup", "processAttributes");
864         }
865         var childNodes = node.childNodes;
866 
867         if (!childNodes) {
868             return false;
869         }
870         for (var loop2 = 0; loop2 < childNodes.length; loop2++) {
871             var attributesNode = childNodes[loop2],
872                 attrName = attributesNode.getAttribute("name"),
873                 attrValue = attributesNode.getAttribute("value");
874 
875             if (!attrName) {
876                 continue;
877             }
878 
879             attrName = _Lang.trim(attrName);
880             /*no value means reset*/
881             //value can be of boolean value hence full check
882             if ('undefined' == typeof attrValue || null == attrValue) {
883                 attrValue = "";
884             }
885 
886             switch (elemId) {
887                 case this.P_VIEWROOT:
888                     throw  this._raiseError(new Error(), _Lang.getMessage("ERR_NO_VIEWROOTATTR", null, "_AjaxResponse.processAttributes"), "processAttributes");
889 
890                 case this.P_VIEWHEAD:
891                     throw  this._raiseError(new Error(), _Lang.getMessage("ERR_NO_HEADATTR", null, "_AjaxResponse.processAttributes"), "processAttributes");
892 
893                 case this.P_VIEWBODY:
894                     var element = document.getElementsByTagName("body")[0];
895                     this._Dom.setAttribute(element, attrName, attrValue);
896                     break;
897 
898                 default:
899                     this._Dom.setAttribute(document.getElementById(elemId), attrName, attrValue);
900                     break;
901             }
902         }
903         return true;
904     },
905 
906     /**
907      * internal helper which raises an error in the
908      * format we need for further processing
909      *
910      * @param message the message
911      * @param title the title of the error (optional)
912      * @param name the name of the error (optional)
913      */
914     _raiseError: function (error, message, caller, title, name) {
915         var _Impl = this.attr("impl");
916         var finalTitle = title || _Impl.MALFORMEDXML;
917         var finalName = name || _Impl.MALFORMEDXML;
918         var finalMessage = message || "";
919 
920         return this._Lang.makeException(error, finalTitle, finalName, this._nameSpace, caller || ( (arguments.caller) ? arguments.caller.toString() : "_raiseError"), finalMessage);
921     }
922 });
923