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

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

Issue 8687012: Moved ChromeOS-specific resources from chrome/browser/resources to chrome/browser/resources/chrom... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 9 years 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) 2011 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 setErrorText_: function(text) {
236 this.filename.textContent = text;
237 },
238
239 supportsPdf_: function() {
240 return 'application/pdf' in navigator.mimeTypes;
241 },
242
243 openFilePath_: function() {
244 chromeSend('openNewFullWindow', this.fileUrl);
245 },
246
247 /**
248 * Determine onclick behavior based on filename.
249 * @private
250 */
251 getFunctionForItem_: function() {
252 var path = this.path;
253 var self = this;
254
255 if (pathIsAudioFile(path)) {
256 return function() {
257 chromeSend('playMediaFile', path);
258 };
259 }
260 if (pathIsVideoFile(path)) {
261 return function() {
262 chromeSend('playMediaFile', path);
263 };
264 }
265 if (pathIsImageFile(path)) {
266 return function() {
267 self.openFilePath_();
268 }
269 }
270 if (pathIsHtmlFile(path)) {
271 return function() {
272 self.openFilePath_();
273 }
274 }
275 if (pathIsPdfFile(path) && this.supportsPdf_()) {
276 return function() {
277 self.openFilePath_();
278 }
279 }
280
281 return function() {
282 self.setErrorText_(localStrings.getStringF('error_unknown_file_type',
283 self.name));
284 };
285 },
286
287 setDangerousIcon_: function(warning) {
288 this.icon.className = warning ? 'iconwarning' : 'icon';
289 this.icon.style.background = warning ? '' :
290 'url(chrome://fileicon' + escape(this.path) +
291 '?iconsize=small) no-repeat';
292 },
293
294 /**
295 * Create the row button for the left of the row.
296 * This contains the icon, filename and error elements.
297 * @private
298 */
299 createRowButton_: function () {
300 this.rowbutton = createChild('div', 'rowbutton rowbg', this.element);
301
302 // Icon.
303 this.icon = createChild('div', 'icon', this.rowbutton);
304 this.setDangerousIcon_(false);
305
306 // Filename.
307 this.filename = createChild('span', 'title', this.rowbutton, this.name);
308 },
309
310 setMenuHidden_: function(hidden) {
311 this.menubutton.hidden = hidden;
312 if (hidden) {
313 this.rowbutton.style.width = '238px';
314 } else {
315 this.rowbutton.style.width = '';
316 }
317 },
318
319 /**
320 * Create the menu button on the right of the row.
321 * This contains the menuicon. The menuicon contains the menu, which
322 * contains items for Pause/Resume and Cancel.
323 * @private
324 */
325 createMenu_: function() {
326 var self = this;
327 this.menubutton = createChild('div', 'menubutton rowbg', this.element, '',
328 function() {
329 menu.clicked(self);
330 });
331
332 this.menuicon = createChild('div', 'menuicon', this.menubutton);
333
334 var menudiv = createChild('div', 'menu', this.menuicon);
335
336 this.pause = createChild('div', 'menuitem', menudiv,
337 localStrings.getString('pause'), function() {
338 self.pauseToggleDownload_();
339 });
340
341 this.cancel = createChild('div', 'menuitem', menudiv,
342 localStrings.getString('cancel'), function() {
343 self.cancelDownload_();
344 });
345 },
346
347 allowDownload_: function() {
348 chromeSend('allowDownload', this.id);
349 },
350
351 cancelDownload_: function() {
352 chromeSend('cancelDownload', this.id);
353 },
354
355 pauseToggleDownload_: function() {
356 this.pause.textContent =
357 (this.pause.textContent == localStrings.getString('pause')) ?
358 localStrings.getString('resume') :
359 localStrings.getString('pause');
360
361 chromeSend('pauseToggleDownload', this.id);
362 },
363
364 changeElemHeight_: function(elem, x) {
365 elem.style.height = elem.clientHeight + x + 'px';
366 },
367
368 changeRowHeight_: function(x) {
369 this.list.rowsHeight += x;
370 this.changeElemHeight_(this.element, x);
371 // rowbutton has 5px padding.
372 this.changeElemHeight_(this.rowbutton, x - 5);
373 this.list.resize();
374 },
375
376 DANGEROUS_HEIGHT: 60,
377 createDangerousPrompt_: function(dangerType) {
378 if (this.dangerous)
379 return;
380
381 this.dangerous = createChild('div', 'dangerousprompt', this.rowbutton);
382
383 // Handle dangerous files, extensions and dangerous urls.
384 var dangerText;
385 if (dangerType == 'DANGEROUS_URL') {
386 dangerText = localStrings.getString('dangerousurl');
387 } else if (dangerType == 'DANGEROUS_CONTENT') {
388 dangerText = localStrings.getStringF('dangerouscontent', this.name);
389 } else if (dangerType == 'DANGEROUS_FILE' && this.path.match(/\.crx$/)) {
390 dangerText = localStrings.getString('dangerousextension');
391 } else {
392 dangerText = localStrings.getStringF('dangerousfile', this.name);
393 }
394 createChild('span', 'dangerousprompttext', this.dangerous, dangerText);
395
396 var self = this;
397 createChild('span', 'confirm', this.dangerous,
398 localStrings.getString('discard'),
399 function() {
400 self.cancelDownload_();
401 });
402 createChild('span', 'confirm', this.dangerous,
403 localStrings.getString('continue'),
404 function() {
405 self.allowDownload_();
406 });
407
408 this.changeRowHeight_(this.DANGEROUS_HEIGHT);
409 this.setDangerousIcon_(true);
410 },
411
412 removeDangerousPrompt_: function() {
413 if (!this.dangerous)
414 return;
415
416 this.rowbutton.removeChild(this.dangerous);
417 this.dangerous = null;
418
419 this.changeRowHeight_(-this.DANGEROUS_HEIGHT);
420 this.setDangerousIcon_(false);
421 },
422
423 PROGRESS_HEIGHT: 8,
424 createProgress_: function() {
425 if (this.progress)
426 return;
427
428 this.progress = createChild('div', 'progress', this.rowbutton);
429
430 this.setMenuHidden_(false);
431 this.changeRowHeight_(this.PROGRESS_HEIGHT);
432 },
433
434 removeProgress_: function() {
435 if (!this.progress)
436 return;
437
438 this.rowbutton.removeChild(this.progress);
439 this.progress = null;
440
441 this.changeRowHeight_(-this.PROGRESS_HEIGHT);
442 this.setMenuHidden_(true);
443 },
444
445 updatePause_: function(result) {
446 var pause = this.pause;
447 var pauseStr = localStrings.getString('pause');
448 var resumeStr = localStrings.getString('resume');
449
450 if (pause &&
451 result.state == 'PAUSED' &&
452 pause.textContent != resumeStr) {
453 pause.textContent = resumeStr;
454 } else if (pause &&
455 result.state == 'IN_PROGRESS' &&
456 pause.textContent != pauseStr) {
457 pause.textContent = pauseStr;
458 }
459 },
460
461 progressStatusText_: function(progress) {
462 if (!progress)
463 return progress;
464
465 /* m looks like this:
466 ["107 MB/s - 108 MB of 672 MB, 5 secs left",
467 "107 MB/s", "108", "MB", "672", "MB", "5 secs left"]
468 We want to return progress text like this:
469 "108 / 672 MB, 5 secs left"
470 or
471 "108 kB / 672 MB, 5 secs left"
472 */
473 var m = progress.match(
474 /([^-]*) - ([0-9\.]*) ([a-zA-Z]*) of ([0-9\.]*) ([a-zA-Z]*), (.*)/);
475 if (!m || m.length != 7)
476 return progress;
477
478 return m[2] + (m[3] == m[5] ? '' : ' ' + m[3]) +
479 ' / ' + m[4] + ' ' + m[5] + ', ' + m[6];
480 },
481
482 updateProgress_: function(result) {
483 this.removeDangerousPrompt_();
484 this.createProgress_();
485 this.progress.textContent =
486 this.progressStatusText_(result.progress_status_text);
487 this.updatePause_(result);
488 },
489
490 /**
491 * Called when the item has finished downloading. Switch the menu
492 * and remove the progress bar.
493 * @private
494 */
495 finishedDownloading_: function() {
496 // Make rowbutton clickable.
497 this.rowbutton.onclick = this.getFunctionForItem_();
498 this.rowbutton.style.cursor = 'pointer';
499
500 // Make rowbutton draggable.
501 this.rowbutton.setAttribute('draggable', 'true');
502 var self = this;
503 this.rowbutton.addEventListener('dragstart', function(e) {
504 e.dataTransfer.effectAllowed = 'copy';
505 e.dataTransfer.setData('Text', self.path);
506 e.dataTransfer.setData('URL', self.fileUrl);
507 }, false);
508
509 this.removeDangerousPrompt_();
510 this.removeProgress_();
511 },
512
513 /**
514 * One of the DownloadItem we are observing has updated.
515 * @param {Object} result JSON representation of DownloadItem.
516 */
517 update: function(result) {
518 this.filename.textContent = result.file_name;
519 this.id = result.id;
520
521 if (result.state != 'COMPLETE') {
522 this.rowbutton.onclick = '';
523 this.rowbutton.style.cursor = '';
524 }
525
526 if (DiscardResult(result)) {
527 this.list.remove(this);
528 } else if (result.state == 'DANGEROUS') {
529 this.createDangerousPrompt_(result.danger_type);
530 } else if (result.percent < 100) {
531 this.updateProgress_(result);
532 } else if (result.state == 'COMPLETE') {
533 this.finishedDownloading_();
534 }
535 },
536 };
537
538 /**
539 * DownloadRowList is a container for DownloadRows.
540 */
541 function DownloadRowList() {
542 this.element = createChild('ul', 'downloadlist', $('main'));
543
544 document.title = localStrings.getString('downloadpath').
545 split('/').pop();
546 }
547
548 DownloadRowList.prototype = {
549
550 /**
551 * numRows is the current number of rows.
552 * rowsHeight is the sum of the heights of all rows.
553 * rowListHeight is the height of the container containing the rows.
554 * rows is the list of DownloadRows.
555 */
556 numRows: 0,
557 rowsHeight: 0,
558 rowListHeight: 72,
559 rows: [],
560
561 /**
562 * Resize the panel to accomodate all rows.
563 */
564 resize: function() {
565 var diff = this.rowsHeight - this.rowListHeight;
566 if (diff != 0 && (this.rowListHeight + diff > 72)) {
567 window.resizeBy(0, diff);
568 this.rowListHeight += diff;
569 }
570 },
571
572 /**
573 * Remove a row from the list, as when a download is canceled, or
574 * the the number of rows has exceeded the max allowed.
575 *
576 * @param {DownloadRow} row Row to be removed.
577 * @private
578 */
579 remove: function(row) {
580 this.rows.splice(this.rows.indexOf(row), 1);
581
582 this.numRows--;
583 this.rowsHeight -= row.element.offsetHeight;
584 this.resize();
585
586 this.element.removeChild(row.element);
587 row.element.row = null;
588 },
589
590 removeList: function(rows) {
591 for (i = 0; i < rows.length; i++) {
592 this.remove(rows[i]);
593 }
594 },
595
596 updateList: function(results) {
597 for (var i = 0; i < results.length; i++) {
598 this.update(results[i]);
599 }
600 },
601
602 /**
603 * Append a new row to the list, removing the last row if we exceed the
604 * maximum allowed.
605 * @param {DownloadRow} row Row to be removed.
606 */
607 append: function(row) {
608 this.rows.push(row);
609
610 var elem = row.element;
611 var list = this.element;
612 if (list.firstChild) {
613 list.insertBefore(elem, list.firstChild);
614 } else {
615 list.appendChild(elem);
616 }
617
618 this.rowsHeight += elem.offsetHeight;
619
620 this.numRows++;
621 // We display no more than 5 elements.
622 if (this.numRows > 5)
623 this.remove(list.lastChild.row);
624
625 this.resize();
626 },
627
628 getRow: function(path) {
629 for (var i = 0; i < this.rows.length; i++) {
630 if (this.rows[i].path == path)
631 return this.rows[i];
632 }
633 },
634
635 /**
636 * Returns the list of rows that are not in the results array.
637 * @param {Array} results Array of JSONified DownloadItems.
638 */
639 findMissing: function(results) {
640 var removeList = [];
641
642 for (var i = 0; i < this.rows.length; i++) {
643 var row = this.rows[i];
644 var found = false;
645 for (var j = 0; j < results.length; j++) {
646 if (row.path == results[j].file_path) {
647 found = true;
648 break;
649 }
650 }
651 if (!found)
652 removeList.push(row);
653 }
654 return removeList;
655 },
656
657 /**
658 * Handle list callback with list of DownloadItems.
659 * @param {Array} results Array of JSONified DownloadItems.
660 */
661 list: function(results) {
662 var rows = this.findMissing(results);
663 this.updateList(results);
664 this.removeList(rows);
665 },
666
667 /**
668 * Handle update of a DownloadItem we're observing.
669 * @param {Object} result JSON representation of DownloadItem.
670 */
671 update: function(result) {
672 var row = this.getRow(result.file_path);
673 if (!row && !DiscardResult(result))
674 row = new DownloadRow(this, result);
675
676 row && row.update(result);
677 },
678 };
679
680 document.addEventListener('DOMContentLoaded', init);
OLDNEW
« no previous file with comments | « chrome/browser/resources/active_downloads.html ('k') | chrome/browser/resources/chromeos/OWNERS » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698