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

Side by Side Diff: chrome/browser/resources/md_downloads/crisper.js

Issue 1428833005: MD Downloads: track downloads in C++, dispatch discrete JS updates (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: merge Created 5 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
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 if (typeof Polymer == 'undefined') 5 if (typeof Polymer == 'undefined')
6 Polymer = {dom: 'shadow'}; 6 Polymer = {dom: 'shadow'};
7 else 7 else
8 console.error('Polymer is already defined.'); 8 console.error('Polymer is already defined.');
9 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 9 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
10 // Use of this source code is governed by a BSD-style license that can be 10 // Use of this source code is governed by a BSD-style license that can be
(...skipping 1513 matching lines...) Expand 10 before | Expand all | Expand 10 after
1524 * @param {string} chromeSendName 1524 * @param {string} chromeSendName
1525 * @return {function(string):void} A chrome.send() callback with curried name. 1525 * @return {function(string):void} A chrome.send() callback with curried name.
1526 */ 1526 */
1527 function chromeSendWithId(chromeSendName) { 1527 function chromeSendWithId(chromeSendName) {
1528 return function(id) { chrome.send(chromeSendName, [id]); }; 1528 return function(id) { chrome.send(chromeSendName, [id]); };
1529 } 1529 }
1530 1530
1531 /** @constructor */ 1531 /** @constructor */
1532 function ActionService() {} 1532 function ActionService() {}
1533 1533
1534 /**
1535 * @param {string} s
1536 * @return {string} |s| without whitespace at the beginning or end.
1537 */
1538 function trim(s) { return s.trim(); }
1539
1540 /**
1541 * @param {string|undefined} value
1542 * @return {boolean} Whether |value| is truthy.
1543 */
1544 function truthy(value) { return !!value; }
1545
1546 /**
1547 * @param {string} searchText Input typed by the user into a search box.
1548 * @return {Array<string>} A list of terms extracted from |searchText|.
1549 */
1550 ActionService.splitTerms = function(searchText) {
1551 // Split quoted terms (e.g., 'The "lazy" dog' => ['The', 'lazy', 'dog']).
1552 return searchText.split(/"([^"]*)"/).map(trim).filter(truthy);
1553 };
1554
1534 ActionService.prototype = { 1555 ActionService.prototype = {
1535 /** @param {string} id ID of the download to cancel. */ 1556 /** @param {string} id ID of the download to cancel. */
1536 cancel: chromeSendWithId('cancel'), 1557 cancel: chromeSendWithId('cancel'),
1537 1558
1538 /** Instructs the browser to clear all finished downloads. */ 1559 /** Instructs the browser to clear all finished downloads. */
1539 clearAll: function() { 1560 clearAll: function() {
1540 if (loadTimeData.getBoolean('allowDeletingHistory')) { 1561 if (loadTimeData.getBoolean('allowDeletingHistory')) {
1541 chrome.send('clearAll'); 1562 chrome.send('clearAll');
1542 this.search(''); 1563 this.search('');
1543 } 1564 }
1544 }, 1565 },
1545 1566
1546 /** @param {string} id ID of the dangerous download to discard. */ 1567 /** @param {string} id ID of the dangerous download to discard. */
1547 discardDangerous: chromeSendWithId('discardDangerous'), 1568 discardDangerous: chromeSendWithId('discardDangerous'),
1548 1569
1549 /** @param {string} url URL of a file to download. */ 1570 /** @param {string} url URL of a file to download. */
1550 download: function(url) { 1571 download: function(url) {
1551 var a = document.createElement('a'); 1572 var a = document.createElement('a');
1552 a.href = url; 1573 a.href = url;
1553 a.setAttribute('download', ''); 1574 a.setAttribute('download', '');
1554 a.click(); 1575 a.click();
1555 }, 1576 },
1556 1577
1557 /** @param {string} id ID of the download that the user started dragging. */ 1578 /** @param {string} id ID of the download that the user started dragging. */
1558 drag: chromeSendWithId('drag'), 1579 drag: chromeSendWithId('drag'),
1559 1580
1581 /** @private {boolean} */
1582 isSearching_: false,
1583
1560 /** 1584 /**
1561 * @return {boolean} Whether the user is currently searching for downloads 1585 * @return {boolean} Whether the user is currently searching for downloads
1562 * (i.e. has a non-empty search term). 1586 * (i.e. has a non-empty search term).
1563 */ 1587 */
1564 isSearching: function() { 1588 isSearching: function() {
1565 return this.searchText_.length > 0; 1589 return this.isSearching_;
1566 }, 1590 },
1567 1591
1568 /** Opens the current local destination for downloads. */ 1592 /** Opens the current local destination for downloads. */
1569 openDownloadsFolder: chrome.send.bind(chrome, 'openDownloadsFolder'), 1593 openDownloadsFolder: chrome.send.bind(chrome, 'openDownloadsFolder'),
1570 1594
1571 /** 1595 /**
1572 * @param {string} id ID of the download to run locally on the user's box. 1596 * @param {string} id ID of the download to run locally on the user's box.
1573 */ 1597 */
1574 openFile: chromeSendWithId('openFile'), 1598 openFile: chromeSendWithId('openFile'),
1575 1599
(...skipping 12 matching lines...) Expand all
1588 */ 1612 */
1589 saveDangerous: chromeSendWithId('saveDangerous'), 1613 saveDangerous: chromeSendWithId('saveDangerous'),
1590 1614
1591 /** @param {string} searchText What to search for. */ 1615 /** @param {string} searchText What to search for. */
1592 search: function(searchText) { 1616 search: function(searchText) {
1593 if (this.searchText_ == searchText) 1617 if (this.searchText_ == searchText)
1594 return; 1618 return;
1595 1619
1596 this.searchText_ = searchText; 1620 this.searchText_ = searchText;
1597 1621
1598 // Split quoted terms (e.g., 'The "lazy" dog' => ['The', 'lazy', 'dog']). 1622 var terms = ActionService.splitTerms(searchText);
1599 function trim(s) { return s.trim(); } 1623 this.isSearching_ = terms.length > 0;
1600 chrome.send('getDownloads', searchText.split(/"([^"]*)"/).map(trim)); 1624
1625 chrome.send('getDownloads', terms);
1601 }, 1626 },
1602 1627
1603 /** 1628 /**
1604 * Shows the local folder a finished download resides in. 1629 * Shows the local folder a finished download resides in.
1605 * @param {string} id ID of the download to show. 1630 * @param {string} id ID of the download to show.
1606 */ 1631 */
1607 show: chromeSendWithId('show'), 1632 show: chromeSendWithId('show'),
1608 1633
1609 /** Undo download removal. */ 1634 /** Undo download removal. */
1610 undo: chrome.send.bind(chrome, 'undo'), 1635 undo: chrome.send.bind(chrome, 'undo'),
(...skipping 15319 matching lines...) Expand 10 before | Expand all | Expand 10 after
16930 // Copyright 2015 The Chromium Authors. All rights reserved. 16955 // Copyright 2015 The Chromium Authors. All rights reserved.
16931 // Use of this source code is governed by a BSD-style license that can be 16956 // Use of this source code is governed by a BSD-style license that can be
16932 // found in the LICENSE file. 16957 // found in the LICENSE file.
16933 16958
16934 cr.define('downloads', function() { 16959 cr.define('downloads', function() {
16935 var Manager = Polymer({ 16960 var Manager = Polymer({
16936 is: 'downloads-manager', 16961 is: 'downloads-manager',
16937 16962
16938 properties: { 16963 properties: {
16939 hasDownloads_: { 16964 hasDownloads_: {
16965 observer: 'hasDownloadsChanged_',
16940 type: Boolean, 16966 type: Boolean,
16941 value: false,
16942 }, 16967 },
16943 16968
16944 items_: { 16969 items_: {
16945 type: Array, 16970 type: Array,
16971 value: function() { return []; },
16946 }, 16972 },
16947 }, 16973 },
16948 16974
16949 hostAttributes: { 16975 hostAttributes: {
16950 loading: true, 16976 loading: true,
16951 }, 16977 },
16952 16978
16979 observers: [
16980 'itemsChanged_(items_.*)',
16981 ],
16982
16983 /** @private */
16984 clearAll_: function() {
16985 this.set('items_', []);
16986 },
16987
16988 /** @private */
16989 hasDownloadsChanged_: function() {
16990 if (loadTimeData.getBoolean('allowDeletingHistory'))
16991 this.$.toolbar.downloadsShowing = this.hasDownloads_;
16992
16993 if (this.hasDownloads_) {
16994 this.$['downloads-list'].fire('iron-resize');
16995 } else {
16996 var isSearching = downloads.ActionService.getInstance().isSearching();
16997 var messageToShow = isSearching ? 'noSearchResults' : 'noDownloads';
16998 this.$['no-downloads'].querySelector('span').textContent =
16999 loadTimeData.getString(messageToShow);
17000 }
17001 },
17002
17003 /**
17004 * @param {number} index
17005 * @param {!Array<!downloads.Data>} list
17006 * @private
17007 */
17008 insertItems_: function(index, list) {
17009 this.splice.apply(this, ['items_', index, 0].concat(list));
17010 this.updateHideDates_(index, index + list.length);
17011 this.removeAttribute('loading');
17012 },
17013
17014 /** @private */
17015 itemsChanged_: function() {
17016 this.hasDownloads_ = this.items_.length > 0;
17017 },
17018
16953 /** 17019 /**
16954 * @param {Event} e 17020 * @param {Event} e
16955 * @private 17021 * @private
16956 */ 17022 */
16957 onCanExecute_: function(e) { 17023 onCanExecute_: function(e) {
16958 e = /** @type {cr.ui.CanExecuteEvent} */(e); 17024 e = /** @type {cr.ui.CanExecuteEvent} */(e);
16959 switch (e.command.id) { 17025 switch (e.command.id) {
16960 case 'undo-command': 17026 case 'undo-command':
16961 e.canExecute = this.$.toolbar.canUndo(); 17027 e.canExecute = this.$.toolbar.canUndo();
16962 break; 17028 break;
(...skipping 18 matching lines...) Expand all
16981 onLoad_: function() { 17047 onLoad_: function() {
16982 cr.ui.decorate('command', cr.ui.Command); 17048 cr.ui.decorate('command', cr.ui.Command);
16983 document.addEventListener('canExecute', this.onCanExecute_.bind(this)); 17049 document.addEventListener('canExecute', this.onCanExecute_.bind(this));
16984 document.addEventListener('command', this.onCommand_.bind(this)); 17050 document.addEventListener('command', this.onCommand_.bind(this));
16985 17051
16986 // Shows all downloads. 17052 // Shows all downloads.
16987 downloads.ActionService.getInstance().search(''); 17053 downloads.ActionService.getInstance().search('');
16988 }, 17054 },
16989 17055
16990 /** 17056 /**
16991 * @return {number} The number of downloads shown on the page. 17057 * @param {number} index
16992 * @private 17058 * @private
16993 */ 17059 */
16994 size_: function() { 17060 removeItem_: function(index) {
16995 return this.items_.length; 17061 this.splice('items_', index, 1);
17062 this.updateHideDates_(index, index);
16996 }, 17063 },
16997 17064
16998 /** 17065 /**
16999 * Called when all items need to be updated. 17066 * @param {number} start
17000 * @param {!Array<!downloads.Data>} list A list of new download data. 17067 * @param {number} end
17001 * @private 17068 * @private
17002 */ 17069 */
17003 updateAll_: function(list) { 17070 updateHideDates_: function(start, end) {
17004 /** @private {!Object<number>} */ 17071 for (var i = start; i <= end; ++i) {
17005 this.idToIndex_ = {}; 17072 var current = this.items_[i];
17006 17073 if (!current)
17007 for (var i = 0; i < list.length; ++i) { 17074 continue;
17008 var data = list[i]; 17075 var prev = this.items_[i - 1];
17009 17076 current.hideDate = !!prev && prev.date_string == current.date_string;
17010 this.idToIndex_[data.id] = data.index = i;
17011
17012 var prev = list[i - 1];
17013 data.hideDate = !!prev && prev.date_string == data.date_string;
17014 } 17077 }
17015
17016 // TODO(dbeam): this resets the scroll position, which is a huge bummer.
17017 // Removing something from the bottom of the list should not scroll you
17018 // back to the top. The grand plan is to restructure how the C++ sends the
17019 // JS data so that it only gets updates (rather than the most recent set
17020 // of items). TL;DR - we can't ship with this bug.
17021 this.items_ = list;
17022
17023 var hasDownloads = this.size_() > 0;
17024 if (!hasDownloads) {
17025 var isSearching = downloads.ActionService.getInstance().isSearching();
17026 var messageToShow = isSearching ? 'noSearchResults' : 'noDownloads';
17027 this.$['no-downloads'].querySelector('span').textContent =
17028 loadTimeData.getString(messageToShow);
17029 }
17030 this.hasDownloads_ = hasDownloads;
17031
17032 if (loadTimeData.getBoolean('allowDeletingHistory'))
17033 this.$.toolbar.downloadsShowing = this.hasDownloads_;
17034
17035 this.removeAttribute('loading');
17036 }, 17078 },
17037 17079
17038 /** 17080 /**
17081 * @param {number} index
17039 * @param {!downloads.Data} data 17082 * @param {!downloads.Data} data
17040 * @private 17083 * @private
17041 */ 17084 */
17042 updateItem_: function(data) { 17085 updateItem_: function(index, data) {
17043 var index = this.idToIndex_[data.id];
17044 this.set('items_.' + index, data); 17086 this.set('items_.' + index, data);
17087 this.updateHideDates_(index, index);
17045 this.$['downloads-list'].updateSizeForItem(index); 17088 this.$['downloads-list'].updateSizeForItem(index);
17046 }, 17089 },
17047 }); 17090 });
17048 17091
17049 Manager.size = function() { 17092 Manager.clearAll = function() {
17050 return document.querySelector('downloads-manager').size_(); 17093 Manager.get().clearAll_();
17051 }; 17094 };
17052 17095
17053 Manager.updateAll = function(list) { 17096 /** @return {!downloads.Manager} */
17054 document.querySelector('downloads-manager').updateAll_(list); 17097 Manager.get = function() {
17098 return /** @type {!downloads.Manager} */(
17099 queryRequiredElement('downloads-manager'));
17055 }; 17100 };
17056 17101
17057 Manager.updateItem = function(item) { 17102 Manager.insertItems = function(index, list) {
17058 document.querySelector('downloads-manager').updateItem_(item); 17103 Manager.get().insertItems_(index, list);
17059 }; 17104 };
17060 17105
17061 Manager.onLoad = function() { 17106 Manager.onLoad = function() {
17062 document.querySelector('downloads-manager').onLoad_(); 17107 Manager.get().onLoad_();
17108 };
17109
17110 Manager.removeItem = function(index) {
17111 Manager.get().removeItem_(index);
17112 };
17113
17114 Manager.updateItem = function(index, data) {
17115 Manager.get().updateItem_(index, data);
17063 }; 17116 };
17064 17117
17065 return {Manager: Manager}; 17118 return {Manager: Manager};
17066 }); 17119 });
17067 // Copyright 2015 The Chromium Authors. All rights reserved. 17120 // Copyright 2015 The Chromium Authors. All rights reserved.
17068 // Use of this source code is governed by a BSD-style license that can be 17121 // Use of this source code is governed by a BSD-style license that can be
17069 // found in the LICENSE file. 17122 // found in the LICENSE file.
17070 17123
17071 window.addEventListener('load', downloads.Manager.onLoad); 17124 window.addEventListener('load', downloads.Manager.onLoad);
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698