1 // Copyright 2015 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.
5 cr.define('downloads', function() {
10 * @param {!downloads.ThrottledIconLoader} iconLoader
11 * @param {!downloads.ActionService} actionService
13 factoryImpl: function(iconLoader, actionService) {
14 /** @private {!downloads.ThrottledIconLoader} */
15 this.iconLoader_ = iconLoader;
17 /** @private {!downloads.ActionService} */
18 this.actionService_ = actionService;
23 reflectToAttribute: true,
31 return new Promise(function(resolve, reject) {
32 this.resolveReadyPromise_ = resolve;
38 observer: 'onScrollbarWidthChange_',
44 computed: 'computeCompletelyOnDisk_(' +
45 'data_.state, data_.file_externally_removed)',
55 cancel: loadTimeData.getString('controlCancel'),
56 discard: loadTimeData.getString('dangerDiscard'),
57 pause: loadTimeData.getString('controlPause'),
58 remove: loadTimeData.getString('controlRemoveFromList'),
59 resume: loadTimeData.getString('controlResume'),
60 restore: loadTimeData.getString('dangerRestore'),
61 retry: loadTimeData.getString('controlRetry'),
62 save: loadTimeData.getString('dangerSave'),
63 show: loadTimeData.getString('controlShowInFolder'),
69 computed: 'computeIsDangerous_(data_.state)',
75 computed: 'computeIsInProgress_(data_.state)',
81 computed: 'computeShowCancel_(data_.state)',
87 computed: 'computeShowProgress_(showCancel_, data_.percent)',
93 computed: 'computeIsMalware_(isDangerous_, data_.danger_type)',
98 // TODO(dbeam): move all properties to |data_|.
101 value: function() { return {}; },
106 this.content = this.$.content;
107 this.resolveReadyPromise_();
110 /** @param {!downloads.Data} data */
111 update: function(data) {
112 assert(!('id' in this.data_) || this.data_.id == data.id);
115 data.by_ext_id === this.data_.by_ext_id &&
116 data.by_ext_name === this.data_.by_ext_name &&
117 data.danger_type === this.data_.danger_type &&
118 data.date_string == this.data_.date_string &&
119 data.file_externally_removed == this.data_.file_externally_removed &&
120 data.file_name == this.data_.file_name &&
121 data.file_path == this.data_.file_path &&
122 data.file_url == this.data_.file_url &&
123 data.last_reason_text === this.data_.last_reason_text &&
124 data.otr == this.data_.otr &&
125 data.percent === this.data_.percent &&
126 data.progress_status_text === this.data_.progress_status_text &&
127 data.received === this.data_.received &&
128 data.resume == this.data_.resume &&
129 data.retry == this.data_.retry &&
130 data.since_string == this.data_.since_string &&
131 data.started == this.data_.started &&
132 data.state == this.data_.state &&
133 data.total == this.data_.total &&
134 data.url == this.data_.url) {
135 // TODO(dbeam): remove this once data binding is fully in place.
139 for (var key in data) {
140 this.set('data_.' + key, data[key]);
143 /** @const */ var isActive =
144 data.state != downloads.States.CANCELLED &&
145 data.state != downloads.States.INTERRUPTED &&
146 !data.file_externally_removed;
147 this.$.content.classList.toggle('is-active', isActive);
148 this.$.content.elevation = isActive ? 1 : 0;
150 // Danger-dependent UI and controls.
151 this.$.content.classList.toggle('dangerous', this.isDangerous_);
153 var description = this.getDangerText_(data) || this.getStatusText_(data);
155 // Status goes in the "tag" (next to the file name) if there's no file.
156 this.ensureTextIs_(this.$.description, isActive ? description : '');
157 this.ensureTextIs_(this.$.tag, isActive ? '' : description);
159 this.$.content.classList.toggle('show-progress', this.showProgress_);
163 if (this.isDangerous_) {
166 this.$['file-link'].href = data.url;
167 this.ensureTextIs_(this.$['file-link'], data.file_name);
169 this.$['file-link'].hidden = !this.completelyOnDisk_;
170 this.$.name.hidden = this.completelyOnDisk_;
172 hideRemove = this.showCancel_ ||
173 !loadTimeData.getBoolean('allowDeletingHistory');
175 /** @const */ var controlledByExtension = data.by_ext_id &&
177 this.$['controlled-by'].hidden = !controlledByExtension;
178 if (controlledByExtension) {
179 var link = this.$['controlled-by'].querySelector('a');
180 link.href = 'chrome://extensions#' + data.by_ext_id;
181 link.textContent = data.by_ext_name;
184 var icon = 'chrome://fileicon/' + encodeURIComponent(data.file_path);
185 this.iconLoader_.loadScaledIcon(this.$['file-icon'], icon);
188 this.$.remove.style.visibility = hideRemove ? 'hidden' : '';
192 computeCompletelyOnDisk_: function() {
193 return this.data_.state == downloads.States.COMPLETE &&
194 !this.data_.file_externally_removed;
198 computeDate_: function() {
199 assert(!this.hideDate);
200 return assert(this.data_.since_string || this.data_.date_string);
204 computeIsInProgress_: function() {
205 return this.data_.state == downloads.States.IN_PROGRESS;
209 computeIsMalware_: function() {
210 return this.isDangerous_ &&
211 (this.data_.danger_type == downloads.DangerType.DANGEROUS_CONTENT ||
212 this.data_.danger_type == downloads.DangerType.DANGEROUS_HOST ||
213 this.data_.danger_type == downloads.DangerType.DANGEROUS_URL ||
214 this.data_.danger_type == downloads.DangerType.POTENTIALLY_UNWANTED);
218 computeIsDangerous_: function() {
219 return this.data_.state == downloads.States.DANGEROUS;
223 computeShowCancel_: function() {
224 return this.data_.state == downloads.States.IN_PROGRESS ||
225 this.data_.state == downloads.States.PAUSED;
229 computeShowProgress_: function() {
230 return this.showCancel_ && isFinite(this.data_.percent);
234 * Overwrite |el|'s textContent if it differs from |text|. This is done
235 * generally so quickly updating text can be copied via text selection.
236 * @param {!Element} el
237 * @param {string} text
240 ensureTextIs_: function(el, text) {
241 if (el.textContent != text)
242 el.textContent = text;
246 * @param {!downloads.Data} data
247 * @return {string} Text describing the danger of a download. Empty if not
250 getDangerText_: function(data) {
251 switch (data.danger_type) {
252 case downloads.DangerType.DANGEROUS_FILE:
253 return loadTimeData.getStringF('dangerFileDesc', data.file_name);
254 case downloads.DangerType.DANGEROUS_URL:
255 return loadTimeData.getString('dangerUrlDesc');
256 case downloads.DangerType.DANGEROUS_CONTENT: // Fall through.
257 case downloads.DangerType.DANGEROUS_HOST:
258 return loadTimeData.getStringF('dangerContentDesc', data.file_name);
259 case downloads.DangerType.UNCOMMON_CONTENT:
260 return loadTimeData.getStringF('dangerUncommonDesc', data.file_name);
261 case downloads.DangerType.POTENTIALLY_UNWANTED:
262 return loadTimeData.getStringF('dangerSettingsDesc', data.file_name);
269 * @param {!downloads.Data} data
270 * @return {string} User-visible status update text.
273 getStatusText_: function(data) {
274 switch (data.state) {
275 case downloads.States.IN_PROGRESS:
276 case downloads.States.PAUSED: // Fallthrough.
277 assert(typeof data.progress_status_text == 'string');
278 return data.progress_status_text;
279 case downloads.States.CANCELLED:
280 return loadTimeData.getString('statusCancelled');
281 case downloads.States.DANGEROUS:
282 break; // Intentionally hit assertNotReached(); at bottom.
283 case downloads.States.INTERRUPTED:
284 assert(typeof data.last_reason_text == 'string');
285 return data.last_reason_text;
286 case downloads.States.COMPLETE:
287 return data.file_externally_removed ?
288 loadTimeData.getString('statusRemoved') : '';
295 isIndeterminate_: function() {
296 assert(this.showProgress_);
297 return this.data_.percent == -1;
301 onCancelClick_: function() {
302 this.actionService_.cancel(this.data_.id);
309 onDragStart_: function(e) {
311 this.actionService_.drag(this.data_.id);
318 onFileLinkClick_: function(e) {
320 this.actionService_.openFile(this.data_.id);
324 onPauseClick_: function() {
325 this.actionService_.pause(this.data_.id);
329 onRemoveClick_: function() {
330 assert(!this.$.remove.disabled);
331 this.actionService_.remove(this.data_.id);
335 onSaveDangerous_: function() {
336 this.actionService_.saveDangerous(this.data_.id);
340 onDiscardDangerous_: function() {
341 this.actionService_.discardDangerous(this.data_.id);
345 onResumeClick_: function() {
346 this.actionService_.resume(this.data_.id);
350 onRetryClick_: function() {
351 this.actionService_.download(this.$['file-link'].href);
355 onScrollbarWidthChange_: function() {
359 var endCap = this.$['end-cap'];
360 endCap.style.flexBasis = '';
362 if (this.scrollbarWidth) {
363 var basis = parseInt(getComputedStyle(endCap).flexBasis, 10);
364 endCap.style.flexBasis = basis - this.scrollbarWidth + 'px';
369 onShowClick_: function() {
370 this.actionService_.show(this.data_.id);