Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / remoting / webapp / base / js / telemetry_event_writer.js
blob87531574b723aee2005bec2b42784ccd9a89b822
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.
5 /** @suppress {duplicate} */
6 var remoting = remoting || {};
8 (function() {
10 'use strict';
12 remoting.TelemetryEventWriter = function() {};
14 /** @enum {string} */
15 var IpcNames = {
16   WRITE: 'remoting.TelemetryEventWriter.write'
19 /**
20  * @param {base.Ipc} ipc
21  * @param {remoting.XhrEventWriter} eventWriter
22  * @constructor
23  * @implements {base.Disposable}
24  */
25 remoting.TelemetryEventWriter.Service = function(ipc, eventWriter) {
26   /** @private */
27   this.eventWriter_ = eventWriter;
28   /** @private */
29   this.ipc_ = ipc;
30   /** @private {base.Disposables} */
31   this.eventHooks_ = new base.Disposables();
33   /** @private */
34   this.sessionMonitor_ = new SessionMonitor(this.eventWriter_);
37 /** @return {Promise} */
38 remoting.TelemetryEventWriter.Service.prototype.init = function() {
39   /** @this {remoting.TelemetryEventWriter.Service} */
40   function init() {
41     this.eventHooks_.add(
42         new base.DomEventHook(window, 'online',
43                               this.eventWriter_.flush.bind(this.eventWriter_),
44                               false),
45         new base.ChromeEventHook(chrome.runtime.onSuspend,
46                                  this.onSuspend_.bind(this)));
48     this.ipc_.register(IpcNames.WRITE, this.write.bind(this));
49     this.eventWriter_.flush();
50   }
51   // Only listen for new incoming requests after we have loaded the pending
52   // ones.  This will ensure that we always process the requests in order.
53   return this.eventWriter_.loadPendingRequests().then(init.bind(this));
56 remoting.TelemetryEventWriter.Service.prototype.dispose = function() {
57   this.ipc_.unregister(IpcNames.WRITE);
58   base.dispose(this.eventHooks_);
59   this.eventHooks_ = null;
62 /**
63  * Unbind any sessions that are associated with |windowId|.
64  * @param {string} windowId
65  */
66 remoting.TelemetryEventWriter.Service.prototype.unbindSession =
67   function(windowId) {
68     this.sessionMonitor_.unbindSession(windowId);
71 /**
72  * @param {string} windowId  The source window id of the IPC.
73  * @param {!Object} event  The event to be written to the server.
74  */
75 remoting.TelemetryEventWriter.Service.prototype.write =
76     function(windowId, event) {
77   this.sessionMonitor_.trackSessionStateChanges(windowId, event);
78   this.eventWriter_.write(event);
81 /**
82  * @private
83  */
84 remoting.TelemetryEventWriter.Service.prototype.onSuspend_ = function() {
85   this.eventWriter_.writeToStorage();
88 /** @return {remoting.TelemetryEventWriter.Service} */
89 remoting.TelemetryEventWriter.Service.create = function() {
90   return new remoting.TelemetryEventWriter.Service(
91       base.Ipc.getInstance(),
92       new remoting.XhrEventWriter(
93           remoting.settings.TELEMETRY_API_BASE_URL,
94           chrome.storage.local,
95           'pending-log-requests'));
98 remoting.TelemetryEventWriter.Client = function() {};
101  * @param {!Object} event
102  * @return {Promise} A promise that resolves when the log message is sent to the
103  *     logging service.
104  */
105 remoting.TelemetryEventWriter.Client.write = function(event) {
106   return base.Ipc.invoke(IpcNames.WRITE, chrome.app.window.current().id, event);
111  * @struct
112  * @constructor
113  * @param {remoting.ChromotingEvent} event
114  */
115 function SessionInfo(event) {
116   this.event = event;
117   this.timestamp = Date.now();
121  * When a window is closed using the context menu, the foreground page doesn't
122  * have a chance to intercept the close event.
123  * This class keeps track of all foreground windows with ongoing sessions, so
124  * that we can report session termination when they are closed.
126  * @param {remoting.XhrEventWriter} eventWriter
127  * @constructor
128  */
129 var SessionMonitor = function(eventWriter) {
130   /** @private */
131   this.eventWriter_ = eventWriter;
132   /** @private {Map<string, SessionInfo>} */
133   this.sessionMap_ = new Map();
137  * @param {string} windowId
138  * @param {Object} entry
139  */
140 SessionMonitor.prototype.trackSessionStateChanges = function(windowId, entry) {
141   var event = /** @type {remoting.ChromotingEvent} */ (base.deepCopy(entry));
143   if (event.type !== remoting.ChromotingEvent.Type.SESSION_STATE) {
144     return;
145   }
147   if (remoting.ChromotingEvent.isEndOfSession(event)) {
148     this.sessionMap_.delete(windowId);
149   } else {
150     this.sessionMap_.set(windowId, new SessionInfo(event));
151   }
155  * Unbinds a session with |windowId| and log any close events if necessary.
156  * @param {string} windowId
157  */
158 SessionMonitor.prototype.unbindSession = function(windowId) {
159   if (this.sessionMap_.has(windowId)) {
160     var sessionInfo = this.sessionMap_.get(windowId);
161     console.assert(sessionInfo !== undefined);
162     inferSessionEndEvent(/** @type {SessionInfo} */ (sessionInfo));
163     this.eventWriter_.write(/** @type {Object} */ (sessionInfo.event));
164     this.sessionMap_.delete(windowId);
165   }
169  * @param {SessionInfo} sessionInfo
170  */
171 function inferSessionEndEvent(sessionInfo) {
172   var event = sessionInfo.event;
173   var SessionState = remoting.ChromotingEvent.SessionState;
175   switch (event.session_state) {
176     case SessionState.INITIALIZING:
177     case SessionState.CONNECTING:
178     case SessionState.AUTHENTICATED:
179       event.session_state = SessionState.CONNECTION_CANCELED;
180       break;
181     default:
182       event.session_state = SessionState.CLOSED;
183   }
184   var elapsed = (Date.now() - sessionInfo.timestamp) / 1000.0;
185   event.session_duration +=  elapsed;
188 })();