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 function pointInElement(p, elem) { 6 return ((p.x >= elem.offsetLeft) && 7 (p.x <= (elem.offsetLeft + elem.offsetWidth)) && 8 (p.y >= elem.offsetTop) && 9 (p.y <= (elem.offsetTop + elem.offsetHeight))); 10 }; 11 12 function setLastOpened() { 13 localStorage.popupLastOpened = (new Date()).getTime(); 14 chrome.runtime.sendMessage('poll'); 15 }; 16 17 function loadI18nMessages() { 18 function setProperty(selector, prop, msg) { 19 document.querySelector(selector)[prop] = chrome.i18n.getMessage(msg); 20 } 21 22 setProperty('title', 'innerText', 'tabTitle'); 23 setProperty('#q', 'placeholder', 'searchPlaceholder'); 24 setProperty('#clear-all', 'title', 'clearAllTitle'); 25 setProperty('#open-folder', 'title', 'openDownloadsFolderTitle'); 26 setProperty('#empty', 'innerText', 'zeroItems'); 27 setProperty('#searching', 'innerText', 'searching'); 28 setProperty('#search-zero', 'innerText', 'zeroSearchResults'); 29 setProperty('#management-permission-info', 'innerText', 30 'managementPermissionInfo'); 31 setProperty('#grant-management-permission', 'innerText', 32 'grantManagementPermission'); 33 setProperty('#older', 'innerText', 'showOlderDownloads'); 34 setProperty('#loading-older', 'innerText', 'loadingOlderDownloads'); 35 setProperty('.pause', 'title', 'pauseTitle'); 36 setProperty('.resume', 'title', 'resumeTitle'); 37 setProperty('.cancel', 'title', 'cancelTitle'); 38 setProperty('.show-folder', 'title', 'showInFolderTitle'); 39 setProperty('.erase', 'title', 'eraseTitle'); 40 setProperty('.url', 'title', 'retryTitle'); 41 setProperty('.referrer', 'title', 'referrerTitle'); 42 setProperty('.open-filename', 'title', 'openTitle'); 43 setProperty('#bad-chrome-version', 'innerText', 'badChromeVersion'); 44 setProperty('.remove-file', 'title', 'removeFileTitle'); 45 46 document.querySelector('.progress').style.minWidth = 47 getTextWidth(formatBytes(1024 * 1024 * 1023.9) + '/' + 48 formatBytes(1024 * 1024 * 1023.9)) + 'px'; 49 50 // This only covers {timeLeft,openWhenComplete}{Finishing,Days}. If 51 // ...Hours/Minutes/Seconds could be longer for any locale, then this should 52 // test them. 53 var max_time_left_width = 0; 54 for (var i = 0; i < 4; ++i) { 55 max_time_left_width = Math.max(max_time_left_width, getTextWidth( 56 formatTimeLeft(0 == (i % 2), 57 (i < 2) ? 0 : ((100 * 24) + 23) * 60 * 60 * 1000))); 58 } 59 document.querySelector('body div.item span.time-left').style.minWidth = 60 max_time_left_width + 'px'; 61 }; 62 63 function getTextWidth(s) { 64 var probe = document.getElementById('text-width-probe'); 65 probe.innerText = s; 66 return probe.offsetWidth; 67 }; 68 69 function formatDateTime(date) { 70 var now = new Date(); 71 var zpad_mins = ':' + (date.getMinutes() < 10 ? '0' : '') + date.getMinutes(); 72 if (date.getYear() != now.getYear()) { 73 return '' + (1900 + date.getYear()); 74 } else if ((date.getMonth() != now.getMonth()) || 75 (date.getDate() != now.getDate())) { 76 return date.getDate() + ' ' + chrome.i18n.getMessage( 77 'month' + date.getMonth() + 'abbr'); 78 } else if (date.getHours() == 12) { 79 return '12' + zpad_mins + 'pm'; 80 } else if (date.getHours() > 12) { 81 return (date.getHours() - 12) + zpad_mins + 'pm'; 82 } 83 return date.getHours() + zpad_mins + 'am'; 84 } 85 86 function formatBytes(n) { 87 if (n < 1024) { 88 return n + 'B'; 89 } 90 var prefixes = 'KMGTPEZY'; 91 var mul = 1024; 92 for (var i = 0; i < prefixes.length; ++i) { 93 if (n < (1024 * mul)) { 94 return (parseInt(n / mul) + '.' + parseInt(10 * ((n / mul) % 1)) + 95 prefixes[i] + 'B'); 96 } 97 mul *= 1024; 98 } 99 return '!!!'; 100 } 101 102 function formatTimeLeft(openWhenComplete, ms) { 103 var prefix = openWhenComplete ? 'openWhenComplete' : 'timeLeft'; 104 if (ms < 1000) { 105 return chrome.i18n.getMessage(prefix + 'Finishing'); 106 } 107 var days = parseInt(ms / (24 * 60 * 60 * 1000)); 108 var hours = parseInt(ms / (60 * 60 * 1000)) % 24; 109 if (days) { 110 return chrome.i18n.getMessage(prefix + 'Days', [days, hours]); 111 } 112 var minutes = parseInt(ms / (60 * 1000)) % 60; 113 if (hours) { 114 return chrome.i18n.getMessage(prefix + 'Hours', [hours, minutes]); 115 } 116 var seconds = parseInt(ms / 1000) % 60; 117 if (minutes) { 118 return chrome.i18n.getMessage(prefix + 'Minutes', [minutes, seconds]); 119 } 120 return chrome.i18n.getMessage(prefix + 'Seconds', [seconds]); 121 } 122 123 function ratchetWidth(w) { 124 var current = parseInt(document.body.style.minWidth) || 0; 125 document.body.style.minWidth = Math.max(w, current) + 'px'; 126 } 127 128 function ratchetHeight(h) { 129 var current = parseInt(document.body.style.minHeight) || 0; 130 document.body.style.minHeight = Math.max(h, current) + 'px'; 131 } 132 133 function binarySearch(array, target, cmp) { 134 var low = 0, high = array.length - 1, i, comparison; 135 while (low <= high) { 136 i = (low + high) >> 1; 137 comparison = cmp(target, array[i]); 138 if (comparison < 0) { 139 low = i + 1; 140 } else if (comparison > 0) { 141 high = i - 1; 142 } else { 143 return i; 144 } 145 } 146 return i; 147 }; 148 149 function arrayFrom(seq) { 150 return Array.prototype.slice.apply(seq); 151 }; 152 153 function DownloadItem(data) { 154 var item = this; 155 for (var prop in data) { 156 item[prop] = data[prop]; 157 } 158 item.startTime = new Date(item.startTime); 159 if (item.canResume == undefined) { 160 DownloadItem.canResumeHack = true; 161 } 162 163 item.div = document.querySelector('body>div.item').cloneNode(true); 164 item.div.id = 'item' + item.id; 165 item.div.item = item; 166 167 var items_div = document.getElementById('items'); 168 if ((items_div.childNodes.length == 0) || 169 (item.startTime.getTime() < items_div.childNodes[ 170 items_div.childNodes.length - 1].item.startTime.getTime())) { 171 items_div.appendChild(item.div); 172 } else if (item.startTime.getTime() > 173 items_div.childNodes[0].item.startTime.getTime()) { 174 items_div.insertBefore(item.div, items_div.childNodes[0]); 175 } else { 176 var adjacent_div = items_div.childNodes[ 177 binarySearch(arrayFrom(items_div.childNodes), 178 item.startTime.getTime(), 179 function(target, other) { 180 return target - other.item.startTime.getTime(); 181 })]; 182 var adjacent_item = adjacent_div.item; 183 if (adjacent_item.startTime.getTime() < item.startTime.getTime()) { 184 items_div.insertBefore(item.div, adjacent_div); 185 } else { 186 items_div.insertBefore(item.div, adjacent_div.nextSibling); 187 } 188 } 189 190 item.getElement('referrer').onclick = function() { 191 chrome.tabs.create({url: item.referrer}); 192 return false; 193 }; 194 item.getElement('by-ext').onclick = function() { 195 chrome.tabs.create({url: 'chrome://extensions#' + item.byExtensionId}); 196 return false; 197 } 198 item.getElement('open-filename').onclick = function() { 199 item.open(); 200 return false; 201 }; 202 item.getElement('open-filename').ondragstart = function() { 203 item.drag(); 204 return false; 205 }; 206 item.getElement('pause').onclick = function() { 207 item.pause(); 208 return false; 209 }; 210 item.getElement('cancel').onclick = function() { 211 item.cancel(); 212 return false; 213 }; 214 item.getElement('resume').onclick = function() { 215 item.resume(); 216 return false; 217 }; 218 item.getElement('show-folder').onclick = function() { 219 item.show(); 220 return false; 221 }; 222 item.getElement('remove-file').onclick = function() { 223 item.removeFile(); 224 return false; 225 }; 226 item.getElement('erase').onclick = function() { 227 item.erase(); 228 return false; 229 }; 230 231 item.more_mousemove = function(evt) { 232 var mouse = {x:evt.x, y:evt.y+document.body.scrollTop}; 233 if (item.getElement('more') && 234 (pointInElement(mouse, item.div) || 235 pointInElement(mouse, item.getElement('more')))) { 236 return; 237 } 238 if (item.getElement('more')) { 239 item.getElement('more').hidden = true; 240 } 241 window.removeEventListener('mousemove', item.more_mousemove); 242 }; 243 [item.div, item.getElement('more')].concat( 244 item.getElement('more').children).forEach(function(elem) { 245 elem.onmouseover = function() { 246 arrayFrom(items_div.children).forEach(function(other) { 247 if (other.item != item) { 248 other.item.getElement('more').hidden = true; 249 } 250 }); 251 item.getElement('more').hidden = false; 252 item.getElement('more').style.top = 253 (item.div.offsetTop + item.div.offsetHeight) + 'px'; 254 item.getElement('more').style.left = item.div.offsetLeft + 'px'; 255 if (window.innerHeight < (parseInt(item.getElement('more').style.top) + 256 item.getElement('more').offsetHeight)) { 257 item.getElement('more').style.top = ( 258 item.div.offsetTop - item.getElement('more').offsetHeight) + 'px'; 259 } 260 window.addEventListener('mousemove', item.more_mousemove); 261 }; 262 }); 263 264 if (item.referrer) { 265 item.getElement('referrer').href = item.referrer; 266 } else { 267 item.getElement('referrer').hidden = true; 268 } 269 item.getElement('url').href = item.url; 270 item.getElement('url').innerText = item.url; 271 item.render(); 272 } 273 DownloadItem.canResumeHack = false; 274 275 DownloadItem.prototype.getElement = function(name) { 276 return document.querySelector('#item' + this.id + ' .' + name); 277 }; 278 279 DownloadItem.prototype.render = function() { 280 var item = this; 281 var now = new Date(); 282 var in_progress = (item.state == 'in_progress') 283 var openable = (item.state != 'interrupted') && item.exists && !item.deleted; 284 285 item.startTime = new Date(item.startTime); 286 if (DownloadItem.canResumeHack) { 287 item.canResume = in_progress && item.paused; 288 } 289 if (item.filename) { 290 item.basename = item.filename.substring(Math.max( 291 item.filename.lastIndexOf('\\'), 292 item.filename.lastIndexOf('/')) + 1); 293 } 294 if (item.estimatedEndTime) { 295 item.estimatedEndTime = new Date(item.estimatedEndTime); 296 } 297 if (item.endTime) { 298 item.endTime = new Date(item.endTime); 299 } 300 301 if (item.filename && !item.icon_url) { 302 chrome.downloads.getFileIcon( 303 item.id, 304 {'size': 32}, 305 function(icon_url) { 306 item.getElement('icon').hidden = !icon_url; 307 if (icon_url) { 308 item.icon_url = icon_url; 309 item.getElement('icon').src = icon_url; 310 } 311 }); 312 } 313 314 item.getElement('removed').style.display = openable ? 'none' : 'inline'; 315 item.getElement('open-filename').style.display = ( 316 openable ? 'inline' : 'none'); 317 item.getElement('in-progress').hidden = !in_progress; 318 item.getElement('pause').style.display = ( 319 !in_progress || item.paused) ? 'none' : 'inline-block'; 320 item.getElement('resume').style.display = ( 321 !in_progress || !item.canResume) ? 'none' : 'inline-block'; 322 item.getElement('cancel').style.display = ( 323 !in_progress ? 'none' : 'inline-block'); 324 item.getElement('remove-file').hidden = ( 325 (item.state != 'complete') || 326 !item.exists || 327 item.deleted || 328 !chrome.downloads.removeFile); 329 item.getElement('erase').hidden = in_progress; 330 331 var could_progress = in_progress || item.canResume; 332 item.getElement('progress').style.display = ( 333 could_progress ? 'inline-block' : 'none'); 334 item.getElement('meter').hidden = !could_progress || !item.totalBytes; 335 336 item.getElement('removed').innerText = item.basename; 337 item.getElement('open-filename').innerText = item.basename; 338 339 function setByExtension(show) { 340 if (show) { 341 item.getElement('by-ext').title = item.byExtensionName; 342 item.getElement('by-ext').href = 343 'chrome://extensions#' + item.byExtensionId; 344 item.getElement('by-ext img').src = 345 'chrome://extension-icon/' + item.byExtensionId + '/48/1'; 346 } else { 347 item.getElement('by-ext').hidden = true; 348 } 349 } 350 if (item.byExtensionId && item.byExtensionName) { 351 chrome.permissions.contains({permissions: ['management']}, 352 function(result) { 353 if (result) { 354 setByExtension(true); 355 } else { 356 setByExtension(false); 357 if (!localStorage.managementPermissionDenied) { 358 document.getElementById('request-management-permission').hidden = 359 false; 360 document.getElementById('grant-management-permission').onclick = 361 function() { 362 chrome.permissions.request({permissions: ['management']}, 363 function(granted) { 364 setByExtension(granted); 365 if (!granted) { 366 localStorage.managementPermissionDenied = true; 367 } 368 }); 369 return false; 370 }; 371 } 372 } 373 }); 374 } else { 375 setByExtension(false); 376 } 377 378 if (!item.getElement('error').hidden) { 379 if (item.error) { 380 // TODO(benjhayden) When https://codereview.chromium.org/16924017/ is 381 // released, set minimum_chrome_version and remove the error_N messages. 382 item.getElement('error').innerText = chrome.i18n.getMessage( 383 'error_' + item.error); 384 if (!item.getElement('error').innerText) { 385 item.getElement('error').innerText = item.error; 386 } 387 } else if (!openable) { 388 item.getElement('error').innerText = chrome.i18n.getMessage( 389 'errorRemoved'); 390 } 391 } 392 393 item.getElement('complete-size').innerText = formatBytes( 394 item.bytesReceived); 395 if (item.totalBytes && (item.state != 'complete')) { 396 item.getElement('progress').innerText = ( 397 item.getElement('complete-size').innerText + '/' + 398 formatBytes(item.totalBytes)); 399 item.getElement('meter').children[0].style.width = parseInt( 400 100 * item.bytesReceived / item.totalBytes) + '%'; 401 } 402 403 if (in_progress) { 404 if (item.estimatedEndTime && !item.paused) { 405 var openWhenComplete = false; 406 try { 407 openWhenComplete = JSON.parse(localStorage.openWhenComplete).indexOf( 408 item.id) >= 0; 409 } catch (e) { 410 } 411 item.getElement('time-left').innerText = formatTimeLeft( 412 openWhenComplete, item.estimatedEndTime.getTime() - now.getTime()); 413 } else { 414 item.getElement('time-left').innerText = String.fromCharCode(160); 415 } 416 } 417 418 if (item.startTime) { 419 item.getElement('start-time').innerText = formatDateTime( 420 item.startTime); 421 } 422 423 ratchetWidth(item.getElement('icon').offsetWidth + 424 item.getElement('file-url').offsetWidth + 425 item.getElement('cancel').offsetWidth + 426 item.getElement('pause').offsetWidth + 427 item.getElement('resume').offsetWidth); 428 ratchetWidth(item.getElement('more').offsetWidth); 429 430 this.maybeAccept(); 431 }; 432 433 DownloadItem.prototype.onChanged = function(delta) { 434 for (var key in delta) { 435 if (key != 'id') { 436 this[key] = delta[key].current; 437 } 438 } 439 this.render(); 440 if (delta.state) { 441 setLastOpened(); 442 } 443 if ((this.state == 'in_progress') && !this.paused) { 444 DownloadManager.startPollingProgress(); 445 } 446 }; 447 448 DownloadItem.prototype.onErased = function() { 449 window.removeEventListener('mousemove', this.more_mousemove); 450 document.getElementById('items').removeChild(this.div); 451 }; 452 453 DownloadItem.prototype.drag = function() { 454 chrome.downloads.drag(this.id); 455 }; 456 457 DownloadItem.prototype.show = function() { 458 chrome.downloads.show(this.id); 459 }; 460 461 DownloadItem.prototype.open = function() { 462 if (this.state == 'complete') { 463 chrome.downloads.open(this.id); 464 return; 465 } 466 chrome.runtime.sendMessage({openWhenComplete:this.id}); 467 }; 468 469 DownloadItem.prototype.removeFile = function() { 470 chrome.downloads.removeFile(this.id); 471 this.deleted = true; 472 this.render(); 473 }; 474 475 DownloadItem.prototype.erase = function() { 476 chrome.downloads.erase({id: this.id}); 477 }; 478 479 DownloadItem.prototype.pause = function() { 480 chrome.downloads.pause(this.id); 481 }; 482 483 DownloadItem.prototype.resume = function() { 484 chrome.downloads.resume(this.id); 485 }; 486 487 DownloadItem.prototype.cancel = function() { 488 chrome.downloads.cancel(this.id); 489 }; 490 491 DownloadItem.prototype.maybeAccept = function() { 492 // This function is safe to call at any time for any item, and it will always 493 // do the right thing, which is to display the danger prompt only if the item 494 // is in_progress and dangerous, and if the prompt is not already displayed. 495 if ((this.state != 'in_progress') || 496 (this.danger == 'safe') || 497 (this.danger == 'accepted') || 498 DownloadItem.prototype.maybeAccept.accepting_danger) { 499 return; 500 } 501 ratchetWidth(400); 502 ratchetHeight(200); 503 DownloadItem.prototype.maybeAccept.accepting_danger = true; 504 // On Mac, window.onload is run while the popup is animating in, before it is 505 // considered "visible". Prompts will not be displayed over an invisible 506 // window, so the popup will become stuck. Just wait a little bit for the 507 // window to finish animating in. http://crbug.com/280107 508 // This has been fixed, so this setTimeout can be removed when the fix has 509 // been released to stable, and minimum_chrome_version can be set. 510 var id = this.id; 511 setTimeout(function() { 512 chrome.downloads.acceptDanger(id, function() { 513 DownloadItem.prototype.maybeAccept.accepting_danger = false; 514 arrayFrom(document.getElementById('items').childNodes).forEach( 515 function(item_div) { item_div.item.maybeAccept(); }); 516 }); 517 }, 500); 518 }; 519 DownloadItem.prototype.maybeAccept.accepting_danger = false; 520 521 var DownloadManager = {}; 522 523 DownloadManager.showingOlder = false; 524 525 DownloadManager.getItem = function(id) { 526 var item_div = document.getElementById('item' + id); 527 return item_div ? item_div.item : null; 528 }; 529 530 DownloadManager.getOrCreate = function(data) { 531 var item = DownloadManager.getItem(data.id); 532 return item ? item : new DownloadItem(data); 533 }; 534 535 DownloadManager.forEachItem = function(cb) { 536 // Calls cb(item, index) in the order that they are displayed, i.e. in order 537 // of decreasing startTime. 538 arrayFrom(document.getElementById('items').childNodes).forEach( 539 function(item_div, index) { cb(item_div.item, index); }); 540 }; 541 542 DownloadManager.startPollingProgress = function() { 543 if (DownloadManager.startPollingProgress.tid < 0) { 544 DownloadManager.startPollingProgress.tid = setTimeout( 545 DownloadManager.startPollingProgress.pollProgress, 546 DownloadManager.startPollingProgress.MS); 547 } 548 } 549 DownloadManager.startPollingProgress.MS = 200; 550 DownloadManager.startPollingProgress.tid = -1; 551 DownloadManager.startPollingProgress.pollProgress = function() { 552 DownloadManager.startPollingProgress.tid = -1; 553 chrome.downloads.search({state: 'in_progress', paused: false}, 554 function(results) { 555 if (!results.length) 556 return; 557 results.forEach(function(result) { 558 var item = DownloadManager.getOrCreate(result); 559 for (var prop in result) { 560 item[prop] = result[prop]; 561 } 562 item.render(); 563 if ((item.state == 'in_progress') && !item.paused) { 564 DownloadManager.startPollingProgress(); 565 } 566 }); 567 }); 568 }; 569 570 DownloadManager.showNew = function() { 571 var any_items = (document.getElementById('items').childNodes.length > 0); 572 document.getElementById('empty').style.display = 573 any_items ? 'none' : 'inline-block'; 574 document.getElementById('head').style.borderBottomWidth = 575 (any_items ? 1 : 0) + 'px'; 576 document.getElementById('clear-all').hidden = !any_items; 577 578 var query_search = document.getElementById('q'); 579 query_search.hidden = !any_items; 580 581 if (!any_items) { 582 return; 583 } 584 var old_ms = (new Date()).getTime() - kOldMs; 585 var any_hidden = false; 586 var any_showing = false; 587 // First show up to kShowNewMax items newer than kOldMs. If there aren't any 588 // items newer than kOldMs, then show up to kShowNewMax items of any age. If 589 // there are any hidden items, show the Show Older button. 590 DownloadManager.forEachItem(function(item, index) { 591 item.div.hidden = !DownloadManager.showingOlder && ( 592 (item.startTime.getTime() < old_ms) || (index >= kShowNewMax)); 593 any_hidden = any_hidden || item.div.hidden; 594 any_showing = any_showing || !item.div.hidden; 595 }); 596 if (!any_showing) { 597 any_hidden = false; 598 DownloadManager.forEachItem(function(item, index) { 599 item.div.hidden = !DownloadManager.showingOlder && (index >= kShowNewMax); 600 any_hidden = any_hidden || item.div.hidden; 601 any_showing = any_showing || !item.div.hidden; 602 }); 603 } 604 document.getElementById('older').hidden = !any_hidden; 605 606 query_search.focus(); 607 }; 608 609 DownloadManager.showOlder = function() { 610 DownloadManager.showingOlder = true; 611 var loading_older_span = document.getElementById('loading-older'); 612 document.getElementById('older').hidden = true; 613 loading_older_span.hidden = false; 614 chrome.downloads.search({}, function(results) { 615 results.forEach(function(result) { 616 var item = DownloadManager.getOrCreate(result); 617 item.div.hidden = false; 618 }); 619 loading_older_span.hidden = true; 620 }); 621 }; 622 623 DownloadManager.onSearch = function() { 624 // split string by space, but ignore space in quotes 625 // http://stackoverflow.com/questions/16261635 626 var query = document.getElementById('q').value.match(/(?:[^\s"]+|"[^"]*")+/g); 627 if (!query) { 628 DownloadManager.showNew(); 629 document.getElementById('search-zero').hidden = true; 630 } else { 631 query = query.map(function(term) { 632 // strip quotes 633 return (term.match(/\s/) && 634 term[0].match(/["']/) && 635 term[term.length - 1] == term[0]) ? 636 term.substr(1, term.length - 2) : term; 637 }); 638 var searching = document.getElementById('searching'); 639 searching.hidden = false; 640 chrome.downloads.search({query: query}, function(results) { 641 document.getElementById('older').hidden = true; 642 DownloadManager.forEachItem(function(item) { 643 item.div.hidden = true; 644 }); 645 results.forEach(function(result) { 646 DownloadManager.getOrCreate(result).div.hidden = false; 647 }); 648 searching.hidden = true; 649 document.getElementById('search-zero').hidden = (results.length != 0); 650 }); 651 } 652 }; 653 654 DownloadManager.clearAll = function() { 655 DownloadManager.forEachItem(function(item) { 656 if (!item.div.hidden) { 657 item.erase(); 658 // The onErased handler should circle back around to loadItems. 659 } 660 }); 661 }; 662 663 var kShowNewMax = 50; 664 var kOldMs = 1000 * 60 * 60 * 24 * 7; 665 666 // These settings can be tuned by modifying localStorage in dev-tools. 667 if ('kShowNewMax' in localStorage) { 668 kShowNewMax = parseInt(localStorage.kShowNewMax); 669 } 670 if ('kOldMs' in localStorage) { 671 kOldMs = parseInt(localStorage.kOldMs); 672 } 673 674 DownloadManager.loadItems = function() { 675 // Request up to kShowNewMax + 1, but only display kShowNewMax; the +1 is a 676 // probe to see if there are any older downloads. 677 // TODO(benjhayden) When https://codereview.chromium.org/16924017/ is 678 // released, set minimum_chrome_version and remove this try/catch. 679 try { 680 chrome.downloads.search({ 681 orderBy: ['-startTime'], 682 limit: kShowNewMax + 1}, 683 function(results) { 684 DownloadManager.loadItems.items = results; 685 DownloadManager.loadItems.onLoaded(); 686 }); 687 } catch (exc) { 688 chrome.downloads.search({ 689 orderBy: '-startTime', 690 limit: kShowNewMax + 1}, 691 function(results) { 692 DownloadManager.loadItems.items = results; 693 DownloadManager.loadItems.onLoaded(); 694 }); 695 } 696 }; 697 DownloadManager.loadItems.items = []; 698 DownloadManager.loadItems.window_loaded = false; 699 700 DownloadManager.loadItems.onLoaded = function() { 701 if (!DownloadManager.loadItems.window_loaded) { 702 return; 703 } 704 DownloadManager.loadItems.items.forEach(function(item) { 705 DownloadManager.getOrCreate(item); 706 }); 707 DownloadManager.loadItems.items = []; 708 DownloadManager.showNew(); 709 }; 710 711 DownloadManager.loadItems.onWindowLoaded = function() { 712 DownloadManager.loadItems.window_loaded = true; 713 DownloadManager.loadItems.onLoaded(); 714 }; 715 716 // If this extension is installed on a stable-channel chrome, where the 717 // downloads API is not available, do not use the downloads API, and link to the 718 // beta channel. 719 if (chrome.downloads) { 720 // Start searching ASAP, don't wait for onload. 721 DownloadManager.loadItems(); 722 723 chrome.downloads.onCreated.addListener(function(item) { 724 DownloadManager.getOrCreate(item); 725 DownloadManager.showNew(); 726 DownloadManager.startPollingProgress(); 727 }); 728 729 chrome.downloads.onChanged.addListener(function(delta) { 730 var item = DownloadManager.getItem(delta.id); 731 if (item) { 732 item.onChanged(delta); 733 } 734 }); 735 736 chrome.downloads.onErased.addListener(function(id) { 737 var item = DownloadManager.getItem(id); 738 if (!item) { 739 return; 740 } 741 item.onErased(); 742 DownloadManager.loadItems(); 743 }); 744 745 window.onload = function() { 746 ratchetWidth( 747 document.getElementById('q-outer').offsetWidth + 748 document.getElementById('clear-all').offsetWidth + 749 document.getElementById('open-folder').offsetWidth); 750 setLastOpened(); 751 loadI18nMessages(); 752 DownloadManager.loadItems.onWindowLoaded(); 753 document.getElementById('older').onclick = function() { 754 DownloadManager.showOlder(); 755 return false; 756 }; 757 document.getElementById('q').onsearch = function() { 758 DownloadManager.onSearch(); 759 }; 760 document.getElementById('clear-all').onclick = function() { 761 DownloadManager.clearAll(); 762 return false; 763 }; 764 if (chrome.downloads.showDefaultFolder) { 765 document.getElementById('open-folder').onclick = function() { 766 chrome.downloads.showDefaultFolder(); 767 return false; 768 }; 769 } else { 770 document.getElementById('open-folder').hidden = true; 771 } 772 }; 773 } else { 774 // The downloads API is not available. 775 // TODO(benjhayden) Remove this when minimum_chrome_version is set. 776 window.onload = function() { 777 loadI18nMessages(); 778 var bad_version = document.getElementById('bad-chrome-version'); 779 bad_version.hidden = false; 780 bad_version.onclick = function() { 781 chrome.tabs.create({url: bad_version.href}); 782 return false; 783 }; 784 document.getElementById('empty').style.display = 'none'; 785 document.getElementById('q').style.display = 'none'; 786 document.getElementById('open-folder').style.display = 'none'; 787 document.getElementById('clear-all').style.display = 'none'; 788 }; 789 } 790