background.js revision a36e5920737c6adbddd3e43b760e5de8431db6e0
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
5if (chrome.downloads.setShelfEnabled)
6  chrome.downloads.setShelfEnabled(false);
7
8var colors = {
9  progressColor: '#0d0',
10  arrow: '#555',
11  danger: 'red',
12  complete: 'green',
13  paused: 'grey',
14  background: 'white',
15};
16
17function drawLine(ctx, x1, y1, x2, y2) {
18  ctx.beginPath();
19  ctx.moveTo(x1, y1);
20  ctx.lineTo(x2, y2);
21  ctx.stroke();
22}
23
24function drawProgressSpinner(ctx, stage) {
25  var center = ctx.canvas.width/2;
26  var radius = center*0.9;
27  const segments = 16;
28  var segArc = 2 * Math.PI / segments;
29  ctx.lineWidth = Math.round(ctx.canvas.width*0.1);
30  for (var seg = 0; seg < segments; ++seg) {
31    var color = ((stage == 'm') ? ((seg % 2) == 0) : (seg <= stage));
32    ctx.fillStyle = ctx.strokeStyle = (
33      color ? colors.progressColor : colors.background);
34    ctx.moveTo(center, center);
35    ctx.beginPath();
36    ctx.arc(center, center, radius, (seg-4)*segArc, (seg-3)*segArc, false);
37    ctx.lineTo(center, center);
38    ctx.fill();
39    ctx.stroke();
40  }
41  ctx.strokeStyle = colors.background;
42  radius += ctx.lineWidth-1;
43  drawLine(ctx, center-radius, center, center+radius, center);
44  drawLine(ctx, center, center-radius, center, center+radius);
45  radius *= Math.sin(Math.PI/4);
46  drawLine(ctx, center-radius, center-radius, center+radius, center+radius);
47  drawLine(ctx, center-radius, center+radius, center+radius, center-radius);
48}
49
50function drawArrow(ctx) {
51  ctx.beginPath();
52  ctx.lineWidth = Math.round(ctx.canvas.width*0.1);
53  ctx.lineJoin = 'round';
54  ctx.strokeStyle = ctx.fillStyle = colors.arrow;
55  var center = ctx.canvas.width/2;
56  var minw2 = center*0.2;
57  var maxw2 = center*0.60;
58  var height2 = maxw2;
59  ctx.moveTo(center-minw2, center-height2);
60  ctx.lineTo(center+minw2, center-height2);
61  ctx.lineTo(center+minw2, center);
62  ctx.lineTo(center+maxw2, center);
63  ctx.lineTo(center, center+height2);
64  ctx.lineTo(center-maxw2, center);
65  ctx.lineTo(center-minw2, center);
66  ctx.lineTo(center-minw2, center-height2);
67  ctx.lineTo(center+minw2, center-height2);
68  ctx.stroke();
69  ctx.fill();
70}
71
72function drawDangerBadge(ctx) {
73  var s = ctx.canvas.width/100;
74  ctx.fillStyle = colors.danger;
75  ctx.strokeStyle = colors.background;
76  ctx.lineWidth = Math.round(s*5);
77  var edge = ctx.canvas.width-ctx.lineWidth;
78  ctx.beginPath();
79  ctx.moveTo(s*75, s*55);
80  ctx.lineTo(edge, edge);
81  ctx.lineTo(s*55, edge);
82  ctx.lineTo(s*75, s*55);
83  ctx.lineTo(edge, edge);
84  ctx.fill();
85  ctx.stroke();
86}
87
88function drawPausedBadge(ctx) {
89  var s = ctx.canvas.width/100;
90  ctx.beginPath();
91  ctx.strokeStyle = colors.background;
92  ctx.lineWidth = Math.round(s*5);
93  ctx.rect(s*55, s*55, s*15, s*35);
94  ctx.fillStyle = colors.paused;
95  ctx.fill();
96  ctx.stroke();
97  ctx.rect(s*75, s*55, s*15, s*35);
98  ctx.fill();
99  ctx.stroke();
100}
101
102function drawCompleteBadge(ctx) {
103  var s = ctx.canvas.width/100;
104  ctx.beginPath();
105  ctx.arc(s*75, s*75, s*15, 0, 2*Math.PI, false);
106  ctx.fillStyle = colors.complete;
107  ctx.fill();
108  ctx.strokeStyle = colors.background;
109  ctx.lineWidth = Math.round(s*5);
110  ctx.stroke();
111}
112
113function drawIcon(side, stage, badge) {
114  var canvas = document.createElement('canvas');
115  canvas.width = canvas.height = side;
116  document.body.appendChild(canvas);
117  var ctx = canvas.getContext('2d');
118  if (stage != 'n') {
119    drawProgressSpinner(ctx, stage);
120  }
121  drawArrow(ctx);
122  if (badge == 'd') {
123    drawDangerBadge(ctx);
124  } else if (badge == 'p') {
125    drawPausedBadge(ctx);
126  } else if (badge == 'c') {
127    drawCompleteBadge(ctx);
128  }
129  return canvas;
130}
131
132function maybeOpen(id) {
133  var openWhenComplete = [];
134  try {
135    openWhenComplete = JSON.parse(localStorage.openWhenComplete);
136  } catch (e) {
137    localStorage.openWhenComplete = JSON.stringify(openWhenComplete);
138  }
139  var openNowIndex = openWhenComplete.indexOf(id);
140  if (openNowIndex >= 0) {
141    chrome.downloads.open(id);
142    openWhenComplete.splice(openNowIndex, 1);
143    localStorage.openWhenComplete = JSON.stringify(openWhenComplete);
144  }
145}
146
147function setBrowserActionIcon(stage, badge) {
148  var canvas1 = drawIcon(19, stage, badge);
149  var canvas2 = drawIcon(38, stage, badge);
150  var imageData = {};
151  imageData['' + canvas1.width] = canvas1.getContext('2d').getImageData(
152        0, 0, canvas1.width, canvas1.height);
153  imageData['' + canvas2.width] = canvas2.getContext('2d').getImageData(
154        0, 0, canvas2.width, canvas2.height);
155  chrome.browserAction.setIcon({imageData:imageData});
156  canvas1.parentNode.removeChild(canvas1);
157  canvas2.parentNode.removeChild(canvas2);
158}
159
160function pollProgress() {
161  pollProgress.tid = -1;
162  chrome.downloads.search({}, function(items) {
163    var popupLastOpened = parseInt(localStorage.popupLastOpened);
164    var totalTotalBytes = 0;
165    var totalBytesReceived = 0;
166    var anyMissingTotalBytes = false;
167    var anyDangerous = false;
168    var anyPaused = false;
169    var anyRecentlyCompleted = false;
170    var anyInProgress = false;
171    items.forEach(function(item) {
172      if (item.state == 'in_progress') {
173        anyInProgress = true;
174        if (item.totalBytes) {
175          totalTotalBytes += item.totalBytes;
176          totalBytesReceived += item.bytesReceived;
177        } else {
178          anyMissingTotalBytes = true;
179        }
180        var dangerous = ((item.danger != 'safe') &&
181                         (item.danger != 'accepted'));
182        anyDangerous = anyDangerous || dangerous;
183        anyPaused = anyPaused || item.paused;
184      } else if ((item.state == 'complete') && item.endTime && !item.error) {
185        var ended = (new Date(item.endTime)).getTime();
186        var recentlyCompleted = (ended >= popupLastOpened);
187        anyRecentlyCompleted = anyRecentlyCompleted || recentlyCompleted;
188        maybeOpen(item.id);
189      }
190    });
191    var stage = !anyInProgress ? 'n' : (anyMissingTotalBytes ? 'm' :
192      parseInt((totalBytesReceived / totalTotalBytes) * 15));
193    var badge = anyDangerous ? 'd' : (anyPaused ? 'p' :
194      (anyRecentlyCompleted ? 'c' : ''));
195
196    var targetIcon = stage + ' ' + badge;
197    if (sessionStorage.currentIcon != targetIcon) {
198      setBrowserActionIcon(stage, badge);
199      sessionStorage.currentIcon = targetIcon;
200    }
201
202    if (anyInProgress &&
203        (pollProgress.tid < 0)) {
204      pollProgress.start();
205    }
206  });
207}
208pollProgress.tid = -1;
209pollProgress.MS = 200;
210
211pollProgress.start = function() {
212  if (pollProgress.tid < 0) {
213    pollProgress.tid = setTimeout(pollProgress, pollProgress.MS);
214  }
215};
216
217function isNumber(n) {
218  return !isNaN(parseFloat(n)) && isFinite(n);
219}
220
221if (!isNumber(localStorage.popupLastOpened)) {
222  localStorage.popupLastOpened = '' + (new Date()).getTime();
223}
224
225chrome.downloads.onCreated.addListener(function(item) {
226  pollProgress();
227});
228
229pollProgress();
230
231function openWhenComplete(downloadId) {
232  var ids = [];
233  try {
234    ids = JSON.parse(localStorage.openWhenComplete);
235  } catch (e) {
236    localStorage.openWhenComplete = JSON.stringify(ids);
237  }
238  pollProgress.start();
239  if (ids.indexOf(downloadId) >= 0) {
240    return;
241  }
242  ids.push(downloadId);
243  localStorage.openWhenComplete = JSON.stringify(ids);
244}
245
246chrome.runtime.onMessage.addListener(function(request) {
247  if (request == 'poll') {
248    pollProgress.start();
249  }
250  if (request == 'icons') {
251    [16, 19, 38, 128].forEach(function(s) {
252      var canvas = drawIcon(s, 'n', '');
253      chrome.downloads.download({
254        url: canvas.toDataURL('image/png', 1.0),
255        filename: 'icon' + s + '.png',
256      });
257      canvas.parentNode.removeChild(canvas);
258    });
259  }
260  if (isNumber(request.openWhenComplete)) {
261    openWhenComplete(request.openWhenComplete);
262  }
263});
264