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 cr.define('extensions', function() { | 7 cr.define('extensions', function() { |
8 'use strict'; | 8 'use strict'; |
9 | 9 |
10 var ExtensionType = chrome.developerPrivate.ExtensionType; | 10 var ExtensionType = chrome.developerPrivate.ExtensionType; |
(...skipping 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
77 function compareLocation(x, y) { | 77 function compareLocation(x, y) { |
78 if (x.location == y.location) | 78 if (x.location == y.location) |
79 return 0; | 79 return 0; |
80 if (x.location == chrome.developerPrivate.Location.UNPACKED) | 80 if (x.location == chrome.developerPrivate.Location.UNPACKED) |
81 return -1; | 81 return -1; |
82 if (y.location == chrome.developerPrivate.Location.UNPACKED) | 82 if (y.location == chrome.developerPrivate.Location.UNPACKED) |
83 return 1; | 83 return 1; |
84 return 0; | 84 return 0; |
85 } | 85 } |
86 return compareLocation(a, b) || | 86 return compareLocation(a, b) || |
87 compare(a.name.toLowerCase(), b.name.toLowerCase()) || | 87 compare(a.name.toLowerCase(), b.name.toLowerCase()) || |
88 compare(a.id, b.id); | 88 compare(a.id, b.id); |
89 } | 89 } |
90 | 90 |
91 /** @interface */ | 91 /** @interface */ |
92 function ExtensionListDelegate() {} | 92 function ExtensionListDelegate() {} |
93 | 93 |
94 ExtensionListDelegate.prototype = { | 94 ExtensionListDelegate.prototype = { |
95 /** | 95 /** |
96 * Called when the number of extensions in the list has changed. | 96 * Called when the number of extensions in the list has changed. |
97 */ | 97 */ |
98 onExtensionCountChanged: assertNotReached, | 98 onExtensionCountChanged: assertNotReached, |
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
164 */ | 164 */ |
165 initialize: function(delegate) { | 165 initialize: function(delegate) { |
166 /** @private {!Array<chrome.developerPrivate.ExtensionInfo>} */ | 166 /** @private {!Array<chrome.developerPrivate.ExtensionInfo>} */ |
167 this.extensions_ = []; | 167 this.extensions_ = []; |
168 | 168 |
169 /** @private {!extensions.ExtensionListDelegate} */ | 169 /** @private {!extensions.ExtensionListDelegate} */ |
170 this.delegate_ = delegate; | 170 this.delegate_ = delegate; |
171 | 171 |
172 this.resetLoadFinished(); | 172 this.resetLoadFinished(); |
173 | 173 |
174 chrome.developerPrivate.onItemStateChanged.addListener( | 174 chrome.developerPrivate.onItemStateChanged.addListener(function( |
175 function(eventData) { | 175 eventData) { |
176 var EventType = chrome.developerPrivate.EventType; | 176 var EventType = chrome.developerPrivate.EventType; |
177 switch (eventData.event_type) { | 177 switch (eventData.event_type) { |
178 case EventType.VIEW_REGISTERED: | 178 case EventType.VIEW_REGISTERED: |
179 case EventType.VIEW_UNREGISTERED: | 179 case EventType.VIEW_UNREGISTERED: |
180 case EventType.INSTALLED: | 180 case EventType.INSTALLED: |
181 case EventType.LOADED: | 181 case EventType.LOADED: |
182 case EventType.UNLOADED: | 182 case EventType.UNLOADED: |
183 case EventType.ERROR_ADDED: | 183 case EventType.ERROR_ADDED: |
184 case EventType.ERRORS_REMOVED: | 184 case EventType.ERRORS_REMOVED: |
185 case EventType.PREFS_CHANGED: | 185 case EventType.PREFS_CHANGED: |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
246 updateExtensionsData: function(incognitoAvailable, enableAppInfoDialog) { | 246 updateExtensionsData: function(incognitoAvailable, enableAppInfoDialog) { |
247 // If we start to need more information about the extension configuration, | 247 // If we start to need more information about the extension configuration, |
248 // consider passing in the full object from the ExtensionSettings. | 248 // consider passing in the full object from the ExtensionSettings. |
249 this.incognitoAvailable_ = incognitoAvailable; | 249 this.incognitoAvailable_ = incognitoAvailable; |
250 this.enableAppInfoDialog_ = enableAppInfoDialog; | 250 this.enableAppInfoDialog_ = enableAppInfoDialog; |
251 /** @private {Promise} */ | 251 /** @private {Promise} */ |
252 this.extensionsUpdated_ = new Promise(function(resolve, reject) { | 252 this.extensionsUpdated_ = new Promise(function(resolve, reject) { |
253 chrome.developerPrivate.getExtensionsInfo( | 253 chrome.developerPrivate.getExtensionsInfo( |
254 {includeDisabled: true, includeTerminated: true}, | 254 {includeDisabled: true, includeTerminated: true}, |
255 function(extensions) { | 255 function(extensions) { |
256 // Sort in order of unpacked vs. packed, followed by name, followed by | 256 // Sort in order of unpacked vs. packed, followed by name, |
257 // id. | 257 // followed by id. |
258 extensions.sort(compareExtensions); | 258 extensions.sort(compareExtensions); |
259 this.extensions_ = extensions; | 259 this.extensions_ = extensions; |
260 this.showExtensionNodes_(); | 260 this.showExtensionNodes_(); |
261 | 261 |
262 // We keep the commands overlay's extension info in sync, so that we | 262 // We keep the commands overlay's extension info in sync, so that |
263 // don't duplicate the same querying logic there. | 263 // we don't duplicate the same querying logic there. |
264 ExtensionCommandsOverlay.updateExtensionsData(this.extensions_); | 264 ExtensionCommandsOverlay.updateExtensionsData(this.extensions_); |
265 | 265 |
266 resolve(); | 266 resolve(); |
267 | 267 |
268 // |resolve| is async so it's necessary to use |then| here in order to | 268 // |resolve| is async so it's necessary to use |then| here in |
269 // do work after other |then|s have finished. This is important so | 269 // order to do work after other |then|s have finished. This is |
270 // elements are visible when these updates happen. | 270 // important so elements are visible when these updates happen. |
271 this.extensionsUpdated_.then(function() { | 271 this.extensionsUpdated_.then(function() { |
272 this.onUpdateFinished_(); | 272 this.onUpdateFinished_(); |
273 this.resolveLoadFinished_(); | 273 this.resolveLoadFinished_(); |
274 }.bind(this)); | 274 }.bind(this)); |
275 }.bind(this)); | 275 }.bind(this)); |
276 }.bind(this)); | 276 }.bind(this)); |
277 return this.extensionsUpdated_; | 277 return this.extensionsUpdated_; |
278 }, | 278 }, |
279 | 279 |
280 /** | 280 /** |
281 * Updates elements that need to be visible in order to update properly. | 281 * Updates elements that need to be visible in order to update properly. |
282 * @private | 282 * @private |
283 */ | 283 */ |
284 onUpdateFinished_: function() { | 284 onUpdateFinished_: function() { |
285 // Cannot focus or highlight a extension if there are none, and we should | 285 // Cannot focus or highlight a extension if there are none, and we should |
286 // only scroll to a particular extension or open the options page once. | 286 // only scroll to a particular extension or open the options page once. |
287 if (this.extensions_.length == 0 || this.didInitialNavigation_) | 287 if (this.extensions_.length == 0 || this.didInitialNavigation_) |
288 return; | 288 return; |
289 | 289 |
290 this.didInitialNavigation_ = true; | 290 this.didInitialNavigation_ = true; |
291 assert(!this.hidden); | 291 assert(!this.hidden); |
292 assert(!this.parentElement.hidden); | 292 assert(!this.parentElement.hidden); |
293 | 293 |
294 var idToHighlight = this.getIdQueryParam_(); | 294 var idToHighlight = this.getIdQueryParam_(); |
295 if (idToHighlight) { | 295 if (idToHighlight) { |
296 var wrapper = $(idToHighlight); | 296 var wrapper = $(idToHighlight); |
297 if (wrapper) { | 297 if (wrapper) { |
298 this.scrollToWrapper_(idToHighlight); | 298 this.scrollToWrapper_(idToHighlight); |
299 | 299 |
300 var focusRow = wrapper.getFocusRow(); | 300 var focusRow = wrapper.getFocusRow(); |
301 (focusRow.getFirstFocusable('enabled') || | 301 (focusRow.getFirstFocusable('enabled') || |
302 focusRow.getFirstFocusable('remove-enterprise') || | 302 focusRow.getFirstFocusable('remove-enterprise') || |
303 focusRow.getFirstFocusable('website') || | 303 focusRow.getFirstFocusable('website') || |
304 focusRow.getFirstFocusable('details')).focus(); | 304 focusRow.getFirstFocusable('details')) |
| 305 .focus(); |
305 } | 306 } |
306 } | 307 } |
307 | 308 |
308 var idToOpenOptions = this.getOptionsQueryParam_(); | 309 var idToOpenOptions = this.getOptionsQueryParam_(); |
309 if (idToOpenOptions && $(idToOpenOptions)) | 310 if (idToOpenOptions && $(idToOpenOptions)) |
310 this.showEmbeddedExtensionOptions_(idToOpenOptions, true); | 311 this.showEmbeddedExtensionOptions_(idToOpenOptions, true); |
311 }, | 312 }, |
312 | 313 |
313 /** @return {number} The number of extensions being displayed. */ | 314 /** @return {number} The number of extensions being displayed. */ |
314 getNumExtensions: function() { | 315 getNumExtensions: function() { |
(...skipping 30 matching lines...) Expand all Loading... |
345 var seenIds = []; | 346 var seenIds = []; |
346 | 347 |
347 // Iterate over the extension data and add each item to the list. | 348 // Iterate over the extension data and add each item to the list. |
348 this.extensions_.forEach(function(extension) { | 349 this.extensions_.forEach(function(extension) { |
349 seenIds.push(extension.id); | 350 seenIds.push(extension.id); |
350 this.updateOrCreateWrapper_(extension); | 351 this.updateOrCreateWrapper_(extension); |
351 }, this); | 352 }, this); |
352 this.focusGrid_.ensureRowActive(); | 353 this.focusGrid_.ensureRowActive(); |
353 | 354 |
354 // Remove extensions that are no longer installed. | 355 // Remove extensions that are no longer installed. |
355 var wrappers = document.querySelectorAll( | 356 var wrappers = |
356 '.extension-list-item-wrapper[id]'); | 357 document.querySelectorAll('.extension-list-item-wrapper[id]'); |
357 Array.prototype.forEach.call(wrappers, function(wrapper) { | 358 Array.prototype.forEach.call(wrappers, function(wrapper) { |
358 if (seenIds.indexOf(wrapper.id) < 0) | 359 if (seenIds.indexOf(wrapper.id) < 0) |
359 this.removeWrapper_(wrapper); | 360 this.removeWrapper_(wrapper); |
360 }, this); | 361 }, this); |
361 }, | 362 }, |
362 | 363 |
363 /** | 364 /** |
364 * Removes the wrapper from the DOM and updates the focused element if | 365 * Removes the wrapper from the DOM and updates the focused element if |
365 * needed. | 366 * needed. |
366 * @param {!Element} wrapper | 367 * @param {!Element} wrapper |
367 * @private | 368 * @private |
368 */ | 369 */ |
369 removeWrapper_: function(wrapper) { | 370 removeWrapper_: function(wrapper) { |
370 // If focus is in the wrapper about to be removed, move it first. This | 371 // If focus is in the wrapper about to be removed, move it first. This |
371 // happens when clicking the trash can to remove an extension. | 372 // happens when clicking the trash can to remove an extension. |
372 if (wrapper.contains(document.activeElement)) { | 373 if (wrapper.contains(document.activeElement)) { |
373 var wrappers = document.querySelectorAll( | 374 var wrappers = |
374 '.extension-list-item-wrapper[id]'); | 375 document.querySelectorAll('.extension-list-item-wrapper[id]'); |
375 var index = Array.prototype.indexOf.call(wrappers, wrapper); | 376 var index = Array.prototype.indexOf.call(wrappers, wrapper); |
376 assert(index != -1); | 377 assert(index != -1); |
377 var focusableWrapper = wrappers[index + 1] || wrappers[index - 1]; | 378 var focusableWrapper = wrappers[index + 1] || wrappers[index - 1]; |
378 if (focusableWrapper) { | 379 if (focusableWrapper) { |
379 var newFocusRow = focusableWrapper.getFocusRow(); | 380 var newFocusRow = focusableWrapper.getFocusRow(); |
380 newFocusRow.getEquivalentElement(document.activeElement).focus(); | 381 newFocusRow.getEquivalentElement(document.activeElement).focus(); |
381 } | 382 } |
382 } | 383 } |
383 | 384 |
384 var focusRow = wrapper.getFocusRow(); | 385 var focusRow = wrapper.getFocusRow(); |
(...skipping 30 matching lines...) Expand all Loading... |
415 * wrapper list). | 416 * wrapper list). |
416 * @private | 417 * @private |
417 */ | 418 */ |
418 createWrapper_: function(extension, nextWrapper) { | 419 createWrapper_: function(extension, nextWrapper) { |
419 var wrapper = new ExtensionWrapper; | 420 var wrapper = new ExtensionWrapper; |
420 wrapper.id = extension.id; | 421 wrapper.id = extension.id; |
421 | 422 |
422 // The 'Permissions' link. | 423 // The 'Permissions' link. |
423 wrapper.setupColumn('details', '.permissions-link', 'click', function(e) { | 424 wrapper.setupColumn('details', '.permissions-link', 'click', function(e) { |
424 if (!this.permissionsPromptIsShowing_) { | 425 if (!this.permissionsPromptIsShowing_) { |
425 chrome.developerPrivate.showPermissionsDialog(extension.id, | 426 chrome.developerPrivate.showPermissionsDialog( |
426 function() { | 427 extension.id, function() { |
427 this.permissionsPromptIsShowing_ = false; | 428 this.permissionsPromptIsShowing_ = false; |
428 }.bind(this)); | 429 }.bind(this)); |
429 this.permissionsPromptIsShowing_ = true; | 430 this.permissionsPromptIsShowing_ = true; |
430 } | 431 } |
431 e.preventDefault(); | 432 e.preventDefault(); |
432 }); | 433 }); |
433 | 434 |
434 wrapper.setupColumn('options', '.options-button', 'click', function(e) { | 435 wrapper.setupColumn('options', '.options-button', 'click', function(e) { |
435 this.showEmbeddedExtensionOptions_(extension.id, false); | 436 this.showEmbeddedExtensionOptions_(extension.id, false); |
436 e.preventDefault(); | 437 e.preventDefault(); |
437 }.bind(this)); | 438 }.bind(this)); |
438 | 439 |
(...skipping 25 matching lines...) Expand all Loading... |
464 var extensionId = extension.id; | 465 var extensionId = extension.id; |
465 assert(this.extensions_.length > 0); | 466 assert(this.extensions_.length > 0); |
466 var newEx = this.extensions_.filter(function(e) { | 467 var newEx = this.extensions_.filter(function(e) { |
467 return e.id == extensionId; | 468 return e.id == extensionId; |
468 })[0]; | 469 })[0]; |
469 var errors = newEx.manifestErrors.concat(newEx.runtimeErrors); | 470 var errors = newEx.manifestErrors.concat(newEx.runtimeErrors); |
470 extensions.ExtensionErrorOverlay.getInstance().setErrorsAndShowOverlay( | 471 extensions.ExtensionErrorOverlay.getInstance().setErrorsAndShowOverlay( |
471 errors, extensionId, newEx.name); | 472 errors, extensionId, newEx.name); |
472 }.bind(this)); | 473 }.bind(this)); |
473 | 474 |
474 wrapper.setupColumn('suspiciousLearnMore', | 475 wrapper.setupColumn( |
475 '.suspicious-install-message .learn-more-link'); | 476 'suspiciousLearnMore', |
| 477 '.suspicious-install-message .learn-more-link'); |
476 | 478 |
477 // The path, if provided by unpacked extension. | 479 // The path, if provided by unpacked extension. |
478 wrapper.setupColumn('loadPath', '.load-path a:first-of-type', 'click', | 480 wrapper.setupColumn( |
479 function(e) { | 481 'loadPath', '.load-path a:first-of-type', 'click', function(e) { |
480 chrome.developerPrivate.showPath(extension.id); | 482 chrome.developerPrivate.showPath(extension.id); |
481 e.preventDefault(); | 483 e.preventDefault(); |
482 }); | 484 }); |
483 | 485 |
484 // The 'allow in incognito' checkbox. | 486 // The 'allow in incognito' checkbox. |
485 wrapper.setupColumn('incognito', '.incognito-control input', 'change', | 487 wrapper.setupColumn( |
486 function(e) { | 488 'incognito', '.incognito-control input', 'change', function(e) { |
487 var butterBar = wrapper.querySelector('.butter-bar'); | 489 var butterBar = wrapper.querySelector('.butter-bar'); |
488 var checked = e.target.checked; | 490 var checked = e.target.checked; |
489 butterBar.hidden = !checked || | 491 butterBar.hidden = |
490 extension.type == ExtensionType.HOSTED_APP; | 492 !checked || extension.type == ExtensionType.HOSTED_APP; |
491 chrome.developerPrivate.updateExtensionConfiguration({ | 493 chrome.developerPrivate.updateExtensionConfiguration( |
492 extensionId: extension.id, | 494 {extensionId: extension.id, incognitoAccess: e.target.checked}); |
493 incognitoAccess: e.target.checked | 495 }.bind(this)); |
494 }); | |
495 }.bind(this)); | |
496 | 496 |
497 // The 'collect errors' checkbox. This should only be visible if the | 497 // The 'collect errors' checkbox. This should only be visible if the |
498 // error console is enabled - we can detect this by the existence of the | 498 // error console is enabled - we can detect this by the existence of the |
499 // |errorCollectionEnabled| property. | 499 // |errorCollectionEnabled| property. |
500 wrapper.setupColumn('collectErrors', '.error-collection-control input', | 500 wrapper.setupColumn( |
501 'change', function(e) { | 501 'collectErrors', '.error-collection-control input', 'change', |
502 chrome.developerPrivate.updateExtensionConfiguration({ | 502 function(e) { |
503 extensionId: extension.id, | 503 chrome.developerPrivate.updateExtensionConfiguration( |
504 errorCollection: e.target.checked | 504 {extensionId: extension.id, errorCollection: e.target.checked}); |
505 }); | 505 }); |
506 }); | |
507 | 506 |
508 // The 'allow on all urls' checkbox. This should only be visible if | 507 // The 'allow on all urls' checkbox. This should only be visible if |
509 // active script restrictions are enabled. If they are not enabled, no | 508 // active script restrictions are enabled. If they are not enabled, no |
510 // extensions should want all urls. | 509 // extensions should want all urls. |
511 wrapper.setupColumn('allUrls', '.all-urls-control input', 'click', | 510 wrapper.setupColumn( |
512 function(e) { | 511 'allUrls', '.all-urls-control input', 'click', function(e) { |
513 chrome.developerPrivate.updateExtensionConfiguration({ | 512 chrome.developerPrivate.updateExtensionConfiguration( |
514 extensionId: extension.id, | 513 {extensionId: extension.id, runOnAllUrls: e.target.checked}); |
515 runOnAllUrls: e.target.checked | 514 }); |
516 }); | |
517 }); | |
518 | 515 |
519 // The 'allow file:// access' checkbox. | 516 // The 'allow file:// access' checkbox. |
520 wrapper.setupColumn('localUrls', '.file-access-control input', 'click', | 517 wrapper.setupColumn( |
521 function(e) { | 518 'localUrls', '.file-access-control input', 'click', function(e) { |
522 chrome.developerPrivate.updateExtensionConfiguration({ | 519 chrome.developerPrivate.updateExtensionConfiguration( |
523 extensionId: extension.id, | 520 {extensionId: extension.id, fileAccess: e.target.checked}); |
524 fileAccess: e.target.checked | 521 }); |
525 }); | |
526 }); | |
527 | 522 |
528 // The 'Reload' terminated link. | 523 // The 'Reload' terminated link. |
529 wrapper.setupColumn('terminatedReload', '.terminated-reload-link', | 524 wrapper.setupColumn( |
530 'click', function(e) { | 525 'terminatedReload', '.terminated-reload-link', 'click', function(e) { |
531 chrome.developerPrivate.reload(extension.id, {failQuietly: true}); | 526 chrome.developerPrivate.reload(extension.id, {failQuietly: true}); |
532 }); | 527 }); |
533 | 528 |
534 // The 'Repair' corrupted link. | 529 // The 'Repair' corrupted link. |
535 wrapper.setupColumn('repair', '.corrupted-repair-button', 'click', | 530 wrapper.setupColumn( |
536 function(e) { | 531 'repair', '.corrupted-repair-button', 'click', function(e) { |
537 chrome.developerPrivate.repairExtension(extension.id); | 532 chrome.developerPrivate.repairExtension(extension.id); |
538 }); | 533 }); |
539 | 534 |
540 // The 'Enabled' checkbox. | 535 // The 'Enabled' checkbox. |
541 wrapper.setupColumn('enabled', '.enable-checkbox input', 'click', | 536 wrapper.setupColumn( |
542 function(e) { | 537 'enabled', '.enable-checkbox input', 'click', function(e) { |
543 var checked = e.target.checked; | 538 var checked = e.target.checked; |
544 // TODO(devlin): What should we do if this fails? Ideally we want to | 539 // TODO(devlin): What should we do if this fails? Ideally we want to |
545 // show some kind of error or feedback to the user if this fails. | 540 // show some kind of error or feedback to the user if this fails. |
546 chrome.management.setEnabled(extension.id, checked); | 541 chrome.management.setEnabled(extension.id, checked); |
547 | 542 |
548 // This may seem counter-intuitive (to not set/clear the checkmark) | 543 // This may seem counter-intuitive (to not set/clear the checkmark) |
549 // but this page will be updated asynchronously if the extension | 544 // but this page will be updated asynchronously if the extension |
550 // becomes enabled/disabled. It also might not become enabled or | 545 // becomes enabled/disabled. It also might not become enabled or |
551 // disabled, because the user might e.g. get prompted when enabling | 546 // disabled, because the user might e.g. get prompted when enabling |
552 // and choose not to. | 547 // and choose not to. |
553 e.preventDefault(); | 548 e.preventDefault(); |
554 }); | 549 }); |
555 | 550 |
556 // 'Remove' button. | 551 // 'Remove' button. |
557 var trash = cloneTemplate('trash'); | 552 var trash = cloneTemplate('trash'); |
558 trash.title = loadTimeData.getString('extensionUninstall'); | 553 trash.title = loadTimeData.getString('extensionUninstall'); |
559 | 554 |
560 wrapper.querySelector('.enable-controls').appendChild(trash); | 555 wrapper.querySelector('.enable-controls').appendChild(trash); |
561 | 556 |
562 wrapper.setupColumn('remove-enterprise', '.trash', 'click', function(e) { | 557 wrapper.setupColumn('remove-enterprise', '.trash', 'click', function(e) { |
563 trash.classList.add('open'); | 558 trash.classList.add('open'); |
564 trash.classList.toggle('mouse-clicked', e.detail > 0); | 559 trash.classList.toggle('mouse-clicked', e.detail > 0); |
565 if (this.uninstallIsShowing_) | 560 if (this.uninstallIsShowing_) |
566 return; | 561 return; |
567 this.uninstallIsShowing_ = true; | 562 this.uninstallIsShowing_ = true; |
568 chrome.management.uninstall(extension.id, | 563 chrome.management.uninstall( |
569 {showConfirmDialog: true}, | 564 extension.id, {showConfirmDialog: true}, function() { |
570 function() { | 565 // TODO(devlin): What should we do if the uninstall fails? |
571 // TODO(devlin): What should we do if the uninstall fails? | 566 this.uninstallIsShowing_ = false; |
572 this.uninstallIsShowing_ = false; | |
573 | 567 |
574 if (trash.classList.contains('mouse-clicked')) | 568 if (trash.classList.contains('mouse-clicked')) |
575 trash.blur(); | 569 trash.blur(); |
576 | 570 |
577 if (chrome.runtime.lastError) { | 571 if (chrome.runtime.lastError) { |
578 // The uninstall failed (e.g. a cancel). Allow the trash to close. | 572 // The uninstall failed (e.g. a cancel). Allow the trash to |
579 trash.classList.remove('open'); | 573 // close. |
580 } else { | 574 trash.classList.remove('open'); |
581 // Leave the trash open if the uninstall succeded. Otherwise it can | 575 } else { |
582 // half-close right before it's removed when the DOM is modified. | 576 // Leave the trash open if the uninstall succeded. Otherwise it |
583 } | 577 // can half-close right before it's removed when the DOM is |
584 }.bind(this)); | 578 // modified. |
| 579 } |
| 580 }.bind(this)); |
585 }.bind(this)); | 581 }.bind(this)); |
586 | 582 |
587 // Maintain the order that nodes should be in when creating as well as | 583 // Maintain the order that nodes should be in when creating as well as |
588 // when adding only one new wrapper. | 584 // when adding only one new wrapper. |
589 this.insertBefore(wrapper, nextWrapper); | 585 this.insertBefore(wrapper, nextWrapper); |
590 this.updateWrapper_(extension, wrapper); | 586 this.updateWrapper_(extension, wrapper); |
591 | 587 |
592 var nextRow = this.focusGrid_.getRowForRoot(nextWrapper); // May be null. | 588 var nextRow = this.focusGrid_.getRowForRoot(nextWrapper); // May be null. |
593 this.focusGrid_.addRowBefore(wrapper.getFocusRow(), nextRow); | 589 this.focusGrid_.addRowBefore(wrapper.getFocusRow(), nextRow); |
594 }, | 590 }, |
595 | 591 |
596 /** | 592 /** |
597 * Updates an HTML element for the extension metadata given in |extension|. | 593 * Updates an HTML element for the extension metadata given in |extension|. |
598 * @param {!chrome.developerPrivate.ExtensionInfo} extension A dictionary of | 594 * @param {!chrome.developerPrivate.ExtensionInfo} extension A dictionary of |
599 * extension metadata. | 595 * extension metadata. |
600 * @param {!Element} wrapper The extension wrapper element to update. | 596 * @param {!Element} wrapper The extension wrapper element to update. |
601 * @private | 597 * @private |
602 */ | 598 */ |
603 updateWrapper_: function(extension, wrapper) { | 599 updateWrapper_: function(extension, wrapper) { |
604 var isActive = | 600 var isActive = |
605 extension.state == chrome.developerPrivate.ExtensionState.ENABLED; | 601 extension.state == chrome.developerPrivate.ExtensionState.ENABLED; |
606 wrapper.classList.toggle('inactive-extension', !isActive); | 602 wrapper.classList.toggle('inactive-extension', !isActive); |
607 wrapper.classList.remove('controlled', 'may-not-remove'); | 603 wrapper.classList.remove('controlled', 'may-not-remove'); |
608 | 604 |
609 if (extension.controlledInfo) { | 605 if (extension.controlledInfo) { |
610 wrapper.classList.add('controlled'); | 606 wrapper.classList.add('controlled'); |
611 } else if (!extension.userMayModify || | 607 } else if ( |
612 extension.mustRemainInstalled || | 608 !extension.userMayModify || extension.mustRemainInstalled || |
613 extension.dependentExtensions.length > 0) { | 609 extension.dependentExtensions.length > 0) { |
614 wrapper.classList.add('may-not-remove'); | 610 wrapper.classList.add('may-not-remove'); |
615 } | 611 } |
616 | 612 |
617 var item = wrapper.querySelector('.extension-list-item'); | 613 var item = wrapper.querySelector('.extension-list-item'); |
618 item.style.backgroundImage = 'url(' + extension.iconUrl + ')'; | 614 item.style.backgroundImage = 'url(' + extension.iconUrl + ')'; |
619 | 615 |
620 this.setText_(wrapper, '.extension-title', extension.name); | 616 this.setText_(wrapper, '.extension-title', extension.name); |
621 this.setText_(wrapper, '.extension-version', extension.version); | 617 this.setText_(wrapper, '.extension-version', extension.version); |
622 this.setText_(wrapper, '.location-text', extension.locationText || ''); | 618 this.setText_(wrapper, '.location-text', extension.locationText || ''); |
623 this.setText_(wrapper, '.blacklist-text', extension.blacklistText || ''); | 619 this.setText_(wrapper, '.blacklist-text', extension.blacklistText || ''); |
624 this.setText_(wrapper, '.extension-description', extension.description); | 620 this.setText_(wrapper, '.extension-description', extension.description); |
625 | 621 |
626 // The 'allow in incognito' checkbox. | 622 // The 'allow in incognito' checkbox. |
627 this.updateVisibility_(wrapper, '.incognito-control', | 623 this.updateVisibility_( |
628 isActive && this.incognitoAvailable_, | 624 wrapper, '.incognito-control', isActive && this.incognitoAvailable_, |
629 function(item) { | 625 function(item) { |
630 var incognito = item.querySelector('input'); | 626 var incognito = item.querySelector('input'); |
631 incognito.disabled = !extension.incognitoAccess.isEnabled; | 627 incognito.disabled = !extension.incognitoAccess.isEnabled; |
632 incognito.checked = extension.incognitoAccess.isActive; | 628 incognito.checked = extension.incognitoAccess.isActive; |
633 }); | 629 }); |
634 var showButterBar = isActive && | 630 var showButterBar = isActive && extension.incognitoAccess.isActive && |
635 extension.incognitoAccess.isActive && | 631 extension.type != ExtensionType.HOSTED_APP; |
636 extension.type != ExtensionType.HOSTED_APP; | |
637 // The 'allow in incognito' butter bar. | 632 // The 'allow in incognito' butter bar. |
638 this.updateVisibility_(wrapper, '.butter-bar', showButterBar); | 633 this.updateVisibility_(wrapper, '.butter-bar', showButterBar); |
639 | 634 |
640 // The 'collect errors' checkbox. This should only be visible if the | 635 // The 'collect errors' checkbox. This should only be visible if the |
641 // error console is enabled - we can detect this by the existence of the | 636 // error console is enabled - we can detect this by the existence of the |
642 // |errorCollectionEnabled| property. | 637 // |errorCollectionEnabled| property. |
643 this.updateVisibility_( | 638 this.updateVisibility_( |
644 wrapper, '.error-collection-control', | 639 wrapper, '.error-collection-control', |
645 isActive && extension.errorCollection.isEnabled, | 640 isActive && extension.errorCollection.isEnabled, function(item) { |
646 function(item) { | 641 item.querySelector('input').checked = |
647 item.querySelector('input').checked = | 642 extension.errorCollection.isActive; |
648 extension.errorCollection.isActive; | 643 }); |
649 }); | |
650 | 644 |
651 // The 'allow on all urls' checkbox. This should only be visible if | 645 // The 'allow on all urls' checkbox. This should only be visible if |
652 // active script restrictions are enabled. If they are not enabled, no | 646 // active script restrictions are enabled. If they are not enabled, no |
653 // extensions should want all urls. | 647 // extensions should want all urls. |
654 this.updateVisibility_( | 648 this.updateVisibility_( |
655 wrapper, '.all-urls-control', | 649 wrapper, '.all-urls-control', |
656 isActive && extension.runOnAllUrls.isEnabled, | 650 isActive && extension.runOnAllUrls.isEnabled, function(item) { |
657 function(item) { | 651 item.querySelector('input').checked = |
658 item.querySelector('input').checked = extension.runOnAllUrls.isActive; | 652 extension.runOnAllUrls.isActive; |
659 }); | 653 }); |
660 | 654 |
661 // The 'allow file:// access' checkbox. | 655 // The 'allow file:// access' checkbox. |
662 this.updateVisibility_(wrapper, '.file-access-control', | 656 this.updateVisibility_( |
663 isActive && extension.fileAccess.isEnabled, | 657 wrapper, '.file-access-control', |
664 function(item) { | 658 isActive && extension.fileAccess.isEnabled, function(item) { |
665 item.querySelector('input').checked = extension.fileAccess.isActive; | 659 item.querySelector('input').checked = extension.fileAccess.isActive; |
666 }); | 660 }); |
667 | 661 |
668 // The 'Options' button or link, depending on its behaviour. | 662 // The 'Options' button or link, depending on its behaviour. |
669 var optionsEnabled = isActive && !!extension.optionsPage; | 663 var optionsEnabled = isActive && !!extension.optionsPage; |
670 this.updateVisibility_(wrapper, '.options-link', optionsEnabled && | 664 this.updateVisibility_( |
671 extension.optionsPage.openInTab); | 665 wrapper, '.options-link', |
672 this.updateVisibility_(wrapper, '.options-button', optionsEnabled && | 666 optionsEnabled && extension.optionsPage.openInTab); |
673 !extension.optionsPage.openInTab); | 667 this.updateVisibility_( |
| 668 wrapper, '.options-button', |
| 669 optionsEnabled && !extension.optionsPage.openInTab); |
674 | 670 |
675 // The 'View in Web Store/View Web Site' link. | 671 // The 'View in Web Store/View Web Site' link. |
676 var siteLinkEnabled = !!extension.homePage.url && | 672 var siteLinkEnabled = |
677 !this.enableAppInfoDialog_; | 673 !!extension.homePage.url && !this.enableAppInfoDialog_; |
678 this.updateVisibility_(wrapper, '.site-link', siteLinkEnabled, | 674 this.updateVisibility_( |
679 function(item) { | 675 wrapper, '.site-link', siteLinkEnabled, function(item) { |
680 item.href = extension.homePage.url; | 676 item.href = extension.homePage.url; |
681 item.textContent = loadTimeData.getString( | 677 item.textContent = loadTimeData.getString( |
682 extension.homePage.specified ? 'extensionSettingsVisitWebsite' : | 678 extension.homePage.specified ? |
683 'extensionSettingsVisitWebStore'); | 679 'extensionSettingsVisitWebsite' : |
684 }); | 680 'extensionSettingsVisitWebStore'); |
| 681 }); |
685 | 682 |
686 var isUnpacked = | 683 var isUnpacked = |
687 extension.location == chrome.developerPrivate.Location.UNPACKED; | 684 extension.location == chrome.developerPrivate.Location.UNPACKED; |
688 // The 'Reload' link. | 685 // The 'Reload' link. |
689 this.updateVisibility_(wrapper, '.reload-link', isActive && isUnpacked); | 686 this.updateVisibility_(wrapper, '.reload-link', isActive && isUnpacked); |
690 | 687 |
691 // The 'Launch' link. | 688 // The 'Launch' link. |
692 this.updateVisibility_( | 689 this.updateVisibility_( |
693 wrapper, '.launch-link', | 690 wrapper, '.launch-link', |
694 isUnpacked && extension.type == ExtensionType.PLATFORM_APP && | 691 isUnpacked && extension.type == ExtensionType.PLATFORM_APP && |
695 isActive); | 692 isActive); |
696 | 693 |
697 // The 'Errors' link. | 694 // The 'Errors' link. |
698 var hasErrors = extension.runtimeErrors.length > 0 || | 695 var hasErrors = extension.runtimeErrors.length > 0 || |
699 extension.manifestErrors.length > 0; | 696 extension.manifestErrors.length > 0; |
700 this.updateVisibility_(wrapper, '.errors-link', hasErrors, | 697 this.updateVisibility_( |
701 function(item) { | 698 wrapper, '.errors-link', hasErrors, function(item) { |
702 var Level = chrome.developerPrivate.ErrorLevel; | 699 var Level = chrome.developerPrivate.ErrorLevel; |
703 | 700 |
704 var map = {}; | 701 var map = {}; |
705 map[Level.LOG] = {weight: 0, name: 'extension-error-info-icon'}; | 702 map[Level.LOG] = {weight: 0, name: 'extension-error-info-icon'}; |
706 map[Level.WARN] = {weight: 1, name: 'extension-error-warning-icon'}; | 703 map[Level.WARN] = {weight: 1, name: 'extension-error-warning-icon'}; |
707 map[Level.ERROR] = {weight: 2, name: 'extension-error-fatal-icon'}; | 704 map[Level.ERROR] = {weight: 2, name: 'extension-error-fatal-icon'}; |
708 | 705 |
709 // Find the highest severity of all the errors; manifest errors all have | 706 // Find the highest severity of all the errors; manifest errors all |
710 // a 'warning' level severity. | 707 // have a 'warning' level severity. |
711 var highestSeverity = extension.runtimeErrors.reduce( | 708 var highestSeverity = |
712 function(prev, error) { | 709 extension.runtimeErrors.reduce(function(prev, error) { |
713 return map[error.severity].weight > map[prev].weight ? | 710 return map[error.severity].weight > map[prev].weight ? |
714 error.severity : prev; | 711 error.severity : |
715 }, extension.manifestErrors.length ? Level.WARN : Level.LOG); | 712 prev; |
| 713 }, extension.manifestErrors.length ? Level.WARN : Level.LOG); |
716 | 714 |
717 // Adjust the class on the icon. | 715 // Adjust the class on the icon. |
718 var icon = item.querySelector('.extension-error-icon'); | 716 var icon = item.querySelector('.extension-error-icon'); |
719 // TODO(hcarmona): Populate alt text with a proper description since | 717 // TODO(hcarmona): Populate alt text with a proper description since |
720 // this icon conveys the severity of the error. (info, warning, fatal). | 718 // this icon conveys the severity of the error. (info, warning, |
721 icon.alt = ''; | 719 // fatal). |
722 icon.className = 'extension-error-icon'; // Remove other classes. | 720 icon.alt = ''; |
723 icon.classList.add(map[highestSeverity].name); | 721 icon.className = 'extension-error-icon'; // Remove other classes. |
724 }); | 722 icon.classList.add(map[highestSeverity].name); |
| 723 }); |
725 | 724 |
726 // The 'Reload' terminated link. | 725 // The 'Reload' terminated link. |
727 var isTerminated = | 726 var isTerminated = |
728 extension.state == chrome.developerPrivate.ExtensionState.TERMINATED; | 727 extension.state == chrome.developerPrivate.ExtensionState.TERMINATED; |
729 this.updateVisibility_(wrapper, '.terminated-reload-link', isTerminated); | 728 this.updateVisibility_(wrapper, '.terminated-reload-link', isTerminated); |
730 | 729 |
731 // The 'Repair' corrupted link. | 730 // The 'Repair' corrupted link. |
732 var canRepair = !isTerminated && | 731 var canRepair = !isTerminated && |
733 extension.disableReasons.corruptInstall && | 732 extension.disableReasons.corruptInstall && |
734 extension.location == | 733 extension.location == chrome.developerPrivate.Location.FROM_STORE; |
735 chrome.developerPrivate.Location.FROM_STORE; | |
736 this.updateVisibility_(wrapper, '.corrupted-repair-button', canRepair); | 734 this.updateVisibility_(wrapper, '.corrupted-repair-button', canRepair); |
737 | 735 |
738 // The 'Enabled' checkbox. | 736 // The 'Enabled' checkbox. |
739 var isOK = !isTerminated && !canRepair; | 737 var isOK = !isTerminated && !canRepair; |
740 this.updateVisibility_(wrapper, '.enable-checkbox', isOK, function(item) { | 738 this.updateVisibility_(wrapper, '.enable-checkbox', isOK, function(item) { |
741 var enableCheckboxDisabled = | 739 var enableCheckboxDisabled = !extension.userMayModify || |
742 !extension.userMayModify || | |
743 extension.disableReasons.suspiciousInstall || | 740 extension.disableReasons.suspiciousInstall || |
744 extension.disableReasons.corruptInstall || | 741 extension.disableReasons.corruptInstall || |
745 extension.disableReasons.updateRequired || | 742 extension.disableReasons.updateRequired || |
746 extension.dependentExtensions.length > 0 || | 743 extension.dependentExtensions.length > 0 || |
747 extension.state == | 744 extension.state == |
748 chrome.developerPrivate.ExtensionState.BLACKLISTED; | 745 chrome.developerPrivate.ExtensionState.BLACKLISTED; |
749 item.querySelector('input').disabled = enableCheckboxDisabled; | 746 item.querySelector('input').disabled = enableCheckboxDisabled; |
750 item.querySelector('input').checked = isActive; | 747 item.querySelector('input').checked = isActive; |
751 }); | 748 }); |
752 | 749 |
(...skipping 29 matching lines...) Expand all Loading... |
782 controlNode.removeChild(indicator); | 779 controlNode.removeChild(indicator); |
783 } | 780 } |
784 | 781 |
785 // Developer mode //////////////////////////////////////////////////////// | 782 // Developer mode //////////////////////////////////////////////////////// |
786 | 783 |
787 // First we have the id. | 784 // First we have the id. |
788 var idLabel = wrapper.querySelector('.extension-id'); | 785 var idLabel = wrapper.querySelector('.extension-id'); |
789 idLabel.textContent = ' ' + extension.id; | 786 idLabel.textContent = ' ' + extension.id; |
790 | 787 |
791 // Then the path, if provided by unpacked extension. | 788 // Then the path, if provided by unpacked extension. |
792 this.updateVisibility_(wrapper, '.load-path', isUnpacked, | 789 this.updateVisibility_(wrapper, '.load-path', isUnpacked, function(item) { |
793 function(item) { | |
794 item.querySelector('a:first-of-type').textContent = | 790 item.querySelector('a:first-of-type').textContent = |
795 ' ' + extension.prettifiedPath; | 791 ' ' + extension.prettifiedPath; |
796 }); | 792 }); |
797 | 793 |
798 // Then the 'managed, cannot uninstall/disable' message. | 794 // Then the 'managed, cannot uninstall/disable' message. |
799 // We would like to hide managed installed message since this | 795 // We would like to hide managed installed message since this |
800 // extension is disabled. | 796 // extension is disabled. |
801 var isRequired = | 797 var isRequired = |
802 !extension.userMayModify || extension.mustRemainInstalled; | 798 !extension.userMayModify || extension.mustRemainInstalled; |
803 this.updateVisibility_(wrapper, '.managed-message', isRequired && | 799 this.updateVisibility_( |
804 !extension.disableReasons.updateRequired); | 800 wrapper, '.managed-message', |
| 801 isRequired && !extension.disableReasons.updateRequired); |
805 | 802 |
806 // Then the 'This isn't from the webstore, looks suspicious' message. | 803 // Then the 'This isn't from the webstore, looks suspicious' message. |
807 var isSuspicious = extension.disableReasons.suspiciousInstall; | 804 var isSuspicious = extension.disableReasons.suspiciousInstall; |
808 this.updateVisibility_(wrapper, '.suspicious-install-message', | 805 this.updateVisibility_( |
809 !isRequired && isSuspicious); | 806 wrapper, '.suspicious-install-message', !isRequired && isSuspicious); |
810 | 807 |
811 // Then the 'This is a corrupt extension' message. | 808 // Then the 'This is a corrupt extension' message. |
812 this.updateVisibility_(wrapper, '.corrupt-install-message', !isRequired && | 809 this.updateVisibility_( |
813 extension.disableReasons.corruptInstall); | 810 wrapper, '.corrupt-install-message', |
| 811 !isRequired && extension.disableReasons.corruptInstall); |
814 | 812 |
815 // Then the 'An update required by enterprise policy' message. Note that | 813 // Then the 'An update required by enterprise policy' message. Note that |
816 // a force-installed extension might be disabled due to being outdated | 814 // a force-installed extension might be disabled due to being outdated |
817 // as well. | 815 // as well. |
818 this.updateVisibility_(wrapper, '.update-required-message', | 816 this.updateVisibility_( |
819 extension.disableReasons.updateRequired); | 817 wrapper, '.update-required-message', |
| 818 extension.disableReasons.updateRequired); |
820 | 819 |
821 // The 'following extensions depend on this extension' list. | 820 // The 'following extensions depend on this extension' list. |
822 var hasDependents = extension.dependentExtensions.length > 0; | 821 var hasDependents = extension.dependentExtensions.length > 0; |
823 wrapper.classList.toggle('developer-extras', hasDependents); | 822 wrapper.classList.toggle('developer-extras', hasDependents); |
824 this.updateVisibility_(wrapper, '.dependent-extensions-message', | 823 this.updateVisibility_( |
825 hasDependents, function(item) { | 824 wrapper, '.dependent-extensions-message', hasDependents, |
826 var dependentList = item.querySelector('ul'); | 825 function(item) { |
827 dependentList.textContent = ''; | 826 var dependentList = item.querySelector('ul'); |
828 extension.dependentExtensions.forEach(function(dependentExtension) { | 827 dependentList.textContent = ''; |
829 var depNode = cloneTemplate('dependent-list-item'); | 828 extension.dependentExtensions.forEach(function(dependentExtension) { |
830 depNode.querySelector('.dep-extension-title').textContent = | 829 var depNode = cloneTemplate('dependent-list-item'); |
831 dependentExtension.name; | 830 depNode.querySelector('.dep-extension-title').textContent = |
832 depNode.querySelector('.dep-extension-id').textContent = | 831 dependentExtension.name; |
833 dependentExtension.id; | 832 depNode.querySelector('.dep-extension-id').textContent = |
834 dependentList.appendChild(depNode); | 833 dependentExtension.id; |
835 }, this); | 834 dependentList.appendChild(depNode); |
836 }.bind(this)); | 835 }, this); |
| 836 }.bind(this)); |
837 | 837 |
838 // The active views. | 838 // The active views. |
839 this.updateVisibility_(wrapper, '.active-views', | 839 this.updateVisibility_( |
840 extension.views.length > 0, function(item) { | 840 wrapper, '.active-views', extension.views.length > 0, function(item) { |
841 var link = item.querySelector('a'); | 841 var link = item.querySelector('a'); |
842 | 842 |
843 // Link needs to be an only child before the list is updated. | 843 // Link needs to be an only child before the list is updated. |
844 while (link.nextElementSibling) | 844 while (link.nextElementSibling) |
845 item.removeChild(link.nextElementSibling); | 845 item.removeChild(link.nextElementSibling); |
846 | 846 |
847 // Link needs to be cleaned up if it was used before. | 847 // Link needs to be cleaned up if it was used before. |
848 link.textContent = ''; | 848 link.textContent = ''; |
849 if (link.clickHandler) | 849 if (link.clickHandler) |
850 link.removeEventListener('click', link.clickHandler); | 850 link.removeEventListener('click', link.clickHandler); |
851 | 851 |
852 extension.views.forEach(function(view, i) { | 852 extension.views.forEach(function(view, i) { |
853 if (view.type == chrome.developerPrivate.ViewType.EXTENSION_DIALOG || | 853 if (view.type == |
854 view.type == chrome.developerPrivate.ViewType.EXTENSION_POPUP) { | 854 chrome.developerPrivate.ViewType.EXTENSION_DIALOG || |
855 return; | 855 view.type == |
856 } | 856 chrome.developerPrivate.ViewType.EXTENSION_POPUP) { |
857 var displayName; | 857 return; |
858 if (view.url.startsWith('chrome-extension://')) { | 858 } |
859 var pathOffset = 'chrome-extension://'.length + 32 + 1; | 859 var displayName; |
860 displayName = view.url.substring(pathOffset); | 860 if (view.url.startsWith('chrome-extension://')) { |
861 if (displayName == '_generated_background_page.html') | 861 var pathOffset = 'chrome-extension://'.length + 32 + 1; |
862 displayName = loadTimeData.getString('backgroundPage'); | 862 displayName = view.url.substring(pathOffset); |
863 } else { | 863 if (displayName == '_generated_background_page.html') |
864 displayName = view.url; | 864 displayName = loadTimeData.getString('backgroundPage'); |
865 } | 865 } else { |
866 var label = displayName + | 866 displayName = view.url; |
867 (view.incognito ? | 867 } |
868 ' ' + loadTimeData.getString('viewIncognito') : '') + | 868 var label = displayName + |
869 (view.renderProcessId == -1 ? | 869 (view.incognito ? |
870 ' ' + loadTimeData.getString('viewInactive') : '') + | 870 ' ' + loadTimeData.getString('viewIncognito') : |
871 (view.isIframe ? | 871 '') + |
872 ' ' + loadTimeData.getString('viewIframe') : ''); | 872 (view.renderProcessId == -1 ? |
873 link.textContent = label; | 873 ' ' + loadTimeData.getString('viewInactive') : |
874 link.clickHandler = function(e) { | 874 '') + |
875 chrome.developerPrivate.openDevTools({ | 875 (view.isIframe ? ' ' + loadTimeData.getString('viewIframe') : |
876 extensionId: extension.id, | 876 ''); |
877 renderProcessId: view.renderProcessId, | 877 link.textContent = label; |
878 renderViewId: view.renderViewId, | 878 link.clickHandler = function(e) { |
879 incognito: view.incognito | 879 chrome.developerPrivate.openDevTools({ |
| 880 extensionId: extension.id, |
| 881 renderProcessId: view.renderProcessId, |
| 882 renderViewId: view.renderViewId, |
| 883 incognito: view.incognito |
| 884 }); |
| 885 }; |
| 886 link.addEventListener('click', link.clickHandler); |
| 887 |
| 888 if (i < extension.views.length - 1) { |
| 889 link = link.cloneNode(true); |
| 890 item.appendChild(link); |
| 891 } |
| 892 |
| 893 wrapper.setupColumn('activeView', '.active-views a:last-of-type'); |
880 }); | 894 }); |
881 }; | 895 }); |
882 link.addEventListener('click', link.clickHandler); | |
883 | |
884 if (i < extension.views.length - 1) { | |
885 link = link.cloneNode(true); | |
886 item.appendChild(link); | |
887 } | |
888 | |
889 wrapper.setupColumn('activeView', '.active-views a:last-of-type'); | |
890 }); | |
891 }); | |
892 | 896 |
893 // The extension warnings (describing runtime issues). | 897 // The extension warnings (describing runtime issues). |
894 this.updateVisibility_(wrapper, '.extension-warnings', | 898 this.updateVisibility_( |
895 extension.runtimeWarnings.length > 0, | 899 wrapper, '.extension-warnings', extension.runtimeWarnings.length > 0, |
896 function(item) { | 900 function(item) { |
897 var warningList = item.querySelector('ul'); | 901 var warningList = item.querySelector('ul'); |
898 warningList.textContent = ''; | 902 warningList.textContent = ''; |
899 extension.runtimeWarnings.forEach(function(warning) { | 903 extension.runtimeWarnings.forEach(function(warning) { |
900 var li = document.createElement('li'); | 904 var li = document.createElement('li'); |
901 warningList.appendChild(li).innerText = warning; | 905 warningList.appendChild(li).innerText = warning; |
902 }); | 906 }); |
903 }); | 907 }); |
904 | 908 |
905 // Install warnings. | 909 // Install warnings. |
906 this.updateVisibility_(wrapper, '.install-warnings', | 910 this.updateVisibility_( |
907 extension.installWarnings.length > 0, | 911 wrapper, '.install-warnings', extension.installWarnings.length > 0, |
908 function(item) { | 912 function(item) { |
909 var installWarningList = item.querySelector('ul'); | 913 var installWarningList = item.querySelector('ul'); |
910 installWarningList.textContent = ''; | 914 installWarningList.textContent = ''; |
911 if (extension.installWarnings) { | 915 if (extension.installWarnings) { |
912 extension.installWarnings.forEach(function(warning) { | 916 extension.installWarnings.forEach(function(warning) { |
913 var li = document.createElement('li'); | 917 var li = document.createElement('li'); |
914 li.innerText = warning; | 918 li.innerText = warning; |
915 installWarningList.appendChild(li); | 919 installWarningList.appendChild(li); |
| 920 }); |
| 921 } |
916 }); | 922 }); |
917 } | |
918 }); | |
919 | 923 |
920 if (location.hash.substr(1) == extension.id) { | 924 if (location.hash.substr(1) == extension.id) { |
921 // Scroll beneath the fixed header so that the extension is not | 925 // Scroll beneath the fixed header so that the extension is not |
922 // obscured. | 926 // obscured. |
923 var topScroll = wrapper.offsetTop - $('page-header').offsetHeight; | 927 var topScroll = wrapper.offsetTop - $('page-header').offsetHeight; |
924 var pad = parseInt(window.getComputedStyle(wrapper).marginTop, 10); | 928 var pad = parseInt(window.getComputedStyle(wrapper).marginTop, 10); |
925 if (!isNaN(pad)) | 929 if (!isNaN(pad)) |
926 topScroll -= pad / 2; | 930 topScroll -= pad / 2; |
927 setScrollTopForDocument(document, topScroll); | 931 setScrollTopForDocument(document, topScroll); |
928 } | 932 } |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
964 * @param {boolean} scroll Whether the page should scroll to the extension | 968 * @param {boolean} scroll Whether the page should scroll to the extension |
965 * @private | 969 * @private |
966 */ | 970 */ |
967 showEmbeddedExtensionOptions_: function(extensionId, scroll) { | 971 showEmbeddedExtensionOptions_: function(extensionId, scroll) { |
968 if (this.optionsShown_) | 972 if (this.optionsShown_) |
969 return; | 973 return; |
970 | 974 |
971 // Get the extension from the given id. | 975 // Get the extension from the given id. |
972 var extension = this.extensions_.filter(function(extension) { | 976 var extension = this.extensions_.filter(function(extension) { |
973 return extension.state == | 977 return extension.state == |
974 chrome.developerPrivate.ExtensionState.ENABLED && | 978 chrome.developerPrivate.ExtensionState.ENABLED && |
975 extension.id == extensionId; | 979 extension.id == extensionId; |
976 })[0]; | 980 })[0]; |
977 | 981 |
978 if (!extension) | 982 if (!extension) |
979 return; | 983 return; |
980 | 984 |
981 if (scroll) | 985 if (scroll) |
982 this.scrollToWrapper_(extensionId); | 986 this.scrollToWrapper_(extensionId); |
983 | 987 |
984 // Add the options query string. Corner case: the 'options' query string | 988 // Add the options query string. Corner case: the 'options' query string |
985 // will clobber the 'id' query string if the options link is clicked when | 989 // will clobber the 'id' query string if the options link is clicked when |
986 // 'id' is in the URL, or if both query strings are in the URL. | 990 // 'id' is in the URL, or if both query strings are in the URL. |
987 window.history.replaceState({}, '', '/?options=' + extensionId); | 991 window.history.replaceState({}, '', '/?options=' + extensionId); |
988 | 992 |
989 var overlay = extensions.ExtensionOptionsOverlay.getInstance(); | 993 var overlay = extensions.ExtensionOptionsOverlay.getInstance(); |
990 var shownCallback = function() { | 994 var shownCallback = function() { |
991 // This overlay doesn't get focused automatically as <extensionoptions> | 995 // This overlay doesn't get focused automatically as <extensionoptions> |
992 // is created after the overlay is shown. | 996 // is created after the overlay is shown. |
993 if (cr.ui.FocusOutlineManager.forDocument(document).visible) | 997 if (cr.ui.FocusOutlineManager.forDocument(document).visible) |
994 overlay.setInitialFocus(); | 998 overlay.setInitialFocus(); |
995 }; | 999 }; |
996 overlay.setExtensionAndShow(extensionId, extension.name, | 1000 overlay.setExtensionAndShow( |
997 extension.iconUrl, shownCallback); | 1001 extensionId, extension.name, extension.iconUrl, shownCallback); |
998 this.optionsShown_ = true; | 1002 this.optionsShown_ = true; |
999 | 1003 |
1000 var self = this; | 1004 var self = this; |
1001 $('overlay').addEventListener('cancelOverlay', function f() { | 1005 $('overlay').addEventListener('cancelOverlay', function f() { |
1002 self.optionsShown_ = false; | 1006 self.optionsShown_ = false; |
1003 $('overlay').removeEventListener('cancelOverlay', f); | 1007 $('overlay').removeEventListener('cancelOverlay', f); |
1004 | 1008 |
1005 // Remove the options query string. | 1009 // Remove the options query string. |
1006 window.history.replaceState({}, '', '/'); | 1010 window.history.replaceState({}, '', '/'); |
1007 }); | 1011 }); |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1055 this.createWrapper_(extension, nextExt ? $(nextExt.id) : null); | 1059 this.createWrapper_(extension, nextExt ? $(nextExt.id) : null); |
1056 } | 1060 } |
1057 } | 1061 } |
1058 }; | 1062 }; |
1059 | 1063 |
1060 return { | 1064 return { |
1061 ExtensionList: ExtensionList, | 1065 ExtensionList: ExtensionList, |
1062 ExtensionListDelegate: ExtensionListDelegate | 1066 ExtensionListDelegate: ExtensionListDelegate |
1063 }; | 1067 }; |
1064 }); | 1068 }); |
OLD | NEW |