Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(33)

Side by Side Diff: chrome/browser/resources/chromeos/active_downloads.js

Issue 10073017: Remove Active Downloads UI. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebased Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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 /**
6 * Wrapper for chrome.send.
7 */
8 function chromeSend(func, arg) {
9 if (arg == undefined)
10 arg = '';
11
12 // Convert to string.
13 if (typeof arg == 'number')
14 arg = '' + arg;
15
16 chrome.send(func, [arg]);
17 };
18
19 /**
20 * Create a child element.
21 *
22 * @param {string} type The type - div, span, etc.
23 * @param {string} className The class name
24 * @param {HTMLElement} parent Parent to append this child to.
25 * @param {string} textContent optional text content of child.
26 * @param {function(*)} onclick onclick function of child.
27 */
28 function createChild(type, className, parent, textContent, onclick) {
29 var elem = document.createElement(type);
30 elem.className = className;
31 if (textContent !== undefined)
32 elem.textContent = textContent;
33 elem.onclick = onclick;
34 parent.appendChild(elem);
35 return elem;
36 };
37
38 var localStrings;
39 var downloadRowList;
40
41 function init() {
42 localStrings = new LocalStrings();
43 initTestHarness();
44
45 window.onkeydown = function(e) {
46 if (e.keyCode == 27) // Escape.
47 menu.clear();
48 e.preventDefault(); // Suppress browser shortcuts.
49 };
50
51 document.body.addEventListener("blur", menu.clear);
52 document.body.addEventListener("click", menu.clear);
53 document.body.addEventListener("contextmenu", function (e) {
54 e.preventDefault(); });
55 document.body.addEventListener("selectstart", function (e) {
56 e.preventDefault(); });
57
58 var sadt = $('showallfilestext');
59 sadt.textContent = localStrings.getString('showallfiles');
60 sadt.addEventListener("click", showAllFiles);
61
62 downloadRowList = new DownloadRowList();
63 chromeSend('getDownloads');
64 }
65
66 /**
67 * Testing. Allow this page to be loaded in a browser.
68 * Create stubs for localStrings and chrome.send.
69 */
70 function initTestHarness() {
71 if (location.protocol != 'file:')
72 return;
73
74 // Enable right click for dom inspector.
75 document.body.oncontextmenu = '';
76
77 // Fix localStrings.
78 localStrings = {
79 getString: function(name) {
80 if (name == 'showallfiles')
81 return 'Show all files';
82 if (name == 'dangerousextension')
83 return 'Extensions, apps, and themes can harm your computer.' +
84 ' Are you sure you want to continue?'
85 if (name == 'continue')
86 return 'Continue';
87 if (name == 'discard')
88 return 'Discard';
89 return name;
90 },
91 getStringF: function(name, path) {
92 return path + ' - Unknown file type.';
93 },
94 };
95
96 // Log chrome.send calls.
97 chrome.send = function(name, ary) {
98 console.log('chrome.send ' + name + ' ' + ary);
99 if (name == 'getDownloads' ||
100 (name == 'openNewFullWindow' &&
101 ary[0] == 'chrome://downloads'))
102 sendTestResults();
103 };
104
105 // Fix resource images.
106 var cssRules = document.styleSheets[0].cssRules;
107 for (var i = 0; i < cssRules.length; i++) {
108 var cssRule = cssRules[i];
109 if (cssRule.selectorText.match(/^div\.icon|^\.menuicon/)) {
110 cssRule.style.backgroundImage =
111 cssRule.style.backgroundImage.replace('chrome://resources', 'shared');
112 }
113 }
114 }
115
116 /**
117 * Create a results array with test data and call downloadsList.
118 */
119 var testElement;
120 var testId = 0;
121 var testResults = [];
122 function sendTestResults() {
123 var testState = (testId % 3 == 0 ? 'IN_PROGRESS' :
124 (testId % 3 == 1 ? 'DANGEROUS' : 'COMPLETE'));
125 state1 = (testId % 3 == 0);
126 testResults.push({
127 state: testState,
128 percent: (testId % 3 == 0 ? 90 : 100),
129 id: testId,
130 file_name: ' Test' + testId + '.pdf',
131 file_path: '/home/achuith/Downloads/Test' + testId + '.pdf',
132 progress_status_text : '107 MB/s - 108 MB of 672 MB, 5 secs left',
133 });
134 testId++;
135 downloadsList(testResults);
136 }
137
138 /**
139 * Current Menu.
140 */
141 var menu = {
142 current_: null,
143
144 /**
145 * Close the current menu.
146 */
147 clear: function() {
148 var current = this.current_;
149 if (current) {
150 current.firstChild.style.display = 'none';
151 current.style.opacity = '';
152 this.current_ = null;
153 }
154 },
155
156 /**
157 * If it's a second click on an open menu, close the menu.
158 * Otherwise, close any other open menu and open the clicked menu.
159 */
160 clicked: function(row) {
161 var menuicon = row.menuicon;
162 if (this.current_ === menuicon) {
163 this.clear();
164 return;
165 }
166 this.clear();
167 if (menuicon.firstChild.style.display != 'block') {
168 menuicon.firstChild.style.display = 'block';
169 menuicon.style.opacity = '1';
170 menuicon.scrollIntoView();
171 this.current_ = menuicon;
172 }
173 window.event.stopPropagation();
174 },
175 };
176
177 function DiscardResult(result) {
178 return (result.state == 'CANCELLED' ||
179 result.state == 'INTERRUPTED' ||
180 result.state == 'REMOVING');
181 };
182
183 /**
184 * C++ api calls.
185 */
186 function downloadsList(results) {
187 downloadRowList.list(results);
188 }
189
190 function downloadUpdated(result) {
191 downloadRowList.update(result);
192 }
193
194 function showAllFiles() {
195 chromeSend('showAllFiles');
196 }
197
198 /**
199 * DownloadRow contains all the elements that go into a row of the downloads
200 * list. It represents a single DownloadItem.
201 *
202 * @param {DownloadRowList} list Global DownloadRowList.
203 * @param {Object} result JSON representation of DownloadItem.
204 * @constructor
205 */
206 function DownloadRow(list, result) {
207 this.path = result.file_path;
208 this.name = result.file_name;
209 this.fileUrl = result.file_url;
210 this.list = list;
211 this.id = result.id;
212
213 this.createRow_(list);
214 this.createMenu_();
215 this.createRowButton_();
216 this.setMenuHidden_(true);
217 }
218
219 DownloadRow.prototype = {
220 /**
221 * Create the row html element and book-keeping for the row.
222 * @param {DownloadRowList} list global DownloadRowList instance.
223 * @private
224 */
225 createRow_: function(list) {
226 var elem = document.createElement('li');
227 elem.className = 'downloadrow';
228 elem.id = this.path;
229 elem.row = this;
230 this.element = elem;
231
232 list.append(this);
233 },
234
235 setDangerousIcon_: function(warning) {
236 var escapedPath = encodeURIComponent(this.path).replace(/'/g,'%27');
237 this.icon.className = warning ? 'iconwarning' : 'icon';
238 this.icon.style.background = warning ? '' :
239 'url(\'chrome://fileicon/' + escapedPath +
240 '?iconsize=small\') no-repeat';
241 },
242
243 /**
244 * Create the row button for the left of the row.
245 * This contains the icon, filename and error elements.
246 * @private
247 */
248 createRowButton_: function () {
249 this.rowbutton = createChild('div', 'rowbutton rowbg', this.element);
250
251 // Icon.
252 this.icon = createChild('div', 'icon', this.rowbutton);
253 this.setDangerousIcon_(false);
254
255 // Filename.
256 this.filename = createChild('span', 'title', this.rowbutton, this.name);
257 },
258
259 setMenuHidden_: function(hidden) {
260 this.menubutton.hidden = hidden;
261 if (hidden) {
262 this.rowbutton.style.width = '238px';
263 } else {
264 this.rowbutton.style.width = '';
265 }
266 },
267
268 /**
269 * Create the menu button on the right of the row.
270 * This contains the menuicon. The menuicon contains the menu, which
271 * contains items for Pause/Resume and Cancel.
272 * @private
273 */
274 createMenu_: function() {
275 var self = this;
276 this.menubutton = createChild('div', 'menubutton rowbg', this.element, '',
277 function() {
278 menu.clicked(self);
279 });
280
281 this.menuicon = createChild('div', 'menuicon', this.menubutton);
282
283 var menudiv = createChild('div', 'menu', this.menuicon);
284
285 this.pause = createChild('div', 'menuitem', menudiv,
286 localStrings.getString('pause'), function() {
287 self.pauseToggleDownload_();
288 });
289
290 this.cancel = createChild('div', 'menuitem', menudiv,
291 localStrings.getString('cancel'), function() {
292 self.cancelDownload_();
293 });
294 },
295
296 allowDownload_: function() {
297 chromeSend('allowDownload', this.id);
298 },
299
300 cancelDownload_: function() {
301 chromeSend('cancelDownload', this.id);
302 },
303
304 pauseToggleDownload_: function() {
305 this.pause.textContent =
306 (this.pause.textContent == localStrings.getString('pause')) ?
307 localStrings.getString('resume') :
308 localStrings.getString('pause');
309
310 chromeSend('pauseToggleDownload', this.id);
311 },
312
313 changeElemHeight_: function(elem, x) {
314 elem.style.height = elem.clientHeight + x + 'px';
315 },
316
317 changeRowHeight_: function(x) {
318 this.list.rowsHeight += x;
319 this.changeElemHeight_(this.element, x);
320 // rowbutton has 5px padding.
321 this.changeElemHeight_(this.rowbutton, x - 5);
322 this.list.resize();
323 },
324
325 DANGEROUS_HEIGHT: 60,
326 createDangerousPrompt_: function(dangerType) {
327 if (this.dangerous)
328 return;
329
330 this.dangerous = createChild('div', 'dangerousprompt', this.rowbutton);
331
332 // Handle dangerous files, extensions and dangerous urls.
333 var dangerText;
334 if (dangerType == 'DANGEROUS_URL') {
335 dangerText = localStrings.getString('dangerousurl');
336 } else if (dangerType == 'DANGEROUS_CONTENT') {
337 dangerText = localStrings.getStringF('dangerouscontent', this.name);
338 } else if (dangerType == 'UNCOMMON_CONTENT') {
339 dangerText = localStrings.getStringF('uncommoncontent', this.name);
340 } else if (dangerType == 'DANGEROUS_FILE' && this.path.match(/\.crx$/)) {
341 dangerText = localStrings.getString('dangerousextension');
342 } else {
343 dangerText = localStrings.getStringF('dangerousfile', this.name);
344 }
345 createChild('span', 'dangerousprompttext', this.dangerous, dangerText);
346
347 var self = this;
348 createChild('span', 'confirm', this.dangerous,
349 localStrings.getString('discard'),
350 function() {
351 self.cancelDownload_();
352 });
353 createChild('span', 'confirm', this.dangerous,
354 localStrings.getString('continue'),
355 function() {
356 self.allowDownload_();
357 });
358
359 this.changeRowHeight_(this.DANGEROUS_HEIGHT);
360 this.setDangerousIcon_(true);
361 },
362
363 removeDangerousPrompt_: function() {
364 if (!this.dangerous)
365 return;
366
367 this.rowbutton.removeChild(this.dangerous);
368 this.dangerous = null;
369
370 this.changeRowHeight_(-this.DANGEROUS_HEIGHT);
371 this.setDangerousIcon_(false);
372 },
373
374 PROGRESS_HEIGHT: 8,
375 createProgress_: function() {
376 if (this.progress)
377 return;
378
379 this.progress = createChild('div', 'progress', this.rowbutton);
380
381 this.setMenuHidden_(false);
382 this.changeRowHeight_(this.PROGRESS_HEIGHT);
383 },
384
385 removeProgress_: function() {
386 if (!this.progress)
387 return;
388
389 this.rowbutton.removeChild(this.progress);
390 this.progress = null;
391
392 this.changeRowHeight_(-this.PROGRESS_HEIGHT);
393 this.setMenuHidden_(true);
394 },
395
396 updatePause_: function(result) {
397 var pause = this.pause;
398 var pauseStr = localStrings.getString('pause');
399 var resumeStr = localStrings.getString('resume');
400
401 if (pause &&
402 result.state == 'PAUSED' &&
403 pause.textContent != resumeStr) {
404 pause.textContent = resumeStr;
405 } else if (pause &&
406 result.state == 'IN_PROGRESS' &&
407 pause.textContent != pauseStr) {
408 pause.textContent = pauseStr;
409 }
410 },
411
412 progressStatusText_: function(progress) {
413 if (!progress)
414 return progress;
415
416 /* m looks like this:
417 ["107 MB/s - 108 MB of 672 MB, 5 secs left",
418 "107 MB/s", "108", "MB", "672", "MB", "5 secs left"]
419 We want to return progress text like this:
420 "108 / 672 MB, 5 secs left"
421 or
422 "108 kB / 672 MB, 5 secs left"
423 */
424 var m = progress.match(
425 /([^-]*) - ([0-9\.]*) ([a-zA-Z]*) of ([0-9\.]*) ([a-zA-Z]*), (.*)/);
426 if (!m || m.length != 7)
427 return progress;
428
429 return m[2] + (m[3] == m[5] ? '' : ' ' + m[3]) +
430 ' / ' + m[4] + ' ' + m[5] + ', ' + m[6];
431 },
432
433 updateProgress_: function(result) {
434 this.removeDangerousPrompt_();
435 this.createProgress_();
436 this.progress.textContent =
437 this.progressStatusText_(result.progress_status_text);
438 this.updatePause_(result);
439 },
440
441 /**
442 * Called when the item has finished downloading. Switch the menu
443 * and remove the progress bar.
444 * @private
445 */
446 finishedDownloading_: function() {
447 // Make rowbutton clickable.
448 var self = this;
449 this.rowbutton.onclick = function() {
450 chromeSend('viewFile', self.path);
451 };
452
453 this.rowbutton.style.cursor = 'pointer';
454
455 // Make rowbutton draggable.
456 this.rowbutton.setAttribute('draggable', 'true');
457 var self = this;
458 this.rowbutton.addEventListener('dragstart', function(e) {
459 e.dataTransfer.effectAllowed = 'copy';
460 e.dataTransfer.setData('Text', self.path);
461 e.dataTransfer.setData('URL', self.fileUrl);
462 }, false);
463
464 this.removeDangerousPrompt_();
465 this.removeProgress_();
466 },
467
468 /**
469 * One of the DownloadItem we are observing has updated.
470 * @param {Object} result JSON representation of DownloadItem.
471 */
472 update: function(result) {
473 this.filename.textContent = result.file_name;
474 this.id = result.id;
475
476 if (result.state != 'COMPLETE') {
477 this.rowbutton.onclick = '';
478 this.rowbutton.style.cursor = '';
479 }
480
481 if (DiscardResult(result)) {
482 this.list.remove(this);
483 } else if (result.state == 'DANGEROUS') {
484 this.createDangerousPrompt_(result.danger_type);
485 } else if (result.percent < 100) {
486 this.updateProgress_(result);
487 } else if (result.state == 'COMPLETE') {
488 this.finishedDownloading_();
489 }
490 },
491 };
492
493 /**
494 * DownloadRowList is a container for DownloadRows.
495 */
496 function DownloadRowList() {
497 this.element = createChild('ul', 'downloadlist', $('main'));
498
499 document.title = localStrings.getString('downloadpath').
500 split('/').pop();
501 }
502
503 DownloadRowList.prototype = {
504
505 /**
506 * numRows is the current number of rows.
507 * rowsHeight is the sum of the heights of all rows.
508 * rowListHeight is the height of the container containing the rows.
509 * rows is the list of DownloadRows.
510 */
511 numRows: 0,
512 rowsHeight: 0,
513 rowListHeight: 72,
514 rows: [],
515
516 /**
517 * Resize the panel to accomodate all rows.
518 */
519 resize: function() {
520 var diff = this.rowsHeight - this.rowListHeight;
521 if (diff != 0 && (this.rowListHeight + diff > 72)) {
522 window.resizeBy(0, diff);
523 this.rowListHeight += diff;
524 }
525 },
526
527 /**
528 * Remove a row from the list, as when a download is canceled, or
529 * the the number of rows has exceeded the max allowed.
530 *
531 * @param {DownloadRow} row Row to be removed.
532 * @private
533 */
534 remove: function(row) {
535 this.rows.splice(this.rows.indexOf(row), 1);
536
537 this.numRows--;
538 this.rowsHeight -= row.element.offsetHeight;
539 this.resize();
540
541 this.element.removeChild(row.element);
542 row.element.row = null;
543 },
544
545 removeList: function(rows) {
546 for (i = 0; i < rows.length; i++) {
547 this.remove(rows[i]);
548 }
549 },
550
551 updateList: function(results) {
552 for (var i = 0; i < results.length; i++) {
553 this.update(results[i]);
554 }
555 },
556
557 /**
558 * Append a new row to the list, removing the last row if we exceed the
559 * maximum allowed.
560 * @param {DownloadRow} row Row to be removed.
561 */
562 append: function(row) {
563 this.rows.push(row);
564
565 var elem = row.element;
566 var list = this.element;
567 if (list.firstChild) {
568 list.insertBefore(elem, list.firstChild);
569 } else {
570 list.appendChild(elem);
571 }
572
573 this.rowsHeight += elem.offsetHeight;
574
575 this.numRows++;
576 // We display no more than 5 elements.
577 if (this.numRows > 5)
578 this.remove(list.lastChild.row);
579
580 this.resize();
581 },
582
583 getRow: function(path) {
584 for (var i = 0; i < this.rows.length; i++) {
585 if (this.rows[i].path == path)
586 return this.rows[i];
587 }
588 },
589
590 /**
591 * Returns the list of rows that are not in the results array.
592 * @param {Array} results Array of JSONified DownloadItems.
593 */
594 findMissing: function(results) {
595 var removeList = [];
596
597 for (var i = 0; i < this.rows.length; i++) {
598 var row = this.rows[i];
599 var found = false;
600 for (var j = 0; j < results.length; j++) {
601 if (row.path == results[j].file_path) {
602 found = true;
603 break;
604 }
605 }
606 if (!found)
607 removeList.push(row);
608 }
609 return removeList;
610 },
611
612 /**
613 * Handle list callback with list of DownloadItems.
614 * @param {Array} results Array of JSONified DownloadItems.
615 */
616 list: function(results) {
617 var rows = this.findMissing(results);
618 this.updateList(results);
619 this.removeList(rows);
620 },
621
622 /**
623 * Handle update of a DownloadItem we're observing.
624 * @param {Object} result JSON representation of DownloadItem.
625 */
626 update: function(result) {
627 var row = this.getRow(result.file_path);
628 if (!row && !DiscardResult(result))
629 row = new DownloadRow(this, result);
630
631 row && row.update(result);
632 },
633 };
634
635 document.addEventListener('DOMContentLoaded', init);
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698