Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(541)

Side by Side Diff: ui/file_manager/file_manager/foreground/js/cws_widget_container.js

Issue 1068653002: Extract some logic from SuggestAppsDialog to a separate class (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: . Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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.
4
5 /**
6 * CWSWidgetContainer contains a Chrome Web Store widget that displays list of
7 * apps that satisfy certain constraints (e.g. fileHandler apps that can handle
8 * files with specific file extension or MIME type) and enables the user to
9 * install apps directly from it.
10 * CWSWidgetContainer implements client side of the widget, which handles
11 * widget loading and app installation.
12 */
13
14 /**
15 * The width of the widget (in pixels)
16 * @type {number}
17 * @const
18 */
19 var WEBVIEW_WIDTH = 735;
20
21 /**
22 * The height of the widget (in pixels).
23 * @type {number}
24 * @const
25 */
26 var WEBVIEW_HEIGHT = 480;
27
28 /**
29 * The URL of the widget showing suggested apps.
30 * @type {string}
31 * @const
32 */
33 var CWS_WIDGET_URL =
34 'https://clients5.google.com/webstore/wall/cros-widget-container';
35
36 /**
37 * The origin of the widget.
38 * @type {string}
39 * @const
40 */
41 var CWS_WIDGET_ORIGIN = 'https://clients5.google.com';
42
43 /**
44 * Creates the widget container element in DOM tree.
45 *
46 * @param {!HTMLDocument} document The document to contain this container.
47 * @param {!HTMLElement} parentNode Node to be parent for this container.
48 * @param {!SuggestAppDialogState} state Static state of suggest app dialog.
49 * @constructor
50 */
51 function CWSWidgetContainer(document, parentNode, state) {
52 /**
53 * The document that will contain the container.
54 * @const {!HTMLDocument}
55 * @private
56 */
57 this.document_ = document;
58
59 /**
60 * The element containing the widget webview.
61 * @type {!Element}
62 * @private
63 */
64 this.webviewContainer_ = document.createElement('div');
65 this.webviewContainer_.id = 'webview-container';
66 this.webviewContainer_.style.width = WEBVIEW_WIDTH + 'px';
67 this.webviewContainer_.style.height = WEBVIEW_HEIGHT + 'px';
68 parentNode.appendChild(this.webviewContainer_);
69
70 /**
71 * Element showing spinner layout in place of Web Store widget.
72 * @type {!Element}
73 */
74 var spinnerLayer = document.createElement('div');
75 spinnerLayer.className = 'spinner-layer';
76 this.webviewContainer_.appendChild(spinnerLayer);
77
78 /**
79 * The widget container's button strip.
80 * @type {!Element}
81 */
82 var buttons = document.createElement('div');
83 buttons.id = 'buttons';
84 parentNode.appendChild(buttons);
85
86 /**
87 * Button that opens the Webstore URL.
88 * @const {!Element}
89 * @private
90 */
91 this.webstoreButton_ = document.createElement('div');
92 this.webstoreButton_.hidden = true;
93 this.webstoreButton_.id = 'webstore-button';
94 this.webstoreButton_.setAttribute('role', 'button');
95 this.webstoreButton_.tabIndex = 0;
96
97 /**
98 * Icon for the Webstore button.
99 * @type {!Element}
100 */
101 var webstoreButtonIcon = this.document_.createElement('span');
102 webstoreButtonIcon.id = 'webstore-button-icon';
103 this.webstoreButton_.appendChild(webstoreButtonIcon);
104
105 /**
106 * The label for the Webstore button.
107 * @type {!Element}
108 */
109 var webstoreButtonLabel = this.document_.createElement('span');
110 webstoreButtonLabel.id = 'webstore-button-label';
111 webstoreButtonLabel.textContent = str('SUGGEST_DIALOG_LINK_TO_WEBSTORE');
112 this.webstoreButton_.appendChild(webstoreButtonLabel);
113
114 this.webstoreButton_.addEventListener(
115 'click', this.onWebstoreLinkActivated_.bind(this));
116 this.webstoreButton_.addEventListener(
117 'keydown', this.onWebstoreLinkKeyDown_.bind(this));
118
119 buttons.appendChild(this.webstoreButton_);
120
121 /**
122 * The webview element containing the Chrome Web Store widget.
123 * @type {?WebView}
124 * @private
125 */
126 this.webview_ = null;
127
128 /**
129 * The Chrome Web Store widget URL.
130 * @const {string}
131 * @private
132 */
133 this.widgetUrl_ = state.overrideCwsContainerUrlForTest || CWS_WIDGET_URL;
134
135 /**
136 * The Chrome Web Store widget origin.
137 * @const {string}
138 * @private
139 */
140 this.widgetOrigin_ = state.overrideCwsContainerOriginForTest ||
141 CWS_WIDGET_ORIGIN;
142
143 /**
144 * Map of options for the widget.
145 * @type {?Object.<string, *>}
146 * @private
147 */
148 this.options_ = null;
149
150 /**
151 * The ID of the item being installed. Null if no items are being installed.
152 * @type {?string}
153 * @private
154 */
155 this.installingItemId_ = null;
156
157 /**
158 * The ID of the the installed item. Null if no item was installed.
159 * @type {?string}
160 * @private
161 */
162 this.installedItemId_ = null;
163
164 /**
165 * The current widget state.
166 * @type {CWSWidgetContainer.State}
167 * @private
168 */
169 this.state_ = CWSWidgetContainer.State.UNINITIALIZED;
170
171 /**
172 * The Chrome Web Store access token to be used when communicating with the
173 * Chrome Web Store widget.
174 * @type {?string}
175 * @private
176 */
177 this.accessToken_ = null;
178
179 /**
180 * Called when the Chrome Web Store widget is done. It resolves the promise
181 * returned by {@code this.start()}.
182 * @type {?function(CWSWidgetContainer.ResolveReason)}
183 * @private
184 */
185 this.resolveStart_ = null;
186
187 /**
188 * Promise for retriving {@code this.accessToken_}.
189 * @type {Promise.<string>}
190 * @private
191 */
192 this.tokenGetter_ = this.createTokenGetter_();
193 }
194
195 /**
196 * @enum {string}
197 * @private
198 */
199 CWSWidgetContainer.State = {
200 UNINITIALIZED: 'CWSWidgetContainer.State.UNINITIALIZED',
201 GETTING_ACCESS_TOKEN: 'CWSWidgetContainer.State.GETTING_ACCESS_TOKEN',
202 ACCESS_TOKEN_READY: 'CWSWidgetContainer.State.ACCESS_TOKEN_READY',
203 INITIALIZING: 'CWSWidgetContainer.State.INITIALIZING',
204 INITIALIZE_FAILED_CLOSING:
205 'CWSWidgetContainer.State.INITIALIZE_FAILED_CLOSING',
206 INITIALIZED: 'CWSWidgetContainer.State.INITIALIZED',
207 INSTALLING: 'CWSWidgetContainer.State.INSTALLING',
208 INSTALLED_CLOSING: 'CWSWidgetContainer.State.INSTALLED_CLOSING',
209 OPENING_WEBSTORE_CLOSING: 'CWSWidgetContainer.State.OPENING_WEBSTORE_CLOSING',
210 CANCELED_CLOSING: 'CWSWidgetContainer.State.CANCELED_CLOSING'
211 };
212 Object.freeze(CWSWidgetContainer.State);
213
214 /**
215 * @enum {string}
216 * @const
217 */
218 CWSWidgetContainer.Result = {
219 /** Install is done. The install app should be opened. */
220 INSTALL_SUCCESSFUL: 'CWSWidgetContainer.Result.INSTALL_SUCCESSFUL',
221 /** User cancelled the suggest app dialog. No message should be shown. */
222 USER_CANCEL: 'CWSWidgetContainer.Result.USER_CANCEL',
223 /** User clicked the link to web store so the dialog is closed. */
224 WEBSTORE_LINK_OPENED: 'CWSWidgetContainer.Result.WEBSTORE_LINK_OPENED',
225 /** Failed to load the widget. Error message should be shown. */
226 FAILED: 'CWSWidgetContainer.Result.FAILED'
227 };
228 Object.freeze(CWSWidgetContainer.Result);
229
230 /**
231 * The reason due to which the container is resolving {@code this.start}
232 * promise.
233 * @enum {string}
234 */
235 CWSWidgetContainer.ResolveReason = {
236 /** The widget container ended up in its final state. */
237 DONE: 'CWSWidgetContainer.ResolveReason.DONE',
238 /** The widget container is being reset. */
239 RESET: 'CWSWidgetContainer.CloserReason.RESET'
240 };
241 Object.freeze(CWSWidgetContainer.ResolveReason);
242
243 /**
244 * @return {!Element} The element that should be focused initially.
245 */
246 CWSWidgetContainer.prototype.getInitiallyFocusedElement = function() {
247 return this.webviewContainer_;
248 };
249
250 /**
251 * Injects headers into the passed request.
252 *
253 * @param {!Object} e Request event.
254 * @return {!BlockingResponse} Modified headers.
255 * @private
256 */
257 CWSWidgetContainer.prototype.authorizeRequest_ = function(e) {
258 e.requestHeaders.push({
259 name: 'Authorization',
260 value: 'Bearer ' + this.accessToken_
261 });
262 return /** @type {!BlockingResponse}*/ ({requestHeaders: e.requestHeaders});
263 };
264
265 /**
266 * Retrieves the authorize token.
267 * @return {Promise.<string>} The promise with the retrived access token.
268 * @private
269 */
270 CWSWidgetContainer.prototype.createTokenGetter_ = function() {
271 return new Promise(function(resolve, reject) {
272 if (window.IN_TEST) {
273 // In test, use a dummy string as token. This must be a non-empty string.
274 resolve('DUMMY_ACCESS_TOKEN_FOR_TEST');
275 return;
276 }
277
278 // Fetch or update the access token.
279 chrome.fileManagerPrivate.requestWebStoreAccessToken(
280 function(accessToken) {
281 if (chrome.runtime.lastError) {
282 reject('Error retriveing Web Store access token: ' +
283 chrome.runtime.lastError.message);
284 }
285 resolve(accessToken)
286 });
287 });
288 };
289
290 /**
291 * Ensures that the widget container is in the state where it can properly
292 * handle showing the Chrome Web Store webview.
293 * @return {Promise} Resolved when the container is ready to be used.
294 */
295 CWSWidgetContainer.prototype.ready = function() {
296 return new Promise(function(resolve, reject) {
297 if (this.state_ !== CWSWidgetContainer.State.UNINITIALIZED) {
298 reject('Invalid state.');
299 return;
300 }
301
302 CWSWidgetContainer.Metrics.recordShowDialog();
303 CWSWidgetContainer.Metrics.startLoad();
304
305 this.state_ = CWSWidgetContainer.State.GETTING_ACCESS_TOKEN;
306
307 this.tokenGetter_.then(function(accessToken) {
308 this.state_ = CWSWidgetContainer.State.ACCESS_TOKEN_READY;
309 this.accessToken_ = accessToken;
310 resolve();
311 }.bind(this), function(error) {
312 this.state_ = CWSWidgetContainer.State.UNINITIALIZED;
313 reject('Failed to get Web Store access token: ' + error);
314 }.bind(this));
315 }.bind(this));
316 };
317
318 /**
319 * Initializes and starts loading the Chrome Web Store widget webview.
320 * Must not be called before {@code this.ready()} is resolved.
321 *
322 * @param {!Object<string, *>} options Map of options for the dialog.
323 * @param {?string} webStoreUrl Url for more results. Null if not supported.
324 * @return {Promise.<CWSWidgetContainer.ResolveReason>} Resolved when app
325 * installation is done, or the installation is cancelled.
326 */
327 CWSWidgetContainer.prototype.start = function(options, webStoreUrl) {
328 return new Promise(function(resolve, reject) {
329 if (this.state_ !== CWSWidgetContainer.State.ACCESS_TOKEN_READY) {
330 this.state_ = CWSWidgetContainer.State.INITIALIZE_FAILED_CLOSING;
331 reject('Invalid state in |start|.');
332 return;
333 }
334
335 if (!this.accessToken_) {
336 this.state_ = CWSWidgetContainer.State.INITIALIZE_FAILED_CLOSING;
337 reject('No access token.');
338 return;
339 }
340
341 this.resolveStart_ = resolve;
342
343 this.state_ = CWSWidgetContainer.State.INITIALIZING;
344
345 this.webStoreUrl_ = webStoreUrl;
346 this.options_ = options;
347
348 this.webstoreButton_.hidden = (webStoreUrl === null);
349
350 this.webview_ =
351 /** @type {!WebView} */(this.document_.createElement('webview'));
352 this.webview_.id = 'cws-widget';
353 this.webview_.partition = 'persist:cwswidgets';
354 this.webview_.style.width = WEBVIEW_WIDTH + 'px';
355 this.webview_.style.height = WEBVIEW_HEIGHT + 'px';
356 this.webview_.request.onBeforeSendHeaders.addListener(
357 this.authorizeRequest_.bind(this),
358 /** @type {!RequestFilter}*/ ({urls: [this.widgetOrigin_ + '/*']}),
359 ['blocking', 'requestHeaders']);
360 this.webview_.addEventListener('newwindow', function(event) {
361 event = /** @type {NewWindowEvent} */ (event);
362 // Discard the window object and reopen in an external window.
363 event.window.discard();
364 window.open(event.targetUrl);
365 event.preventDefault();
366 });
367 this.webviewContainer_.appendChild(this.webview_);
368
369 this.webviewContainer_.classList.add('show-spinner');
370
371 this.webviewClient_ = new CWSContainerClient(
372 this.webview_,
373 WEBVIEW_WIDTH,
374 WEBVIEW_HEIGHT,
375 this.widgetUrl_,
376 this.widgetOrigin_,
377 this.options_);
378 this.webviewClient_.addEventListener(CWSContainerClient.Events.LOADED,
379 this.onWidgetLoaded_.bind(this));
380 this.webviewClient_.addEventListener(CWSContainerClient.Events.LOAD_FAILED,
381 this.onWidgetLoadFailed_.bind(this));
382 this.webviewClient_.addEventListener(
383 CWSContainerClient.Events.REQUEST_INSTALL,
384 this.onInstallRequest_.bind(this));
385 this.webviewClient_.load();
386 }.bind(this));
387 };
388
389 /**
390 * Called when the 'See more...' button is activated. It opens
391 * {@code this.webstoreUrl_}.
392 * @param {Event} e The event that activated the link. Either mouse click or
393 * key down event.
394 * @private
395 */
396 CWSWidgetContainer.prototype.onWebstoreLinkActivated_ = function(e) {
397 if (!this.webStoreUrl_)
398 return;
399 util.visitURL(this.webStoreUrl_);
400 this.state_ = CWSWidgetContainer.State.OPENING_WEBSTORE_CLOSING;
401 this.reportDone_();
402 };
403
404 /**
405 * Key down event handler for webstore button element. If the key is enter, it
406 * activates the button.
407 * @param {Event} e The event
408 * @private
409 */
410 CWSWidgetContainer.prototype.onWebstoreLinkKeyDown_ = function(e) {
411 if (e.keyCode !== 13 /* Enter */)
412 return;
413 this.onWebstoreLinkActivated_(e);
414 };
415
416 /**
417 * Called when the widget is loaded successfully.
418 * @param {Event} event Event.
419 * @private
420 */
421 CWSWidgetContainer.prototype.onWidgetLoaded_ = function(event) {
422 CWSWidgetContainer.Metrics.finishLoad();
423 CWSWidgetContainer.Metrics.recordLoad(
424 CWSWidgetContainer.Metrics.LOAD.SUCCEEDED);
425
426 this.webviewContainer_.classList.remove('show-spinner');
427 this.state_ = CWSWidgetContainer.State.INITIALIZED;
428
429 this.webview_.focus();
430 };
431
432 /**
433 * Called when the widget is failed to load.
434 * @param {Event} event Event.
435 * @private
436 */
437 CWSWidgetContainer.prototype.onWidgetLoadFailed_ = function(event) {
438 CWSWidgetContainer.Metrics.recordLoad(CWSWidgetContainer.Metrics.LOAD.FAILED);
439
440 this.webviewContainer_.classList.remove('show-spinner');
441 this.state_ = CWSWidgetContainer.State.INITIALIZE_FAILED_CLOSING;
442 this.reportDone_();
443 };
444
445 /**
446 * Called when the connection status is changed to offline.
447 */
448 CWSWidgetContainer.prototype.onConnectionLost = function() {
449 if (this.state_ !== CWSWidgetContainer.State.UNINITIALIZED) {
450 this.state_ = CWSWidgetContainer.State.INITIALIZE_FAILED_CLOSING;
451 this.reportDone_();
452 }
453 };
454
455 /**
456 * Called when receiving the install request from the webview client.
457 * @param {Event} e Event.
458 * @private
459 */
460 CWSWidgetContainer.prototype.onInstallRequest_ = function(e) {
461 var itemId = e.itemId;
462 this.installingItemId_ = itemId;
463
464 this.appInstaller_ = new AppInstaller(itemId);
465 this.appInstaller_.install(this.onInstallCompleted_.bind(this));
466
467 this.webviewContainer_.classList.add('show-spinner');
468 this.state_ = CWSWidgetContainer.State.INSTALLING;
469 };
470
471 /**
472 * Called when the installation is completed from the app installer.
473 * @param {AppInstaller.Result} result Result of the installation.
474 * @param {string} error Detail of the error.
475 * @private
476 */
477 CWSWidgetContainer.prototype.onInstallCompleted_ = function(result, error) {
478 var success = (result === AppInstaller.Result.SUCCESS);
479
480 this.webviewContainer_.classList.remove('show-spinner');
481 this.state_ = success ?
482 CWSWidgetContainer.State.INSTALLED_CLOSING :
483 CWSWidgetContainer.State.INITIALIZED; // Back to normal state.
484 this.webviewClient_.onInstallCompleted(success, this.installingItemId_);
485 this.installedItemId_ = this.installingItemId_;
486 this.installingItemId_ = null;
487
488 switch (result) {
489 case AppInstaller.Result.SUCCESS:
490 CWSWidgetContainer.Metrics.recordInstall(
491 CWSWidgetContainer.Metrics.INSTALL.SUCCEEDED);
492 this.reportDone_();
493 break;
494 case AppInstaller.Result.CANCELLED:
495 CWSWidgetContainer.Metrics.recordInstall(
496 CWSWidgetContainer.Metrics.INSTALL.CANCELLED);
497 // User cancelled the installation. Do nothing.
498 break;
499 case AppInstaller.Result.ERROR:
500 CWSWidgetContainer.Metrics.recordInstall(
501 CWSWidgetContainer.Metrics.INSTALL.FAILED);
502 // TODO(tbarzic): Remove dialog showing call from this class.
503 fileManager.ui.errorDialog.show(
504 str('SUGGEST_DIALOG_INSTALLATION_FAILED'),
505 null,
506 null,
507 null);
508 break;
509 }
510 };
511
512 /**
513 * Resolves the promise returned by {@code this.start} when widget is done with
514 * installing apps.
515 * @private
516 */
517 CWSWidgetContainer.prototype.reportDone_ = function() {
518 if (this.resolveStart_)
519 this.resolveStart_(CWSWidgetContainer.ResolveReason.DONE);
520 this.resolveStart_ = null;
521 };
522
523 /**
524 * Finalizes the widget container state and returns the final app instalation
525 * result. The widget should not be used after calling this. If called before
526 * promise returned by {@code this.start} is resolved, the reported result will
527 * be as if the widget was cancelled.
528 * @return {{result: CWSWidgetContainer.Result, installedItemId: ?string}}
529 */
530 CWSWidgetContainer.prototype.finalizeAndGetResult = function() {
531 switch (this.state_) {
532 case CWSWidgetContainer.State.INSTALLING:
533 // Install is being aborted. Send the failure result.
534 // Cancels the install.
535 if (this.webviewClient_)
536 this.webviewClient_.onInstallCompleted(false, this.installingItemId_);
537 this.installingItemId_ = null;
538
539 // Assumes closing the dialog as canceling the install.
540 this.state_ = CWSWidgetContainer.State.CANCELED_CLOSING;
541 break;
542 case CWSWidgetContainer.State.GETTING_ACCESS_TOKEN:
543 case CWSWidgetContainer.State.ACCESS_TOKEN_READY:
544 case CWSWidgetContainer.State.INITIALIZING:
545 CWSWidgetContainer.Metrics.recordLoad(
546 CWSWidgetContainer.Metrics.LOAD.CANCELLED);
547 this.state_ = CWSWidgetContainer.State.CANCELED_CLOSING;
548 break;
549 case CWSWidgetContainer.State.INSTALLED_CLOSING:
550 case CWSWidgetContainer.State.INITIALIZE_FAILED_CLOSING:
551 case CWSWidgetContainer.State.OPENING_WEBSTORE_CLOSING:
552 // Do nothing.
553 break;
554 case CWSWidgetContainer.State.INITIALIZED:
555 this.state_ = CWSWidgetContainer.State.CANCELED_CLOSING;
556 break;
557 default:
558 this.state_ = CWSWidgetContainer.State.CANCELED_CLOSING;
559 console.error('Invalid state.');
560 }
561
562 var result;
563 switch (this.state_) {
564 case CWSWidgetContainer.State.INSTALLED_CLOSING:
565 result = CWSWidgetContainer.Result.INSTALL_SUCCESSFUL;
566 CWSWidgetContainer.Metrics.recordCloseDialog(
567 CWSWidgetContainer.Metrics.CLOSE_DIALOG.ITEM_INSTALLED);
568 break;
569 case CWSWidgetContainer.State.INITIALIZE_FAILED_CLOSING:
570 result = CWSWidgetContainer.Result.FAILED;
571 break;
572 case CWSWidgetContainer.State.CANCELED_CLOSING:
573 result = CWSWidgetContainer.Result.USER_CANCEL;
574 CWSWidgetContainer.Metrics.recordCloseDialog(
575 CWSWidgetContainer.Metrics.CLOSE_DIALOG.USER_CANCELLED);
576 break;
577 case CWSWidgetContainer.State.OPENING_WEBSTORE_CLOSING:
578 result = CWSWidgetContainer.Result.WEBSTORE_LINK_OPENED;
579 CWSWidgetContainer.Metrics.recordCloseDialog(
580 CWSWidgetContainer.Metrics.CLOSE_DIALOG.WEBSTORE_LINK_OPENED);
581 break;
582 default:
583 result = CWSWidgetContainer.Result.USER_CANCEL;
584 CWSWidgetContainer.Metrics.recordCloseDialog(
585 CWSWidgetContainer.Metrics.CLOSE_DIALOG.UNKNOWN_ERROR);
586 }
587
588 this.state_ = CWSWidgetContainer.State.UNINITIALIZED;
589
590 this.reset_();
591
592 return {result: result, installedItemId: this.installedItemId_};
593 };
594
595 /**
596 * Resets the widget.
597 * @private
598 */
599 CWSWidgetContainer.prototype.reset_ = function () {
600 if (this.state_ !== CWSWidgetContainer.State.UNINITIALIZED)
601 console.error('Widget reset before its state was finalized.');
602
603 if (this.resolveStart_) {
604 this.resolveStart_(CWSWidgetContainer.ResolveReason.RESET);
605 this.resolveStart_ = null;
606 }
607
608 if (this.webviewClient_) {
609 this.webviewClient_.dispose();
610 this.webviewClient_ = null;
611 }
612
613 if (this.webview_)
614 this.webviewContainer_.removeChild(this.webview_);
615 this.options_ = null;
616 };
617
618 /**
619 * Utility methods and constants to record histograms.
620 */
621 CWSWidgetContainer.Metrics = {};
622
623 /**
624 * @enum {number}
625 * @const
626 */
627 CWSWidgetContainer.Metrics.LOAD = {
628 SUCCEEDED: 0,
629 CANCELLED: 1,
630 FAILED: 2,
631 };
632
633 /**
634 * @enum {number}
635 * @const
636 */
637 CWSWidgetContainer.Metrics.CLOSE_DIALOG = {
638 UNKNOWN_ERROR: 0,
639 ITEM_INSTALLED: 1,
640 USER_CANCELLED: 2,
641 WEBSTORE_LINK_OPENED: 3,
642 };
643
644 /**
645 * @enum {number}
646 * @const
647 */
648 CWSWidgetContainer.Metrics.INSTALL = {
649 SUCCEEDED: 0,
650 CANCELLED: 1,
651 FAILED: 2,
652 };
653
654 /**
655 * @param {number} result Result of load, which must be defined in
656 * CWSWidgetContainer.Metrics.LOAD.
657 */
658 CWSWidgetContainer.Metrics.recordLoad = function(result) {
659 if (0 <= result && result < 3)
660 metrics.recordEnum('SuggestApps.Load', result, 3);
661 };
662
663 /**
664 * @param {number} reason Reason of closing dialog, which must be defined in
665 * CWSWidgetContainer.Metrics.CLOSE_DIALOG.
666 */
667 CWSWidgetContainer.Metrics.recordCloseDialog = function(reason) {
668 if (0 <= reason && reason < 4)
669 metrics.recordEnum('SuggestApps.CloseDialog', reason, 4);
670 };
671
672 /**
673 * @param {number} result Result of installation, which must be defined in
674 * CWSWidgetContainer.Metrics.INSTALL.
675 */
676 CWSWidgetContainer.Metrics.recordInstall = function(result) {
677 if (0 <= result && result < 3)
678 metrics.recordEnum('SuggestApps.Install', result, 3);
679 };
680
681 CWSWidgetContainer.Metrics.recordShowDialog = function() {
682 metrics.recordUserAction('SuggestApps.ShowDialog');
683 };
684
685 CWSWidgetContainer.Metrics.startLoad = function() {
686 metrics.startInterval('SuggestApps.LoadTime');
687 };
688
689 CWSWidgetContainer.Metrics.finishLoad = function() {
690 metrics.recordInterval('SuggestApps.LoadTime');
691 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698