Port downloads.ItemView to a Polymer component.

This results in a very ugly, somewhat functional MD chrome://downloads.

[email protected]
BUG=425626

NOPRESUBMIT=true

Review URL: https://codereview.chromium.org/1224623013

Cr-Commit-Position: refs/heads/master@{#338221}
diff --git a/chrome/browser/browser_resources.grd b/chrome/browser/browser_resources.grd
index c2f4cedd..6faddb66 100644
--- a/chrome/browser/browser_resources.grd
+++ b/chrome/browser/browser_resources.grd
@@ -25,9 +25,8 @@
         <structure name="IDR_APP_LIST_START_PAGE_JS" file="resources\app_list\start_page.js" flattenhtml="true" type="chrome_html" />
       </if>
       <if expr="not is_android">
-        <structure name="IDR_DOWNLOADS_HTML" file="resources\downloads\downloads.html" flattenhtml="true" allowexternalscript="true" type="chrome_html" />
-        <!-- TODO(dbeam): remove flattening from all new MD UIs. -->
-        <structure name="IDR_MD_DOWNLOADS_HTML" file="resources\md_downloads\downloads.html" flattenhtml="true" allowexternalscript="true" type="chrome_html" />
+        <structure name="IDR_DOWNLOADS_DOWNLOADS_HTML" file="resources\downloads\downloads.html" flattenhtml="true" allowexternalscript="true" type="chrome_html" />
+        <structure name="IDR_MD_DOWNLOADS_DOWNLOADS_HTML" file="resources\md_downloads\downloads.html" flattenhtml="true" allowexternalscript="true" type="chrome_html" />
       </if>
       <if expr="enable_extensions">
         <structure name="IDR_EXTENSIONS_HTML" file="resources\extensions\extensions.html" flattenhtml="true" type="chrome_html" />
@@ -126,13 +125,19 @@
       <include name="IDR_DOMAIN_RELIABILITY_INTERNALS_CSS" file="resources\domain_reliability_internals.css" type="BINDATA" />
       <include name="IDR_DOMAIN_RELIABILITY_INTERNALS_JS" file="resources\domain_reliability_internals.js" type="BINDATA" />
       <if expr="not is_android">
-        <include name="IDR_DOWNLOAD_CONSTANTS_JS" file="resources\downloads\constants.js" type="BINDATA" />
-        <include name="IDR_DOWNLOAD_FOCUS_ROW_JS" file="resources\downloads\focus_row.js" type="BINDATA" />
-        <include name="IDR_DOWNLOAD_ITEM_VIEW_JS" file="resources\downloads\item_view.js" type="BINDATA" />
-        <include name="IDR_DOWNLOAD_MANAGER_JS" file="resources\downloads\manager.js" type="BINDATA" />
-        <include name="IDR_DOWNLOAD_THROTTLED_ICON_LOADER_HTML" file="resources\downloads\throttled_icon_loader.html" type="BINDATA" />
-        <include name="IDR_DOWNLOAD_THROTTLED_ICON_LOADER_JS" file="resources\downloads\throttled_icon_loader.js" type="BINDATA" />
-        <include name="IDR_MD_DOWNLOAD_MANAGER_JS" file="resources\md_downloads\manager.js" type="BINDATA" />
+        <include name="IDR_DOWNLOADS_CONSTANTS_HTML" file="resources\downloads\constants.html" type="BINDATA" />
+        <include name="IDR_DOWNLOADS_CONSTANTS_JS" file="resources\downloads\constants.js" type="BINDATA" />
+        <include name="IDR_DOWNLOADS_FOCUS_ROW_JS" file="resources\downloads\focus_row.js" type="BINDATA" />
+        <include name="IDR_DOWNLOADS_ITEM_VIEW_JS" file="resources\downloads\item_view.js" type="BINDATA" />
+        <include name="IDR_DOWNLOADS_MANAGER_JS" file="resources\downloads\manager.js" type="BINDATA" />
+        <include name="IDR_DOWNLOADS_THROTTLED_ICON_LOADER_HTML" file="resources\downloads\throttled_icon_loader.html" type="BINDATA" />
+        <include name="IDR_DOWNLOADS_THROTTLED_ICON_LOADER_JS" file="resources\downloads\throttled_icon_loader.js" type="BINDATA" />
+        <include name="IDR_MD_DOWNLOADS_DOWNLOADS_CSS" file="resources\md_downloads\downloads.css" type="BINDATA" />
+        <include name="IDR_MD_DOWNLOADS_ITEM_VIEW_HTML" file="resources\md_downloads\item_view.html" type="BINDATA" />
+        <include name="IDR_MD_DOWNLOADS_ITEM_VIEW_JS" file="resources\md_downloads\item_view.js" type="BINDATA" />
+        <include name="IDR_MD_DOWNLOADS_MANAGER_HTML" file="resources\md_downloads\manager.html" type="BINDATA" />
+        <include name="IDR_MD_DOWNLOADS_MANAGER_JS" file="resources\md_downloads\manager.js" type="BINDATA" />
+        <include name="IDR_MD_DOWNLOADS_STRINGS_HTML" file="resources\md_downloads\strings.html" type="BINDATA" />
       </if>
       <if expr="enable_extensions">
         <include name="IDR_EXTENSION_COMMAND_LIST_JS" file="resources\extensions\extension_command_list.js" flattenhtml="true" type="BINDATA" />
diff --git a/chrome/browser/resources/downloads/compiled_resources2.gyp b/chrome/browser/resources/downloads/compiled_resources2.gyp
index 7a7fafb..af609c9d 100644
--- a/chrome/browser/resources/downloads/compiled_resources2.gyp
+++ b/chrome/browser/resources/downloads/compiled_resources2.gyp
@@ -37,10 +37,9 @@
     {
       'target_name': 'manager',
       'dependencies': [
-        '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:action_link',
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert',
-        '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data',
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
+        '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data',
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:event_tracker',
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:util',
         '<(DEPTH)/ui/webui/resources/js/cr/compiled_resources2.gyp:ui',
diff --git a/chrome/browser/resources/downloads/constants.html b/chrome/browser/resources/downloads/constants.html
new file mode 100644
index 0000000..0ac6c26b
--- /dev/null
+++ b/chrome/browser/resources/downloads/constants.html
@@ -0,0 +1,2 @@
+<link rel="import" href="chrome://resources/html/cr.html">
+<script src="chrome://downloads/constants.js"></script>
diff --git a/chrome/browser/resources/downloads/downloads.html b/chrome/browser/resources/downloads/downloads.html
index e389e04..96dedb5 100644
--- a/chrome/browser/resources/downloads/downloads.html
+++ b/chrome/browser/resources/downloads/downloads.html
@@ -16,7 +16,7 @@
   <script src="chrome://resources/js/event_tracker.js"></script>
   <script src="chrome://resources/js/cr/ui/focus_row.js"></script>
   <script src="chrome://resources/js/cr/ui/focus_grid.js"></script>
-  <script src="chrome://downloads/constants.js"></script>
+  <link rel="import" href="chrome://downloads/constants.html">
   <link rel="import" href="chrome://downloads/throttled_icon_loader.html">
   <script src="chrome://downloads/item_view.js"></script>
   <script src="chrome://downloads/focus_row.js"></script>
diff --git a/chrome/browser/resources/md_downloads/compiled_resources.gyp b/chrome/browser/resources/md_downloads/compiled_resources.gyp
index b615a89..75e200f2 100644
--- a/chrome/browser/resources/md_downloads/compiled_resources.gyp
+++ b/chrome/browser/resources/md_downloads/compiled_resources.gyp
@@ -14,6 +14,9 @@
           '../../../../ui/webui/resources/js/cr/ui.js',
           '../../../../ui/webui/resources/js/cr/ui/command.js',
           '../../../../ui/webui/resources/js/util.js',
+          '../downloads/constants.js',
+          '../downloads/throttled_icon_loader.js',
+          'item_view.js',
         ],
         'externs': [
           '<(EXTERNS_DIR)/chrome_send.js',
diff --git a/chrome/browser/resources/md_downloads/compiled_resources2.gyp b/chrome/browser/resources/md_downloads/compiled_resources2.gyp
index 7f74d88..7b4afb7 100644
--- a/chrome/browser/resources/md_downloads/compiled_resources2.gyp
+++ b/chrome/browser/resources/md_downloads/compiled_resources2.gyp
@@ -4,15 +4,25 @@
 {
   'targets': [
     {
+      'target_name': 'item_view',
+      'dependencies': [
+        '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
+        '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data',
+        '../downloads/compiled_resources2.gyp:constants',
+        '../downloads/compiled_resources2.gyp:throttled_icon_loader',
+        '../downloads/compiled_resources2.gyp:externs',
+      ],
+      'includes': ['../../../../third_party/closure_compiler/compile_js2.gypi'],
+    },
+    {
       'target_name': 'manager',
       'dependencies': [
-        '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:action_link',
-        '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:assert',
-        '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data',
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:cr',
+        '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:load_time_data',
         '<(DEPTH)/ui/webui/resources/js/compiled_resources2.gyp:util',
         '<(DEPTH)/ui/webui/resources/js/cr/compiled_resources2.gyp:ui',
         '<(DEPTH)/ui/webui/resources/js/cr/ui/compiled_resources2.gyp:command',
+        'item_view',
         '<(EXTERNS_GYP):chrome_send',
         '../downloads/compiled_resources2.gyp:externs',
       ],
diff --git a/chrome/browser/resources/md_downloads/downloads.html b/chrome/browser/resources/md_downloads/downloads.html
index aeb638d0..00cf7c9 100644
--- a/chrome/browser/resources/md_downloads/downloads.html
+++ b/chrome/browser/resources/md_downloads/downloads.html
@@ -4,28 +4,19 @@
   <meta charset="utf-8">
   <title i18n-content="title"></title>
 
+  <link rel="import" href="chrome://resources/html/action_link.html">
+  <link rel="import" href="chrome://resources/html/polymer_config.html">
   <link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html">
-
   <link rel="import" href="chrome://resources/polymer/v1_0/iron-flex-layout/classes/iron-flex-layout.html">
   <link rel="import" href="chrome://resources/polymer/v1_0/iron-icons/iron-icons.html">
   <link rel="import" href="chrome://resources/polymer/v1_0/paper-icon-button/paper-icon-button.html">
   <link rel="import" href="chrome://resources/polymer/v1_0/paper-input/paper-input.html">
   <link rel="import" href="chrome://resources/polymer/v1_0/paper-toolbar/paper-toolbar.html">
 
-  <link rel="import" href="chrome://resources/html/action_link.html">
-  <link rel="import" href="chrome://resources/html/assert.html">
-  <link rel="import" href="chrome://resources/html/cr.html">
-
-  <script src="chrome://resources/js/util.js"></script>
-  <script src="chrome://resources/js/load_time_data.js"></script>
-  <script src="chrome://resources/js/cr/ui.js"></script>
-  <script src="chrome://resources/js/cr/ui/command.js"></script>
-
-  <script src="chrome://downloads/constants.js"></script>
-  <script src="chrome://downloads/manager.js"></script>
+  <link rel="import" href="chrome://downloads/manager.html">
 
   <link rel="stylesheet" href="chrome://resources/css/roboto.css">
-  <link rel="stylesheet" href="downloads.css">
+  <link rel="stylesheet" href="chrome://downloads/downloads.css">
 </head>
 <body class="fullbleed">
 
@@ -59,8 +50,9 @@
   <command id="undo-command" shortcut="Ctrl-U+005A"><!-- Ctrl+Z -->
 </if>
 
-  <script src="chrome://downloads/strings.js"></script>
-  <script src="chrome://resources/js/i18n_template.js"></script>
+  <link rel="import" href="chrome://resources/html/load_time_data.html">
+  <link rel="import" href="chrome://downloads/strings.html">
+  <link rel="import" href="chrome://resources/html/i18n_template.html"></script>
 
 </body>
 </html>
diff --git a/chrome/browser/resources/md_downloads/item_view.html b/chrome/browser/resources/md_downloads/item_view.html
new file mode 100644
index 0000000..279b9ba
--- /dev/null
+++ b/chrome/browser/resources/md_downloads/item_view.html
@@ -0,0 +1,58 @@
+<dom-module id="item-view">
+  <template>
+    <div id="date-container" hidden$="{{hideDate}}">
+      <div id="since"></div>
+      <div id="date"></div>
+    </div>
+
+    <div id="safe" hidden$="{{isDangerous}}">
+      <div id="progress"></div>
+      <img id="safe-icon" alt="">
+      <div id="title-area">
+        <a is="action-link" id="file-link" column-type="name"></a>
+        <span id="name"></span>
+        <span id="status"></span>
+      </div>
+      <div>
+        <a id="src-url" target="_blank" column-type="url"></a>
+      </div>
+      <div>
+        <a is="action-link" id="show" column-type="show"
+            i18n-content="control_showinfolder"></a>
+        <a id="retry" column-type="retry" i18n-content="control_retry"
+            download></a>
+        <a is="action-link" id="pause" column-type="pause"
+            i18n-content="control_pause"></a>
+        <a is="action-link" id="resume" column-type="resume"
+            i18n-content="control_resume"></a>
+        <a is="action-link" id="safe-remove" column-type="remove"
+            i18n-content="control_removefromlist"></a>
+        <a is="action-link" id="cancel" column-type="cancel"
+            i18n-content="control_cancel"></a>
+        <span id="controlled-by"
+            i18n-values=".innerHTML:control_by_extension"></span>
+      </div>
+    </div>
+
+    <div id="dangerous" hidden$="{{!isDangerous}}">
+      <img id="dangerous-icon" alt="">
+      <div id="description"></div>
+      <div id="malware-controls" hidden$="{{!isMalware}}">
+        <a is="action-link" id="restore" column-type="save"
+            i18n-content="danger_restore"></a>
+        <a is="action-link" id="dangerous-remove" column-type="discard"
+            i18n-content="control_removefromlist"></a>
+      </div>
+      <button id="save" column-type="save" i18n-content="danger_save"
+          hidden$="{{isMalware}}"></button>
+      <button id="discard" column-type="discard" i18n-content="danger_discard"
+          hidden$="{{isMalware}}"></button>
+    </div>
+  </template>
+  <link rel="import" href="chrome://resources/html/cr.html">
+  <link rel="import" href="chrome://resources/html/load_time_data.html">
+  <link rel="import" href="chrome://resources/polymer/v1_0/polymer/polymer.html">
+  <link rel="import" href="chrome://downloads/constants.html">
+  <link rel="import" href="chrome://downloads/throttled_icon_loader.html">
+  <script src="chrome://downloads/item_view.js"></script>
+</dom-module>
diff --git a/chrome/browser/resources/md_downloads/item_view.js b/chrome/browser/resources/md_downloads/item_view.js
new file mode 100644
index 0000000..d0b7490
--- /dev/null
+++ b/chrome/browser/resources/md_downloads/item_view.js
@@ -0,0 +1,250 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+cr.define('downloads', function() {
+  var ItemView = Polymer({
+    is: 'item-view',
+
+    /** @param {!downloads.ThrottledIconLoader} iconLoader */
+    factoryImpl: function(iconLoader) {
+      /** @private {!downloads.ThrottledIconLoader} */
+      this.iconLoader_ = iconLoader;
+    },
+
+    properties: {
+      hideDate: {type: Boolean, value: false},
+      isDangerous: {type: Boolean, value: false},
+      // Only use |isMalware| if |isDangerous| is true.
+      isMalware: Boolean,
+    },
+
+    ready: function() {
+      this.$.safe.ondragstart = this.onSafeDragstart_.bind(this);
+      this.$['file-link'].onclick = this.onFileLinkClick_.bind(this);
+      this.$.show.onclick = this.onShowClick_.bind(this);
+      this.$.pause.onclick = this.onPauseClick_.bind(this);
+      this.$.resume.onclick = this.onResumeClick_.bind(this);
+      this.$['safe-remove'].onclick = this.onSafeRemoveClick_.bind(this);
+      this.$.cancel.onclick = this.onCancelClick_.bind(this);
+      this.$.restore.onclick = this.onRestoreClick_.bind(this);
+      this.$.save.onclick = this.onSaveClick_.bind(this);
+      this.$['dangerous-remove'].onclick = this.onDangerRemoveClick_.bind(this);
+      this.$.discard.onclick = this.onDiscardClick_.bind(this);
+    },
+
+    /** @param {!downloads.Data} data */
+    update: function(data) {
+      assert(!this.id_ || data.id == this.id_);
+      this.id_ = data.id;  // This is the only thing saved from |data|.
+
+      this.classList.toggle('otr', data.otr);
+
+      this.ensureTextIs_(this.$.since, data.since_string);
+      this.ensureTextIs_(this.$.date, data.date_string);
+
+      var dangerText = this.getDangerText_(data);
+      this.isDangerous = !!dangerText;
+
+      if (dangerText) {
+        this.ensureTextIs_(this.$.description, dangerText);
+
+        var dangerType = data.danger_type;
+        var dangerousFile = dangerType == downloads.DangerType.DANGEROUS_FILE;
+        this.$.description.classList.toggle('malware', !dangerousFile);
+
+        var idr = dangerousFile ? 'IDR_WARNING' : 'IDR_SAFEBROWSING_WARNING';
+        var iconUrl = 'chrome://theme/' + idr;
+        this.iconLoader_.loadScaledIcon(this.$['dangerous-icon'], iconUrl);
+
+        this.isMalware =
+            dangerType == downloads.DangerType.DANGEROUS_CONTENT ||
+            dangerType == downloads.DangerType.DANGEROUS_HOST ||
+            dangerType == downloads.DangerType.DANGEROUS_URL ||
+            dangerType == downloads.DangerType.POTENTIALLY_UNWANTED;
+      } else {
+        var iconUrl = 'chrome://fileicon/' + encodeURIComponent(data.file_path);
+        this.iconLoader_.loadScaledIcon(this.$['safe-icon'], iconUrl);
+
+        /** @const */ var isInProgress =
+            data.state == downloads.States.IN_PROGRESS;
+        this.classList.toggle('in-progress', isInProgress);
+
+        /** @const */ var completelyOnDisk =
+            data.state == downloads.States.COMPLETE &&
+            !data.file_externally_removed;
+
+        this.$['file-link'].href = data.url;
+        this.ensureTextIs_(this.$['file-link'], data.file_name);
+        this.$['file-link'].hidden = !completelyOnDisk;
+
+        /** @const */ var isInterrupted =
+            data.state == downloads.States.INTERRUPTED;
+        this.$.name.classList.toggle('interrupted', isInterrupted);
+        this.ensureTextIs_(this.$.name, data.file_name);
+        this.$.name.hidden = completelyOnDisk;
+
+        this.$.show.hidden = !completelyOnDisk;
+
+        this.$.retry.href = data.url;
+        this.$.retry.hidden = !data.retry;
+
+        this.$.pause.hidden = !isInProgress;
+
+        this.$.resume.hidden = !data.resume;
+
+        /** @const */ var isPaused = data.state == downloads.States.PAUSED;
+        /** @const */ var showCancel = isPaused || isInProgress;
+        this.$.cancel.hidden = !showCancel;
+
+        this.$['safe-remove'].hidden = showCancel ||
+            !loadTimeData.getBoolean('allow_deleting_history');
+
+        /** @const */ var controlledByExtension = data.by_ext_id &&
+                                                  data.by_ext_name;
+        this.$['controlled-by'].hidden = !controlledByExtension;
+        if (controlledByExtension) {
+          var link = this.$['controlled-by'].querySelector('a');
+          link.href = 'chrome://extensions#' + data.by_ext_id;
+          link.setAttribute('column-type', 'controlled-by');
+          link.textContent = data.by_ext_name;
+        }
+
+        this.ensureTextIs_(this.$['src-url'], data.url);
+        this.$['src-url'].href = data.url;
+        this.ensureTextIs_(this.$.status, this.getStatusText_(data));
+
+        this.$.progress.hidden = !isInProgress;
+
+        // TODO(dbeam): implement progress.
+      }
+    },
+
+    /**
+     * Overwrite |el|'s textContent if it differs from |text|.
+     * @param {!Element} el
+     * @param {string} text
+     * @private
+     */
+    ensureTextIs_: function(el, text) {
+      if (el.textContent != text)
+        el.textContent = text;
+    },
+
+    /**
+     * @param {!downloads.Data} data
+     * @return {string} Text describing the danger of a download. Empty if not
+     *     dangerous.
+     */
+    getDangerText_: function(data) {
+      switch (data.danger_type) {
+        case downloads.DangerType.DANGEROUS_FILE:
+          return loadTimeData.getStringF('danger_file_desc', data.file_name);
+        case downloads.DangerType.DANGEROUS_URL:
+          return loadTimeData.getString('danger_url_desc');
+        case downloads.DangerType.DANGEROUS_CONTENT:  // Fall through.
+        case downloads.DangerType.DANGEROUS_HOST:
+          return loadTimeData.getStringF('danger_content_desc', data.file_name);
+        case downloads.DangerType.UNCOMMON_CONTENT:
+          return loadTimeData.getStringF('danger_uncommon_desc',
+                                         data.file_name);
+        case downloads.DangerType.POTENTIALLY_UNWANTED:
+          return loadTimeData.getStringF('danger_settings_desc',
+                                         data.file_name);
+        default:
+          return '';
+      }
+    },
+
+    /**
+     * @param {!downloads.Data} data
+     * @return {string} User-visible status update text.
+     * @private
+     */
+    getStatusText_: function(data) {
+      switch (data.state) {
+        case downloads.States.IN_PROGRESS:
+        case downloads.States.PAUSED:  // Fallthrough.
+          assert(typeof data.progress_status_text == 'string');
+          return data.progress_status_text;
+        case downloads.States.CANCELLED:
+          return loadTimeData.getString('status_cancelled');
+        case downloads.States.DANGEROUS:
+          break;  // Intentionally hit assertNotReached(); at bottom.
+        case downloads.States.INTERRUPTED:
+          assert(typeof data.last_reason_text == 'string');
+          return data.last_reason_text;
+        case downloads.States.COMPLETE:
+          return data.file_externally_removed ?
+              loadTimeData.getString('status_removed') : '';
+      }
+      assertNotReached();
+      return '';
+    },
+
+    /**
+     * @private
+     * @param {Event} e
+     */
+    onSafeDragstart_: function(e) {
+      e.preventDefault();
+      chrome.send('drag', [this.id_]);
+    },
+
+    /**
+     * @param {Event} e
+     * @private
+     */
+    onFileLinkClick_: function(e) {
+      e.preventDefault();
+      chrome.send('openFile', [this.id_]);
+    },
+
+    /** @private */
+    onShowClick_: function() {
+      chrome.send('show', [this.id_]);
+    },
+
+    /** @private */
+    onPauseClick_: function() {
+      chrome.send('pause', [this.id_]);
+    },
+
+    /** @private */
+    onResumeClick_: function() {
+      chrome.send('resume', [this.id_]);
+    },
+
+    /** @private */
+    onSafeRemoveClick_: function() {
+      chrome.send('remove', [this.id_]);
+    },
+
+    /** @private */
+    onCancelClick_: function() {
+      chrome.send('cancel', [this.id_]);
+    },
+
+    /** @private */
+    onRestoreClick_: function() {
+      this.onSaveClick_();
+    },
+
+    /** @private */
+    onSaveClick_: function() {
+      chrome.send('saveDangerous', [this.id_]);
+    },
+
+    /** @private */
+    onDangerRemoveClick_: function() {
+      this.onDiscardClick_();
+    },
+
+    /** @private */
+    onDiscardClick_: function() {
+      chrome.send('discardDangerous', [this.id_]);
+    },
+  });
+
+  return {ItemView: ItemView};
+});
diff --git a/chrome/browser/resources/md_downloads/manager.html b/chrome/browser/resources/md_downloads/manager.html
new file mode 100644
index 0000000..53b11d38
--- /dev/null
+++ b/chrome/browser/resources/md_downloads/manager.html
@@ -0,0 +1,8 @@
+<link rel="import" href="chrome://resources/html/cr.html">
+<link rel="import" href="chrome://resources/html/load_time_data.html">
+<link rel="import" href="chrome://resources/html/cr/ui.html">
+<link rel="import" href="chrome://resources/html/cr/ui/command.html">
+<link rel="import" href="chrome://resources/html/util.html">
+<link rel="import" href="chrome://downloads/constants.html">
+<link rel="import" href="chrome://downloads/item_view.html">
+<script src="chrome://downloads/manager.js"></script>
diff --git a/chrome/browser/resources/md_downloads/manager.js b/chrome/browser/resources/md_downloads/manager.js
index 16caf24..99c3547 100644
--- a/chrome/browser/resources/md_downloads/manager.js
+++ b/chrome/browser/resources/md_downloads/manager.js
@@ -32,13 +32,78 @@
     },
 
     /**
+     * @return {number} A guess at how many items could be visible at once.
+     * @private
+     */
+    guesstimateNumberOfVisibleItems_: function() {
+      var toolbarHeight = $('downloads-toolbar').offsetHeight;
+      var summaryHeight = $('downloads-summary').offsetHeight;
+      var nonItemSpace = toolbarHeight + summaryHeight;
+      return Math.floor((window.innerHeight - nonItemSpace) / 46) + 1;
+    },
+
+    /**
      * Called when all items need to be updated.
      * @param {!Array<!downloads.Data>} list A list of new download data.
      * @private
      */
     updateAll_: function(list) {
-      /** @private {!Array} */
-      this.items_ = list;
+      var oldIdMap = this.idMap_ || {};
+
+      /** @private {!Object<!downloads.ItemView>} */
+      this.idMap_ = {};
+
+      /** @private {!Array<!downloads.ItemView>} */
+      this.items_ = [];
+
+      if (!this.iconLoader_) {
+        var guesstimate = Math.max(this.guesstimateNumberOfVisibleItems_(), 1);
+        /** @private {downloads.ThrottledIconLoader} */
+        this.iconLoader_ = new downloads.ThrottledIconLoader(guesstimate);
+      }
+
+      for (var i = 0; i < list.length; ++i) {
+        var data = list[i];
+        var id = data.id;
+
+        // Re-use old items when possible (saves work, preserves focus).
+        var item = oldIdMap[id] || new downloads.ItemView(this.iconLoader_);
+
+        this.idMap_[id] = item;  // Associated by ID for fast lookup.
+        this.items_.push(item);  // Add to sorted list for order.
+
+        // Render |item| but don't actually add to the DOM yet. |this.items_|
+        // must be fully created to be able to find the right spot to insert.
+        item.update(data);
+
+        // Collapse redundant dates.
+        var prev = list[i - 1];
+        item.hideDate = !!prev && prev.date_string == data.date_string;
+
+        delete oldIdMap[id];
+      }
+
+      // Remove stale, previously rendered items from the DOM.
+      for (var id in oldIdMap) {
+        if (oldIdMap[id].parentNode)
+          oldIdMap[id].parentNode.removeChild(oldIdMap[id]);
+        delete oldIdMap[id];
+      }
+
+      for (var i = 0; i < this.items_.length; ++i) {
+        var item = this.items_[i];
+        if (item.parentNode)  // Already in the DOM; skip.
+          continue;
+
+        var before = null;
+        // Find the next rendered item after this one, and insert before it.
+        for (var j = i + 1; !before && j < this.items_.length; ++j) {
+          if (this.items_[j].parentNode)
+            before = this.items_[j];
+        }
+        // If |before| is null, |item| will just get added at the end.
+        this.node_.insertBefore(item, before);
+      }
 
       var noDownloadsOrResults = $('no-downloads-or-results');
       noDownloadsOrResults.textContent = loadTimeData.getString(
@@ -53,6 +118,14 @@
     },
 
     /**
+     * @param {!downloads.Data} data
+     * @private
+     */
+    updateItem_: function(data) {
+      this.idMap_[data.id].update(data);
+    },
+
+    /**
      * @return {number} The number of downloads shown on the page.
      * @private
      */
@@ -139,7 +212,9 @@
     Manager.getInstance().updateAll_(list);
   };
 
-  Manager.updateItem = function(item) {};
+  Manager.updateItem = function(item) {
+    Manager.getInstance().updateItem_(item);
+  };
 
   Manager.setSearchText = function(searchText) {
     Manager.getInstance().setSearchText_(searchText);
@@ -156,4 +231,4 @@
   return {Manager: Manager};
 });
 
-window.addEventListener('DOMContentLoaded', downloads.Manager.onLoad);
+window.addEventListener('load', downloads.Manager.onLoad);
diff --git a/chrome/browser/resources/md_downloads/strings.html b/chrome/browser/resources/md_downloads/strings.html
new file mode 100644
index 0000000..ecbe002
--- /dev/null
+++ b/chrome/browser/resources/md_downloads/strings.html
@@ -0,0 +1,2 @@
+<link rel="import" href="chrome://resources/html/load_time_data.html">
+<script src="chrome://downloads/strings.js"></script>
diff --git a/chrome/browser/ui/webui/downloads_ui.cc b/chrome/browser/ui/webui/downloads_ui.cc
index 1b51926..8fc489e 100644
--- a/chrome/browser/ui/webui/downloads_ui.cc
+++ b/chrome/browser/ui/webui/downloads_ui.cc
@@ -90,20 +90,26 @@
                      !profile->IsSupervised());
 
   source->SetJsonPath("strings.js");
-  source->AddResourcePath("constants.js", IDR_DOWNLOAD_CONSTANTS_JS);
+  source->AddResourcePath("constants.html", IDR_DOWNLOADS_CONSTANTS_HTML);
+  source->AddResourcePath("constants.js", IDR_DOWNLOADS_CONSTANTS_JS);
   source->AddResourcePath("throttled_icon_loader.html",
-                          IDR_DOWNLOAD_THROTTLED_ICON_LOADER_HTML);
+                          IDR_DOWNLOADS_THROTTLED_ICON_LOADER_HTML);
   source->AddResourcePath("throttled_icon_loader.js",
-                          IDR_DOWNLOAD_THROTTLED_ICON_LOADER_JS);
+                          IDR_DOWNLOADS_THROTTLED_ICON_LOADER_JS);
 
   if (switches::MdDownloadsEnabled()) {
-    source->AddResourcePath("manager.js", IDR_MD_DOWNLOAD_MANAGER_JS);
-    source->SetDefaultResource(IDR_MD_DOWNLOADS_HTML);
+    source->AddResourcePath("downloads.css", IDR_MD_DOWNLOADS_DOWNLOADS_CSS);
+    source->AddResourcePath("item_view.html", IDR_MD_DOWNLOADS_ITEM_VIEW_HTML);
+    source->AddResourcePath("item_view.js", IDR_MD_DOWNLOADS_ITEM_VIEW_JS);
+    source->AddResourcePath("manager.html", IDR_MD_DOWNLOADS_MANAGER_HTML);
+    source->AddResourcePath("manager.js", IDR_MD_DOWNLOADS_MANAGER_JS);
+    source->AddResourcePath("strings.html", IDR_MD_DOWNLOADS_STRINGS_HTML);
+    source->SetDefaultResource(IDR_MD_DOWNLOADS_DOWNLOADS_HTML);
   } else {
-    source->AddResourcePath("item_view.js", IDR_DOWNLOAD_ITEM_VIEW_JS);
-    source->AddResourcePath("focus_row.js", IDR_DOWNLOAD_FOCUS_ROW_JS);
-    source->AddResourcePath("manager.js", IDR_DOWNLOAD_MANAGER_JS);
-    source->SetDefaultResource(IDR_DOWNLOADS_HTML);
+    source->AddResourcePath("item_view.js", IDR_DOWNLOADS_ITEM_VIEW_JS);
+    source->AddResourcePath("focus_row.js", IDR_DOWNLOADS_FOCUS_ROW_JS);
+    source->AddResourcePath("manager.js", IDR_DOWNLOADS_MANAGER_JS);
+    source->SetDefaultResource(IDR_DOWNLOADS_DOWNLOADS_HTML);
   }
 
   return source;