Home | History | Annotate | Download | only in download_manager
      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 if (chrome.downloads.setShelfEnabled)
      6   chrome.downloads.setShelfEnabled(false);
      7 
      8 var colors = {
      9   progressColor: '#0d0',
     10   arrow: '#555',
     11   danger: 'red',
     12   complete: 'green',
     13   paused: 'grey',
     14   background: 'white',
     15 };
     16 
     17 function drawLine(ctx, x1, y1, x2, y2) {
     18   ctx.beginPath();
     19   ctx.moveTo(x1, y1);
     20   ctx.lineTo(x2, y2);
     21   ctx.stroke();
     22 }
     23 
     24 function 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 
     50 function 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 
     72 function 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 
     88 function 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 
    102 function 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 
    113 function 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 
    132 function 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 
    147 function 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 
    160 function 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 }
    208 pollProgress.tid = -1;
    209 pollProgress.MS = 200;
    210 
    211 pollProgress.start = function() {
    212   if (pollProgress.tid < 0) {
    213     pollProgress.tid = setTimeout(pollProgress, pollProgress.MS);
    214   }
    215 };
    216 
    217 function isNumber(n) {
    218   return !isNaN(parseFloat(n)) && isFinite(n);
    219 }
    220 
    221 if (!isNumber(localStorage.popupLastOpened)) {
    222   localStorage.popupLastOpened = '' + (new Date()).getTime();
    223 }
    224 
    225 chrome.downloads.onCreated.addListener(function(item) {
    226   pollProgress();
    227 });
    228 
    229 pollProgress();
    230 
    231 function 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 
    246 chrome.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