33 * Licensed under the MIT License. See License.txt in the project root for license information.
44 *--------------------------------------------------------------------------------------------*/
55
6- import { distinct , flatten } from 'vs/base/common/arrays' ;
6+ import { distinct } from 'vs/base/common/arrays' ;
77import { CancellationToken } from 'vs/base/common/cancellation' ;
88import { canceled , getErrorMessage , isPromiseCanceledError } from 'vs/base/common/errors' ;
99import { getOrDefault } from 'vs/base/common/objects' ;
1010import { IPager } from 'vs/base/common/paging' ;
1111import { isWeb } from 'vs/base/common/platform' ;
12- import { equalsIgnoreCase } from 'vs/base/common/strings' ;
1312import { URI } from 'vs/base/common/uri' ;
1413import { IHeaders , IRequestContext , IRequestOptions } from 'vs/base/parts/request/common/request' ;
1514import { IEnvironmentService } from 'vs/platform/environment/common/environment' ;
16- import { arePlatformsValid , CURRENT_TARGET_PLATFORM , DefaultIconPath , IExtensionGalleryService , IExtensionIdentifier , IExtensionIdentifierWithVersion , IGalleryExtension , IGalleryExtensionAsset , IGalleryExtensionAssets , IGalleryExtensionVersion , InstallOperation , IQueryOptions , IReportedExtension , isIExtensionIdentifier , ITranslation , SortBy , SortOrder , StatisticType , TargetPlatform , toTargetPlatform , WEB_EXTENSION_TAG } from 'vs/platform/extensionManagement/common/extensionManagement' ;
15+ import { CURRENT_TARGET_PLATFORM , DefaultIconPath , IExtensionGalleryService , IExtensionIdentifier , IExtensionIdentifierWithVersion , IGalleryExtension , IGalleryExtensionAsset , IGalleryExtensionAssets , IGalleryExtensionVersion , InstallOperation , IQueryOptions , IReportedExtension , isIExtensionIdentifier , ITranslation , SortBy , SortOrder , StatisticType , TargetPlatform , toTargetPlatform , WEB_EXTENSION_TAG } from 'vs/platform/extensionManagement/common/extensionManagement' ;
1716import { adoptToGalleryExtensionId , areSameExtensions , getGalleryExtensionId , getGalleryExtensionTelemetryData } from 'vs/platform/extensionManagement/common/extensionManagementUtil' ;
1817import { IExtensionManifest } from 'vs/platform/extensions/common/extensions' ;
1918import { isEngineValid } from 'vs/platform/extensions/common/extensionValidator' ;
@@ -184,17 +183,6 @@ type GalleryServiceQueryEvent = QueryTelemetryData & {
184183 readonly count ?: string ;
185184} ;
186185
187- const ANY_TARGET_PLATFORMS = Object . freeze ( [
188- TargetPlatform . WIN32_X64 ,
189- TargetPlatform . WIN32_IA32 ,
190- TargetPlatform . WIN32_ARM64 ,
191- TargetPlatform . LINUX_X64 ,
192- TargetPlatform . LINUX_ARM64 ,
193- TargetPlatform . LINUX_ARMHF ,
194- TargetPlatform . DARWIN_X64 ,
195- TargetPlatform . DARWIN_ARM64 ,
196- ] ) ;
197-
198186class Query {
199187
200188 constructor ( private state = DefaultQueryState ) { }
@@ -324,11 +312,75 @@ function getIsPreview(flags: string): boolean {
324312 return flags . indexOf ( 'preview' ) !== - 1 ;
325313}
326314
327- function getTargetPlatforms ( version : IRawGalleryExtensionVersion ) : TargetPlatform [ ] {
328- return version . targetPlatform && ! equalsIgnoreCase ( version . targetPlatform , 'universal' ) ? [ toTargetPlatform ( version . targetPlatform ) ] : [ ...ANY_TARGET_PLATFORMS ] ;
315+ function getTargetPlatform ( version : IRawGalleryExtensionVersion ) : TargetPlatform {
316+ return version . targetPlatform ? toTargetPlatform ( version . targetPlatform ) : TargetPlatform . UNIVERSAL ;
317+ }
318+
319+ function getAllTargetPlatforms ( rawGalleryExtension : IRawGalleryExtension ) : TargetPlatform [ ] {
320+ const allTargetPlatforms = distinct ( rawGalleryExtension . versions . map ( getTargetPlatform ) ) ;
321+
322+ // Is a web extension only if it has WEB_EXTENSION_TAG
323+ const isWebExtension = ! ! rawGalleryExtension . tags ?. includes ( WEB_EXTENSION_TAG ) ;
324+
325+ // Include Web Target Platform only if it is a web extension
326+ const webTargetPlatformIndex = allTargetPlatforms . indexOf ( TargetPlatform . WEB ) ;
327+ if ( isWebExtension ) {
328+ if ( webTargetPlatformIndex === - 1 ) {
329+ // Web extension but does not has web target platform -> add it
330+ allTargetPlatforms . push ( TargetPlatform . WEB ) ;
331+ }
332+ } else {
333+ if ( webTargetPlatformIndex !== - 1 ) {
334+ // Not a web extension but has web target platform -> remove it
335+ allTargetPlatforms . splice ( webTargetPlatformIndex , 1 ) ;
336+ }
337+ }
338+
339+ return allTargetPlatforms ;
340+ }
341+
342+ function isNotWebExtensionInWebTargetPlatform ( allTargetPlatforms : TargetPlatform [ ] , productTargetPlatform : TargetPlatform ) : boolean {
343+ // Not a web extension in web target platform
344+ return productTargetPlatform === TargetPlatform . WEB && ! allTargetPlatforms . includes ( TargetPlatform . WEB ) ;
345+ }
346+
347+ function isTargetPlatformCompatible ( extensionTargetPlatform : TargetPlatform , allTargetPlatforms : TargetPlatform [ ] , productTargetPlatform : TargetPlatform ) : boolean {
348+ // Not compatible when extension is not a web extension in web target platform
349+ if ( isNotWebExtensionInWebTargetPlatform ( allTargetPlatforms , productTargetPlatform ) ) {
350+ return false ;
351+ }
352+
353+ // Compatible when extension target platform is universal
354+ if ( extensionTargetPlatform === TargetPlatform . UNIVERSAL ) {
355+ return true ;
356+ }
357+
358+ // Not compatible when extension target platform is unknown
359+ if ( extensionTargetPlatform === TargetPlatform . UNKNOWN ) {
360+ return false ;
361+ }
362+
363+ // Compatible when extension and product target platforms matches
364+ if ( extensionTargetPlatform === productTargetPlatform ) {
365+ return true ;
366+ }
367+
368+ // Fallback
369+ switch ( productTargetPlatform ) {
370+ case TargetPlatform . WIN32_X64 : return extensionTargetPlatform === TargetPlatform . WIN32_IA32 ;
371+ case TargetPlatform . WIN32_ARM64 : return extensionTargetPlatform === TargetPlatform . WIN32_IA32 ;
372+ default : return false ;
373+ }
374+ }
375+
376+ function toExtensionWithLatestVersion ( galleryExtension : IRawGalleryExtension , index : number , query : Query , querySource ?: string ) : IGalleryExtension {
377+ const allTargetPlatforms = getAllTargetPlatforms ( galleryExtension ) ;
378+ let latestVersion = galleryExtension . versions [ 0 ] ;
379+ latestVersion = galleryExtension . versions . find ( version => version . version === latestVersion . version && isTargetPlatformCompatible ( getTargetPlatform ( version ) , allTargetPlatforms , CURRENT_TARGET_PLATFORM ) ) || latestVersion ;
380+ return toExtension ( galleryExtension , latestVersion , allTargetPlatforms , index , query , querySource ) ;
329381}
330382
331- function toExtension ( galleryExtension : IRawGalleryExtension , version : IRawGalleryExtensionVersion , index : number , query : Query , querySource ?: string ) : IGalleryExtension {
383+ function toExtension ( galleryExtension : IRawGalleryExtension , version : IRawGalleryExtensionVersion , allTargetPlatforms : TargetPlatform [ ] , index : number , query : Query , querySource ?: string ) : IGalleryExtension {
332384 const assets = < IGalleryExtensionAssets > {
333385 manifest : getVersionAsset ( version , AssetType . Manifest ) ,
334386 readme : getVersionAsset ( version , AssetType . Details ) ,
@@ -340,16 +392,6 @@ function toExtension(galleryExtension: IRawGalleryExtension, version: IRawGaller
340392 coreTranslations : getCoreTranslationAssets ( version )
341393 } ;
342394
343- const allTargetPlatforms = distinct ( flatten ( galleryExtension . versions . map ( getTargetPlatforms ) ) ) ;
344- if ( galleryExtension . tags ?. includes ( WEB_EXTENSION_TAG ) && ! allTargetPlatforms . includes ( TargetPlatform . WEB ) ) {
345- allTargetPlatforms . push ( TargetPlatform . WEB ) ;
346- }
347-
348- const targetPlatforms = getTargetPlatforms ( version ) ;
349- if ( allTargetPlatforms . includes ( TargetPlatform . WEB ) && ! targetPlatforms . includes ( TargetPlatform . WEB ) ) {
350- targetPlatforms . push ( TargetPlatform . WEB ) ;
351- }
352-
353395 return {
354396 identifier : {
355397 id : getGalleryExtensionId ( galleryExtension . publisher . publisherName , galleryExtension . extensionName ) ,
@@ -376,7 +418,7 @@ function toExtension(galleryExtension: IRawGalleryExtension, version: IRawGaller
376418 extensionPack : getExtensions ( version , PropertyType . ExtensionPack ) ,
377419 engine : getEngine ( version ) ,
378420 localizedLanguages : getLocalizedLanguages ( version ) ,
379- targetPlatforms ,
421+ targetPlatform : getTargetPlatform ( version ) ,
380422 } ,
381423 preview : getIsPreview ( galleryExtension . flags ) ,
382424 /* __GDPR__FRAGMENT__
@@ -392,11 +434,6 @@ function toExtension(galleryExtension: IRawGalleryExtension, version: IRawGaller
392434 } ;
393435}
394436
395- function getLatestVersion ( versions : IRawGalleryExtensionVersion [ ] ) : IRawGalleryExtensionVersion {
396- const latestVersion = versions [ 0 ] ;
397- return versions . find ( v => v . version === latestVersion . version && arePlatformsValid ( getTargetPlatforms ( v ) , CURRENT_TARGET_PLATFORM ) ) || latestVersion ;
398- }
399-
400437interface IRawExtensionsReport {
401438 malicious : string [ ] ;
402439 slow : string [ ] ;
@@ -457,10 +494,10 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
457494 if ( version ) {
458495 const versionAsset = galleryExtension . versions . find ( v => v . version === version ) ;
459496 if ( versionAsset ) {
460- result . push ( toExtension ( galleryExtension , versionAsset , index , query ) ) ;
497+ result . push ( toExtension ( galleryExtension , versionAsset , getAllTargetPlatforms ( galleryExtension ) , index , query ) ) ;
461498 }
462499 } else {
463- result . push ( toExtension ( galleryExtension , getLatestVersion ( galleryExtension . versions ) , index , query ) ) ;
500+ result . push ( toExtensionWithLatestVersion ( galleryExtension , index , query ) ) ;
464501 }
465502 }
466503
@@ -469,8 +506,13 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
469506
470507 async getCompatibleExtension ( arg1 : IExtensionIdentifier | IGalleryExtension , targetPlatform : TargetPlatform ) : Promise < IGalleryExtension | null > {
471508 const extension : IGalleryExtension | null = isIExtensionIdentifier ( arg1 ) ? null : arg1 ;
472- if ( extension && extension . properties . engine && this . isCompatible ( extension . properties . engine , extension . properties . targetPlatforms , targetPlatform ) ) {
473- return extension ;
509+ if ( extension ) {
510+ if ( isNotWebExtensionInWebTargetPlatform ( extension . allTargetPlatforms , targetPlatform ) ) {
511+ return null ;
512+ }
513+ if ( await this . isExtensionCompatible ( extension , targetPlatform ) ) {
514+ return extension ;
515+ }
474516 }
475517 const { id, uuid } = extension ? extension . identifier : < IExtensionIdentifier > arg1 ;
476518 let query = new Query ( )
@@ -490,14 +532,33 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
490532 return null ;
491533 }
492534
493- const rawVersion = await this . getLastValidExtensionVersion ( rawExtension , rawExtension . versions , targetPlatform ) ;
494- if ( rawVersion ) {
495- return toExtension ( rawExtension , rawVersion , 0 , query ) ;
535+ const allTargetPlatforms = getAllTargetPlatforms ( rawExtension ) ;
536+ if ( isNotWebExtensionInWebTargetPlatform ( allTargetPlatforms , targetPlatform ) ) {
537+ return null ;
496538 }
539+
540+ for ( let rawVersion of rawExtension . versions ) {
541+ // set engine property if does not exist
542+ if ( ! getEngine ( rawVersion ) ) {
543+ const engine = await this . getEngine ( rawVersion ) ;
544+ rawVersion = {
545+ ...rawVersion ,
546+ properties : [ ...( rawVersion . properties || [ ] ) , { key : PropertyType . Engine , value : engine } ]
547+ } ;
548+ }
549+ if ( await this . isRawExtensionVersionCompatible ( rawVersion , allTargetPlatforms , targetPlatform ) ) {
550+ return toExtension ( rawExtension , rawVersion , allTargetPlatforms , 0 , query ) ;
551+ }
552+ }
553+
497554 return null ;
498555 }
499556
500557 async isExtensionCompatible ( extension : IGalleryExtension , targetPlatform : TargetPlatform ) : Promise < boolean > {
558+ if ( ! isTargetPlatformCompatible ( extension . properties . targetPlatform , extension . allTargetPlatforms , targetPlatform ) ) {
559+ return false ;
560+ }
561+
501562 let engine = extension . properties . engine ;
502563 if ( ! engine ) {
503564 const manifest = await this . getManifest ( extension , CancellationToken . None ) ;
@@ -506,7 +567,16 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
506567 }
507568 engine = manifest . engines . vscode ;
508569 }
509- return this . isCompatible ( engine , extension . properties . targetPlatforms , targetPlatform ) ;
570+ return isEngineValid ( engine , this . productService . version , this . productService . date ) ;
571+ }
572+
573+ private async isRawExtensionVersionCompatible ( rawExtensionVersion : IRawGalleryExtensionVersion , allTargetPlatforms : TargetPlatform [ ] , targetPlatform : TargetPlatform ) : Promise < boolean > {
574+ if ( ! isTargetPlatformCompatible ( getTargetPlatform ( rawExtensionVersion ) , allTargetPlatforms , targetPlatform ) ) {
575+ return false ;
576+ }
577+
578+ const engine = await this . getEngine ( rawExtensionVersion ) ;
579+ return isEngineValid ( engine , this . productService . version , this . productService . date ) ;
510580 }
511581
512582 query ( token : CancellationToken ) : Promise < IPager < IGalleryExtension > > ;
@@ -571,14 +641,14 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
571641 }
572642
573643 const { galleryExtensions, total } = await this . queryGallery ( query , token ) ;
574- const extensions = galleryExtensions . map ( ( e , index ) => toExtension ( e , getLatestVersion ( e . versions ) , index , query , options . source ) ) ;
644+ const extensions = galleryExtensions . map ( ( e , index ) => toExtensionWithLatestVersion ( e , index , query , options . source ) ) ;
575645 const getPage = async ( pageIndex : number , ct : CancellationToken ) => {
576646 if ( ct . isCancellationRequested ) {
577647 throw canceled ( ) ;
578648 }
579649 const nextPageQuery = query . withPage ( pageIndex + 1 ) ;
580650 const { galleryExtensions } = await this . queryGallery ( nextPageQuery , ct ) ;
581- return galleryExtensions . map ( ( e , index ) => toExtension ( e , getLatestVersion ( e . versions ) , index , nextPageQuery , options . source ) ) ;
651+ return galleryExtensions . map ( ( e , index ) => toExtensionWithLatestVersion ( e , index , nextPageQuery , options . source ) ) ;
582652 } ;
583653
584654 return { firstPage : extensions , total, pageSize : query . pageSize , getPage } as IPager < IGalleryExtension > ;
@@ -711,6 +781,16 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
711781 return null ;
712782 }
713783
784+ private async getManifestFromRawExtensionVersion ( rawExtensionVersion : IRawGalleryExtensionVersion , token : CancellationToken ) : Promise < IExtensionManifest | null > {
785+ const manifestAsset = getVersionAsset ( rawExtensionVersion , AssetType . Manifest ) ;
786+ if ( ! manifestAsset ) {
787+ throw new Error ( 'Manifest was not found' ) ;
788+ }
789+ const headers = { 'Accept-Encoding' : 'gzip' } ;
790+ const context = await this . getAsset ( manifestAsset , { headers } ) ;
791+ return await asJson < IExtensionManifest > ( context ) ;
792+ }
793+
714794 async getCoreTranslation ( extension : IGalleryExtension , languageId : string ) : Promise < ITranslation | null > {
715795 const asset = extension . assets . coreTranslations . filter ( t => t [ 0 ] === languageId . toUpperCase ( ) ) [ 0 ] ;
716796 if ( asset ) {
@@ -742,17 +822,23 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
742822 query = query . withFilter ( FilterType . ExtensionName , extension . identifier . id ) ;
743823 }
744824
745- const result : IGalleryExtensionVersion [ ] = [ ] ;
746825 const { galleryExtensions } = await this . queryGallery ( query , CancellationToken . None ) ;
747- if ( galleryExtensions . length ) {
748- await Promise . all ( galleryExtensions [ 0 ] . versions . map ( async v => {
749- try {
750- const engine = await this . getEngine ( v ) ;
751- if ( this . isCompatible ( engine , getTargetPlatforms ( v ) , targetPlatform ) ) {
752- result . push ( { version : v ! . version , date : v ! . lastUpdated } ) ;
753- }
754- } catch ( error ) { /* Ignore error and skip version */ }
755- } ) ) ;
826+ if ( ! galleryExtensions . length ) {
827+ return [ ] ;
828+ }
829+
830+ const allTargetPlatforms = getAllTargetPlatforms ( galleryExtensions [ 0 ] ) ;
831+ if ( isNotWebExtensionInWebTargetPlatform ( allTargetPlatforms , targetPlatform ) ) {
832+ return [ ] ;
833+ }
834+
835+ const result : IGalleryExtensionVersion [ ] = [ ] ;
836+ for ( const version of galleryExtensions [ 0 ] . versions ) {
837+ try {
838+ if ( await this . isRawExtensionVersionCompatible ( version , allTargetPlatforms , targetPlatform ) ) {
839+ result . push ( { version : version ! . version , date : version ! . lastUpdated } ) ;
840+ }
841+ } catch ( error ) { /* Ignore error and skip version */ }
756842 }
757843 return result ;
758844 }
@@ -795,67 +881,16 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
795881 }
796882 }
797883
798- private async getLastValidExtensionVersion ( extension : IRawGalleryExtension , versions : IRawGalleryExtensionVersion [ ] , targetPlatform : TargetPlatform ) : Promise < IRawGalleryExtensionVersion | null > {
799- const version = this . getLastValidExtensionVersionFromProperties ( extension , versions , targetPlatform ) ;
800- if ( version ) {
801- return version ;
802- }
803- return this . getLastValidExtensionVersionRecursively ( extension , versions , targetPlatform ) ;
804- }
805-
806- private getLastValidExtensionVersionFromProperties ( extension : IRawGalleryExtension , versions : IRawGalleryExtensionVersion [ ] , targetPlatform : TargetPlatform ) : IRawGalleryExtensionVersion | null {
807- for ( const version of versions ) {
808- const engine = getEngine ( version ) ;
809- if ( ! engine ) {
810- return null ;
811- }
812- if ( this . isCompatible ( engine , getTargetPlatforms ( version ) , targetPlatform ) ) {
813- return version ;
884+ private async getEngine ( rawExtensionVersion : IRawGalleryExtensionVersion ) : Promise < string > {
885+ let engine = getEngine ( rawExtensionVersion ) ;
886+ if ( ! engine ) {
887+ const manifest = await this . getManifestFromRawExtensionVersion ( rawExtensionVersion , CancellationToken . None ) ;
888+ if ( ! manifest ) {
889+ throw new Error ( 'Manifest was not found' ) ;
814890 }
891+ engine = manifest . engines . vscode ;
815892 }
816- return null ;
817- }
818-
819- private async getEngine ( version : IRawGalleryExtensionVersion ) : Promise < string > {
820- const engine = getEngine ( version ) ;
821- if ( engine ) {
822- return engine ;
823- }
824-
825- const manifestAsset = getVersionAsset ( version , AssetType . Manifest ) ;
826- if ( ! manifestAsset ) {
827- throw new Error ( 'Manifest was not found' ) ;
828- }
829-
830- const headers = { 'Accept-Encoding' : 'gzip' } ;
831- const context = await this . getAsset ( manifestAsset , { headers } ) ;
832- const manifest = await asJson < IExtensionManifest > ( context ) ;
833- if ( manifest ) {
834- return manifest . engines . vscode ;
835- }
836-
837- throw new Error ( 'Error while reading manifest' ) ;
838- }
839-
840- private async getLastValidExtensionVersionRecursively ( extension : IRawGalleryExtension , versions : IRawGalleryExtensionVersion [ ] , targetPlatform : TargetPlatform ) : Promise < IRawGalleryExtensionVersion | null > {
841- if ( ! versions . length ) {
842- return null ;
843- }
844-
845- const version = versions [ 0 ] ;
846- const engine = await this . getEngine ( version ) ;
847- if ( ! this . isCompatible ( engine , getTargetPlatforms ( version ) , targetPlatform ) ) {
848- return this . getLastValidExtensionVersionRecursively ( extension , versions . slice ( 1 ) , targetPlatform ) ;
849- }
850-
851- return {
852- ...version ,
853- properties : [ ...( version . properties || [ ] ) , { key : PropertyType . Engine , value : engine } ]
854- } ;
855- }
856-
857- private isCompatible ( engine : string , targetPlatforms : TargetPlatform [ ] , targetPlatform : TargetPlatform ) : boolean {
858- return isEngineValid ( engine , this . productService . version , this . productService . date ) && arePlatformsValid ( targetPlatforms , targetPlatform ) ;
893+ return engine ;
859894 }
860895
861896 async getExtensionsReport ( ) : Promise < IReportedExtension [ ] > {
0 commit comments