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