| Index: chrome/browser/resources/print_preview/data/destination_store.js
|
| diff --git a/chrome/browser/resources/print_preview/data/destination_store.js b/chrome/browser/resources/print_preview/data/destination_store.js
|
| index 69f766ad9c88fde6636ea1d7cb1b367fc266c3bb..6c8d52db851509039e935d30397f763f6f8b256c 100644
|
| --- a/chrome/browser/resources/print_preview/data/destination_store.js
|
| +++ b/chrome/browser/resources/print_preview/data/destination_store.js
|
| @@ -8,20 +8,36 @@ cr.define('print_preview', function() {
|
| /**
|
| * A data store that stores destinations and dispatches events when the data
|
| * store changes.
|
| + * @param {!print_preview.NativeLayer} nativeLayer Used to fetch local print
|
| + * destinations.
|
| * @constructor
|
| * @extends {cr.EventTarget}
|
| */
|
| - function DestinationStore() {
|
| + function DestinationStore(nativeLayer) {
|
| cr.EventTarget.call(this);
|
|
|
| /**
|
| + * Used to fetch local print destinations.
|
| + * @type {!print_preview.NativeLayer}
|
| + * @private
|
| + */
|
| + this.nativeLayer_ = nativeLayer;
|
| +
|
| + /**
|
| * Internal backing store for the data store.
|
| - * @type {!Array.<print_preview.Destination>}
|
| + * @type {!Array.<!print_preview.Destination>}
|
| * @private
|
| */
|
| this.destinations_ = [];
|
|
|
| /**
|
| + * Cache used for constant lookup of printers.
|
| + * @type {object.<string, !print_preview.Destination>}
|
| + * @private
|
| + */
|
| + this.destinationMap_ = {};
|
| +
|
| + /**
|
| * Currently selected destination.
|
| * @type {print_preview.Destination}
|
| * @private
|
| @@ -44,6 +60,41 @@ cr.define('print_preview', function() {
|
| * @private
|
| */
|
| this.isInAutoSelectMode_ = false;
|
| +
|
| + /**
|
| + * Event tracker used to track event listeners of the destination store.
|
| + * @type {!EventTracker}
|
| + * @private
|
| + */
|
| + this.tracker_ = new EventTracker();
|
| +
|
| + /**
|
| + * Used to fetch cloud-based print destinations.
|
| + * @type {print_preview.CloudPrintInterface}
|
| + * @private
|
| + */
|
| + this.cloudPrintInterface_ = null;
|
| +
|
| + /**
|
| + * Whether the destination store has already loaded or is loading all cloud
|
| + * destinations.
|
| + * @type {boolean}
|
| + * @private
|
| + */
|
| + this.hasLoadedAllCloudDestinations_ = false;
|
| +
|
| + /**
|
| + * ID of a timeout after the initial destination ID is set. If no inserted
|
| + * destination matches the initial destination ID after the specified
|
| + * timeout, the first destination in the store will be automatically
|
| + * selected.
|
| + * @type {?number}
|
| + * @private
|
| + */
|
| + this.autoSelectTimeout_ = null;
|
| +
|
| + this.addEventListeners_();
|
| + this.reset_();
|
| };
|
|
|
| /**
|
| @@ -53,7 +104,44 @@ cr.define('print_preview', function() {
|
| DestinationStore.EventType = {
|
| DESTINATIONS_INSERTED:
|
| 'print_preview.DestinationStore.DESTINATIONS_INSERTED',
|
| - DESTINATION_SELECT: 'print_preview.DestinationStore.DESTINATION_SELECT'
|
| + DESTINATION_SELECT: 'print_preview.DestinationStore.DESTINATION_SELECT',
|
| + SELECTED_DESTINATION_CAPABILITIES_READY:
|
| + 'print_preview.DestinationStore.SELECTED_DESTINATION_CAPABILITIES_READY'
|
| + };
|
| +
|
| + /**
|
| + * Delay in milliseconds before the destination store ignores the initial
|
| + * destination ID and just selects any printer (since the initial destination
|
| + * was not found).
|
| + * @type {number}
|
| + * @const
|
| + * @private
|
| + */
|
| + DestinationStore.AUTO_SELECT_TIMEOUT_ = 2000;
|
| +
|
| + /**
|
| + * Creates a local PDF print destination.
|
| + * @return {!print_preview.Destination} Created print destination.
|
| + * @private
|
| + */
|
| + DestinationStore.createLocalPdfPrintDestination_ = function() {
|
| + var dest = new print_preview.Destination(
|
| + print_preview.Destination.GooglePromotedId.SAVE_AS_PDF,
|
| + print_preview.Destination.Type.LOCAL,
|
| + localStrings.getString('printToPDF'),
|
| + false /*isRecent*/);
|
| + dest.capabilities = new print_preview.ChromiumCapabilities(
|
| + false /*hasCopiesCapability*/,
|
| + '1' /*defaultCopiesStr*/,
|
| + false /*hasCollateCapability*/,
|
| + false /*defaultIsCollateEnabled*/,
|
| + false /*hasDuplexCapability*/,
|
| + false /*defaultIsDuplexEnabled*/,
|
| + true /*hasOrientationCapability*/,
|
| + false /*defaultIsLandscapeEnabled*/,
|
| + true /*hasColorCapability*/,
|
| + true /*defaultIsColorEnabled*/);
|
| + return dest;
|
| };
|
|
|
| DestinationStore.prototype = {
|
| @@ -80,31 +168,68 @@ cr.define('print_preview', function() {
|
| * match this ID, that destination will be automatically selected. This
|
| * occurs only once for every time this setter is called or if the store is
|
| * cleared.
|
| - * @param {string} ID of the destination that should be selected
|
| - * automatically when added to the store.
|
| + * @param {?string} ID of the destination that should be selected
|
| + * automatically when added to the store or {@code null} if the first
|
| + * destination that is inserted should be selected.
|
| */
|
| setInitialDestinationId: function(initialDestinationId) {
|
| this.initialDestinationId_ = initialDestinationId;
|
| this.isInAutoSelectMode_ = true;
|
| - if (this.initialDestinationId_ == null && this.destinations_.length > 0) {
|
| + if (this.initialDestinationId_ == null) {
|
| + assert(this.destinations_.length > 0,
|
| + 'No destinations available to select');
|
| this.selectDestination(this.destinations_[0]);
|
| - } else if (this.initialDestinationId_ != null) {
|
| - for (var dest, i = 0; dest = this.destinations_[i]; i++) {
|
| - if (dest.id == initialDestinationId) {
|
| - this.selectDestination(dest);
|
| - break;
|
| - }
|
| + } else {
|
| + var candidate = this.destinationMap_[this.initialDestinationId_];
|
| + if (candidate != null) {
|
| + this.selectDestination(candidate);
|
| }
|
| }
|
| },
|
|
|
| + /**
|
| + * Sets the destination store's Google Cloud Print interface.
|
| + * @param {!print_preview.CloudPrintInterface} cloudPrintInterface Interface
|
| + * to set.
|
| + */
|
| + setCloudPrintInterface: function(cloudPrintInterface) {
|
| + this.cloudPrintInterface_ = cloudPrintInterface;
|
| + this.tracker_.add(
|
| + this.cloudPrintInterface_,
|
| + cloudprint.CloudPrintInterface.EventType.SEARCH_DONE,
|
| + this.onCloudPrintSearchDone_.bind(this));
|
| + this.tracker_.add(
|
| + this.cloudPrintInterface_,
|
| + cloudprint.CloudPrintInterface.EventType.PRINTER_DONE,
|
| + this.onCloudPrintPrinterDone_.bind(this));
|
| + },
|
| +
|
| /** @param {!print_preview.Destination} Destination to select. */
|
| selectDestination: function(destination) {
|
| this.selectedDestination_ = destination;
|
| this.selectedDestination_.isRecent = true;
|
| this.isInAutoSelectMode_ = false;
|
| + if (this.autoSelectTimeout_ != null) {
|
| + clearTimeout(this.autoSelectTimeout_);
|
| + this.autoSelectTimeout_ = null;
|
| + }
|
| cr.dispatchSimpleEvent(
|
| this, DestinationStore.EventType.DESTINATION_SELECT);
|
| + if (destination.capabilities == null) {
|
| + if (destination.isLocal) {
|
| + this.nativeLayer_.startGetLocalDestinationCapabilities(
|
| + destination.id);
|
| + } else {
|
| + assert(this.cloudPrintInterface_ != null,
|
| + 'Selected destination is a cloud destination, but Google ' +
|
| + 'Cloud Print is not enabled');
|
| + this.cloudPrintInterface_.printer(destination.id);
|
| + }
|
| + } else {
|
| + cr.dispatchSimpleEvent(
|
| + this,
|
| + DestinationStore.EventType.SELECTED_DESTINATION_CAPABILITIES_READY);
|
| + }
|
| },
|
|
|
| /**
|
| @@ -115,16 +240,13 @@ cr.define('print_preview', function() {
|
| * insert.
|
| */
|
| insertDestination: function(destination) {
|
| - this.destinations_.push(destination);
|
| - cr.dispatchSimpleEvent(
|
| - this, DestinationStore.EventType.DESTINATIONS_INSERTED);
|
| - if (this.isInAutoSelectMode_) {
|
| - if (this.initialDestinationId_ == null) {
|
| + if (this.insertDestination_(destination)) {
|
| + cr.dispatchSimpleEvent(
|
| + this, DestinationStore.EventType.DESTINATIONS_INSERTED);
|
| + if (this.isInAutoSelectMode_ &&
|
| + (this.initialDestinationId_ == null ||
|
| + destination.id == this.initialDestinationId_)) {
|
| this.selectDestination(destination);
|
| - } else {
|
| - if (destination.id == this.initialDestinationId_) {
|
| - this.selectDestination(destination);
|
| - }
|
| }
|
| }
|
| },
|
| @@ -137,20 +259,25 @@ cr.define('print_preview', function() {
|
| * destinations to insert.
|
| */
|
| insertDestinations: function(destinations) {
|
| - this.destinations_ = this.destinations_.concat(destinations);
|
| - cr.dispatchSimpleEvent(
|
| - this, DestinationStore.EventType.DESTINATIONS_INSERTED);
|
| - if (this.isInAutoSelectMode_) {
|
| - if (this.initialDestinationId_ == null && destinations.length > 0) {
|
| - this.selectDestination(destinations[0]);
|
| - } else if (this.initialDestinationId_ != null) {
|
| - for (var dest, i = 0; dest = destinations[i]; i++) {
|
| - if (dest.id == this.initialDestinationId_) {
|
| - this.selectDestination(dest);
|
| - break;
|
| - }
|
| + var insertedDestination = false;
|
| + var destinationToAutoSelect = null;
|
| + destinations.forEach(function(dest) {
|
| + if (this.insertDestination_(dest)) {
|
| + insertedDestination = true;
|
| + if (this.isInAutoSelectMode_ &&
|
| + destinationToAutoSelect == null &&
|
| + (this.initialDestinationId_ == null ||
|
| + dest.id == this.initialDestinationId_)) {
|
| + destinationToAutoSelect = dest;
|
| }
|
| }
|
| + }, this);
|
| + if (insertedDestination) {
|
| + cr.dispatchSimpleEvent(
|
| + this, DestinationStore.EventType.DESTINATIONS_INSERTED);
|
| + }
|
| + if (destinationToAutoSelect != null) {
|
| + this.selectDestination(destinationToAutoSelect);
|
| }
|
| },
|
|
|
| @@ -162,14 +289,8 @@ cr.define('print_preview', function() {
|
| * updated.
|
| */
|
| updateDestination: function(destination) {
|
| - var existingDestination = null;
|
| - for (var d, i = 0; d = this.destinations_[i]; i++) {
|
| - if (destination.id == d.id) {
|
| - existingDestination = d;
|
| - break;
|
| - }
|
| - }
|
| - if (existingDestination) {
|
| + var existingDestination = this.destinationMap_[destination.id];
|
| + if (existingDestination != null) {
|
| existingDestination.capabilities = destination.capabilities;
|
| return existingDestination;
|
| } else {
|
| @@ -177,11 +298,163 @@ cr.define('print_preview', function() {
|
| }
|
| },
|
|
|
| - /** Clears all print destinations. */
|
| - clear: function() {
|
| + /** Initiates loading of local print destinations. */
|
| + startLoadLocalDestinations: function() {
|
| + this.nativeLayer_.startGetLocalDestinations();
|
| + },
|
| +
|
| + /** Initiates loading of recent cloud destinations. */
|
| + startLoadRecentCloudDestinations: function() {
|
| + if (this.cloudPrintInterface_ != null) {
|
| + this.cloudPrintInterface_.search(true /*isRecent*/);
|
| + }
|
| + },
|
| +
|
| + /** Initiates loading of all cloud destinations. */
|
| + startLoadAllCloudDestinations: function() {
|
| + if (this.cloudPrintInterface_ != null &&
|
| + !this.hasLoadedAllCloudDestinations_) {
|
| + this.cloudPrintInterface_.search(false /*isRecent*/);
|
| + this.hasLoadedAllCloudDestinations_ = true;
|
| + }
|
| + },
|
| +
|
| + /**
|
| + * Inserts a destination into the store without dispatching any events.
|
| + * @return {boolean} Whether the inserted destination was not already in the
|
| + * store.
|
| + * @private
|
| + */
|
| + insertDestination_: function(destination) {
|
| + if (this.destinationMap_[destination.id] == null) {
|
| + this.destinations_.push(destination);
|
| + this.destinationMap_[destination.id] = destination;
|
| + return true;
|
| + } else {
|
| + return false;
|
| + }
|
| + },
|
| +
|
| + /**
|
| + * Binds handlers to events.
|
| + * @private
|
| + */
|
| + addEventListeners_: function() {
|
| + this.tracker_.add(
|
| + this.nativeLayer_,
|
| + print_preview.NativeLayer.EventType.LOCAL_DESTINATIONS_SET,
|
| + this.onLocalDestinationsSet_.bind(this));
|
| + this.tracker_.add(
|
| + this.nativeLayer_,
|
| + print_preview.NativeLayer.EventType.CAPABILITIES_SET,
|
| + this.onLocalDestinationCapabilitiesSet_.bind(this));
|
| + this.tracker_.add(
|
| + this.nativeLayer_,
|
| + print_preview.NativeLayer.EventType.DESTINATIONS_RELOAD,
|
| + this.onDestinationsReload_.bind(this));
|
| + },
|
| +
|
| + /**
|
| + * Resets the state of the destination store to its initial state.
|
| + * @private
|
| + */
|
| + reset_: function() {
|
| this.destinations_ = [];
|
| + this.destinationMap_ = {};
|
| this.selectedDestination_ = null;
|
| + this.hasLoadedAllCloudDestinations_ = false;
|
| + this.insertDestination(
|
| + DestinationStore.createLocalPdfPrintDestination_());
|
| + this.autoSelectTimeout_ = setTimeout(
|
| + this.onAutoSelectTimeoutExpired_.bind(this),
|
| + DestinationStore.AUTO_SELECT_TIMEOUT_);
|
| + },
|
| +
|
| + /**
|
| + * Called when the local destinations have been got from the native layer.
|
| + * @param {cr.Event} Contains the local destinations.
|
| + * @private
|
| + */
|
| + onLocalDestinationsSet_: function(event) {
|
| + var localDestinations = event.destinationInfos.map(function(destInfo) {
|
| + return print_preview.LocalDestinationParser.parse(destInfo);
|
| + });
|
| + this.insertDestinations(localDestinations);
|
| + },
|
| +
|
| + /**
|
| + * Called when the native layer retrieves the capabilities for the selected
|
| + * local destination.
|
| + * @param {cr.Event} event Contains the capabilities of the local print
|
| + * destination.
|
| + * @private
|
| + */
|
| + onLocalDestinationCapabilitiesSet_: function(event) {
|
| + // TODO(rltoscano): There may be a race condition here. This method is
|
| + // assumed to return capabilities for the currently selected printer. But
|
| + // between the time the local printer was selected and the capabilities
|
| + // were retrieved, the selected printer can change. One way to address
|
| + // this is to include the destination ID in the event.settingsInfo
|
| + // parameter.
|
| + if (this.selectedDestination_ && this.selectedDestination_.isLocal) {
|
| + var capabilities = print_preview.LocalCapabilitiesParser.parse(
|
| + event.settingsInfo);
|
| + this.selectedDestination_.capabilities = capabilities;
|
| + cr.dispatchSimpleEvent(
|
| + this,
|
| + DestinationStore.EventType.SELECTED_DESTINATION_CAPABILITIES_READY);
|
| + }
|
| + },
|
| +
|
| + /**
|
| + * Called when the /search call completes. Adds the fetched printers to the
|
| + * destination store.
|
| + * @param {cr.Event} event Contains the fetched printers.
|
| + * @private
|
| + */
|
| + onCloudPrintSearchDone_: function(event) {
|
| + this.insertDestinations(event.printers);
|
| + },
|
| +
|
| + /**
|
| + * Called when /printer call completes. Updates the specified destination's
|
| + * print capabilities.
|
| + * @param {cr.Event} event Contains detailed information about the
|
| + * destination.
|
| + * @private
|
| + */
|
| + onCloudPrintPrinterDone_: function(event) {
|
| + var dest = this.updateDestination(event.printer);
|
| + if (this.selectedDestination_ == dest) {
|
| + cr.dispatchSimpleEvent(
|
| + this,
|
| + DestinationStore.EventType.SELECTED_DESTINATION_CAPABILITIES_READY);
|
| + }
|
| + },
|
| +
|
| + /**
|
| + * Called from native layer after the user was requested to sign in, and did
|
| + * so successfully.
|
| + * @private
|
| + */
|
| + onDestinationsReload_: function() {
|
| + this.reset_();
|
| this.isInAutoSelectMode_ = true;
|
| + this.startLoadLocalDestinations();
|
| + this.startLoadRecentCloudDestinations();
|
| + this.startLoadAllCloudDestinations();
|
| + },
|
| +
|
| + /**
|
| + * Called when no destination was auto-selected after some timeout. Selects
|
| + * the first destination in store.
|
| + * @private
|
| + */
|
| + onAutoSelectTimeoutExpired_: function() {
|
| + this.autoSelectTimeout_ = null;
|
| + assert(this.destinations_.length > 0,
|
| + 'No destinations were loaded before auto-select timeout expired');
|
| + this.selectDestination(this.destinations_[0]);
|
| }
|
| };
|
|
|
|
|