Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / remoting / webapp / base / js / xmpp_error_cache.js
blob79a91385ad3cf90b540390ede38e9a3cc507ca9c
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
6 /** @suppress {duplicate} */
7 var remoting = remoting || {};
9 (function() {
11 'use strict';
13 /**
14  * This class monitors incoming XMPP stanza and records the first
15  * error encountered.  It also strips all PII from the error stanza,
16  * so that it can be uploaded to the cloud for error analysis.
17  *
18  * @constructor
19  */
21 remoting.XmppErrorCache = function() {
22   /** @private {remoting.ChromotingEvent.XmppError} */
23   this.firstError_ = null;
26 /**
27  * @return {remoting.ChromotingEvent.XmppError} The first XMPP error that the
28  *     monitor encountered.  Returns null if no errors have been encountered so
29  *     far.
30  */
31 remoting.XmppErrorCache.prototype.getFirstError = function() {
32   return this.firstError_;
35 /**
36  * Monitor the incoming stanza for error.
37  *
38  * @param {Element} iqNode
39  */
40 remoting.XmppErrorCache.prototype.processStanza = function(iqNode) {
41   if (this.firstError_ != null) {
42     return;
43   }
44   // The XML structure is as follows:
45   // <iq type='error'>
46   //   <jingle action='session-accept'/>
47   //   <error type='modify' code=''/>
48   // </iq>
49   if (iqNode.getAttribute('type') != 'error') {
50     return;
51   }
53   var strippedStanza = this.stripPII_(iqNode);
54   this.firstError_ = new remoting.ChromotingEvent.XmppError(strippedStanza);
57 /**
58  * @param {Element} iqNode
59  * @return {string} Return a string representation of |iqNode| with all PII
60  *    removed.
61  * @private
62  */
63 remoting.XmppErrorCache.prototype.stripPII_ = function(iqNode) {
64   var parser = new DOMParser();
65   var outDocument = parser.parseFromString('<iq/>', 'text/xml');
66   stripPII(iqNode, outDocument.firstElementChild);
67   return new XMLSerializer().serializeToString(outDocument.firstElementChild);
70 /**
71  * @param {Element} node
72  * @param {Element} outNode
73  */
74 function stripPII(node, outNode) {
75   var attributesWhiteList = new Set([
76     'action',
77     'code',
78     'codec',
79     'creator',
80     'height',
81     'id',
82     'name',
83     'sid',
84     'supported-methods',
85     'transport',
86     'type',
87     'version',
88     'width',
89     'xmlns',
90   ]);
92   var nodesWhiteList = new Set([
93     'jingle',
94     'content',
95     'description',
96     'error',
97     'standard-ice',
98     'control',
99     'event',
100     'video',
101     'audio',
102     'initial-resolution',
103     'authentication',
104     'quic-config',
105     'service-unavailable'
106   ]);
108   // Strip PII from attributes.
109   var attributes = node.attributes;
110   for (var i = 0; i < attributes.length; i++) {
111     var attribute = /** @type {Attr} */ (attributes[i]);
112     var value = 'REDACTED';
113     if (attributesWhiteList.has(attribute.nodeName)) {
114       value = attribute.nodeValue;
115     }
116     outNode.setAttribute(attribute.nodeName, value);
117   }
119   // Copy the text content.
120   if (node.childNodes.length == 1 &&
121       node.firstChild.nodeType == Node.TEXT_NODE) {
122     var textValue = 'REDACTED';
123     if (nodesWhiteList.has(node.tagName)) {
124       textValue = node.firstChild.nodeValue;
125     }
126     outNode.appendChild(outNode.ownerDocument.createTextNode(textValue));
127     return;
128   }
130   // Strip PII from child nodes.
131   var children = node.children;
132   for (i = 0; i < children.length; i++) {
133     var child = /** @type{Element}*/ (children[i]);
134     var childCopy = outNode.ownerDocument.createElement(child.tagName);
135     outNode.appendChild(childCopy);
136     stripPII(child, childCopy);
137   }
140 })();