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
|| {};
12 remoting
.TelemetryEventWriter = function() {};
16 WRITE
: 'remoting.TelemetryEventWriter.write'
20 * @param {base.Ipc} ipc
21 * @param {remoting.XhrEventWriter} eventWriter
23 * @implements {base.Disposable}
25 remoting
.TelemetryEventWriter
.Service = function(ipc
, eventWriter
) {
27 this.eventWriter_
= eventWriter
;
30 /** @private {base.Disposables} */
31 this.eventHooks_
= new base
.Disposables();
34 this.sessionMonitor_
= new SessionMonitor(this.eventWriter_
);
37 /** @return {Promise} */
38 remoting
.TelemetryEventWriter
.Service
.prototype.init = function() {
39 /** @this {remoting.TelemetryEventWriter.Service} */
42 new base
.DomEventHook(window
, 'online',
43 this.eventWriter_
.flush
.bind(this.eventWriter_
),
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();
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;
63 * Unbind any sessions that are associated with |windowId|.
64 * @param {string} windowId
66 remoting
.TelemetryEventWriter
.Service
.prototype.unbindSession
=
68 this.sessionMonitor_
.unbindSession(windowId
);
72 * @param {string} windowId The source window id of the IPC.
73 * @param {!Object} event The event to be written to the server.
75 remoting
.TelemetryEventWriter
.Service
.prototype.write
=
76 function(windowId
, event
) {
77 this.sessionMonitor_
.trackSessionStateChanges(windowId
, event
);
78 this.eventWriter_
.write(event
);
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
,
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
105 remoting
.TelemetryEventWriter
.Client
.write = function(event
) {
106 return base
.Ipc
.invoke(IpcNames
.WRITE
, chrome
.app
.window
.current().id
, event
);
113 * @param {remoting.ChromotingEvent} event
115 function SessionInfo(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
129 var SessionMonitor = function(eventWriter
) {
131 this.eventWriter_
= eventWriter
;
132 /** @private {Map<string, SessionInfo>} */
133 this.sessionMap_
= new Map();
137 * @param {string} windowId
138 * @param {Object} entry
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
) {
147 if (remoting
.ChromotingEvent
.isEndOfSession(event
)) {
148 this.sessionMap_
.delete(windowId
);
150 this.sessionMap_
.set(windowId
, new SessionInfo(event
));
155 * Unbinds a session with |windowId| and log any close events if necessary.
156 * @param {string} windowId
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
);
169 * @param {SessionInfo} sessionInfo
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
;
182 event
.session_state
= SessionState
.CLOSED
;
184 var elapsed
= (Date
.now() - sessionInfo
.timestamp
) / 1000.0;
185 event
.session_duration
+= elapsed
;