home *** CD-ROM | disk | FTP | other *** search
/ Freelog 115 / FreelogNo115-MaiJuin2013.iso / Internet / AvantBrowser / asetup.exe / _data / webkit / chrome.dll / 0 / BINDATA / 525 < prev    next >
Encoding:
Text File  |  2013-04-03  |  24.5 KB  |  760 lines

  1. // Copyright (c) 2012 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. // TODO(jhawkins): Use hidden instead of showInline* and display:none.
  6.  
  7. /**
  8.  * Sets the display style of a node.
  9.  * @param {!Element} node The target element to show or hide.
  10.  * @param {boolean} isShow Should the target element be visible.
  11.  */
  12. function showInline(node, isShow) {
  13.   node.style.display = isShow ? 'inline' : 'none';
  14. }
  15.  
  16. /**
  17.  * Sets the display style of a node.
  18.  * @param {!Element} node The target element to show or hide.
  19.  * @param {boolean} isShow Should the target element be visible.
  20.  */
  21. function showInlineBlock(node, isShow) {
  22.   node.style.display = isShow ? 'inline-block' : 'none';
  23. }
  24.  
  25. /**
  26.  * Creates an element of a specified type with a specified class name.
  27.  * @param {string} type The node type.
  28.  * @param {string} className The class name to use.
  29.  * @return {Element} The created element.
  30.  */
  31. function createElementWithClassName(type, className) {
  32.   var elm = document.createElement(type);
  33.   elm.className = className;
  34.   return elm;
  35. }
  36.  
  37. /**
  38.  * Creates a link with a specified onclick handler and content.
  39.  * @param {function()} onclick The onclick handler.
  40.  * @param {string} value The link text.
  41.  * @return {Element} The created link element.
  42.  */
  43. function createLink(onclick, value) {
  44.   var link = document.createElement('a');
  45.   link.onclick = onclick;
  46.   link.href = '#';
  47.   link.textContent = value;
  48.   link.oncontextmenu = function() { return false; };
  49.   return link;
  50. }
  51.  
  52. /**
  53.  * Creates a button with a specified onclick handler and content.
  54.  * @param {function()} onclick The onclick handler.
  55.  * @param {string} value The button text.
  56.  * @return {Element} The created button.
  57.  */
  58. function createButton(onclick, value) {
  59.   var button = document.createElement('input');
  60.   button.type = 'button';
  61.   button.value = value;
  62.   button.onclick = onclick;
  63.   return button;
  64. }
  65.  
  66. ///////////////////////////////////////////////////////////////////////////////
  67. // Downloads
  68. /**
  69.  * Class to hold all the information about the visible downloads.
  70.  * @constructor
  71.  */
  72. function Downloads() {
  73.   this.downloads_ = {};
  74.   this.node_ = $('downloads-display');
  75.   this.summary_ = $('downloads-summary-text');
  76.   this.searchText_ = '';
  77.  
  78.   // Keep track of the dates of the newest and oldest downloads so that we
  79.   // know where to insert them.
  80.   this.newestTime_ = -1;
  81.  
  82.   // Icon load request queue.
  83.   this.iconLoadQueue_ = [];
  84.   this.isIconLoading_ = false;
  85. }
  86.  
  87. /**
  88.  * Called when a download has been updated or added.
  89.  * @param {Object} download A backend download object (see downloads_ui.cc)
  90.  */
  91. Downloads.prototype.updated = function(download) {
  92.   var id = download.id;
  93.   if (!!this.downloads_[id]) {
  94.     this.downloads_[id].update(download);
  95.   } else {
  96.     this.downloads_[id] = new Download(download);
  97.     // We get downloads in display order, so we don't have to worry about
  98.     // maintaining correct order - we can assume that any downloads not in
  99.     // display order are new ones and so we can add them to the top of the
  100.     // list.
  101.     if (download.started > this.newestTime_) {
  102.       this.node_.insertBefore(this.downloads_[id].node, this.node_.firstChild);
  103.       this.newestTime_ = download.started;
  104.     } else {
  105.       this.node_.appendChild(this.downloads_[id].node);
  106.     }
  107.   }
  108.   // Download.prototype.update may change its nodeSince_ and nodeDate_, so
  109.   // update all the date displays.
  110.   // TODO(benjhayden) Only do this if its nodeSince_ or nodeDate_ actually did
  111.   // change since this may touch 150 elements and Downloads.prototype.updated
  112.   // may be called 150 times.
  113.   this.updateDateDisplay_();
  114. };
  115.  
  116. /**
  117.  * Set our display search text.
  118.  * @param {string} searchText The string we're searching for.
  119.  */
  120. Downloads.prototype.setSearchText = function(searchText) {
  121.   this.searchText_ = searchText;
  122. };
  123.  
  124. /**
  125.  * Update the summary block above the results
  126.  */
  127. Downloads.prototype.updateSummary = function() {
  128.   if (this.searchText_) {
  129.     this.summary_.textContent = loadTimeData.getStringF('searchresultsfor',
  130.                                                         this.searchText_);
  131.   } else {
  132.     this.summary_.textContent = loadTimeData.getString('downloads');
  133.   }
  134.  
  135.   var hasDownloads = false;
  136.   for (var i in this.downloads_) {
  137.     hasDownloads = true;
  138.     break;
  139.   }
  140. };
  141.  
  142. /**
  143.  * Update the date visibility in our nodes so that no date is
  144.  * repeated.
  145.  * @private
  146.  */
  147. Downloads.prototype.updateDateDisplay_ = function() {
  148.   var dateContainers = document.getElementsByClassName('date-container');
  149.   var displayed = {};
  150.   for (var i = 0, container; container = dateContainers[i]; i++) {
  151.     var dateString = container.getElementsByClassName('date')[0].innerHTML;
  152.     if (!!displayed[dateString]) {
  153.       container.style.display = 'none';
  154.     } else {
  155.       displayed[dateString] = true;
  156.       container.style.display = 'block';
  157.     }
  158.   }
  159. };
  160.  
  161. /**
  162.  * Remove a download.
  163.  * @param {number} id The id of the download to remove.
  164.  */
  165. Downloads.prototype.remove = function(id) {
  166.   this.node_.removeChild(this.downloads_[id].node);
  167.   delete this.downloads_[id];
  168.   this.updateDateDisplay_();
  169. };
  170.  
  171. /**
  172.  * Clear all downloads and reset us back to a null state.
  173.  */
  174. Downloads.prototype.clear = function() {
  175.   for (var id in this.downloads_) {
  176.     this.downloads_[id].clear();
  177.     this.remove(id);
  178.   }
  179. };
  180.  
  181. /**
  182.  * Schedule icon load.
  183.  * @param {HTMLImageElement} elem Image element that should contain the icon.
  184.  * @param {string} iconURL URL to the icon.
  185.  */
  186. Downloads.prototype.scheduleIconLoad = function(elem, iconURL) {
  187.   var self = this;
  188.  
  189.   // Sends request to the next icon in the queue and schedules
  190.   // call to itself when the icon is loaded.
  191.   function loadNext() {
  192.     self.isIconLoading_ = true;
  193.     while (self.iconLoadQueue_.length > 0) {
  194.       var request = self.iconLoadQueue_.shift();
  195.       var oldSrc = request.element.src;
  196.       request.element.onabort = request.element.onerror =
  197.           request.element.onload = loadNext;
  198.       request.element.src = request.url;
  199.       if (oldSrc != request.element.src)
  200.         return;
  201.     }
  202.     self.isIconLoading_ = false;
  203.   }
  204.  
  205.   // Create new request
  206.   var loadRequest = {element: elem, url: iconURL};
  207.   this.iconLoadQueue_.push(loadRequest);
  208.  
  209.   // Start loading if none scheduled yet
  210.   if (!this.isIconLoading_)
  211.     loadNext();
  212. };
  213.  
  214. /**
  215.  * Returns whether the displayed list needs to be updated or not.
  216.  * @param {Array} downloads Array of download nodes.
  217.  * @return {boolean} Returns true if the displayed list is to be updated.
  218.  */
  219. Downloads.prototype.isUpdateNeeded = function(downloads) {
  220.   var size = 0;
  221.   for (var i in this.downloads_)
  222.     size++;
  223.   if (size != downloads.length)
  224.     return true;
  225.   // Since there are the same number of items in the incoming list as
  226.   // |this.downloads_|, there won't be any removed downloads without some
  227.   // downloads having been inserted.  So check only for new downloads in
  228.   // deciding whether to update.
  229.   for (var i = 0; i < downloads.length; i++) {
  230.     if (!this.downloads_[downloads[i].id])
  231.       return true;
  232.   }
  233.   return false;
  234. };
  235.  
  236. ///////////////////////////////////////////////////////////////////////////////
  237. // Download
  238. /**
  239.  * A download and the DOM representation for that download.
  240.  * @param {Object} download A backend download object (see downloads_ui.cc)
  241.  * @constructor
  242.  */
  243. function Download(download) {
  244.   // Create DOM
  245.   this.node = createElementWithClassName(
  246.       'div', 'download' + (download.otr ? ' otr' : ''));
  247.  
  248.   // Dates
  249.   this.dateContainer_ = createElementWithClassName('div', 'date-container');
  250.   this.node.appendChild(this.dateContainer_);
  251.  
  252.   this.nodeSince_ = createElementWithClassName('div', 'since');
  253.   this.nodeDate_ = createElementWithClassName('div', 'date');
  254.   this.dateContainer_.appendChild(this.nodeSince_);
  255.   this.dateContainer_.appendChild(this.nodeDate_);
  256.  
  257.   // Container for all 'safe download' UI.
  258.   this.safe_ = createElementWithClassName('div', 'safe');
  259.   this.safe_.ondragstart = this.drag_.bind(this);
  260.   this.node.appendChild(this.safe_);
  261.  
  262.   if (download.state != Download.States.COMPLETE) {
  263.     this.nodeProgressBackground_ =
  264.         createElementWithClassName('div', 'progress background');
  265.     this.safe_.appendChild(this.nodeProgressBackground_);
  266.  
  267.     this.nodeProgressForeground_ =
  268.         createElementWithClassName('canvas', 'progress');
  269.     this.nodeProgressForeground_.width = Download.Progress.width;
  270.     this.nodeProgressForeground_.height = Download.Progress.height;
  271.     this.canvasProgress_ = this.nodeProgressForeground_.getContext('2d');
  272.  
  273.     this.canvasProgressForegroundImage_ = new Image();
  274.     this.canvasProgressForegroundImage_.src =
  275.         'chrome://theme/IDR_DOWNLOAD_PROGRESS_FOREGROUND_32@' +
  276.         window.devicePixelRatio + 'x';
  277.     this.safe_.appendChild(this.nodeProgressForeground_);
  278.   }
  279.  
  280.   this.nodeImg_ = createElementWithClassName('img', 'icon');
  281.   this.safe_.appendChild(this.nodeImg_);
  282.  
  283.   // FileLink is used for completed downloads, otherwise we show FileName.
  284.   this.nodeTitleArea_ = createElementWithClassName('div', 'title-area');
  285.   this.safe_.appendChild(this.nodeTitleArea_);
  286.  
  287.   this.nodeFileLink_ = createLink(this.openFile_.bind(this), '');
  288.   this.nodeFileLink_.className = 'name';
  289.   this.nodeFileLink_.style.display = 'none';
  290.   this.nodeTitleArea_.appendChild(this.nodeFileLink_);
  291.  
  292.   this.nodeFileName_ = createElementWithClassName('span', 'name');
  293.   this.nodeFileName_.style.display = 'none';
  294.   this.nodeTitleArea_.appendChild(this.nodeFileName_);
  295.  
  296.   this.nodeStatus_ = createElementWithClassName('span', 'status');
  297.   this.nodeTitleArea_.appendChild(this.nodeStatus_);
  298.  
  299.   var nodeURLDiv = createElementWithClassName('div', 'url-container');
  300.   this.safe_.appendChild(nodeURLDiv);
  301.  
  302.   this.nodeURL_ = createElementWithClassName('a', 'src-url');
  303.   this.nodeURL_.target = '_blank';
  304.   nodeURLDiv.appendChild(this.nodeURL_);
  305.  
  306.   // Controls.
  307.   this.nodeControls_ = createElementWithClassName('div', 'controls');
  308.   this.safe_.appendChild(this.nodeControls_);
  309.  
  310.   // We don't need 'show in folder' in chromium os. See download_ui.cc and
  311.   // http://code.google.com/p/chromium-os/issues/detail?id=916.
  312.   if (loadTimeData.valueExists('control_showinfolder')) {
  313.     this.controlShow_ = createLink(this.show_.bind(this),
  314.         loadTimeData.getString('control_showinfolder'));
  315.     this.nodeControls_.appendChild(this.controlShow_);
  316.   } else {
  317.     this.controlShow_ = null;
  318.   }
  319.  
  320.   this.controlRetry_ = document.createElement('a');
  321.   this.controlRetry_.textContent = loadTimeData.getString('control_retry');
  322.   this.nodeControls_.appendChild(this.controlRetry_);
  323.  
  324.   // Pause/Resume are a toggle.
  325.   this.controlPause_ = createLink(this.togglePause_.bind(this),
  326.       loadTimeData.getString('control_pause'));
  327.   this.nodeControls_.appendChild(this.controlPause_);
  328.  
  329.   this.controlResume_ = createLink(this.togglePause_.bind(this),
  330.       loadTimeData.getString('control_resume'));
  331.   this.nodeControls_.appendChild(this.controlResume_);
  332.  
  333.   this.controlRemove_ = createLink(this.remove_.bind(this),
  334.       loadTimeData.getString('control_removefromlist'));
  335.   this.nodeControls_.appendChild(this.controlRemove_);
  336.  
  337.   this.controlCancel_ = createLink(this.cancel_.bind(this),
  338.       loadTimeData.getString('control_cancel'));
  339.   this.nodeControls_.appendChild(this.controlCancel_);
  340.  
  341.   // Container for 'unsafe download' UI.
  342.   this.danger_ = createElementWithClassName('div', 'show-dangerous');
  343.   this.node.appendChild(this.danger_);
  344.  
  345.   this.dangerDesc_ = document.createElement('div');
  346.   this.danger_.appendChild(this.dangerDesc_);
  347.  
  348.   this.dangerSave_ = createButton(this.saveDangerous_.bind(this),
  349.       loadTimeData.getString('danger_save'));
  350.   this.danger_.appendChild(this.dangerSave_);
  351.  
  352.   this.dangerDiscard_ = createButton(this.discardDangerous_.bind(this),
  353.       loadTimeData.getString('danger_discard'));
  354.   this.danger_.appendChild(this.dangerDiscard_);
  355.  
  356.   // Update member vars.
  357.   this.update(download);
  358. }
  359.  
  360. /**
  361.  * The states a download can be in. These correspond to states defined in
  362.  * DownloadsDOMHandler::CreateDownloadItemValue
  363.  */
  364. Download.States = {
  365.   IN_PROGRESS: 'IN_PROGRESS',
  366.   CANCELLED: 'CANCELLED',
  367.   COMPLETE: 'COMPLETE',
  368.   PAUSED: 'PAUSED',
  369.   DANGEROUS: 'DANGEROUS',
  370.   INTERRUPTED: 'INTERRUPTED',
  371. };
  372.  
  373. /**
  374.  * Explains why a download is in DANGEROUS state.
  375.  */
  376. Download.DangerType = {
  377.   NOT_DANGEROUS: 'NOT_DANGEROUS',
  378.   DANGEROUS_FILE: 'DANGEROUS_FILE',
  379.   DANGEROUS_URL: 'DANGEROUS_URL',
  380.   DANGEROUS_CONTENT: 'DANGEROUS_CONTENT',
  381.   UNCOMMON_CONTENT: 'UNCOMMON_CONTENT'
  382. };
  383.  
  384. /**
  385.  * Constants for the progress meter.
  386.  */
  387.  
  388. Download.Progress = (function() {
  389.   var scale = window.devicePixelRatio;
  390.   return {
  391.     width: 48 * scale,
  392.     height: 48 * scale,
  393.     radius: 24 * scale,
  394.     centerX: 24 * scale,
  395.     centerY: 24 * scale,
  396.     base: -0.5 * Math.PI,
  397.     dir: false,
  398.   };
  399. })();
  400.  
  401. /**
  402.  * Updates the download to reflect new data.
  403.  * @param {Object} download A backend download object (see downloads_ui.cc)
  404.  */
  405. Download.prototype.update = function(download) {
  406.   this.id_ = download.id;
  407.   this.filePath_ = download.file_path;
  408.   this.fileUrl_ = download.file_url;
  409.   this.fileName_ = download.file_name;
  410.   this.url_ = download.url;
  411.   this.state_ = download.state;
  412.   this.fileExternallyRemoved_ = download.file_externally_removed;
  413.   this.dangerType_ = download.danger_type;
  414.   this.lastReasonDescription_ = download.last_reason_text;
  415.  
  416.   this.since_ = download.since_string;
  417.   this.date_ = download.date_string;
  418.  
  419.   // See DownloadItem::PercentComplete
  420.   this.percent_ = Math.max(download.percent, 0);
  421.   this.progressStatusText_ = download.progress_status_text;
  422.   this.received_ = download.received;
  423.  
  424.   if (this.state_ == Download.States.DANGEROUS) {
  425.     if (this.dangerType_ == Download.DangerType.DANGEROUS_FILE) {
  426.       this.dangerDesc_.textContent = loadTimeData.getStringF('danger_file_desc',
  427.                                                              this.fileName_);
  428.     } else if (this.dangerType_ == Download.DangerType.DANGEROUS_URL) {
  429.       this.dangerDesc_.textContent = loadTimeData.getString('danger_url_desc');
  430.     } else if (this.dangerType_ == Download.DangerType.DANGEROUS_CONTENT) {
  431.       this.dangerDesc_.textContent = loadTimeData.getStringF(
  432.           'danger_content_desc', this.fileName_);
  433.     } else if (this.dangerType_ == Download.DangerType.UNCOMMON_CONTENT) {
  434.       this.dangerDesc_.textContent = loadTimeData.getStringF(
  435.           'danger_uncommon_desc', this.fileName_);
  436.     }
  437.     this.danger_.style.display = 'block';
  438.     this.safe_.style.display = 'none';
  439.   } else {
  440.     downloads.scheduleIconLoad(this.nodeImg_,
  441.                                'chrome://fileicon/' +
  442.                                    encodeURIComponent(this.filePath_) +
  443.                                    '?scale=' + window.devicePixelRatio + 'x');
  444.  
  445.     if (this.state_ == Download.States.COMPLETE &&
  446.         !this.fileExternallyRemoved_) {
  447.       this.nodeFileLink_.textContent = this.fileName_;
  448.       this.nodeFileLink_.href = this.fileUrl_;
  449.       this.nodeFileLink_.oncontextmenu = null;
  450.     } else if (this.nodeFileName_.textContent != this.fileName_) {
  451.       this.nodeFileName_.textContent = this.fileName_;
  452.     }
  453.     if (this.state_ == Download.States.INTERRUPTED)
  454.       this.nodeFileName_.classList.add('interrupted');
  455.  
  456.     showInline(this.nodeFileLink_,
  457.                this.state_ == Download.States.COMPLETE &&
  458.                    !this.fileExternallyRemoved_);
  459.     // nodeFileName_ has to be inline-block to avoid the 'interaction' with
  460.     // nodeStatus_. If both are inline, it appears that their text contents
  461.     // are merged before the bidi algorithm is applied leading to an
  462.     // undesirable reordering. http://crbug.com/13216
  463.     showInlineBlock(this.nodeFileName_,
  464.                     this.state_ != Download.States.COMPLETE ||
  465.                         this.fileExternallyRemoved_);
  466.  
  467.     if (this.state_ == Download.States.IN_PROGRESS) {
  468.       this.nodeProgressForeground_.style.display = 'block';
  469.       this.nodeProgressBackground_.style.display = 'block';
  470.  
  471.       // Draw a pie-slice for the progress.
  472.       this.canvasProgress_.globalCompositeOperation = 'copy';
  473.       this.canvasProgress_.drawImage(this.canvasProgressForegroundImage_, 0, 0);
  474.       this.canvasProgress_.globalCompositeOperation = 'destination-in';
  475.       this.canvasProgress_.beginPath();
  476.       this.canvasProgress_.moveTo(Download.Progress.centerX,
  477.                                   Download.Progress.centerY);
  478.  
  479.       // Draw an arc CW for both RTL and LTR. http://crbug.com/13215
  480.       this.canvasProgress_.arc(Download.Progress.centerX,
  481.                                Download.Progress.centerY,
  482.                                Download.Progress.radius,
  483.                                Download.Progress.base,
  484.                                Download.Progress.base + Math.PI * 0.02 *
  485.                                Number(this.percent_),
  486.                                false);
  487.  
  488.       this.canvasProgress_.lineTo(Download.Progress.centerX,
  489.                                   Download.Progress.centerY);
  490.       this.canvasProgress_.fill();
  491.       this.canvasProgress_.closePath();
  492.     } else if (this.nodeProgressBackground_) {
  493.       this.nodeProgressForeground_.style.display = 'none';
  494.       this.nodeProgressBackground_.style.display = 'none';
  495.     }
  496.  
  497.     if (this.controlShow_) {
  498.       showInline(this.controlShow_,
  499.                  this.state_ == Download.States.COMPLETE &&
  500.                      !this.fileExternallyRemoved_);
  501.     }
  502.     showInline(this.controlRetry_, this.state_ == Download.States.CANCELLED);
  503.     this.controlRetry_.href = this.url_;
  504.     showInline(this.controlPause_, this.state_ == Download.States.IN_PROGRESS);
  505.     showInline(this.controlResume_, this.state_ == Download.States.PAUSED);
  506.     var showCancel = this.state_ == Download.States.IN_PROGRESS ||
  507.                      this.state_ == Download.States.PAUSED;
  508.     showInline(this.controlCancel_, showCancel);
  509.     showInline(this.controlRemove_, !showCancel);
  510.  
  511.     this.nodeSince_.textContent = this.since_;
  512.     this.nodeDate_.textContent = this.date_;
  513.     // Don't unnecessarily update the url, as doing so will remove any
  514.     // text selection the user has started (http://crbug.com/44982).
  515.     if (this.nodeURL_.textContent != this.url_) {
  516.       this.nodeURL_.textContent = this.url_;
  517.       this.nodeURL_.href = this.url_;
  518.     }
  519.     this.nodeStatus_.textContent = this.getStatusText_();
  520.  
  521.     this.danger_.style.display = 'none';
  522.     this.safe_.style.display = 'block';
  523.   }
  524. };
  525.  
  526. /**
  527.  * Removes applicable bits from the DOM in preparation for deletion.
  528.  */
  529. Download.prototype.clear = function() {
  530.   this.safe_.ondragstart = null;
  531.   this.nodeFileLink_.onclick = null;
  532.   if (this.controlShow_) {
  533.     this.controlShow_.onclick = null;
  534.   }
  535.   this.controlCancel_.onclick = null;
  536.   this.controlPause_.onclick = null;
  537.   this.controlResume_.onclick = null;
  538.   this.dangerDiscard_.onclick = null;
  539.  
  540.   this.node.innerHTML = '';
  541. };
  542.  
  543. /**
  544.  * @private
  545.  * @return {string} User-visible status update text.
  546.  */
  547. Download.prototype.getStatusText_ = function() {
  548.   switch (this.state_) {
  549.     case Download.States.IN_PROGRESS:
  550.       return this.progressStatusText_;
  551.     case Download.States.CANCELLED:
  552.       return loadTimeData.getString('status_cancelled');
  553.     case Download.States.PAUSED:
  554.       return loadTimeData.getString('status_paused');
  555.     case Download.States.DANGEROUS:
  556.       // danger_url_desc is also used by DANGEROUS_CONTENT.
  557.       var desc = this.dangerType_ == Download.DangerType.DANGEROUS_FILE ?
  558.           'danger_file_desc' : 'danger_url_desc';
  559.       return loadTimeData.getString(desc);
  560.     case Download.States.INTERRUPTED:
  561.       return this.lastReasonDescription_;
  562.     case Download.States.COMPLETE:
  563.       return this.fileExternallyRemoved_ ?
  564.           loadTimeData.getString('status_removed') : '';
  565.   }
  566. };
  567.  
  568. /**
  569.  * Tells the backend to initiate a drag, allowing users to drag
  570.  * files from the download page and have them appear as native file
  571.  * drags.
  572.  * @return {boolean} Returns false to prevent the default action.
  573.  * @private
  574.  */
  575. Download.prototype.drag_ = function() {
  576.   chrome.send('drag', [this.id_.toString()]);
  577.   return false;
  578. };
  579.  
  580. /**
  581.  * Tells the backend to open this file.
  582.  * @return {boolean} Returns false to prevent the default action.
  583.  * @private
  584.  */
  585. Download.prototype.openFile_ = function() {
  586.   chrome.send('openFile', [this.id_.toString()]);
  587.   return false;
  588. };
  589.  
  590. /**
  591.  * Tells the backend that the user chose to save a dangerous file.
  592.  * @return {boolean} Returns false to prevent the default action.
  593.  * @private
  594.  */
  595. Download.prototype.saveDangerous_ = function() {
  596.   chrome.send('saveDangerous', [this.id_.toString()]);
  597.   return false;
  598. };
  599.  
  600. /**
  601.  * Tells the backend that the user chose to discard a dangerous file.
  602.  * @return {boolean} Returns false to prevent the default action.
  603.  * @private
  604.  */
  605. Download.prototype.discardDangerous_ = function() {
  606.   chrome.send('discardDangerous', [this.id_.toString()]);
  607.   downloads.remove(this.id_);
  608.   return false;
  609. };
  610.  
  611. /**
  612.  * Tells the backend to show the file in explorer.
  613.  * @return {boolean} Returns false to prevent the default action.
  614.  * @private
  615.  */
  616. Download.prototype.show_ = function() {
  617.   chrome.send('show', [this.id_.toString()]);
  618.   return false;
  619. };
  620.  
  621. /**
  622.  * Tells the backend to pause this download.
  623.  * @return {boolean} Returns false to prevent the default action.
  624.  * @private
  625.  */
  626. Download.prototype.togglePause_ = function() {
  627.   chrome.send('togglepause', [this.id_.toString()]);
  628.   return false;
  629. };
  630.  
  631. /**
  632.  * Tells the backend to remove this download from history and download shelf.
  633.  * @return {boolean} Returns false to prevent the default action.
  634.  * @private
  635.  */
  636.  Download.prototype.remove_ = function() {
  637.   chrome.send('remove', [this.id_.toString()]);
  638.   return false;
  639. };
  640.  
  641. /**
  642.  * Tells the backend to cancel this download.
  643.  * @return {boolean} Returns false to prevent the default action.
  644.  * @private
  645.  */
  646. Download.prototype.cancel_ = function() {
  647.   chrome.send('cancel', [this.id_.toString()]);
  648.   return false;
  649. };
  650.  
  651. ///////////////////////////////////////////////////////////////////////////////
  652. // Page:
  653. var downloads, resultsTimeout;
  654.  
  655. // TODO(benjhayden): Rename Downloads to DownloadManager, downloads to
  656. // downloadManager or theDownloadManager or DownloadManager.get() to prevent
  657. // confusing Downloads with Download.
  658.  
  659. /**
  660.  * The FIFO array that stores updates of download files to be appeared
  661.  * on the download page. It is guaranteed that the updates in this array
  662.  * are reflected to the download page in a FIFO order.
  663. */
  664. var fifo_results;
  665.  
  666. function load() {
  667.   chrome.send('onPageLoaded');
  668.   fifo_results = new Array();
  669.   downloads = new Downloads();
  670.   $('term').focus();
  671.   setSearch('');
  672.  
  673.   var clearAllLink = $('clear-all');
  674.   clearAllLink.onclick = clearAll;
  675.   clearAllLink.oncontextmenu = function() { return false; };
  676.  
  677.   // TODO(jhawkins): Use a link-button here.
  678.   var openDownloadsFolderLink = $('open-downloads-folder');
  679.   openDownloadsFolderLink.onclick = function() {
  680.     chrome.send('openDownloadsFolder');
  681.   };
  682.   openDownloadsFolderLink.oncontextmenu = function() { return false; };
  683.  
  684.   $('search-link').onclick = function(e) {
  685.     setSearch('');
  686.     e.preventDefault();
  687.     $('term').value = '';
  688.     return false;
  689.   };
  690.  
  691.   $('term').onsearch = function(e) {
  692.     setSearch(this.value);
  693.   };
  694. }
  695.  
  696. function setSearch(searchText) {
  697.   fifo_results.length = 0;
  698.   downloads.setSearchText(searchText);
  699.   chrome.send('getDownloads', [searchText.toString()]);
  700. }
  701.  
  702. function clearAll() {
  703.   fifo_results.length = 0;
  704.   downloads.clear();
  705.   downloads.setSearchText('');
  706.   chrome.send('clearAll');
  707. }
  708.  
  709. ///////////////////////////////////////////////////////////////////////////////
  710. // Chrome callbacks:
  711. /**
  712.  * Our history system calls this function with results from searches or when
  713.  * downloads are added or removed.
  714.  * @param {Array.<Object>} results List of updates.
  715.  */
  716. function downloadsList(results) {
  717.   if (downloads && downloads.isUpdateNeeded(results)) {
  718.     if (resultsTimeout)
  719.       clearTimeout(resultsTimeout);
  720.     fifo_results.length = 0;
  721.     downloads.clear();
  722.     downloadUpdated(results);
  723.   }
  724.   downloads.updateSummary();
  725. }
  726.  
  727. /**
  728.  * When a download is updated (progress, state change), this is called.
  729.  * @param {Array.<Object>} results List of updates for the download process.
  730.  */
  731. function downloadUpdated(results) {
  732.   // Sometimes this can get called too early.
  733.   if (!downloads)
  734.     return;
  735.  
  736.   fifo_results = fifo_results.concat(results);
  737.   tryDownloadUpdatedPeriodically();
  738. }
  739.  
  740. /**
  741.  * Try to reflect as much updates as possible within 50ms.
  742.  * This function is scheduled again and again until all updates are reflected.
  743.  */
  744. function tryDownloadUpdatedPeriodically() {
  745.   var start = Date.now();
  746.   while (fifo_results.length) {
  747.     var result = fifo_results.shift();
  748.     downloads.updated(result);
  749.     // Do as much as we can in 50ms.
  750.     if (Date.now() - start > 50) {
  751.       clearTimeout(resultsTimeout);
  752.       resultsTimeout = setTimeout(tryDownloadUpdatedPeriodically, 5);
  753.       break;
  754.     }
  755.   }
  756. }
  757.  
  758. // Add handlers to HTML elements.
  759. window.addEventListener('DOMContentLoaded', load);
  760.