event_page.js revision 1320f92c476a1ad9d19dba2a48c72b75566198e9
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
5var WALLPAPER_PICKER_WIDTH = 574;
6var WALLPAPER_PICKER_HEIGHT = 420;
7
8var wallpaperPickerWindow;
9
10var surpriseWallpaper = null;
11
12function 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 */
20SurpriseWallpaper.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 */
30SurpriseWallpaper.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 */
50SurpriseWallpaper.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 */
59SurpriseWallpaper.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 */
77SurpriseWallpaper.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 */
114SurpriseWallpaper.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 */
133SurpriseWallpaper.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 */
168SurpriseWallpaper.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 */
189SurpriseWallpaper.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 */
200SurpriseWallpaper.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 */
211SurpriseWallpaper.prototype.nextUpdateTime = function(now) {
212  var nextUpdate = new Date(now.setDate(now.getDate() + 1)).toDateString();
213  return new Date(nextUpdate).getTime();
214};
215
216chrome.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
239chrome.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
275chrome.alarms.onAlarm.addListener(function() {
276  SurpriseWallpaper.getInstance().next();
277});
278