Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / remoting / webapp / base / js / xhr_event_writer.js
blobd4acdb980a10b9e0ba99df7627edf3df92221345
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 /**
13 * A class that writes log events to our back-end using XHR.
14 * If a log request fails due to network failure, it will be stored to
15 * |storage| for retrying in the future by calling flush().
17 * @param {!string} url
18 * @param {!StorageArea} storage
19 * @param {!string} storageKey
20 * @constructor
22 remoting.XhrEventWriter = function(url, storage, storageKey) {
23 /** @private */
24 this.url_ = url;
25 /** @private */
26 this.storage_ = storage;
27 /** @private @const */
28 this.storageKey_ = storageKey;
29 /** @private */
30 this.pendingRequests_ = new Map();
31 /** @private {base.Deferred} */
32 this.pendingFlush_ = null;
35 /**
36 * @return {Promise} A promise that resolves when initialization is completed.
38 remoting.XhrEventWriter.prototype.loadPendingRequests = function() {
39 var that = this;
40 var deferred = new base.Deferred();
41 this.storage_.get(this.storageKey_, function(entry) {
42 try {
43 that.pendingRequests_ = new Map(entry[that.storageKey_]);
44 } catch(e) {
45 that.pendingRequests_ = new Map();
47 deferred.resolve(that.pendingRequests_);
48 });
49 return deferred.promise();
52 /**
53 * @param {Object} event The event to be written to the server.
54 * @return {Promise} A promise that resolves on success.
56 remoting.XhrEventWriter.prototype.write = function(event) {
57 console.log('Writing Event - ' + JSON.stringify(event));
58 this.markPending_(event);
59 return this.flush();
62 /**
63 * @return {Promise} A promise that resolves on success.
65 remoting.XhrEventWriter.prototype.flush = function() {
66 if (!this.pendingFlush_) {
67 var that = this;
68 this.pendingFlush_ = new base.Deferred();
70 var onFailure = function(/** * */e) {
71 that.pendingFlush_.reject(e);
72 that.pendingFlush_ = null;
75 var flushAll = function() {
76 if (that.pendingRequests_.size > 0) {
77 that.doFlush_().then(flushAll, onFailure);
78 } else {
79 that.pendingFlush_.resolve();
80 that.pendingFlush_ = null;
84 // Ensures that |this.pendingFlush_| won't be set to null
85 // in the same stack frame.
86 Promise.resolve().then(flushAll);
89 return this.pendingFlush_.promise();
92 /**
93 * @return {Promise} A promise that resolves on success.
94 * @private
96 remoting.XhrEventWriter.prototype.doFlush_ = function() {
97 var payLoad = [];
98 var requestIds = [];
100 // Map.forEach enumerates the entires of the map in insertion order.
101 this.pendingRequests_.forEach(
102 function(/** Object */ event, /** string */ requestId) {
103 requestIds.push(requestId);
104 payLoad.push(event);
107 return this.doXhr_(requestIds, {'event': payLoad});
111 * @return {Promise} A promise that resolves when the pending requests are
112 * written to disk.
114 remoting.XhrEventWriter.prototype.writeToStorage = function() {
115 var deferred = new base.Deferred();
116 var map = [];
117 this.pendingRequests_.forEach(
118 function(/** Object */ request, /** string */ id) {
119 map.push([id, request]);
122 var entry = {};
123 entry[this.storageKey_] = map;
124 this.storage_.set(entry, deferred.resolve.bind(deferred));
125 return deferred.promise();
129 * @param {Array<string>} requestIds
130 * @param {Object} event
131 * @return {Promise}
132 * @private
134 remoting.XhrEventWriter.prototype.doXhr_ = function(requestIds, event) {
135 var that = this;
136 var XHR_RETRY_ATTEMPTS = 20;
137 var xhr = new remoting.AutoRetryXhr(
138 {method: 'POST', url: this.url_, jsonContent: event, useIdentity: true},
139 XHR_RETRY_ATTEMPTS);
140 return xhr.start().then(function(response) {
141 var error = remoting.Error.fromHttpStatus(response.status);
142 // Only store requests that are failed with NETWORK_FAILURE, so that
143 // malformed requests won't be stuck in the client forever.
144 if (!error.hasTag(remoting.Error.Tag.NETWORK_FAILURE)) {
145 requestIds.forEach(function(/** string */ requestId) {
146 that.pendingRequests_.delete(requestId);
149 if (!error.isNone()) {
150 throw error;
156 * @param {number} length
157 * @return {string} A random string of length |length|
159 function randomString(length) {
160 var random = new Uint8Array(length);
161 window.crypto.getRandomValues(random);
162 return window.btoa(String.fromCharCode.apply(null, random));
166 * @param {Object} event
167 * @private
169 remoting.XhrEventWriter.prototype.markPending_ = function(event) {
170 var requestId = Date.now() + '_' + randomString(16);
171 console.assert(!this.pendingRequests_.has(requestId),
172 'There is already an event with id ' + requestId + '.');
173 this.pendingRequests_.set(requestId, event);
176 })();