Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / resources / print_preview / search / provisional_destination_resolver.js
bloba78732f0ee87af42008a70bc17a6e01e0f85079b
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 cr.define('print_preview', function() {
6   'use strict';
8   /** @enum {string} */
9   var ResolverState = {
10     INITIAL: 'INITIAL',
11     ACTIVE: 'ACTIVE',
12     GRANTING_PERMISSION: 'GRANTING_PERMISSION',
13     ERROR: 'ERROR',
14     DONE: 'DONE'
15   };
17   /**
18    * Utility class for bundling a promise object with it's resolver methods.
19    * @param {!Promise<!print_preview.Destination>} promise A promise returning
20    *     a destination.
21    * @param {function(!print_preview.Destination)} resolve Function resolving
22    *     the promise.
23    * @param {function()} reject Function for rejecting the promise.
24    * @constructor @struct
25    */
26   function PromiseResolver(promise, resolve, reject) {
27     /** @type {!Promise<!print_preview.Destination>} */
28     this.promise = promise;
29     /** @type {function(!print_preview.Destination)} */
30     this.resolve = resolve;
31     /** @type {function()} */
32     this.reject = reject;
33   }
35   /**
36    * Create a Promise and an associated PromiseResolver.
37    * @return {!PromiseResolver}
38    */
39   PromiseResolver.create = function() {
40     var reject = null;
41     var resolve = null;
42     /** @type {!Promise<!print_preview.Destination>} */
43     var promise = new Promise(function(resolvePromise, rejectPromise) {
44       resolve = /** @type {function(!print_preview.Destination)}*/(
45           resolvePromise);
46       reject = /** @type {function()} */(rejectPromise);
47     });
49     return new PromiseResolver(promise, resolve, reject);
50   };
52   /**
53    * Overlay used to resolve a provisional extension destination. The user is
54    * prompted to allow print preview to grant a USB device access to an
55    * extension associated with the destination. If user agrees destination
56    * resolvement is attempted (which includes granting the extension USB access
57    * and requesting destination description from the extension). The overlay is
58    * hidden when destination resolving is done.
59    *
60    * @param {!print_preview.DestinationStore} destinationStore The destination
61    *    store containing the destination. Used as a proxy to native layer for
62    *    resolving the destination.
63    * @param {!print_preview.Destination} destination The destination that has
64    *     to be resolved.
65    * @constructor
66    * @extends {print_preview.Overlay}
67    */
68   function ProvisionalDestinationResolver(destinationStore, destination) {
69     print_preview.Overlay.call(this);
71     /** @private {!print_preview.DestinationStore} */
72     this.destinationStore_ = destinationStore;
73     /** @private {!print_preview.Destination} */
74     this.destination_ = destination;
76     /** @private {ResolverState} */
77     this.state_ = ResolverState.INITIAL;
79     /**
80      * Promise resolver for promise returned by {@code this.run}.
81      * @private {?PromiseResolver}
82      */
83     this.promiseResolver_ = null;
84   }
86   /**
87    * @param {!print_preview.DestinationStore} store
88    * @param {!print_preview.Destination} destination
89    * @return {?ProvisionalDestinationResolver}
90    */
91   ProvisionalDestinationResolver.create = function(store, destination) {
92     if (destination.provisionalType !=
93             print_preview.Destination.ProvisionalType.NEEDS_USB_PERMISSION) {
94       return null;
95     }
96     return new ProvisionalDestinationResolver(store, destination);
97   };
99   ProvisionalDestinationResolver.prototype = {
100     __proto__: print_preview.Overlay.prototype,
102     /** @override */
103     enterDocument: function() {
104       print_preview.Overlay.prototype.enterDocument.call(this);
106       this.tracker.add(
107           this.getChildElement('.usb-permission-ok-button'),
108           'click',
109           this.startResolveDestination_.bind(this));
110       this.tracker.add(
111           this.getChildElement('.cancel'),
112           'click',
113           this.cancel.bind(this));
115       this.tracker.add(
116           this.destinationStore_,
117           print_preview.DestinationStore.EventType
118               .PROVISIONAL_DESTINATION_RESOLVED,
119           this.onDestinationResolved_.bind(this));
120     },
122     /** @override */
123     onSetVisibleInternal: function(visible) {
124       if (visible) {
125         assert(this.state_ == ResolverState.INITIAL,
126                'Showing overlay while not in initial state.');
127         assert(!this.promiseResolver_, 'Promise resolver already set.');
128         this.setState_(ResolverState.ACTIVE);
129         this.promiseResolver_ = PromiseResolver.create();
130         this.getChildElement('.default').focus();
131       } else if (this.state_ != ResolverState.DONE) {
132         assert(this.state_ != ResolverState.INITIAL, 'Hiding in initial state');
133         this.setState_(ResolverState.DONE);
134         this.promiseResolver_.reject();
135         this.promiseResolver_ = null;
136       }
137     },
139     /** @override */
140     createDom: function() {
141       this.setElementInternal(this.cloneTemplateInternal(
142           'extension-usb-resolver'));
144       var extNameEl = this.getChildElement('.usb-permission-extension-name');
145       extNameEl.title = this.destination_.extensionName;
146       extNameEl.textContent = this.destination_.extensionName;
148       var extIconEl = this.getChildElement('.usb-permission-extension-icon');
149       extIconEl.style.backgroundImage = '-webkit-image-set(' +
150           'url(chrome://extension-icon/' +
151                this.destination_.extensionId + '/24/1) 1x,' +
152           'url(chrome://extension-icon/' +
153                this.destination_.extensionId + '/48/1) 2x)';
154     },
156     /**
157      * Handler for click on OK button. It initiates destination resolving.
158      * @private
159      */
160     startResolveDestination_: function() {
161       assert(this.state_ == ResolverState.ACTIVE,
162              'Invalid state in request grant permission');
164       this.setState_(ResolverState.GRANTING_PERMISSION);
165       this.destinationStore_.resolveProvisionalDestination(this.destination_);
166     },
168     /**
169      * Handler for PROVISIONAL_DESTINATION_RESOLVED event. It finalizes the
170      * resolver state once the destination associated with the resolver gets
171      * resolved.
172      * @param {Event} event
173      * @private
174      */
175     onDestinationResolved_: function(event) {
176       if (this.state_ == ResolverState.DONE)
177         return;
179       if (event.provisionalId != this.destination_.id)
180         return;
182       if (event.destination) {
183         this.setState_(ResolverState.DONE);
184         this.promiseResolver_.resolve(event.destination);
185         this.promiseResolver_ = null;
186         this.setIsVisible(false);
187       } else {
188         this.setState_(ResolverState.ERROR);
189       }
190     },
192     /**
193      * Sets new resolver state and updates the UI accordingly.
194      * @param {ResolverState} state
195      * @private
196      */
197     setState_: function(state) {
198       if (this.state_ == state)
199         return;
201       this.state_ = state;
202       this.updateUI_();
203     },
205     /**
206      * Updates the resolver overlay UI to match the resolver state.
207      * @private
208      */
209     updateUI_: function() {
210       this.getChildElement('.usb-permission-ok-button').hidden =
211           this.state_ == ResolverState.ERROR;
212       this.getChildElement('.usb-permission-ok-button').disabled =
213           this.state_ != ResolverState.ACTIVE;
215       // If OK button is disabled, make sure Cancel button gets focus.
216       if (this.state_ != ResolverState.ACTIVE)
217         this.getChildElement('.cancel').focus();
219       this.getChildElement('.throbber-placeholder').classList.toggle(
220           'throbber',
221           this.state_ == ResolverState.GRANTING_PERMISSION);
223       this.getChildElement('.usb-permission-extension-desc').hidden =
224           this.state_ == ResolverState.ERROR;
226       this.getChildElement('.usb-permission-message').textContent =
227           this.state_ == ResolverState.ERROR ?
228               loadTimeData.getStringF('resolveExtensionUSBErrorMessage',
229                                       this.destination_.extensionName) :
230               loadTimeData.getString('resolveExtensionUSBPermissionMessage');
231     },
233     /**
234      * Initiates and shows the resolver overlay.
235      * @param {!HTMLElement} parent The element that should parent the resolver
236      *     UI.
237      * @return {!Promise<!print_preview.Destination>} Promise that will be
238      * fulfilled when the destination resolving is finished.
239      */
240     run: function(parent) {
241       this.render(parent);
242       this.setIsVisible(true);
244       assert(this.promiseResolver_, 'Promise resolver not created.');
245       return this.promiseResolver_.promise;
246     }
247   };
249   return {
250     ProvisionalDestinationResolver: ProvisionalDestinationResolver
251   };