diff --git a/toolkit/components/extensions/ExtensionProcessScript.sys.mjs b/toolkit/components/extensions/ExtensionProcessScript.sys.mjs index 19c3371b4547..fad5791a128e 100644 --- a/toolkit/components/extensions/ExtensionProcessScript.sys.mjs +++ b/toolkit/components/extensions/ExtensionProcessScript.sys.mjs @@ -226,6 +226,23 @@ sys . mjs " +ExtensionUserScriptsContent +: +" +resource +: +/ +/ +gre +/ +modules +/ +ExtensionUserScriptsContent +. +sys +. +mjs +" ExtensionWorkerChild : " @@ -610,6 +627,20 @@ addMessageListener " Extension : +UpdateUserScriptWorlds +" +this +) +; +Services +. +cpmm +. +addMessageListener +( +" +Extension +: UpdatePermissions " this @@ -1898,6 +1929,55 @@ case " Extension : +UpdateUserScriptWorlds +" +: +{ +let +policy += +WebExtensionPolicy +. +getByID +( +data +. +id +) +; +if +( +policy +) +{ +lazy +. +ExtensionUserScriptsContent +. +updateWorldConfig +( +extensions +. +get +( +policy +) +data +. +reset +data +. +update +) +; +} +break +; +} +case +" +Extension +: UpdatePermissions " : diff --git a/toolkit/components/extensions/ExtensionUserScripts.sys.mjs b/toolkit/components/extensions/ExtensionUserScripts.sys.mjs index 9ea7b2671c72..7e4fd5e5e2af 100644 --- a/toolkit/components/extensions/ExtensionUserScripts.sys.mjs +++ b/toolkit/components/extensions/ExtensionUserScripts.sys.mjs @@ -237,6 +237,41 @@ RegisteredUserScript * * * +WorldProperties +as +received +from +userScripts +. +configureWorld +. +* +* +typedef +{ +object +} +WorldProperties +* +property +{ +string +} +worldId +* +property +{ +string +| +null +} +csp +* +/ +/ +* +* +* User scripts are @@ -934,6 +969,15 @@ Map ( ) ; +this +. +worldConfigs += +new +Map +( +) +; } runReadTask ( @@ -1067,6 +1111,16 @@ initializeFromDatabase ( ) { +let +worldConfigInitPromise += +this +. +# +initializeWorldsFromDatabase +( +) +; const dbScriptEntries = @@ -1098,6 +1152,35 @@ _script_0 ; / / +Init +worlds +before +registering +scripts +to +make +sure +that +if +the +scripts +/ +/ +are +injected +that +thet +have +the +expected +world +configuration +. +await +worldConfigInitPromise +; +/ +/ The database returns @@ -2263,6 +2346,382 @@ scriptId ) ; } +async +# +initializeWorldsFromDatabase +( +) +{ +const +dbWorldEntries += +await +store +. +getAllEntries +( +{ +this +. +extension +. +id +} +/ +_world_ +/ +{ +this +. +extension +. +id +} +/ +_world_0 +) +; +const +allProperties += +dbWorldEntries +. +map +( +( +[ +properties +] +) += +> +properties +) +; +for +( +let +properties +of +allProperties +) +{ +this +. +worldConfigs +. +set +( +properties +. +worldId +properties +) +; +} +this +. +extension +. +setSharedData +( +" +userScriptsWorldConfigs +" +this +. +worldConfigs +) +; +if +( +this +. +extension +. +shouldSendSharedData +( +) +) +{ +/ +/ +Broadcast +changes +to +existing +processes +if +we +are +past +startup +. +await +this +. +extension +. +broadcast +( +" +Extension +: +UpdateUserScriptWorlds +" +{ +id +: +this +. +extension +. +id +reset +: +null +update +: +allProperties +} +) +; +} +} +/ +* +* +* +param +{ +WorldProperties +} +properties +* +/ +async +configureWorld +( +properties +) +{ +const +worldId += +properties +. +worldId +; +this +. +worldConfigs +. +set +( +worldId +properties +) +; +this +. +extension +. +setSharedData +( +" +userScriptsWorldConfigs +" +this +. +worldConfigs +) +; +await +this +. +extension +. +broadcast +( +" +Extension +: +UpdateUserScriptWorlds +" +{ +id +: +this +. +extension +. +id +reset +: +null +update +: +[ +properties +] +} +) +; +await +store +. +writeMany +( +[ +[ +this +. +# +makeDbKey +( +worldId +" +_world_ +" +) +properties +] +] +) +; +} +/ +* +* +* +param +{ +string +} +worldId +* +/ +async +resetWorldConfiguration +( +worldId +) +{ +if +( +! +this +. +worldConfigs +. +delete +( +worldId +) +) +{ +return +; +} +this +. +extension +. +setSharedData +( +" +userScriptsWorldConfigs +" +this +. +worldConfigs +) +; +await +this +. +extension +. +broadcast +( +" +Extension +: +UpdateUserScriptWorlds +" +{ +id +: +this +. +extension +. +id +reset +: +[ +worldId +] +update +: +null +} +) +; +await +store +. +writeMany +( +[ +[ +this +. +# +makeDbKey +( +worldId +" +_world_ +" +) +null +] +] +) +; +} +/ +* +* +returns +{ +WorldProperties +[ +] +} +* +/ +async +getWorldConfigurations +( +) +{ +return +Array +. +from +( +this +. +worldConfigs +. +values +( +) +) +; +} / / userScripts diff --git a/toolkit/components/extensions/ExtensionUserScriptsContent.sys.mjs b/toolkit/components/extensions/ExtensionUserScriptsContent.sys.mjs index 7bdd071dba22..a8870f54a452 100644 --- a/toolkit/components/extensions/ExtensionUserScriptsContent.sys.mjs +++ b/toolkit/components/extensions/ExtensionUserScriptsContent.sys.mjs @@ -173,11 +173,151 @@ mjs const { DefaultMap +DefaultWeakMap } = ExtensionUtils ; class +WorldConfigHolder +{ +/ +* +* +type +{ +Map +< +ExtensionChild +WorldConfigHolder +> +} +* +/ +static +allMaps += +new +DefaultWeakMap +( +ext += +> +new +WorldConfigHolder +( +ext +) +) +; +constructor +( +extension +) +{ +this +. +defaultCSP += +extension +. +policy +. +baseCSP +; +this +. +configs += +new +Map +( +extension +. +getSharedData +( +" +userScriptsWorldConfigs +" +) +) +; +} +configureWorld +( +properties +) +{ +this +. +configs +. +set +( +properties +. +worldId +properties +) +; +} +resetWorldConfiguration +( +worldId +) +{ +this +. +configs +. +delete +( +worldId +) +; +} +getCSPForWorldId +( +worldId +) +{ +return +( +this +. +configs +. +get +( +worldId +) +? +. +csp +? +? +this +. +configs +. +get +( +" +" +) +? +. +csp +? +? +this +. +defaultCSP +) +; +} +} +class WorldCollection { / @@ -373,6 +513,21 @@ context = context ; +this +. +configHolder += +WorldConfigHolder +. +allMaps +. +get +( +context +. +extension +) +; context . callOnClose @@ -537,9 +692,14 @@ sandboxPrototype contentWindow sandboxContentSecurityPolicy : -policy +this . -baseCSP +configHolder +. +getCSPForWorldId +( +worldId +) sameZoneAs : contentWindow @@ -599,5 +759,69 @@ worldId ) ; } +updateWorldConfig +( +extension +reset +update +) +{ +let +configHolder += +WorldConfigHolder +. +allMaps +. +get +( +extension +) +; +if +( +reset +) +{ +for +( +let +worldId +of +reset +) +{ +configHolder +. +resetWorldConfiguration +( +worldId +) +; +} +} +if +( +update +) +{ +for +( +let +properties +of +update +) +{ +configHolder +. +configureWorld +( +properties +) +; +} +} +} } ; diff --git a/toolkit/components/extensions/parent/ext-userScripts.js b/toolkit/components/extensions/parent/ext-userScripts.js index 2b577594dac4..68c4e0276964 100644 --- a/toolkit/components/extensions/parent/ext-userScripts.js +++ b/toolkit/components/extensions/parent/ext-userScripts.js @@ -944,6 +944,108 @@ ids ) ; } +configureWorld +: +async +properties += +> +{ +ensureValidWorldId +( +properties +. +worldId +) +; +return +usm +. +runWriteTask +( +async +( +) += +> +{ +await +usm +. +configureWorld +( +properties +) +; +} +) +; +} +resetWorldConfiguration +: +async +worldId += +> +{ +ensureValidWorldId +( +worldId +) +; +return +usm +. +runWriteTask +( +async +( +) += +> +{ +await +usm +. +resetWorldConfiguration +( +worldId +) +; +} +) +; +} +getWorldConfigurations +: +async +( +) += +> +{ +return +usm +. +runReadTask +( +async +( +) += +> +{ +return +usm +. +getWorldConfigurations +( +) +; +} +) +; +} } } ; diff --git a/toolkit/components/extensions/schemas/user_scripts.json b/toolkit/components/extensions/schemas/user_scripts.json index 4b6474ad642c..3dcaee57470d 100644 --- a/toolkit/components/extensions/schemas/user_scripts.json +++ b/toolkit/components/extensions/schemas/user_scripts.json @@ -1573,6 +1573,160 @@ string } ] } +{ +" +id +" +: +" +WorldProperties +" +" +min_manifest_version +" +: +3 +" +type +" +: +" +object +" +" +description +" +: +" +The +configuration +of +a +USER_SCRIPT +world +. +" +" +properties +" +: +{ +" +worldId +" +: +{ +" +type +" +: +" +string +" +" +optional +" +: +true +" +default +" +: +" +" +" +description +" +: +" +The +identifier +of +the +world +. +Values +with +leading +underscores +( +_ +) +are +reserved +. +The +maximum +length +is +256 +. +Defaults +to +the +default +USER_SCRIPT +world +( +\ +" +\ +" +) +. +" +} +" +csp +" +: +{ +" +type +" +: +" +string +" +" +optional +" +: +true +" +description +" +: +" +The +world +' +s +Content +Security +Policy +. +Defaults +to +the +CSP +of +regular +content +scripts +which +prohibits +dynamic +code +execution +such +as +eval +. +" +} +} +} ] " functions @@ -2224,6 +2378,310 @@ scripts } ] } +{ +" +name +" +: +" +configureWorld +" +" +min_manifest_version +" +: +3 +" +type +" +: +" +function +" +" +description +" +: +" +Configures +the +environment +for +scripts +running +in +a +USER_SCRIPT +world +. +" +" +async +" +: +true +" +parameters +" +: +[ +{ +" +name +" +: +" +properties +" +" +ref +" +: +" +WorldProperties +" +" +description +" +: +" +The +desired +configuration +for +a +USER_SCRIPT +world +. +" +} +] +} +{ +" +name +" +: +" +resetWorldConfiguration +" +" +min_manifest_version +" +: +3 +" +type +" +: +" +function +" +" +description +" +: +" +Resets +the +configuration +for +a +given +world +. +That +world +will +fall +back +to +the +default +world +' +s +configuration +. +" +" +async +" +: +true +" +parameters +" +: +[ +{ +" +name +" +: +" +worldId +" +" +type +" +: +" +string +" +" +optional +" +: +true +" +default +" +: +" +" +" +description +" +: +" +The +ID +of +the +USER_SCRIPT +world +to +reset +. +If +omitted +or +empty +resets +the +default +world +' +s +configuration +. +" +} +] +} +{ +" +name +" +: +" +getWorldConfigurations +" +" +min_manifest_version +" +: +3 +" +type +" +: +" +function +" +" +description +" +: +" +Returns +all +registered +USER_SCRIPT +world +configurations +. +" +" +async +" +: +" +callback +" +" +parameters +" +: +[ +{ +" +name +" +: +" +callback +" +" +type +" +: +" +function +" +" +parameters +" +: +[ +{ +" +name +" +: +" +configurations +" +" +type +" +: +" +array +" +" +items +" +: +{ +" +ref +" +: +" +WorldProperties +" +} +" +description +" +: +" +All +configurations +registered +with +configureWorld +( +) +. +" +} +] +} +] +} ] } ] diff --git a/toolkit/components/extensions/test/xpcshell/test_ext_userScripts_mv3_csp.js b/toolkit/components/extensions/test/xpcshell/test_ext_userScripts_mv3_csp.js new file mode 100644 index 000000000000..57860a1f6235 --- /dev/null +++ b/toolkit/components/extensions/test/xpcshell/test_ext_userScripts_mv3_csp.js @@ -0,0 +1,2227 @@ +" +use +strict +" +; +const +{ +ExtensionUserScripts +} += +ChromeUtils +. +importESModule +( +" +resource +: +/ +/ +gre +/ +modules +/ +ExtensionUserScripts +. +sys +. +mjs +" +) +; +const +server += +createHttpServer +( +{ +hosts +: +[ +" +example +. +com +" +" +example +. +net +" +] +} +) +; +server +. +registerPathHandler +( +" +/ +evalChecker +" +( +request +response +) += +> +{ +response +. +setStatusLine +( +request +. +httpVersion +200 +" +OK +" +) +; +response +. +write +( +< +script +> +var +res += +{ +evalOk +: +[ +] +evalBlocked +: +[ +] +} +; +< +/ +script +> +) +; +} +) +; +AddonTestUtils +. +init +( +this +) +; +AddonTestUtils +. +overrideCertDB +( +) +; +add_setup +( +async +( +) += +> +{ +Services +. +prefs +. +setBoolPref +( +" +extensions +. +userScripts +. +mv3 +. +enabled +" +true +) +; +await +ExtensionTestUtils +. +startAddonManager +( +) +; +} +) +; +async +function +testEvalCheckerWithUserScripts +( +) +{ +/ +/ +Open +page +. +User +script +( +s +) +should +run +there +. +let +contentPage += +await +ExtensionTestUtils +. +loadContentPage +( +" +http +: +/ +/ +example +. +com +/ +evalChecker +" +) +; +const +res += +await +contentPage +. +spawn +( +[ +] +( +) += +> +content +. +wrappedJSObject +. +res +) +; +await +contentPage +. +close +( +) +; +return +{ +/ +/ +We +do +not +want +to +rely +on +a +particular +execution +order +so +sort +results +. +evalOk +: +res +. +evalOk +. +toSorted +( +) +evalBlocked +: +res +. +evalBlocked +. +toSorted +( +) +} +; +} +async +function +startEvalTesterExtension +( +) +{ +let +extension += +ExtensionTestUtils +. +loadExtension +( +{ +useAddonManager +: +" +permanent +" +manifest +: +{ +manifest_version +: +3 +permissions +: +[ +" +userScripts +" +] +host_permissions +: +[ +" +* +: +/ +/ +example +. +com +/ +* +" +] +} +background +( +) +{ +/ +/ +This +function +will +be +serialized +and +executed +as +a +user +script +. +function +testEvalInWorld +( +worldId +) +{ +try +{ +/ +/ +eslint +- +disable +- +next +- +line +no +- +eval +eval +( +" +/ +/ +If +blocked +by +CSP +then +this +will +throw +" +) +; +window +. +wrappedJSObject +. +res +. +evalOk +. +push +( +worldId +) +; +} +catch +{ +window +. +wrappedJSObject +. +res +. +evalBlocked +. +push +( +worldId +) +; +} +} +browser +. +test +. +onMessage +. +addListener +( +async +( +msg +args +) += +> +{ +if +( +msg += += += +" +registerUserScriptForWorldId +" +) +{ +const +worldId += +args +; +await +browser +. +userScripts +. +register +( +[ +{ +id +: +script +for +world +{ +worldId +} +. +matches +: +[ +" +* +: +/ +/ +example +. +com +/ +evalChecker +" +] +js +: +[ +{ +code +: +( +{ +testEvalInWorld +} +) +( +" +{ +worldId +} +" +) +} +] +runAt +: +" +document_end +" +worldId +: +worldId +} +] +) +; +} +else +if +( +msg += += += +" +configureWorld +" +) +{ +await +browser +. +userScripts +. +configureWorld +( +args +) +; +} +else +if +( +msg += += += +" +resetWorldConfiguration +" +) +{ +await +browser +. +userScripts +. +resetWorldConfiguration +( +args +) +; +} +else +if +( +msg += += += +" +getWorldConfigurations +" +) +{ +let +res += +await +browser +. +userScripts +. +getWorldConfigurations +( +) +; +browser +. +test +. +sendMessage +( +" +getWorldConfigurations +: +done +" +res +) +; +return +; +} +else +{ +browser +. +test +. +fail +( +Unexpected +message +: +{ +msg +} +) +; +} +browser +. +test +. +sendMessage +( +{ +msg +} +: +done +) +; +} +) +; +} +} +) +; +await +extension +. +startup +( +) +; +async +function +queryExtension +( +msg +args +) +{ +info +( +queryExtension +( +{ +msg +} +{ +args +& +& +JSON +. +stringify +( +args +) +} +) +) +; +extension +. +sendMessage +( +msg +args +) +; +return +extension +. +awaitMessage +( +{ +msg +} +: +done +) +; +} +return +{ +extension +queryExtension +} +; +} +/ +/ +The +default +behavior +is +to +enforce +a +strict +CSP +that +blocks +eval +( +) +as +shown +/ +/ +by +default_USER_SCRIPT_world_behavior +in +test_ext_userScripts_mv3_worlds +. +js +. +/ +/ +This +test +shows +the +most +common +scenario +for +user +script +extensions +: +that +/ +/ +they +can +relax +the +CSP +. +add_task +( +async +function +test_clear_csp_in_default_USER_SCRIPT_world +( +) +{ +let +{ +extension +queryExtension +} += +await +startEvalTesterExtension +( +) +; +await +queryExtension +( +" +configureWorld +" +{ +csp +: +" +" +} +) +; +await +queryExtension +( +" +registerUserScriptForWorldId +" +" +" +) +; +await +queryExtension +( +" +registerUserScriptForWorldId +" +" +otherWorld +" +) +; +Assert +. +deepEqual +( +await +testEvalCheckerWithUserScripts +( +) +{ +evalOk +: +[ +" +" +" +otherWorld +" +] +evalBlocked +: +[ +] +} +" +With +empty +csp +in +configureWorld +( +) +user +scripts +can +call +eval +( +) +. +" +) +; +Assert +. +deepEqual +( +await +queryExtension +( +" +getWorldConfigurations +" +) +[ +{ +worldId +: +" +" +csp +: +" +" +} +] +" +getWorldConfigurations +( +) +returns +current +CSP +configuration +" +) +; +await +extension +. +unload +( +) +; +} +) +; +/ +/ +Tests +configureWorld +resetWorldConfiguration +and +getWorldConfigurations +for +/ +/ +multiple +worldId +( +including +default +world +) +: +/ +/ +- +verify +that +world +- +specific +CSP +can +be +set +. +/ +/ +- +verify +that +if +csp +is +null +that +we +fall +back +to +the +default +world +' +s +CSP +/ +/ +or +the +extension +' +s +( +strict +) +default +CSP +if +there +is +no +default +configured +. +add_task +( +async +function +test_world_specific_csp_override +( +) +{ +let +{ +extension +queryExtension +} += +await +startEvalTesterExtension +( +) +; +/ +/ +Override +default +: +instead +of +blocking +eval +( +) +permit +eval +( +) +: +await +queryExtension +( +" +configureWorld +" +{ +worldId +: +" +world_1 +" +csp +: +" +" +} +) +; +await +queryExtension +( +" +configureWorld +" +{ +csp +: +" +script +- +src +' +none +' +" +/ +/ +Stricter +than +the +default +and +blocks +eval +( +) +. +worldId +: +" +world_3 +" +} +) +; +await +queryExtension +( +" +registerUserScriptForWorldId +" +" +" +) +; +await +queryExtension +( +" +registerUserScriptForWorldId +" +" +world_1 +" +) +; +await +queryExtension +( +" +registerUserScriptForWorldId +" +" +world_2 +" +) +; +await +queryExtension +( +" +registerUserScriptForWorldId +" +" +world_3 +" +) +; +Assert +. +deepEqual +( +await +testEvalCheckerWithUserScripts +( +) +{ +evalOk +: +[ +" +world_1 +" +] +evalBlocked +: +[ +" +" +" +world_2 +" +" +world_3 +" +] +} +" +Can +override +CSP +for +specific +world +without +affecting +default +world +" +) +; +info +( +" +Setting +empty +CSP +by +default +to +allow +eval +( +) +. +" +) +; +await +queryExtension +( +" +configureWorld +" +{ +worldId +: +" +" +csp +: +" +" +} +) +; +Assert +. +deepEqual +( +await +testEvalCheckerWithUserScripts +( +) +{ +evalOk +: +[ +" +" +" +world_1 +" +" +world_2 +" +] +evalBlocked +: +[ +" +world_3 +" +] +} +" +CSP +empty +by +default +except +world_3 +that +blocked +it +explicitly +" +) +; +info +( +" +Resetting +CSP +of +default +world +" +) +; +await +queryExtension +( +" +resetWorldConfiguration +" +null +) +; +Assert +. +deepEqual +( +await +testEvalCheckerWithUserScripts +( +) +{ +evalOk +: +[ +" +world_1 +" +] +evalBlocked +: +[ +" +" +" +world_2 +" +" +world_3 +" +] +} +" +Default +( +strict +) +CSP +is +applied +by +default +except +where +allowed +( +world_1 +) +" +) +; +/ +/ +Sanity +check +: +Which +worlds +do +we +have +configured +? +Assert +. +deepEqual +( +await +queryExtension +( +" +getWorldConfigurations +" +) +[ +{ +worldId +: +" +world_1 +" +csp +: +" +" +} +{ +worldId +: +" +world_3 +" +csp +: +" +script +- +src +' +none +' +" +} +] +" +getWorldConfigurations +( +) +returns +current +CSP +configurations +" +) +; +/ +/ +Test +that +we +can +reset +a +non +- +default +world +. +await +queryExtension +( +" +resetWorldConfiguration +" +" +world_3 +" +) +; +Assert +. +deepEqual +( +await +testEvalCheckerWithUserScripts +( +) +{ +evalOk +: +[ +" +world_1 +" +] +evalBlocked +: +[ +" +" +" +world_2 +" +" +world_3 +" +] +} +" +world_3 +now +defaults +to +the +default +which +still +blocks +eval +( +) +" +) +; +Assert +. +deepEqual +( +await +queryExtension +( +" +getWorldConfigurations +" +) +[ +{ +worldId +: +" +world_1 +" +csp +: +" +" +} +] +" +getWorldConfigurations +( +) +returns +only +remaining +configuration +" +) +; +/ +/ +Test +that +when +a +world +is +configured +and +the +CSP +is +omitted +that +it +/ +/ +falls +back +to +the +default +world +. +await +queryExtension +( +" +configureWorld +" +{ +worldId +: +" +world_2 +" +} +) +; +await +queryExtension +( +" +configureWorld +" +{ +worldId +: +" +world_3 +" +csp +: +null +} +) +; +Assert +. +deepEqual +( +await +testEvalCheckerWithUserScripts +( +) +{ +evalOk +: +[ +" +world_1 +" +] +evalBlocked +: +[ +" +" +" +world_2 +" +" +world_3 +" +] +} +" +world_2 +and +world_3 +fall +back +to +the +default +world +' +s +CSP +" +) +; +/ +/ +Now +relax +the +default +world +again +. +await +queryExtension +( +" +configureWorld +" +{ +worldId +: +" +" +csp +: +" +" +} +) +; +/ +/ +Verify +that +it +just +updated +the +default +world +' +s +csp +and +not +all +others +/ +/ +that +would +fall +back +to +the +default +. +Assert +. +deepEqual +( +await +queryExtension +( +" +getWorldConfigurations +" +) +[ +{ +worldId +: +" +world_1 +" +csp +: +" +" +} +{ +worldId +: +" +world_2 +" +csp +: +null +} +{ +worldId +: +" +world_3 +" +csp +: +null +} +{ +worldId +: +" +" +csp +: +" +" +} +] +" +getWorldConfigurations +( +) +returns +null +where +csp +was +not +explicitly +set +" +) +; +Assert +. +deepEqual +( +await +testEvalCheckerWithUserScripts +( +) +{ +evalOk +: +[ +" +" +" +world_1 +" +" +world_2 +" +" +world_3 +" +] +evalBlocked +: +[ +] +} +" +world_2 +and +world_3 +use +the +new +( +relaxed +) +default +world +CSP +" +) +; +/ +/ +Now +set +the +default +world +without +explicit +CSP +. +Should +default +to +strict +. +await +queryExtension +( +" +configureWorld +" +{ +worldId +: +" +" +} +) +; +Assert +. +deepEqual +( +await +testEvalCheckerWithUserScripts +( +) +{ +evalOk +: +[ +" +world_1 +" +] +evalBlocked +: +[ +" +" +" +world_2 +" +" +world_3 +" +] +} +" +When +default +world +does +not +have +explicit +csp +default +CSP +is +strict +" +) +; +Assert +. +deepEqual +( +await +queryExtension +( +" +getWorldConfigurations +" +) +[ +{ +worldId +: +" +world_1 +" +csp +: +" +" +} +{ +worldId +: +" +world_2 +" +csp +: +null +} +{ +worldId +: +" +world_3 +" +csp +: +null +} +{ +worldId +: +" +" +csp +: +null +} +] +" +getWorldConfigurations +( +) +returns +null +where +csp +was +not +explicitly +set +" +) +; +await +extension +. +unload +( +) +; +} +) +; +add_task +( +async +function +test_configuration_persists_across_restarts +( +) +{ +let +{ +extension +queryExtension +} += +await +startEvalTesterExtension +( +) +; +await +queryExtension +( +" +registerUserScriptForWorldId +" +" +" +) +; +await +queryExtension +( +" +registerUserScriptForWorldId +" +" +world_1 +" +) +; +await +queryExtension +( +" +registerUserScriptForWorldId +" +" +world_2 +" +) +; +await +queryExtension +( +" +registerUserScriptForWorldId +" +" +world_3 +" +) +; +/ +/ +Default +world +: +use +defaults +( +fall +back +to +default +strict +CSP +) +. +await +queryExtension +( +" +configureWorld +" +{ +} +) +; +/ +/ +Set +and +reset +world_1 +just +to +show +that +there +is +no +lingering +state +. +await +queryExtension +( +" +configureWorld +" +{ +worldId +: +" +world_1 +" +csp +: +" +" +} +) +; +await +queryExtension +( +" +resetWorldConfiguration +" +" +world_1 +" +) +; +/ +/ +world_2 +: +relax +CSP +. +await +queryExtension +( +" +configureWorld +" +{ +worldId +: +" +world_2 +" +csp +: +" +" +} +) +; +/ +/ +world_3 +: +fall +back +to +default +which +ultimately +falls +back +to +strict +CSP +. +await +queryExtension +( +" +configureWorld +" +{ +worldId +: +" +world_3 +" +csp +: +null +} +) +; +/ +/ +The +test_world_specific_csp_override +test +already +verified +that +the +CSP +is +/ +/ +correctly +enforced +immediately +so +we +restart +immediately +to +verify +that +/ +/ +the +configurations +persist +. +await +AddonTestUtils +. +promiseShutdownManager +( +) +; +ExtensionUserScripts +. +_getStoreForTesting +( +) +. +_uninitForTesting +( +) +; +await +AddonTestUtils +. +promiseStartupManager +( +) +; +await +extension +. +wakeupBackground +( +) +; +Assert +. +deepEqual +( +await +queryExtension +( +" +getWorldConfigurations +" +) +[ +{ +worldId +: +" +" +csp +: +null +} +{ +worldId +: +" +world_2 +" +csp +: +" +" +} +{ +worldId +: +" +world_3 +" +csp +: +null +} +] +" +getWorldConfigurations +( +) +returns +null +where +csp +was +not +explicitly +set +" +) +; +Assert +. +deepEqual +( +await +testEvalCheckerWithUserScripts +( +) +{ +evalOk +: +[ +" +world_2 +" +] +evalBlocked +: +[ +" +" +" +world_1 +" +" +world_3 +" +] +} +" +World +- +specific +CSP +configurations +persist +across +restarts +" +) +; +await +extension +. +unload +( +) +; +} +) +; diff --git a/toolkit/components/extensions/test/xpcshell/xpcshell-common.toml b/toolkit/components/extensions/test/xpcshell/xpcshell-common.toml index c0fbd8b3574f..9582fc3e507f 100644 --- a/toolkit/components/extensions/test/xpcshell/xpcshell-common.toml +++ b/toolkit/components/extensions/test/xpcshell/xpcshell-common.toml @@ -3605,6 +3605,13 @@ js ] [ " +test_ext_userScripts_mv3_csp +. +js +" +] +[ +" test_ext_userScripts_mv3_injection . js