@@ -67,6 +67,7 @@ define(function (require, exports, module) {
6767 * Extension status constants.
6868 */
6969 var ENABLED = "enabled" ,
70+ DISABLED = "disabled" ,
7071 START_FAILED = "startFailed" ;
7172
7273 /**
@@ -103,8 +104,9 @@ define(function (require, exports, module) {
103104 /**
104105 * Requested changes to the installed extensions.
105106 */
106- var _idsToRemove = [ ] ,
107- _idsToUpdate = [ ] ;
107+ var _idsToRemove = { } ,
108+ _idsToUpdate = { } ,
109+ _idsToDisable = { } ;
108110
109111 PreferencesManager . stateManager . definePreference ( FOLDER_AUTOINSTALL , "object" , undefined ) ;
110112
@@ -186,8 +188,9 @@ define(function (require, exports, module) {
186188 */
187189 function _reset ( ) {
188190 exports . extensions = extensions = { } ;
189- _idsToRemove = [ ] ;
190- _idsToUpdate = [ ] ;
191+ _idsToRemove = { } ;
192+ _idsToUpdate = { } ;
193+ _idsToDisable = { } ;
191194 }
192195
193196 /**
@@ -240,8 +243,9 @@ define(function (require, exports, module) {
240243 * @param {string } path The local path of the loaded extension's folder.
241244 */
242245 function _handleExtensionLoad ( e , path ) {
243- function setData ( id , metadata ) {
246+ function setData ( metadata ) {
244247 var locationType ,
248+ id = metadata . name ,
245249 userExtensionPath = ExtensionLoader . getUserExtensionPath ( ) ;
246250 if ( path . indexOf ( userExtensionPath ) === 0 ) {
247251 locationType = LOCATION_USER ;
@@ -265,27 +269,33 @@ define(function (require, exports, module) {
265269 metadata : metadata ,
266270 path : path ,
267271 locationType : locationType ,
268- status : ( e . type === "loadFailed" ? START_FAILED : ENABLED )
272+ status : ( e . type === "loadFailed" ? START_FAILED : ( e . type === "disabled" ? DISABLED : ENABLED ) )
269273 } ;
270274
271275 synchronizeEntry ( id ) ;
272276 loadTheme ( id ) ;
273277 exports . trigger ( "statusChange" , id ) ;
274278 }
279+
280+ function deduceMetadata ( ) {
281+ var match = path . match ( / \/ ( [ ^ \/ ] + ) $ / ) ,
282+ name = ( match && match [ 1 ] ) || path ,
283+ metadata = { name : name , title : name } ;
284+ return metadata ;
285+ }
275286
276- ExtensionUtils . loadPackageJson ( path )
287+ ExtensionUtils . loadMetadata ( path )
277288 . done ( function ( metadata ) {
278- setData ( metadata . name , metadata ) ;
289+ setData ( metadata ) ;
279290 } )
280- . fail ( function ( ) {
291+ . fail ( function ( disabled ) {
281292 // If there's no package.json, this is a legacy extension. It was successfully loaded,
282293 // but we don't have an official ID or metadata for it, so we just create an id and
283294 // "title" for it (which is the last segment of its pathname)
284295 // and record that it's enabled.
285- var match = path . match ( / \/ ( [ ^ \/ ] + ) $ / ) ,
286- name = ( match && match [ 1 ] ) || path ,
287- metadata = { name : name , title : name } ;
288- setData ( name , metadata ) ;
296+ var metadata = deduceMetadata ( ) ;
297+ metadata . disabled = disabled ;
298+ setData ( metadata ) ;
289299 } ) ;
290300 }
291301
@@ -398,6 +408,58 @@ define(function (require, exports, module) {
398408 }
399409 return result . promise ( ) ;
400410 }
411+
412+ /**
413+ * @private
414+ *
415+ * Disables or enables the installed extensions.
416+ *
417+ * @param {string } id The id of the extension to disable or enable.
418+ * @param {boolean } enable A boolean indicating whether to enable or disable.
419+ * @return {$.Promise } A promise that's resolved when the extension action is
420+ * completed or rejected with an error that prevents the action from completion.
421+ */
422+ function _enableOrDisable ( id , enable ) {
423+ var result = new $ . Deferred ( ) ,
424+ extension = extensions [ id ] ;
425+ if ( extension && extension . installInfo ) {
426+ Package [ ( enable ? "enable" : "disable" ) ] ( extension . installInfo . path )
427+ . done ( function ( ) {
428+ extension . installInfo . status = enable ? ENABLED : DISABLED ;
429+ extension . installInfo . metadata . disabled = ! enable ;
430+ result . resolve ( ) ;
431+ exports . trigger ( "statusChange" , id ) ;
432+ } )
433+ . fail ( function ( err ) {
434+ result . reject ( err ) ;
435+ } ) ;
436+ } else {
437+ result . reject ( StringUtils . format ( Strings . EXTENSION_NOT_INSTALLED , id ) ) ;
438+ }
439+ return result . promise ( ) ;
440+ }
441+
442+ /**
443+ * Disables the installed extension with the given id.
444+ *
445+ * @param {string } id The id of the extension to disable.
446+ * @return {$.Promise } A promise that's resolved when the extenion is disabled or
447+ * rejected with an error that prevented the disabling.
448+ */
449+ function disable ( id ) {
450+ return _enableOrDisable ( id , false ) ;
451+ }
452+
453+ /**
454+ * Enables the installed extension with the given id.
455+ *
456+ * @param {string } id The id of the extension to enable.
457+ * @return {$.Promise } A promise that's resolved when the extenion is enabled or
458+ * rejected with an error that prevented the enabling.
459+ */
460+ function enable ( id ) {
461+ return _enableOrDisable ( id , true ) ;
462+ }
401463
402464 /**
403465 * Updates an installed extension with the given package file.
@@ -452,7 +514,7 @@ define(function (require, exports, module) {
452514 }
453515 exports . trigger ( "statusChange" , id ) ;
454516 }
455-
517+
456518 /**
457519 * Returns true if an extension is marked for removal.
458520 * @param {string } id The id of the extension to check.
@@ -461,7 +523,7 @@ define(function (require, exports, module) {
461523 function isMarkedForRemoval ( id ) {
462524 return ! ! ( _idsToRemove [ id ] ) ;
463525 }
464-
526+
465527 /**
466528 * Returns true if there are any extensions marked for removal.
467529 * @return {boolean } true if there are extensions to remove
@@ -470,6 +532,46 @@ define(function (require, exports, module) {
470532 return Object . keys ( _idsToRemove ) . length > 0 ;
471533 }
472534
535+ /**
536+ * Marks an extension for disabling later, or unmarks an extension previously marked.
537+ *
538+ * @param {string } id The id of the extension
539+ * @param {boolean } mark Whether to mark or unmark the extension.
540+ */
541+ function markForDisabling ( id , mark ) {
542+ if ( mark ) {
543+ _idsToDisable [ id ] = true ;
544+ } else {
545+ delete _idsToDisable [ id ] ;
546+ }
547+ exports . trigger ( "statusChange" , id ) ;
548+ }
549+
550+ /**
551+ * Returns true if an extension is mark for disabling.
552+ *
553+ * @param {string } id The id of the extension to check.
554+ * @return {boolean } true if it's been mark for disabling, false otherwise.
555+ */
556+ function isMarkedForDisabling ( id ) {
557+ return ! ! ( _idsToDisable [ id ] ) ;
558+ }
559+
560+ /**
561+ * Returns true if there are any extensions marked for disabling.
562+ * @return {boolean } true if there are extensions to disable
563+ */
564+ function hasExtensionsToDisable ( ) {
565+ return Object . keys ( _idsToDisable ) . length > 0 ;
566+ }
567+
568+ /**
569+ * Unmarks all the extensions that have been marked for disabling.
570+ */
571+ function unmarkAllForDisabling ( ) {
572+ _idsToDisable = { } ;
573+ }
574+
473575 /**
474576 * If a downloaded package appears to be an update, mark the extension for update.
475577 * If an extension was previously marked for removal, marking for update will
@@ -542,6 +644,25 @@ define(function (require, exports, module) {
542644 }
543645 ) ;
544646 }
647+
648+ /**
649+ * Disables extensions marked for disabling.
650+ *
651+ * If the return promise is rejected, the argument will contain an array of objects. Each
652+ * element is an object identifying the extension failed with "item" property set to the
653+ * extension id which has failed to be disabled and "error" property set to the error.
654+ *
655+ * @return {$.Promise } A promise that's resolved when all extensions marked for disabling are
656+ * disabled or rejected if one or more extensions can't be disabled.
657+ */
658+ function disableMarkedExtensions ( ) {
659+ return Async . doInParallel_aggregateErrors (
660+ Object . keys ( _idsToDisable ) ,
661+ function ( id ) {
662+ return disable ( id ) ;
663+ }
664+ ) ;
665+ }
545666
546667 /**
547668 * Updates extensions previously marked for update.
@@ -764,7 +885,8 @@ define(function (require, exports, module) {
764885 // Listen to extension load and loadFailed events
765886 ExtensionLoader
766887 . on ( "load" , _handleExtensionLoad )
767- . on ( "loadFailed" , _handleExtensionLoad ) ;
888+ . on ( "loadFailed" , _handleExtensionLoad )
889+ . on ( "disabled" , _handleExtensionLoad ) ;
768890
769891
770892 EventDispatcher . makeEventDispatcher ( exports ) ;
@@ -775,24 +897,32 @@ define(function (require, exports, module) {
775897 exports . getExtensionURL = getExtensionURL ;
776898 exports . remove = remove ;
777899 exports . update = update ;
900+ exports . disable = disable ;
901+ exports . enable = enable ;
778902 exports . extensions = extensions ;
779903 exports . cleanupUpdates = cleanupUpdates ;
780904 exports . markForRemoval = markForRemoval ;
781905 exports . isMarkedForRemoval = isMarkedForRemoval ;
782906 exports . unmarkAllForRemoval = unmarkAllForRemoval ;
783907 exports . hasExtensionsToRemove = hasExtensionsToRemove ;
908+ exports . markForDisabling = markForDisabling ;
909+ exports . isMarkedForDisabling = isMarkedForDisabling ;
910+ exports . unmarkAllForDisabling = unmarkAllForDisabling ;
911+ exports . hasExtensionsToDisable = hasExtensionsToDisable ;
784912 exports . updateFromDownload = updateFromDownload ;
785913 exports . removeUpdate = removeUpdate ;
786914 exports . isMarkedForUpdate = isMarkedForUpdate ;
787915 exports . hasExtensionsToUpdate = hasExtensionsToUpdate ;
788916 exports . removeMarkedExtensions = removeMarkedExtensions ;
917+ exports . disableMarkedExtensions = disableMarkedExtensions ;
789918 exports . updateExtensions = updateExtensions ;
790919 exports . getAvailableUpdates = getAvailableUpdates ;
791920 exports . cleanAvailableUpdates = cleanAvailableUpdates ;
792921
793922 exports . hasDownloadedRegistry = false ;
794923
795924 exports . ENABLED = ENABLED ;
925+ exports . DISABLED = DISABLED ;
796926 exports . START_FAILED = START_FAILED ;
797927
798928 exports . LOCATION_DEFAULT = LOCATION_DEFAULT ;
0 commit comments