cygprofile: increase timeouts to allow showing web contents
[chromium-blink-merge.git] / chrome / browser / resources / cryptotoken / multiplesigner.js
blob1c4ffc29b18b94b45069fd55d0d8447e5027313e
1 // Copyright 2014 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 /**
6 * @fileoverview A multiple gnubby signer wraps the process of opening a number
7 * of gnubbies, signing each challenge in an array of challenges until a
8 * success condition is satisfied, and yielding each succeeding gnubby.
11 'use strict';
13 /**
14 * @typedef {{
15 * code: number,
16 * gnubbyId: GnubbyDeviceId,
17 * challenge: (SignHelperChallenge|undefined),
18 * info: (ArrayBuffer|undefined)
19 * }}
21 var MultipleSignerResult;
23 /**
24 * Creates a new sign handler that manages signing with all the available
25 * gnubbies.
26 * @param {boolean} forEnroll Whether this signer is signing for an attempted
27 * enroll operation.
28 * @param {function(boolean)} allCompleteCb Called when this signer completes
29 * sign attempts, i.e. no further results will be produced. The parameter
30 * indicates whether any gnubbies are present that have not yet produced a
31 * final result.
32 * @param {function(MultipleSignerResult, boolean)} gnubbyCompleteCb
33 * Called with each gnubby/challenge that yields a final result, along with
34 * whether this signer expects to produce more results. The boolean is a
35 * hint rather than a promise: it's possible for this signer to produce
36 * further results after saying it doesn't expect more, or to fail to
37 * produce further results after saying it does.
38 * @param {number} timeoutMillis A timeout value, beyond whose expiration the
39 * signer will not attempt any new operations, assuming the caller is no
40 * longer interested in the outcome.
41 * @param {string=} opt_logMsgUrl A URL to post log messages to.
42 * @constructor
44 function MultipleGnubbySigner(forEnroll, allCompleteCb, gnubbyCompleteCb,
45 timeoutMillis, opt_logMsgUrl) {
46 /** @private {boolean} */
47 this.forEnroll_ = forEnroll;
48 /** @private {function(boolean)} */
49 this.allCompleteCb_ = allCompleteCb;
50 /** @private {function(MultipleSignerResult, boolean)} */
51 this.gnubbyCompleteCb_ = gnubbyCompleteCb;
52 /** @private {string|undefined} */
53 this.logMsgUrl_ = opt_logMsgUrl;
55 /** @private {Array<SignHelperChallenge>} */
56 this.challenges_ = [];
57 /** @private {boolean} */
58 this.challengesSet_ = false;
59 /** @private {boolean} */
60 this.complete_ = false;
61 /** @private {number} */
62 this.numComplete_ = 0;
63 /** @private {!Object<string, GnubbyTracker>} */
64 this.gnubbies_ = {};
65 /** @private {Countdown} */
66 this.timer_ = DEVICE_FACTORY_REGISTRY.getCountdownFactory()
67 .createTimer(timeoutMillis);
68 /** @private {Countdown} */
69 this.reenumerateTimer_ = DEVICE_FACTORY_REGISTRY.getCountdownFactory()
70 .createTimer(timeoutMillis);
73 /**
74 * @typedef {{
75 * index: string,
76 * signer: SingleGnubbySigner,
77 * stillGoing: boolean,
78 * errorStatus: number
79 * }}
81 var GnubbyTracker;
83 /**
84 * Closes this signer's gnubbies, if any are open.
86 MultipleGnubbySigner.prototype.close = function() {
87 for (var k in this.gnubbies_) {
88 this.gnubbies_[k].signer.close();
90 this.reenumerateTimer_.clearTimeout();
91 this.timer_.clearTimeout();
92 if (this.reenumerateIntervalTimer_) {
93 this.reenumerateIntervalTimer_.clearTimeout();
97 /**
98 * Begins signing the given challenges.
99 * @param {Array<SignHelperChallenge>} challenges The challenges to sign.
100 * @return {boolean} whether the challenges were successfully added.
102 MultipleGnubbySigner.prototype.doSign = function(challenges) {
103 if (this.challengesSet_) {
104 // Can't add new challenges once they're finalized.
105 return false;
108 if (challenges) {
109 for (var i = 0; i < challenges.length; i++) {
110 var decodedChallenge = {};
111 var challenge = challenges[i];
112 decodedChallenge['challengeHash'] =
113 B64_decode(challenge['challengeHash']);
114 decodedChallenge['appIdHash'] = B64_decode(challenge['appIdHash']);
115 decodedChallenge['keyHandle'] = B64_decode(challenge['keyHandle']);
116 if (challenge['version']) {
117 decodedChallenge['version'] = challenge['version'];
119 this.challenges_.push(decodedChallenge);
122 this.challengesSet_ = true;
123 this.enumerateGnubbies_();
124 return true;
128 * Signals this signer to rescan for gnubbies. Useful when the caller has
129 * knowledge that the last device has been removed, and can notify this class
130 * before it will discover it on its own.
132 MultipleGnubbySigner.prototype.reScanDevices = function() {
133 if (this.reenumerateIntervalTimer_) {
134 this.reenumerateIntervalTimer_.clearTimeout();
136 this.maybeReEnumerateGnubbies_(true);
140 * Enumerates gnubbies.
141 * @private
143 MultipleGnubbySigner.prototype.enumerateGnubbies_ = function() {
144 DEVICE_FACTORY_REGISTRY.getGnubbyFactory().enumerate(
145 this.enumerateCallback_.bind(this));
149 * Called with the result of enumerating gnubbies.
150 * @param {number} rc The return code from enumerating.
151 * @param {Array<GnubbyDeviceId>} ids The gnubbies enumerated.
152 * @private
154 MultipleGnubbySigner.prototype.enumerateCallback_ = function(rc, ids) {
155 if (this.complete_) {
156 return;
158 if (rc || !ids || !ids.length) {
159 this.maybeReEnumerateGnubbies_(true);
160 return;
162 for (var i = 0; i < ids.length; i++) {
163 this.addGnubby_(ids[i]);
165 this.maybeReEnumerateGnubbies_(false);
169 * How frequently to reenumerate gnubbies when none are found, in milliseconds.
170 * @const
172 MultipleGnubbySigner.ACTIVE_REENUMERATE_INTERVAL_MILLIS = 200;
175 * How frequently to reenumerate gnubbies when some are found, in milliseconds.
176 * @const
178 MultipleGnubbySigner.PASSIVE_REENUMERATE_INTERVAL_MILLIS = 3000;
181 * Reenumerates gnubbies if there's still time.
182 * @param {boolean} activeScan Whether to poll more aggressively, e.g. if
183 * there are no devices present.
184 * @private
186 MultipleGnubbySigner.prototype.maybeReEnumerateGnubbies_ =
187 function(activeScan) {
188 if (this.reenumerateTimer_.expired()) {
189 // If the timer is expired, call timeout_ if there aren't any still-running
190 // gnubbies. (If there are some still running, the last will call timeout_
191 // itself.)
192 if (!this.anyPending_()) {
193 this.timeout_(false);
195 return;
197 // Reenumerate more aggressively if there are no gnubbies present than if
198 // there are any.
199 var reenumerateTimeoutMillis;
200 if (activeScan) {
201 reenumerateTimeoutMillis =
202 MultipleGnubbySigner.ACTIVE_REENUMERATE_INTERVAL_MILLIS;
203 } else {
204 reenumerateTimeoutMillis =
205 MultipleGnubbySigner.PASSIVE_REENUMERATE_INTERVAL_MILLIS;
207 if (reenumerateTimeoutMillis >
208 this.reenumerateTimer_.millisecondsUntilExpired()) {
209 reenumerateTimeoutMillis =
210 this.reenumerateTimer_.millisecondsUntilExpired();
212 /** @private {Countdown} */
213 this.reenumerateIntervalTimer_ =
214 DEVICE_FACTORY_REGISTRY.getCountdownFactory().createTimer(
215 reenumerateTimeoutMillis, this.enumerateGnubbies_.bind(this));
219 * Adds a new gnubby to this signer's list of gnubbies. (Only possible while
220 * this signer is still signing: without this restriction, the completed
221 * callback could be called more than once, in violation of its contract.)
222 * If this signer has challenges to sign, begins signing on the new gnubby with
223 * them.
224 * @param {GnubbyDeviceId} gnubbyId The id of the gnubby to add.
225 * @return {boolean} Whether the gnubby was added successfully.
226 * @private
228 MultipleGnubbySigner.prototype.addGnubby_ = function(gnubbyId) {
229 var index = JSON.stringify(gnubbyId);
230 if (this.gnubbies_.hasOwnProperty(index)) {
231 // Can't add the same gnubby twice.
232 return false;
234 var tracker = {
235 index: index,
236 errorStatus: 0,
237 stillGoing: false,
238 signer: null
240 tracker.signer = new SingleGnubbySigner(
241 gnubbyId,
242 this.forEnroll_,
243 this.signCompletedCallback_.bind(this, tracker),
244 this.timer_.clone(),
245 this.logMsgUrl_);
246 this.gnubbies_[index] = tracker;
247 this.gnubbies_[index].stillGoing =
248 tracker.signer.doSign(this.challenges_);
249 if (!this.gnubbies_[index].errorStatus) {
250 this.gnubbies_[index].errorStatus = 0;
252 return true;
256 * Called by a SingleGnubbySigner upon completion.
257 * @param {GnubbyTracker} tracker The tracker object of the gnubby whose result
258 * this is.
259 * @param {SingleSignerResult} result The result of the sign operation.
260 * @private
262 MultipleGnubbySigner.prototype.signCompletedCallback_ =
263 function(tracker, result) {
264 console.log(
265 UTIL_fmt((result.code ? 'failure.' : 'success!') +
266 ' gnubby ' + tracker.index +
267 ' got code ' + result.code.toString(16)));
268 if (!tracker.stillGoing) {
269 console.log(UTIL_fmt('gnubby ' + tracker.index + ' no longer running!'));
270 // Shouldn't ever happen? Disregard.
271 return;
273 tracker.stillGoing = false;
274 tracker.errorStatus = result.code;
275 var moreExpected = this.tallyCompletedGnubby_();
276 switch (result.code) {
277 case DeviceStatusCodes.GONE_STATUS:
278 // Squelch removed gnubbies: the caller can't act on them. But if this
279 // was the last one, speed up reenumerating.
280 if (!moreExpected) {
281 this.maybeReEnumerateGnubbies_(true);
283 break;
285 default:
286 // Report any other results directly to the caller.
287 this.notifyGnubbyComplete_(tracker, result, moreExpected);
288 break;
290 if (!moreExpected && this.timer_.expired()) {
291 this.timeout_(false);
296 * Counts another gnubby has having completed, and returns whether more results
297 * are expected.
298 * @return {boolean} Whether more gnubbies are still running.
299 * @private
301 MultipleGnubbySigner.prototype.tallyCompletedGnubby_ = function() {
302 this.numComplete_++;
303 return this.anyPending_();
307 * @return {boolean} Whether more gnubbies are still running.
308 * @private
310 MultipleGnubbySigner.prototype.anyPending_ = function() {
311 return this.numComplete_ < Object.keys(this.gnubbies_).length;
315 * Called upon timeout.
316 * @param {boolean} anyPending Whether any gnubbies are awaiting results.
317 * @private
319 MultipleGnubbySigner.prototype.timeout_ = function(anyPending) {
320 if (this.complete_) return;
321 this.complete_ = true;
322 // Defer notifying the caller that all are complete, in case the caller is
323 // doing work in response to a gnubbyFound callback and has an inconsistent
324 // view of the state of this signer.
325 var self = this;
326 window.setTimeout(function() {
327 self.allCompleteCb_(anyPending);
328 }, 0);
332 * @param {GnubbyTracker} tracker The tracker object of the gnubby whose result
333 * this is.
334 * @param {SingleSignerResult} result Result object.
335 * @param {boolean} moreExpected Whether more gnubbies may still produce an
336 * outcome.
337 * @private
339 MultipleGnubbySigner.prototype.notifyGnubbyComplete_ =
340 function(tracker, result, moreExpected) {
341 console.log(UTIL_fmt('gnubby ' + tracker.index + ' complete (' +
342 result.code.toString(16) + ')'));
343 var signResult = {
344 'code': result.code,
345 'gnubby': result.gnubby,
346 'gnubbyId': tracker.signer.getDeviceId()
348 if (result['challenge'])
349 signResult['challenge'] = result['challenge'];
350 if (result['info'])
351 signResult['info'] = result['info'];
352 this.gnubbyCompleteCb_(signResult, moreExpected);