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