OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 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 <include src="extension_error.js"> | 5 <include src="extension_error.js"> |
6 | 6 |
7 /////////////////////////////////////////////////////////////////////////////// | 7 /////////////////////////////////////////////////////////////////////////////// |
8 // ExtensionFocusRow: | 8 // ExtensionFocusRow: |
9 | 9 |
10 /** | 10 /** |
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
133 isDeveloperOption_: function(element) { | 133 isDeveloperOption_: function(element) { |
134 return /^dev-/.test(element.getAttribute('column-type')); | 134 return /^dev-/.test(element.getAttribute('column-type')); |
135 }, | 135 }, |
136 }; | 136 }; |
137 | 137 |
138 cr.define('extensions', function() { | 138 cr.define('extensions', function() { |
139 'use strict'; | 139 'use strict'; |
140 | 140 |
141 /** | 141 /** |
142 * Creates a new list of extensions. | 142 * Creates a new list of extensions. |
143 * @param {Object=} opt_propertyBag Optional properties. | |
144 * @constructor | 143 * @constructor |
145 * @extends {HTMLDivElement} | 144 * @extends {HTMLDivElement} |
146 */ | 145 */ |
147 var ExtensionList = cr.ui.define('div'); | 146 function ExtensionList() { |
| 147 var div = document.createElement('div'); |
| 148 div.__proto__ = ExtensionList.prototype; |
| 149 /** @private {!Array<ExtensionInfo>} */ |
| 150 div.extensions_ = []; |
| 151 return div; |
| 152 } |
148 | 153 |
149 /** | 154 /** |
150 * @type {Object<string, number>} A map from extension id to last reloaded | 155 * @type {Object<string, number>} A map from extension id to last reloaded |
151 * timestamp. The timestamp is recorded when the user click the 'Reload' | 156 * timestamp. The timestamp is recorded when the user click the 'Reload' |
152 * link. It is used to refresh the icon of an unpacked extension. | 157 * link. It is used to refresh the icon of an unpacked extension. |
153 * This persists between calls to decorate. | 158 * This persists between calls to decorate. |
154 */ | 159 */ |
155 var extensionReloadedTimestamp = {}; | 160 var extensionReloadedTimestamp = {}; |
156 | 161 |
157 ExtensionList.prototype = { | 162 ExtensionList.prototype = { |
(...skipping 23 matching lines...) Expand all Loading... |
181 * @private {boolean} | 186 * @private {boolean} |
182 */ | 187 */ |
183 permissionsPromptIsShowing_: false, | 188 permissionsPromptIsShowing_: false, |
184 | 189 |
185 /** | 190 /** |
186 * Necessary to only show the butterbar once. | 191 * Necessary to only show the butterbar once. |
187 * @private {boolean} | 192 * @private {boolean} |
188 */ | 193 */ |
189 butterbarShown_: false, | 194 butterbarShown_: false, |
190 | 195 |
191 decorate: function() { | 196 /** |
192 chrome.developerPrivate.getExtensionsInfo( | 197 * Whether or not incognito mode is available. |
193 {includeDisabled: true, includeTerminated: true}, | 198 * @private {boolean} |
194 function(extensions) { | 199 */ |
195 // Sort in order of unpacked vs. packed, followed by name, followed by | 200 incognitoAvailable_: false, |
196 // id. | 201 |
197 extensions.sort(function(a, b) { | 202 /** |
198 function compare(x, y) { | 203 * Whether or not the app info dialog is enabled. |
199 return x < y ? -1 : (x > y ? 1 : 0); | 204 * @private {boolean} |
200 } | 205 */ |
201 function compareLocation(x, y) { | 206 enableAppInfoDialog_: false, |
202 return x.location == chrome.developerPrivate.Location.UNPACKED ? | 207 |
203 -1 : (x.location == y.location ? 0 : 1); | 208 /** |
204 } | 209 * Updates the extensions on the page. |
205 return compareLocation(a, b) || | 210 * @param {boolean} incognitoAvailable Whether or not incognito is allowed. |
206 compare(a.name.toLowerCase(), b.name.toLowerCase()) || | 211 * @param {boolean} enableAppInfoDialog Whether or not the app info dialog |
207 compare(a.id, b.id); | 212 * is enabled. |
208 }); | 213 * @return {Promise} A promise that is resolved once the extensions data is |
209 this.extensions_ = extensions; | 214 * fully updated. |
210 this.showExtensionNodes_(); | 215 */ |
| 216 updateExtensionsData: function(incognitoAvailable, enableAppInfoDialog) { |
| 217 // If we start to need more information about the extension configuration, |
| 218 // consider passing in the full object from the ExtensionSettings. |
| 219 this.incognitoAvailable_ = incognitoAvailable; |
| 220 this.enableAppInfoDialog_ = enableAppInfoDialog; |
| 221 return new Promise(function(resolve, reject) { |
| 222 chrome.developerPrivate.getExtensionsInfo( |
| 223 {includeDisabled: true, includeTerminated: true}, |
| 224 function(extensions) { |
| 225 // Sort in order of unpacked vs. packed, followed by name, followed by |
| 226 // id. |
| 227 extensions.sort(function(a, b) { |
| 228 function compare(x, y) { |
| 229 return x < y ? -1 : (x > y ? 1 : 0); |
| 230 } |
| 231 function compareLocation(x, y) { |
| 232 return x.location == chrome.developerPrivate.Location.UNPACKED ? |
| 233 -1 : (x.location == y.location ? 0 : 1); |
| 234 } |
| 235 return compareLocation(a, b) || |
| 236 compare(a.name.toLowerCase(), b.name.toLowerCase()) || |
| 237 compare(a.id, b.id); |
| 238 }); |
| 239 this.extensions_ = extensions; |
| 240 this.showExtensionNodes_(); |
| 241 resolve(); |
| 242 }.bind(this)); |
211 }.bind(this)); | 243 }.bind(this)); |
212 }, | 244 }, |
213 | 245 |
| 246 /** @return {number} The number of extensions being displayed. */ |
| 247 getNumExtensions: function() { |
| 248 return this.extensions_.length; |
| 249 }, |
| 250 |
214 getIdQueryParam_: function() { | 251 getIdQueryParam_: function() { |
215 return parseQueryParams(document.location)['id']; | 252 return parseQueryParams(document.location)['id']; |
216 }, | 253 }, |
217 | 254 |
218 getOptionsQueryParam_: function() { | 255 getOptionsQueryParam_: function() { |
219 return parseQueryParams(document.location)['options']; | 256 return parseQueryParams(document.location)['options']; |
220 }, | 257 }, |
221 | 258 |
222 /** | 259 /** |
223 * Creates or updates all extension items from scratch. | 260 * Creates or updates all extension items from scratch. |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
261 } | 298 } |
262 } | 299 } |
263 | 300 |
264 var idToHighlight = this.getIdQueryParam_(); | 301 var idToHighlight = this.getIdQueryParam_(); |
265 if (idToHighlight && $(idToHighlight)) | 302 if (idToHighlight && $(idToHighlight)) |
266 this.scrollToNode_(idToHighlight); | 303 this.scrollToNode_(idToHighlight); |
267 | 304 |
268 var idToOpenOptions = this.getOptionsQueryParam_(); | 305 var idToOpenOptions = this.getOptionsQueryParam_(); |
269 if (idToOpenOptions && $(idToOpenOptions)) | 306 if (idToOpenOptions && $(idToOpenOptions)) |
270 this.showEmbeddedExtensionOptions_(idToOpenOptions, true); | 307 this.showEmbeddedExtensionOptions_(idToOpenOptions, true); |
271 | |
272 var noExtensions = this.extensions_.length == 0; | |
273 this.classList.toggle('empty-extension-list', noExtensions); | |
274 }, | 308 }, |
275 | 309 |
276 /** Updates each row's focusable elements without rebuilding the grid. */ | 310 /** Updates each row's focusable elements without rebuilding the grid. */ |
277 updateFocusableElements: function() { | 311 updateFocusableElements: function() { |
278 var rows = document.querySelectorAll('.extension-list-item-wrapper[id]'); | 312 var rows = document.querySelectorAll('.extension-list-item-wrapper[id]'); |
279 for (var i = 0; i < rows.length; ++i) { | 313 for (var i = 0; i < rows.length; ++i) { |
280 assertInstanceof(rows[i], ExtensionFocusRow).updateFocusableElements(); | 314 assertInstanceof(rows[i], ExtensionFocusRow).updateFocusableElements(); |
281 } | 315 } |
282 }, | 316 }, |
283 | 317 |
(...skipping 254 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
538 this.setText_(row, '.location-text', extension.locationText || ''); | 572 this.setText_(row, '.location-text', extension.locationText || ''); |
539 this.setText_(row, '.blacklist-text', extension.blacklistText || ''); | 573 this.setText_(row, '.blacklist-text', extension.blacklistText || ''); |
540 this.setText_(row, '.extension-description', extension.description); | 574 this.setText_(row, '.extension-description', extension.description); |
541 | 575 |
542 // The 'Show Browser Action' button. | 576 // The 'Show Browser Action' button. |
543 this.updateVisibility_(row, '.show-button', | 577 this.updateVisibility_(row, '.show-button', |
544 isActive && extension.actionButtonHidden); | 578 isActive && extension.actionButtonHidden); |
545 | 579 |
546 // The 'allow in incognito' checkbox. | 580 // The 'allow in incognito' checkbox. |
547 this.updateVisibility_(row, '.incognito-control', | 581 this.updateVisibility_(row, '.incognito-control', |
548 isActive && this.data_.incognitoAvailable, | 582 isActive && this.incognitoAvailable_, |
549 function(item) { | 583 function(item) { |
550 var incognito = item.querySelector('input'); | 584 var incognito = item.querySelector('input'); |
551 incognito.disabled = !extension.incognitoAccess.isEnabled; | 585 incognito.disabled = !extension.incognitoAccess.isEnabled; |
552 incognito.checked = extension.incognitoAccess.isActive; | 586 incognito.checked = extension.incognitoAccess.isActive; |
553 }); | 587 }); |
554 | 588 |
555 // Hide butterBar if incognito is not enabled for the extension. | 589 // Hide butterBar if incognito is not enabled for the extension. |
556 var butterBar = row.querySelector('.butter-bar'); | 590 var butterBar = row.querySelector('.butter-bar'); |
557 butterBar.hidden = | 591 butterBar.hidden = |
558 butterBar.hidden || !extension.incognitoAccess.isEnabled; | 592 butterBar.hidden || !extension.incognitoAccess.isEnabled; |
(...skipping 28 matching lines...) Expand all Loading... |
587 | 621 |
588 // The 'Options' button or link, depending on its behaviour. | 622 // The 'Options' button or link, depending on its behaviour. |
589 var optionsEnabled = isActive && !!extension.optionsPage; | 623 var optionsEnabled = isActive && !!extension.optionsPage; |
590 this.updateVisibility_(row, '.options-link', optionsEnabled && | 624 this.updateVisibility_(row, '.options-link', optionsEnabled && |
591 extension.optionsPage.openInTab); | 625 extension.optionsPage.openInTab); |
592 this.updateVisibility_(row, '.options-button', optionsEnabled && | 626 this.updateVisibility_(row, '.options-button', optionsEnabled && |
593 !extension.optionsPage.openInTab); | 627 !extension.optionsPage.openInTab); |
594 | 628 |
595 // The 'View in Web Store/View Web Site' link. | 629 // The 'View in Web Store/View Web Site' link. |
596 var siteLinkEnabled = !!extension.homepageUrl && | 630 var siteLinkEnabled = !!extension.homepageUrl && |
597 !this.data_.enableAppInfoDialog; | 631 !this.enableAppInfoDialog_; |
598 this.updateVisibility_(row, '.site-link', siteLinkEnabled, | 632 this.updateVisibility_(row, '.site-link', siteLinkEnabled, |
599 function(item) { | 633 function(item) { |
600 item.href = extension.homepageUrl; | 634 item.href = extension.homepageUrl; |
601 item.textContent = loadTimeData.getString( | 635 item.textContent = loadTimeData.getString( |
602 extension.homepageProvided ? 'extensionSettingsVisitWebsite' : | 636 extension.homepageProvided ? 'extensionSettingsVisitWebsite' : |
603 'extensionSettingsVisitWebStore'); | 637 'extensionSettingsVisitWebStore'); |
604 }); | 638 }); |
605 | 639 |
606 var isUnpacked = | 640 var isUnpacked = |
607 extension.location == chrome.developerPrivate.Location.UNPACKED; | 641 extension.location == chrome.developerPrivate.Location.UNPACKED; |
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
714 } | 748 } |
715 if (!dependentExtension) | 749 if (!dependentExtension) |
716 return; | 750 return; |
717 | 751 |
718 var depNode = dependentTemplate.cloneNode(true); | 752 var depNode = dependentTemplate.cloneNode(true); |
719 depNode.querySelector('.dep-extension-title').textContent = | 753 depNode.querySelector('.dep-extension-title').textContent = |
720 dependentExtension.name; | 754 dependentExtension.name; |
721 depNode.querySelector('.dep-extension-id').textContent = | 755 depNode.querySelector('.dep-extension-id').textContent = |
722 dependentExtension.id; | 756 dependentExtension.id; |
723 dependentList.appendChild(depNode); | 757 dependentList.appendChild(depNode); |
724 }); | 758 }, this); |
725 }); | 759 }); |
726 | 760 |
727 // The active views. | 761 // The active views. |
728 this.updateVisibility_(row, '.active-views', extension.views.length > 0, | 762 this.updateVisibility_(row, '.active-views', extension.views.length > 0, |
729 function(item) { | 763 function(item) { |
730 var link = item.querySelector('a'); | 764 var link = item.querySelector('a'); |
731 | 765 |
732 // Link needs to be an only child before the list is updated. | 766 // Link needs to be an only child before the list is updated. |
733 while (link.nextElementSibling) | 767 while (link.nextElementSibling) |
734 item.removeChild(link.nextElementSibling); | 768 item.removeChild(link.nextElementSibling); |
(...skipping 205 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
940 // TODO(dbeam): why do we need to focus <extensionoptions> before and | 974 // TODO(dbeam): why do we need to focus <extensionoptions> before and |
941 // after its showing animation? Makes very little sense to me. | 975 // after its showing animation? Makes very little sense to me. |
942 overlay.setInitialFocus(); | 976 overlay.setInitialFocus(); |
943 }, | 977 }, |
944 }; | 978 }; |
945 | 979 |
946 return { | 980 return { |
947 ExtensionList: ExtensionList | 981 ExtensionList: ExtensionList |
948 }; | 982 }; |
949 }); | 983 }); |
OLD | NEW |