Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / resources / print_preview / data / destination_store.js
index 4cb7b6d..27769cc 100644 (file)
@@ -10,12 +10,13 @@ cr.define('print_preview', function() {
    * store changes.
    * @param {!print_preview.NativeLayer} nativeLayer Used to fetch local print
    *     destinations.
+   * @param {!print_preview.UserInfo} userInfo User information repository.
    * @param {!print_preview.AppState} appState Application state.
    * @param {!print_preview.Metrics} metrics Metrics.
    * @constructor
    * @extends {cr.EventTarget}
    */
-  function DestinationStore(nativeLayer, appState, metrics) {
+  function DestinationStore(nativeLayer, userInfo, appState, metrics) {
     cr.EventTarget.call(this);
 
     /**
@@ -26,6 +27,13 @@ cr.define('print_preview', function() {
     this.nativeLayer_ = nativeLayer;
 
     /**
+     * User information repository.
+     * @type {!print_preview.UserInfo}
+     * @private
+     */
+    this.userInfo_ = userInfo;
+
+    /**
      * Used to load and persist the selected destination.
      * @type {!print_preview.AppState}
      * @private
@@ -61,24 +69,8 @@ cr.define('print_preview', function() {
     this.selectedDestination_ = null;
 
     /**
-     * Initial destination ID used to auto-select the first inserted destination
-     * that matches. If {@code null}, the first destination inserted into the
-     * store will be selected.
-     * @type {?string}
-     * @private
-     */
-    this.initialDestinationId_ = null;
-
-    /**
-     * Initial origin used to auto-select destination.
-     * @type {print_preview.Destination.Origin}
-     * @private
-     */
-    this.initialDestinationOrigin_ = print_preview.Destination.Origin.LOCAL;
-
-    /**
      * Whether the destination store will auto select the destination that
-     * matches the initial destination.
+     * matches the last used destination stored in appState_.
      * @type {boolean}
      * @private
      */
@@ -99,12 +91,12 @@ cr.define('print_preview', function() {
     this.cloudPrintInterface_ = null;
 
     /**
-     * Whether the destination store has already loaded or is loading all cloud
-     * destinations.
-     * @type {boolean}
+     * Maps user account to the list of origins for which destinations are
+     * already loaded.
+     * @type {!Object.<string, Array.<print_preview.Destination.Origin>>}
      * @private
      */
-    this.hasLoadedAllCloudDestinations_ = false;
+    this.loadedCloudOrigins_ = {};
 
     /**
      * ID of a timeout after the initial destination ID is set. If no inserted
@@ -234,11 +226,19 @@ cr.define('print_preview', function() {
     __proto__: cr.EventTarget.prototype,
 
     /**
-     * @return {!Array.<!print_preview.Destination>} List of destinations in
-     *     the store.
+     * @param {string=} opt_account Account to filter destinations by. When
+     *     omitted, all destinations are returned.
+     * @return {!Array.<!print_preview.Destination>} List of destinations
+     *     accessible by the {@code account}.
      */
-    get destinations() {
-      return this.destinations_.slice(0);
+    destinations: function(opt_account) {
+      if (opt_account) {
+        return this.destinations_.filter(function(destination) {
+          return !destination.account || destination.account == opt_account;
+        });
+      } else {
+        return this.destinations_.slice(0);
+      }
     },
 
     /**
@@ -270,41 +270,35 @@ cr.define('print_preview', function() {
      * destination. If any inserted destinations match this ID, that destination
      * will be automatically selected. This method must be called after the
      * print_preview.AppState has been initialized.
-     * @param {?string} systemDefaultDestinationId ID of the system default
-     *     destination.
      * @private
      */
-    init: function(systemDefaultDestinationId) {
-      if (this.appState_.selectedDestinationId &&
-          this.appState_.selectedDestinationOrigin) {
-        this.initialDestinationId_ = this.appState_.selectedDestinationId;
-        this.initialDestinationOrigin_ =
-            this.appState_.selectedDestinationOrigin;
-      } else if (systemDefaultDestinationId) {
-        this.initialDestinationId_ = systemDefaultDestinationId;
-        this.initialDestinationOrigin_ = print_preview.Destination.Origin.LOCAL;
-      }
+    init: function() {
       this.isInAutoSelectMode_ = true;
-      if (!this.initialDestinationId_ || !this.initialDestinationOrigin_) {
+      if (!this.appState_.selectedDestinationId ||
+          !this.appState_.selectedDestinationOrigin) {
         this.onAutoSelectFailed_();
       } else {
-        var key = this.getDestinationKey_(this.initialDestinationOrigin_,
-                                          this.initialDestinationId_);
+        var key = this.getDestinationKey_(
+            this.appState_.selectedDestinationOrigin,
+            this.appState_.selectedDestinationId,
+            this.appState_.selectedDestinationAccount);
         var candidate = this.destinationMap_[key];
         if (candidate != null) {
           this.selectDestination(candidate);
-        } else if (this.initialDestinationOrigin_ ==
+        } else if (this.appState_.selectedDestinationOrigin ==
                    print_preview.Destination.Origin.LOCAL) {
           this.nativeLayer_.startGetLocalDestinationCapabilities(
-              this.initialDestinationId_);
+              this.appState_.selectedDestinationId);
         } else if (this.cloudPrintInterface_ &&
-                   (this.initialDestinationOrigin_ ==
-                    print_preview.Destination.Origin.COOKIES ||
-                    this.initialDestinationOrigin_ ==
-                    print_preview.Destination.Origin.DEVICE)) {
-          this.cloudPrintInterface_.printer(this.initialDestinationId_,
-                                            this.initialDestinationOrigin_);
-        } else if (this.initialDestinationOrigin_ ==
+                   (this.appState_.selectedDestinationOrigin ==
+                        print_preview.Destination.Origin.COOKIES ||
+                    this.appState_.selectedDestinationOrigin ==
+                        print_preview.Destination.Origin.DEVICE)) {
+          this.cloudPrintInterface_.printer(
+              this.appState_.selectedDestinationId,
+              this.appState_.selectedDestinationOrigin,
+              this.appState_.selectedDestinationAccount);
+        } else if (this.appState_.selectedDestinationOrigin ==
                    print_preview.Destination.Origin.PRIVET) {
           // TODO(noamsml): Resolve a specific printer instead of listing all
           // privet printers in this case.
@@ -316,7 +310,7 @@ cr.define('print_preview', function() {
           // destination store. When the real destination is created, this
           // destination will be overwritten.
           this.selectedDestination_ = new print_preview.Destination(
-              this.initialDestinationId_,
+              this.appState_.selectedDestinationId,
               print_preview.Destination.Type.LOCAL,
               print_preview.Destination.Origin.PRIVET,
               destinationName,
@@ -349,7 +343,7 @@ cr.define('print_preview', function() {
       this.tracker_.add(
           this.cloudPrintInterface_,
           cloudprint.CloudPrintInterface.EventType.SEARCH_FAILED,
-          this.onCloudPrintSearchFailed_.bind(this));
+          this.onCloudPrintSearchDone_.bind(this));
       this.tracker_.add(
           this.cloudPrintInterface_,
           cloudprint.CloudPrintInterface.EventType.PRINTER_DONE,
@@ -365,6 +359,7 @@ cr.define('print_preview', function() {
      *     loaded.
      */
     hasOnlyDefaultCloudDestinations: function() {
+      // TODO: Move the logic to print_preview.
       return this.destinations_.every(function(dest) {
         return dest.isLocal ||
             dest.id == print_preview.Destination.GooglePromotedId.DOCS ||
@@ -374,30 +369,34 @@ cr.define('print_preview', function() {
 
     /** @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;
+      this.cancelAutoSelectTimeout_();
+      if (destination == this.selectedDestination_) {
+        return;
+      }
+      if (destination == null) {
+        this.selectedDestination_ = null;
+        cr.dispatchSimpleEvent(
+            this, DestinationStore.EventType.DESTINATION_SELECT);
+        return;
       }
+      // Update and persist selected destination.
+      this.selectedDestination_ = destination;
+      this.selectedDestination_.isRecent = true;
       if (destination.id == print_preview.Destination.GooglePromotedId.FEDEX &&
           !destination.isTosAccepted) {
         assert(this.cloudPrintInterface_ != null,
-               'Selected FedEx Office destination, but Google Cloud Print is ' +
-               'not enabled');
+               'Selected FedEx destination, but GCP API is not available');
         destination.isTosAccepted = true;
-        this.cloudPrintInterface_.updatePrinterTosAcceptance(destination.id,
-                                                             destination.origin,
-                                                             true);
+        this.cloudPrintInterface_.updatePrinterTosAcceptance(destination, true);
       }
       this.appState_.persistSelectedDestination(this.selectedDestination_);
-
+      // Adjust metrics.
       if (destination.cloudID &&
-          this.destinations.some(function(otherDestination) {
+          this.destinations_.some(function(otherDestination) {
             return otherDestination.cloudID == destination.cloudID &&
                 otherDestination != destination;
-            })) {
+          })) {
         if (destination.isPrivet) {
           this.metrics_.incrementDestinationSearchBucket(
               print_preview.Metrics.DestinationSearchBucket.
@@ -408,9 +407,10 @@ cr.define('print_preview', function() {
                   CLOUD_DUPLICATE_SELECTED);
         }
       }
-
+      // Notify about selected destination change.
       cr.dispatchSimpleEvent(
           this, DestinationStore.EventType.DESTINATION_SELECT);
+      // Request destination capabilities, of not known yet.
       if (destination.capabilities == null) {
         if (destination.isPrivet) {
           this.nativeLayer_.startGetPrivetDestinationCapabilities(
@@ -421,10 +421,9 @@ cr.define('print_preview', function() {
               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,
-                                            destination.origin);
+                 'Cloud destination selected, but GCP is not enabled');
+          this.cloudPrintInterface_.printer(
+              destination.id, destination.origin, destination.account);
         }
       } else {
         cr.dispatchSimpleEvent(
@@ -434,78 +433,16 @@ cr.define('print_preview', function() {
     },
 
     /**
-     * Inserts a print destination to the data store and dispatches a
-     * DESTINATIONS_INSERTED event. If the destination matches the initial
-     * destination ID, then the destination will be automatically selected.
-     * @param {!print_preview.Destination} destination Print destination to
-     *     insert.
-     */
-    insertDestination: function(destination) {
-      if (this.insertDestination_(destination)) {
-        cr.dispatchSimpleEvent(
-            this, DestinationStore.EventType.DESTINATIONS_INSERTED);
-        if (this.isInAutoSelectMode_ &&
-            this.matchInitialDestination_(destination.id, destination.origin)) {
-          this.selectDestination(destination);
-        }
-      }
-    },
-
-    /**
-     * Inserts multiple print destinations to the data store and dispatches one
-     * DESTINATIONS_INSERTED event. If any of the destinations match the initial
-     * destination ID, then that destination will be automatically selected.
-     * @param {!Array.<print_preview.Destination>} destinations Print
-     *     destinations to insert.
+     * Selects 'Save to PDF' destination (since it always exists).
+     * @private
      */
-    insertDestinations: function(destinations) {
-      var insertedDestination = false;
-      var destinationToAutoSelect = null;
-      destinations.forEach(function(dest) {
-        if (this.insertDestination_(dest)) {
-          insertedDestination = true;
-          if (this.isInAutoSelectMode_ &&
-              destinationToAutoSelect == null &&
-              this.matchInitialDestination_(dest.id, dest.origin)) {
-            destinationToAutoSelect = dest;
-          }
-        }
-      }, this);
-      if (insertedDestination) {
-        cr.dispatchSimpleEvent(
-            this, DestinationStore.EventType.DESTINATIONS_INSERTED);
-      }
-      if (destinationToAutoSelect != null) {
-        this.selectDestination(destinationToAutoSelect);
-      }
-    },
-
-    /**
-     * Updates an existing print destination with capabilities and display name
-     * information. If the destination doesn't already exist, it will be added.
-     * @param {!print_preview.Destination} destination Destination to update.
-     * @return {!print_preview.Destination} The existing destination that was
-     *     updated or {@code null} if it was the new destination.
-     */
-    updateDestination: function(destination) {
-      assert(destination.constructor !== Array, 'Single printer expected');
-      var key = this.getDestinationKey_(destination.origin, destination.id);
-      var existingDestination = this.destinationMap_[key];
-      if (existingDestination != null) {
-        existingDestination.capabilities = destination.capabilities;
-      } else {
-        this.insertDestination(destination);
-      }
-
-      if (existingDestination == this.selectedDestination_ ||
-          destination == this.selectedDestination_) {
-        this.appState_.persistSelectedDestination(this.selectedDestination_);
-        cr.dispatchSimpleEvent(
-            this,
-            DestinationStore.EventType.SELECTED_DESTINATION_CAPABILITIES_READY);
-      }
-
-      return existingDestination;
+    selectDefaultDestination_: function() {
+      var destination = this.destinationMap_[this.getDestinationKey_(
+          print_preview.Destination.Origin.LOCAL,
+          print_preview.Destination.GooglePromotedId.SAVE_AS_PDF,
+          '')] || null;
+      assert(destination != null, 'Save to PDF printer not found');
+      this.selectDestination(destination);
     },
 
     /** Initiates loading of local print destinations. */
@@ -534,15 +471,31 @@ cr.define('print_preview', function() {
 
     /**
      * Initiates loading of cloud destinations.
-     */
-    startLoadCloudDestinations: function() {
-      if (this.cloudPrintInterface_ != null &&
-          !this.hasLoadedAllCloudDestinations_) {
-        this.hasLoadedAllCloudDestinations_ = true;
-        this.cloudPrintInterface_.search(true);
-        this.cloudPrintInterface_.search(false);
+     * @param {print_preview.Destination.Origin=} opt_origin Search destinations
+     *     for the specified origin only.
+     */
+    startLoadCloudDestinations: function(opt_origin) {
+      if (this.cloudPrintInterface_ != null) {
+        var origins = this.loadedCloudOrigins_[this.userInfo_.activeUser] || [];
+        if (origins.length == 0 ||
+            (opt_origin && origins.indexOf(opt_origin) < 0)) {
+          this.cloudPrintInterface_.search(
+              this.userInfo_.activeUser, opt_origin);
+          cr.dispatchSimpleEvent(
+              this, DestinationStore.EventType.DESTINATION_SEARCH_STARTED);
+        }
+      }
+    },
+
+    /** Requests load of COOKIE based cloud destinations. */
+    reloadUserCookieBasedDestinations: function() {
+      var origins = this.loadedCloudOrigins_[this.userInfo_.activeUser] || [];
+      if (origins.indexOf(print_preview.Destination.Origin.COOKIES) >= 0) {
         cr.dispatchSimpleEvent(
-            this, DestinationStore.EventType.DESTINATION_SEARCH_STARTED);
+            this, DestinationStore.EventType.DESTINATION_SEARCH_DONE);
+      } else {
+        this.startLoadCloudDestinations(
+            print_preview.Destination.Origin.COOKIES);
       }
     },
 
@@ -555,6 +508,87 @@ cr.define('print_preview', function() {
     },
 
     /**
+     * Inserts {@code destination} to the data store and dispatches a
+     * DESTINATIONS_INSERTED event.
+     * @param {!print_preview.Destination} destination Print destination to
+     *     insert.
+     * @private
+     */
+    insertDestination_: function(destination) {
+      if (this.insertIntoStore_(destination)) {
+        this.destinationsInserted_(destination);
+      }
+    },
+
+    /**
+     * Inserts multiple {@code destinations} to the data store and dispatches
+     * single DESTINATIONS_INSERTED event.
+     * @param {!Array.<print_preview.Destination>} destinations Print
+     *     destinations to insert.
+     * @private
+     */
+    insertDestinations_: function(destinations) {
+      var inserted = false;
+      destinations.forEach(function(destination) {
+        inserted = this.insertIntoStore_(destination) || inserted;
+      }, this);
+      if (inserted) {
+        this.destinationsInserted_();
+      }
+    },
+
+    /**
+     * Dispatches DESTINATIONS_INSERTED event. In auto select mode, tries to
+     * update selected destination to match {@code appState_} settings.
+     * @param {print_preview.Destination=} opt_destination The only destination
+     *     that was changed or skipped if possibly more than one destination was
+     *     changed. Used as a hint to limit destination search scope in
+     *     {@code isInAutoSelectMode_).
+     */
+    destinationsInserted_: function(opt_destination) {
+      cr.dispatchSimpleEvent(
+          this, DestinationStore.EventType.DESTINATIONS_INSERTED);
+      if (this.isInAutoSelectMode_) {
+        var destinationsToSearch =
+            opt_destination && [opt_destination] || this.destinations_;
+        destinationsToSearch.some(function(destination) {
+          if (this.matchPersistedDestination_(destination)) {
+            this.selectDestination(destination);
+            return true;
+          }
+        }, this);
+      }
+    },
+
+    /**
+     * Updates an existing print destination with capabilities and display name
+     * information. If the destination doesn't already exist, it will be added.
+     * @param {!print_preview.Destination} destination Destination to update.
+     * @return {!print_preview.Destination} The existing destination that was
+     *     updated or {@code null} if it was the new destination.
+     * @private
+     */
+    updateDestination_: function(destination) {
+      assert(destination.constructor !== Array, 'Single printer expected');
+      var existingDestination = this.destinationMap_[this.getKey_(destination)];
+      if (existingDestination != null) {
+        existingDestination.capabilities = destination.capabilities;
+      } else {
+        this.insertDestination_(destination);
+      }
+
+      if (existingDestination == this.selectedDestination_ ||
+          destination == this.selectedDestination_) {
+        this.appState_.persistSelectedDestination(this.selectedDestination_);
+        cr.dispatchSimpleEvent(
+            this,
+            DestinationStore.EventType.SELECTED_DESTINATION_CAPABILITIES_READY);
+      }
+
+      return existingDestination;
+    },
+
+    /**
      * Called when the search for Privet printers is done.
      * @private
      */
@@ -572,8 +606,8 @@ cr.define('print_preview', function() {
      *     store.
      * @private
      */
-    insertDestination_: function(destination) {
-      var key = this.getDestinationKey_(destination.origin, destination.id);
+    insertIntoStore_: function(destination) {
+      var key = this.getKey_(destination);
       var existingDestination = this.destinationMap_[key];
       if (existingDestination == null) {
         this.destinations_.push(destination);
@@ -628,17 +662,37 @@ cr.define('print_preview', function() {
     reset_: function() {
       this.destinations_ = [];
       this.destinationMap_ = {};
-      this.selectedDestination_ = null;
-      this.hasLoadedAllCloudDestinations_ = false;
+      this.selectDestination(null);
+      this.loadedCloudOrigins_ = {};
       this.hasLoadedAllLocalDestinations_ = false;
-      this.insertDestination(
+      this.insertDestination_(
           DestinationStore.createLocalPdfPrintDestination_());
+      this.resetAutoSelectTimeout_();
+    },
+
+    /**
+     * Resets destination auto selection timeout.
+     * @private
+     */
+    resetAutoSelectTimeout_: function() {
+      this.cancelAutoSelectTimeout_();
       this.autoSelectTimeout_ =
           setTimeout(this.onAutoSelectFailed_.bind(this),
                      DestinationStore.AUTO_SELECT_TIMEOUT_);
     },
 
     /**
+     * Cancels destination auto selection timeout.
+     * @private
+     */
+    cancelAutoSelectTimeout_: function() {
+      if (this.autoSelectTimeout_ != null) {
+        clearTimeout(this.autoSelectTimeout_);
+        this.autoSelectTimeout_ = null;
+      }
+    },
+
+    /**
      * Called when the local destinations have been got from the native layer.
      * @param {Event} Contains the local destinations.
      * @private
@@ -647,7 +701,7 @@ cr.define('print_preview', function() {
       var localDestinations = event.destinationInfos.map(function(destInfo) {
         return print_preview.LocalDestinationParser.parse(destInfo);
       });
-      this.insertDestinations(localDestinations);
+      this.insertDestinations_(localDestinations);
       this.isLocalDestinationSearchInProgress_ = false;
       cr.dispatchSimpleEvent(
           this, DestinationStore.EventType.DESTINATION_SEARCH_DONE);
@@ -664,9 +718,10 @@ cr.define('print_preview', function() {
      */
     onLocalDestinationCapabilitiesSet_: function(event) {
       var destinationId = event.settingsInfo['printerId'];
-      var key =
-          this.getDestinationKey_(print_preview.Destination.Origin.LOCAL,
-                                  destinationId);
+      var key = this.getDestinationKey_(
+          print_preview.Destination.Origin.LOCAL,
+          destinationId,
+          '');
       var destination = this.destinationMap_[key];
       var capabilities = print_preview.LocalCapabilitiesParser.parse(
             event.settingsInfo);
@@ -684,7 +739,7 @@ cr.define('print_preview', function() {
         destination = print_preview.LocalDestinationParser.parse(
             {deviceName: destinationId, printerName: destinationId});
         destination.capabilities = capabilities;
-        this.insertDestination(destination);
+        this.insertDestination_(destination);
       }
       if (this.selectedDestination_ &&
           this.selectedDestination_.id == destinationId) {
@@ -705,33 +760,28 @@ cr.define('print_preview', function() {
       console.error('Failed to get print capabilities for printer ' +
                     event.destinationId);
       if (this.isInAutoSelectMode_ &&
-          this.matchInitialDestinationStrict_(event.destinationId,
-                                              event.destinationOrigin)) {
-        assert(this.destinations_.length > 0,
-               'No destinations were loaded when failed to get initial ' +
-               'destination');
-        this.selectDestination(this.destinations_[0]);
+          this.sameAsPersistedDestination_(event.destinationId,
+                                           event.destinationOrigin)) {
+        this.selectDefaultDestination_();
       }
     },
 
     /**
-     * Called when the /search call completes. Adds the fetched destinations to
-     * the destination store.
-     * @param {Event} event Contains the fetched destinations.
+     * Called when the /search call completes, either successfully or not.
+     * In case of success, stores fetched destinations.
+     * @param {Event} event Contains the request result.
      * @private
      */
     onCloudPrintSearchDone_: function(event) {
-      this.insertDestinations(event.printers);
-      cr.dispatchSimpleEvent(
-          this, DestinationStore.EventType.DESTINATION_SEARCH_DONE);
-    },
-
-    /**
-     * Called when the /search call fails. Updates outstanding request count and
-     * dispatches CLOUD_DESTINATIONS_LOADED event.
-     * @private
-     */
-    onCloudPrintSearchFailed_: function() {
+      if (event.printers) {
+        this.insertDestinations_(event.printers);
+      }
+      if (event.searchDone) {
+        var origins = this.loadedCloudOrigins_[event.user] || [];
+        if (origins.indexOf(event.origin) < 0) {
+          this.loadedCloudOrigins_[event.user] = origins.concat([event.origin]);
+        }
+      }
       cr.dispatchSimpleEvent(
           this, DestinationStore.EventType.DESTINATION_SEARCH_DONE);
     },
@@ -744,7 +794,7 @@ cr.define('print_preview', function() {
      * @private
      */
     onCloudPrintPrinterDone_: function(event) {
-      this.updateDestination(event.printer);
+      this.updateDestination_(event.printer);
     },
 
     /**
@@ -757,13 +807,11 @@ cr.define('print_preview', function() {
      */
     onCloudPrintPrinterFailed_: function(event) {
       if (this.isInAutoSelectMode_ &&
-          this.matchInitialDestinationStrict_(event.destinationId,
-                                              event.destinationOrigin)) {
-        console.error('Could not find initial printer: ' + event.destinationId);
-        assert(this.destinations_.length > 0,
-               'No destinations were loaded when failed to get initial ' +
-               'destination');
-        this.selectDestination(this.destinations_[0]);
+          this.sameAsPersistedDestination_(event.destinationId,
+                                           event.destinationOrigin)) {
+        console.error(
+            'Failed to fetch last used printer caps: ' + event.destinationId);
+        this.selectDefaultDestination_();
       }
     },
 
@@ -778,7 +826,7 @@ cr.define('print_preview', function() {
         this.waitForRegisterDestination_ = null;
         this.onDestinationsReload_();
       } else {
-        this.insertDestinations(
+        this.insertDestinations_(
             print_preview.PrivetDestinationParser.parse(event.printer));
       }
     },
@@ -794,7 +842,7 @@ cr.define('print_preview', function() {
           print_preview.PrivetDestinationParser.parse(event.printer);
       destinations.forEach(function(dest) {
         dest.capabilities = event.capabilities;
-        this.updateDestination(dest);
+        this.updateDestination_(dest);
       }, this);
     },
 
@@ -816,10 +864,8 @@ cr.define('print_preview', function() {
      * @private
      */
     onAutoSelectFailed_: function() {
-      this.autoSelectTimeout_ = null;
-      assert(this.destinations_.length > 0,
-             'No destinations were loaded before auto-select timeout expired');
-      this.selectDestination(this.destinations_[0]);
+      this.cancelAutoSelectTimeout_();
+      this.selectDefaultDestination_();
     },
 
     // TODO(vitalybuka): Remove three next functions replacing Destination.id
@@ -827,23 +873,35 @@ cr.define('print_preview', function() {
     /**
      * Returns key to be used with {@code destinationMap_}.
      * @param {!print_preview.Destination.Origin} origin Destination origin.
-     * @return {!string} id Destination id.
+     * @return {string} id Destination id.
+     * @return {string} account User account destination is registered for.
      * @private
      */
-    getDestinationKey_: function(origin, id) {
-      return origin + '/' + id;
+    getDestinationKey_: function(origin, id, account) {
+      return origin + '/' + id + '/' + account;
     },
 
     /**
-     * @param {?string} id Id of the destination.
-     * @param {?string} origin Oring of the destination.
-     * @return {boolean} Whether a initial destination matches provided.
+     * Returns key to be used with {@code destinationMap_}.
+     * @param {!print_preview.Destination} destination Destination.
+     * @private
+     */
+    getKey_: function(destination) {
+      return this.getDestinationKey_(
+          destination.origin, destination.id, destination.account);
+    },
+
+    /**
+     * @param {!print_preview.Destination} destination Destination to match.
+     * @return {boolean} Whether {@code destination} matches the last user
+     *     selected one.
      * @private
      */
-    matchInitialDestination_: function(id, origin) {
-      return this.initialDestinationId_ == null ||
-             this.initialDestinationOrigin_ == null ||
-             this.matchInitialDestinationStrict_(id, origin);
+    matchPersistedDestination_: function(destination) {
+      return !this.appState_.selectedDestinationId ||
+             !this.appState_.selectedDestinationOrigin ||
+             this.sameAsPersistedDestination_(
+                 destination.id, destination.origin);
     },
 
     /**
@@ -852,9 +910,9 @@ cr.define('print_preview', function() {
      * @return {boolean} Whether destination is the same as initial.
      * @private
      */
-    matchInitialDestinationStrict_: function(id, origin) {
-      return id == this.initialDestinationId_ &&
-             origin == this.initialDestinationOrigin_;
+    sameAsPersistedDestination_: function(id, origin) {
+      return id == this.appState_.selectedDestinationId &&
+             origin == this.appState_.selectedDestinationOrigin;
     }
   };