Home | History | Annotate | Download | only in js
      1 // Copyright (c) 2013 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 var WALLPAPER_PICKER_WIDTH = 574;
      6 var WALLPAPER_PICKER_HEIGHT = 420;
      7 
      8 var wallpaperPickerWindow;
      9 
     10 var surpriseWallpaper = null;
     11 
     12 function SurpriseWallpaper() {
     13 }
     14 
     15 /**
     16  * Gets SurpriseWallpaper instance. In case it hasn't been initialized, a new
     17  * instance is created.
     18  * @return {SurpriseWallpaper} A SurpriseWallpaper instance.
     19  */
     20 SurpriseWallpaper.getInstance = function() {
     21   if (!surpriseWallpaper)
     22     surpriseWallpaper = new SurpriseWallpaper();
     23   return surpriseWallpaper;
     24 };
     25 
     26 /**
     27  * Tries to change wallpaper to a new one in the background. May fail due to a
     28  * network issue.
     29  */
     30 SurpriseWallpaper.prototype.tryChangeWallpaper = function() {
     31   var self = this;
     32   var onFailure = function() {
     33     self.retryLater_();
     34     self.fallbackToLocalRss_();
     35   };
     36   // Try to fetch newest rss as document from server first. If any error occurs,
     37   // proceed with local copy of rss.
     38   WallpaperUtil.fetchURL(Constants.WallpaperRssURL, 'document', function(xhr) {
     39     WallpaperUtil.saveToStorage(Constants.AccessRssKey,
     40         new XMLSerializer().serializeToString(xhr.responseXML), false);
     41     self.updateSurpriseWallpaper(xhr.responseXML);
     42   }, onFailure);
     43 };
     44 
     45 /**
     46  * Retries changing the wallpaper 1 hour later. This is called when fetching the
     47  * rss or wallpaper from server fails.
     48  * @private
     49  */
     50 SurpriseWallpaper.prototype.retryLater_ = function() {
     51   chrome.alarms.create('RetryAlarm', {delayInMinutes: 60});
     52 };
     53 
     54 /**
     55  * Fetches the cached rss feed from local storage in the event of being unable
     56  * to download the online feed.
     57  * @private
     58  */
     59 SurpriseWallpaper.prototype.fallbackToLocalRss_ = function() {
     60   var self = this;
     61   Constants.WallpaperLocalStorage.get(Constants.AccessRssKey, function(items) {
     62     var rssString = items[Constants.AccessRssKey];
     63     if (rssString) {
     64       self.updateSurpriseWallpaper(new DOMParser().parseFromString(rssString,
     65                                                                    'text/xml'));
     66     } else {
     67       self.updateSurpriseWallpaper();
     68     }
     69   });
     70 };
     71 
     72 /**
     73  * Starts to change wallpaper. Called after rss is fetched.
     74  * @param {Document=} opt_rss The fetched rss document. If opt_rss is null, uses
     75  *     a random wallpaper.
     76  */
     77 SurpriseWallpaper.prototype.updateSurpriseWallpaper = function(opt_rss) {
     78   if (opt_rss) {
     79     var items = opt_rss.querySelectorAll('item');
     80     var date = new Date(new Date().toDateString()).getTime();
     81     for (var i = 0; i < items.length; i++) {
     82       item = items[i];
     83       var disableDate = new Date(item.getElementsByTagNameNS(
     84           Constants.WallpaperNameSpaceURI, 'disableDate')[0].textContent).
     85               getTime();
     86       var enableDate = new Date(item.getElementsByTagNameNS(
     87           Constants.WallpaperNameSpaceURI, 'enableDate')[0].textContent).
     88               getTime();
     89       var regionsString = item.getElementsByTagNameNS(
     90           Constants.WallpaperNameSpaceURI, 'regions')[0].textContent;
     91       var regions = regionsString.split(', ');
     92       if (enableDate <= date && disableDate > date &&
     93           regions.indexOf(navigator.language) != -1) {
     94         var self = this;
     95         this.setWallpaperFromRssItem_(item,
     96                                       function() {},
     97                                       function() {
     98                                         self.retryLater_();
     99                                         self.updateRandomWallpaper_();
    100                                       });
    101         return;
    102       }
    103     }
    104   }
    105   // No surprise wallpaper for today at current locale or fetching rss feed
    106   // fails. Fallback to use a random one from wallpaper server.
    107   this.updateRandomWallpaper_();
    108 };
    109 
    110 /**
    111  * Sets a new random wallpaper if one has not already been set today.
    112  * @private
    113  */
    114 SurpriseWallpaper.prototype.updateRandomWallpaper_ = function() {
    115   var self = this;
    116   Constants.WallpaperSyncStorage.get(
    117       Constants.AccessLastSurpriseWallpaperChangedDate, function(items) {
    118     var dateString = new Date().toDateString();
    119     // At most one random wallpaper per day.
    120     if (items[Constants.AccessLastSurpriseWallpaperChangedDate] != dateString) {
    121       self.setRandomWallpaper_(dateString);
    122     }
    123   });
    124 };
    125 
    126 /**
    127  * Sets wallpaper to one of the wallpapers displayed in wallpaper picker. If
    128  * the wallpaper download fails, retry one hour later. Wallpapers that are
    129  * disabled for surprise me are excluded.
    130  * @param {string} dateString String representation of current local date.
    131  * @private
    132  */
    133 SurpriseWallpaper.prototype.setRandomWallpaper_ = function(dateString) {
    134   var self = this;
    135   Constants.WallpaperLocalStorage.get(Constants.AccessManifestKey,
    136                                       function(items) {
    137     var manifest = items[Constants.AccessManifestKey];
    138     if (manifest && manifest.wallpaper_list) {
    139       var filtered = manifest.wallpaper_list.filter(function(element) {
    140         // Older version manifest do not have available_for_surprise_me field.
    141         // In this case, no wallpaper should be filtered out.
    142         return element.available_for_surprise_me ||
    143             element.available_for_surprise_me == undefined;
    144       });
    145       var index = Math.floor(Math.random() * filtered.length);
    146       var wallpaper = filtered[index];
    147       var wallpaperURL = wallpaper.base_url + Constants.HighResolutionSuffix;
    148       var onSuccess = function() {
    149         WallpaperUtil.saveToStorage(
    150             Constants.AccessLastSurpriseWallpaperChangedDate,
    151             dateString,
    152             true);
    153       };
    154       WallpaperUtil.setOnlineWallpaper(wallpaperURL, wallpaper.default_layout,
    155           onSuccess, self.retryLater_.bind(self));
    156     }
    157   });
    158 };
    159 
    160 /**
    161  * Sets wallpaper to the wallpaper specified by item from rss. If downloading
    162  * the wallpaper fails, retry one hour later.
    163  * @param {Element} item The wallpaper rss item element.
    164  * @param {function} onSuccess Success callback.
    165  * @param {function} onFailure Failure callback.
    166  * @private
    167  */
    168 SurpriseWallpaper.prototype.setWallpaperFromRssItem_ = function(item,
    169                                                                 onSuccess,
    170                                                                 onFailure) {
    171   var url = item.querySelector('link').textContent;
    172   var layout = item.getElementsByTagNameNS(
    173         Constants.WallpaperNameSpaceURI, 'layout')[0].textContent;
    174   var self = this;
    175   WallpaperUtil.fetchURL(url, 'arraybuffer', function(xhr) {
    176     if (xhr.response != null) {
    177       chrome.wallpaperPrivate.setCustomWallpaper(xhr.response, layout, false,
    178                                                  'surprise_wallpaper',
    179                                                  onSuccess);
    180     } else {
    181       onFailure();
    182     }
    183   }, onFailure);
    184 };
    185 
    186 /**
    187  * Disables the wallpaper surprise me feature. Clear all alarms and states.
    188  */
    189 SurpriseWallpaper.prototype.disable = function() {
    190   chrome.alarms.clearAll();
    191   // Makes last changed date invalid.
    192   WallpaperUtil.saveToStorage(Constants.AccessLastSurpriseWallpaperChangedDate,
    193                               '', true);
    194 };
    195 
    196 /**
    197  * Changes current wallpaper and sets up an alarm to schedule next change around
    198  * midnight.
    199  */
    200 SurpriseWallpaper.prototype.next = function() {
    201   var nextUpdate = this.nextUpdateTime(new Date());
    202   chrome.alarms.create({when: nextUpdate});
    203   this.tryChangeWallpaper();
    204 };
    205 
    206 /**
    207  * Calculates when the next wallpaper change should be triggered.
    208  * @param {Date} now Current time.
    209  * @return {number} The time when next wallpaper change should happen.
    210  */
    211 SurpriseWallpaper.prototype.nextUpdateTime = function(now) {
    212   var nextUpdate = new Date(now.setDate(now.getDate() + 1)).toDateString();
    213   return new Date(nextUpdate).getTime();
    214 };
    215 
    216 chrome.app.runtime.onLaunched.addListener(function() {
    217   if (wallpaperPickerWindow && !wallpaperPickerWindow.contentWindow.closed) {
    218     wallpaperPickerWindow.focus();
    219     chrome.wallpaperPrivate.minimizeInactiveWindows();
    220     return;
    221   }
    222 
    223   chrome.app.window.create('main.html', {
    224     frame: 'none',
    225     width: WALLPAPER_PICKER_WIDTH,
    226     height: WALLPAPER_PICKER_HEIGHT,
    227     resizable: false,
    228     alphaEnabled: true
    229   }, function(w) {
    230     wallpaperPickerWindow = w;
    231     chrome.wallpaperPrivate.minimizeInactiveWindows();
    232     w.onClosed.addListener(function() {
    233       chrome.wallpaperPrivate.restoreMinimizedWindows();
    234     });
    235     WallpaperUtil.testSendMessage('wallpaper-window-created');
    236   });
    237 });
    238 
    239 chrome.storage.onChanged.addListener(function(changes, namespace) {
    240   if (changes[Constants.AccessSurpriseMeEnabledKey]) {
    241     if (changes[Constants.AccessSurpriseMeEnabledKey].newValue) {
    242       SurpriseWallpaper.getInstance().next();
    243     } else {
    244       SurpriseWallpaper.getInstance().disable();
    245     }
    246   }
    247 
    248   if (changes[Constants.AccessSyncWallpaperInfoKey]) {
    249     var newValue = changes[Constants.AccessSyncWallpaperInfoKey].newValue;
    250     Constants.WallpaperLocalStorage.get(Constants.AccessLocalWallpaperInfoKey,
    251                                         function(items) {
    252       // Normally, the wallpaper info saved in local storage and sync storage
    253       // are the same. If the synced value changed by sync service, they may
    254       // different. In that case, change wallpaper to the one saved in sync
    255       // storage and update the local value.
    256       var localValue = items[Constants.AccessLocalWallpaperInfoKey];
    257       if (localValue == undefined ||
    258           localValue.url != newValue.url ||
    259           localValue.layout != newValue.layout ||
    260           localValue.source != newValue.source) {
    261         if (newValue.source == Constants.WallpaperSourceEnum.Online) {
    262           // TODO(bshe): Consider schedule an alarm to set online wallpaper
    263           // later when failed. Note that we need to cancel the retry if user
    264           // set another wallpaper before retry alarm invoked.
    265           WallpaperUtil.setOnlineWallpaper(newValue.url, newValue.layout,
    266             function() {}, function() {});
    267         }
    268         WallpaperUtil.saveToStorage(Constants.AccessLocalWallpaperInfoKey,
    269                                     newValue, false);
    270       }
    271     });
    272   }
    273 });
    274 
    275 chrome.alarms.onAlarm.addListener(function() {
    276   SurpriseWallpaper.getInstance().next();
    277 });
    278