İçerik komut dosyaları, web sayfaları bağlamında çalışan dosyalardır. Standart Document Object Model'i (DOM) kullanarak tarayıcının ziyaret ettiği web sayfalarının ayrıntılarını okuyabilir, bu sayfalarda değişiklik yapabilir ve bilgileri üst uzantılarına iletebilirler.
İçerik komut dosyası özelliklerini anlama
İçerik komut dosyaları, aşağıdaki uzantı API'lerine doğrudan erişebilir:
dom
i18n
storage
runtime.connect()
runtime.getManifest()
runtime.getURL()
runtime.id
runtime.onConnect
runtime.onMessage
runtime.sendMessage()
İçerik komut dosyaları diğer API'lere doğrudan erişemez. Ancak uzantınızın diğer bölümleriyle mesaj alışverişi yaparak bu verilere dolaylı olarak erişebilirler.
Ayrıca, fetch()
gibi API'leri kullanarak uzantınızdaki diğer dosyalara da içerik komut dosyasından erişebilirsiniz. Bunu yapmak için bu kaynakları web'de erişilebilen kaynaklar olarak bildirmeniz gerekir. Bu durumun, kaynakları aynı sitede çalışan birinci taraf veya üçüncü taraf komut dosyalarına da maruz bıraktığını unutmayın.
İzole edilmiş dünyalarda çalışma
İçerik komut dosyaları, kendi JavaScript ortamlarında çalışır. Bu sayede, sayfayla veya diğer uzantıların içerik komut dosyalarıyla çakışmadan JavaScript ortamında değişiklik yapabilirler.
Uzantılar, aşağıdaki örneğe benzer kod içeren web sayfalarında çalışabilir.
webPage.html
<html>
<button id="mybutton">click me</button>
<script>
var greeting = "hello, ";
var button = document.getElementById("mybutton");
button.person_name = "Bob";
button.addEventListener(
"click", () => alert(greeting + button.person_name + "."), false);
</script>
</html>
Bu uzantı, Komut dosyası yerleştirme bölümünde açıklanan tekniklerden birini kullanarak aşağıdaki içerik komut dosyasını yerleştirebilir.
content-script.js
var greeting = "hola, ";
var button = document.getElementById("mybutton");
button.person_name = "Roberto";
button.addEventListener(
"click", () => alert(greeting + button.person_name + "."), false);
Bu değişiklikle birlikte, düğme tıklandığında her iki uyarı da sırayla gösterilir.
Komut dosyası yerleştirme
İçerik komut dosyaları statik olarak tanımlanabilir, dinamik olarak tanımlanabilir veya programatik olarak eklenebilir.
Statik bildirimlerle ekleme
İyi bilinen bir dizi sayfada otomatik olarak çalıştırılması gereken komut dosyaları için manifest.json dosyasında statik içerik komut dosyası bildirimlerini kullanın.
Statik olarak beyan edilen komut dosyaları, manifest dosyasında "content_scripts"
anahtarı altında kaydedilir.
JavaScript dosyaları, CSS dosyaları veya her ikisi de olabilir. Otomatik olarak çalışan tüm içerik komut dosyaları eşleşme kalıplarını belirtmelidir.
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"css": ["my-styles.css"],
"js": ["content-script.js"]
}
],
...
}
Ad | Tür | Açıklama |
---|---|---|
matches |
dize dizisi | Zorunludur. Bu içerik komut dosyasının hangi sayfalara ekleneceğini belirtir. Bu dizelerin söz dizimi hakkında ayrıntılı bilgi için Eşleşme Kalıpları'nı, URL'leri hariç tutma hakkında bilgi için ise Eşleşme kalıpları ve globlar'ı inceleyin. |
css |
dize dizisi | İsteğe bağlıdır. Eşleşen sayfalara yerleştirilecek CSS dosyalarının listesi. Bunlar, sayfa için herhangi bir DOM oluşturulmadan veya görüntülenmeden önce bu dizide göründükleri sırayla yerleştirilir. |
js |
|
İsteğe bağlıdır. Eşleşen sayfalara yerleştirilecek JavaScript dosyalarının listesi. Dosyalar, bu dizide göründükleri sırayla yerleştirilir. Bu listedeki her dize, uzantının kök dizinindeki bir kaynağın göreli yolunu içermelidir. Baştaki eğik çizgiler (`/`) otomatik olarak kırpılır. |
run_at |
RunAt | İsteğe bağlıdır. Komut dosyasının sayfaya ne zaman eklenmesi gerektiğini belirtir. Varsayılan olarak document_idle değerine ayarlanır. |
match_about_blank |
boolean | İsteğe bağlıdır. Komut dosyasının, üst veya açıcı çerçevenin matches içinde belirtilen kalıplardan biriyle eşleştiği bir about:blank çerçevesine yerleştirilip yerleştirilmeyeceği. Varsayılan olarak false değerine ayarlanır. |
match_origin_as_fallback |
boolean |
İsteğe bağlıdır. Komut dosyasının, eşleşen bir kaynak tarafından oluşturulan ancak URL'si veya kaynağı doğrudan kalıpla eşleşmeyebilen çerçevelere yerleştirilip yerleştirilmeyeceği. Bunlar arasında about: , data: , blob: ve filesystem: gibi farklı şemalara sahip çerçeveler yer alır. Ayrıca İlgili çerçevelere yerleştirme başlıklı makaleyi de inceleyin.
|
world |
ExecutionWorld |
İsteğe bağlıdır. Bir komut dosyasının içinde yürütüleceği JavaScript dünyası. Varsayılan olarak ISOLATED değerine ayarlanır. Ayrıca İzole dünyalarda çalışma başlıklı makaleyi de inceleyin.
|
Dinamik bildirimlerle ekleme
Dinamik içerik komut dosyaları, içerik komut dosyalarının eşleşme kalıpları iyi bilinmediğinde veya içerik komut dosyaları bilinen ana makinelere her zaman eklenmemesi gerektiğinde kullanışlıdır.
Chrome 96'da kullanıma sunulan dinamik bildirimler, statik bildirimlere benzer ancak içerik komut dosyası nesnesi, manifest.json'da değil chrome.scripting
ad alanındaki yöntemler kullanılarak Chrome'a kaydedilir. Scripting API, uzantı geliştiricilerin şunları yapmasına da olanak tanır:
- İçerik komut dosyalarını kaydedin.
- Kayıtlı içerik komut dosyalarının listesini alın.
- Kayıtlı içerik komut dosyaları listesini güncelleyin.
- Kayıtlı içerik komut dosyalarını kaldırın.
Statik bildirimler gibi dinamik bildirimler de JavaScript dosyaları, CSS dosyaları veya her ikisini de içerebilir.
service-worker.js
chrome.scripting
.registerContentScripts([{
id: "session-script",
js: ["content.js"],
persistAcrossSessions: false,
matches: ["*://example.com/*"],
runAt: "document_start",
}])
.then(() => console.log("registration complete"))
.catch((err) => console.warn("unexpected error", err))
service-worker.js
chrome.scripting
.updateContentScripts([{
id: "session-script",
excludeMatches: ["*://admin.example.com/*"],
}])
.then(() => console.log("registration updated"));
service-worker.js
chrome.scripting
.getRegisteredContentScripts()
.then(scripts => console.log("registered content scripts", scripts));
service-worker.js
chrome.scripting
.unregisterContentScripts({ ids: ["session-script"] })
.then(() => console.log("un-registration complete"));
Programatik olarak ekleme
Etkinliklere yanıt olarak veya belirli durumlarda çalışması gereken içerik komut dosyaları için programatik yerleştirme kullanın.
Bir içerik komut dosyasını programatik olarak yerleştirmek için uzantınızın, komut dosyalarını yerleştirmeye çalıştığı sayfa için ana makine izinlerine sahip olması gerekir. Ana makine izinleri, uzantınızın manifestinde istenerek veya geçici olarak "activeTab"
kullanılarak verilebilir.
Aşağıda, activeTab tabanlı bir uzantının farklı sürümleri verilmiştir.
manifest.json:
{
"name": "My extension",
...
"permissions": [
"activeTab",
"scripting"
],
"background": {
"service_worker": "background.js"
},
"action": {
"default_title": "Action Button"
}
}
İçerik komut dosyaları dosya olarak yerleştirilebilir.
content-script.js
document.body.style.backgroundColor = "orange";
service-worker.js:
chrome.action.onClicked.addListener((tab) => {
chrome.scripting.executeScript({
target: { tabId: tab.id },
files: ["content-script.js"]
});
});
Alternatif olarak, bir işlev gövdesi içerik komut dosyası olarak yerleştirilip yürütülebilir.
service-worker.js:
function injectedFunction() {
document.body.style.backgroundColor = "orange";
}
chrome.action.onClicked.addListener((tab) => {
chrome.scripting.executeScript({
target : {tabId : tab.id},
func : injectedFunction,
});
});
Yerleştirilen işlevin, chrome.scripting.executeScript()
çağrısında referans verilen işlevin bir kopyası olduğunu, orijinal işlevin kendisi olmadığını unutmayın. Bu nedenle, işlevin gövdesi bağımsız olmalıdır. İşlevin dışındaki değişkenlere yapılan referanslar, içerik komut dosyasının ReferenceError
oluşturmasına neden olur.
İşlev olarak yerleştirirken işleve bağımsız değişkenler de iletebilirsiniz.
service-worker.js
function injectedFunction(color) {
document.body.style.backgroundColor = color;
}
chrome.action.onClicked.addListener((tab) => {
chrome.scripting.executeScript({
target : {tabId : tab.id},
func : injectedFunction,
args : [ "orange" ],
});
});
Eşleşmeleri ve globları hariç tutma
Belirtilen sayfa eşleşmesini özelleştirmek için aşağıdaki alanları bildirimli bir kayda ekleyin.
Ad | Tür | Açıklama |
---|---|---|
exclude_matches |
dize dizisi | İsteğe bağlıdır. Bu içerik komut dosyasının aksi takdirde içine yerleştirileceği sayfaları hariç tutar. Bu dizelerin söz dizimi hakkında ayrıntılı bilgi için Eşleşme Kalıpları bölümüne bakın. |
include_globs |
dize dizisi | İsteğe bağlıdır. Yalnızca bu glob ile de eşleşen URL'leri içerecek şekilde matches tarihinden sonra uygulanır. Bu, @include
Greasemonkey anahtar kelimesini taklit etmek için tasarlanmıştır. |
exclude_globs |
dize dizisi | İsteğe bağlıdır. Bu glob ile eşleşen URL'leri hariç tutmak için matches sonrasında uygulanır. @exclude
Greasemonkey anahtar kelimesini taklit etmek için tasarlanmıştır. |
Aşağıdakilerin her ikisi de doğruysa içerik komut dosyası bir sayfaya yerleştirilir:
- URL'si herhangi bir
matches
kalıbı ve herhangi birinclude_globs
kalıbıyla eşleşir. - URL,
exclude_matches
veyaexclude_globs
kalıbıyla da eşleşmiyor.matches
özelliği zorunlu olduğundanexclude_matches
,include_globs
veexclude_globs
yalnızca hangi sayfaların etkileneceğini sınırlamak için kullanılabilir.
Aşağıdaki uzantı, içerik komut dosyasını https://www.nytimes.com/health
içine yerleştirir ancak https://www.nytimes.com/business
içine yerleştirmez .
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"exclude_matches": ["*://*/*business*"],
"js": ["contentScript.js"]
}
],
...
}
service-worker.js
chrome.scripting.registerContentScripts([{
id : "test",
matches : [ "https://*.nytimes.com/*" ],
excludeMatches : [ "*://*/*business*" ],
js : [ "contentScript.js" ],
}]);
Glob özellikleri, eşleşme kalıplarından farklı ve daha esnek bir söz dizimi kullanır. Kabul edilebilir glob dizeleri, "joker karakter" yıldız işaretleri ve soru işaretleri içerebilen URL'lerdir. Yıldız işareti (*
), boş dize de dahil olmak üzere herhangi bir uzunluktaki herhangi bir dizeyle eşleşirken soru işareti (?
) herhangi bir tek karakterle eşleşir.
Örneğin, https://???.example.com/foo/\*
glob'u aşağıdakilerden herhangi biriyle eşleşir:
https://www.example.com/foo/bar
https://the.example.com/foo/
Ancak aşağıdaki öğelerle eşleşmez:
https://my.example.com/foo/bar
https://example.com/foo/
https://www.example.com/foo
Bu uzantı, içerik komut dosyasını https://www.nytimes.com/arts/index.html
ve https://www.nytimes.com/jobs/index.htm*
içine yerleştirir ancak https://www.nytimes.com/sports/index.html
içine yerleştirmez:
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"include_globs": ["*nytimes.com/???s/*"],
"js": ["contentScript.js"]
}
],
...
}
Bu uzantı, içerik komut dosyasını https://history.nytimes.com
ve https://.nytimes.com/history
içine yerleştirir ancak https://science.nytimes.com
veya https://www.nytimes.com/science
içine yerleştirmez:
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"exclude_globs": ["*science*"],
"js": ["contentScript.js"]
}
],
...
}
Doğru kapsamı elde etmek için bunlardan biri, tamamı veya bir kısmı dahil edilebilir.
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"exclude_matches": ["*://*/*business*"],
"include_globs": ["*nytimes.com/???s/*"],
"exclude_globs": ["*science*"],
"js": ["contentScript.js"]
}
],
...
}
Süre
run_at
alanı, JavaScript dosyalarının web sayfasına ne zaman yerleştirileceğini kontrol eder. Tercih edilen ve varsayılan değer "document_idle"
. Diğer olası değerler için RunAt türüne bakın.
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"run_at": "document_idle",
"js": ["contentScript.js"]
}
],
...
}
service-worker.js
chrome.scripting.registerContentScripts([{
id : "test",
matches : [ "https://*.nytimes.com/*" ],
runAt : "document_idle",
js : [ "contentScript.js" ],
}]);
Ad | Tür | Açıklama |
---|---|---|
document_idle |
dize | Tercih edilen. Mümkün olduğunda "document_idle" kullanın.Tarayıcı, "document_end" ile window.onload etkinliği tetiklendikten hemen sonraki bir zamanı seçerek komut dosyalarını yerleştirir. Ekleme anı, belgenin karmaşıklığına ve yüklenmesinin ne kadar sürdüğüne bağlıdır ve sayfa yükleme hızı için optimize edilmiştir."document_idle" konumunda çalışan içerik komut dosyalarının window.onload etkinliğini dinlemesi gerekmez. Bu komut dosyalarının, DOM tamamlandıktan sonra çalışacağı garanti edilir. Bir komut dosyasının kesinlikle window.onload 'dan sonra çalışması gerekiyorsa uzantı, document.readyState özelliğini kullanarak onload 'ın zaten tetiklenip tetiklenmediğini kontrol edebilir. |
document_start |
dize | Komut dosyaları, css kaynaklı dosyalardan sonra ancak başka bir DOM oluşturulmadan veya başka bir komut dosyası çalıştırılmadan önce yerleştirilir. |
document_end |
dize | Komut dosyaları, DOM tamamlandıktan hemen sonra ancak resimler ve çerçeveler gibi alt kaynaklar yüklenmeden önce yerleştirilir. |
Çerçeveleri belirtme
Manifest'te belirtilen bildirimli içerik komut dosyaları için "all_frames"
alanı, uzantının JavaScript ve CSS dosyalarının belirtilen URL koşullarıyla eşleşen tüm çerçevelere mi yoksa yalnızca bir sekmedeki en üstteki çerçeveye mi yerleştirileceğini belirtmesine olanak tanır:
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"all_frames": true,
"js": ["contentScript.js"]
}
],
...
}
chrome.scripting.registerContentScripts(...)
kullanılarak içerik komut dosyaları programatik olarak kaydedilirken allFrames
parametresi, içerik komut dosyasının belirtilen URL koşullarıyla eşleşen tüm çerçevelere mi yoksa yalnızca bir sekmedeki en üstteki çerçeveye mi yerleştirileceğini belirtmek için kullanılabilir. Bu yalnızca tabId ile kullanılabilir ve frameIds veya documentIds belirtilmişse kullanılamaz:
service-worker.js
chrome.scripting.registerContentScripts([{
id: "test",
matches : [ "https://*.nytimes.com/*" ],
allFrames : true,
js : [ "contentScript.js" ],
}]);
İlgili çerçevelere yerleştirme
Uzantılar, eşleşen bir çerçeveyle ilişkili olan ancak kendileri eşleşmeyen çerçevelerde komut dosyaları çalıştırmak isteyebilir. Bu durumun söz konusu olduğu yaygın bir senaryo, eşleşen bir çerçeve tarafından oluşturulan ancak URL'leri komut dosyasının belirttiği kalıplarla eşleşmeyen URL'lere sahip çerçevelerdir.
Bu durum, bir uzantı about:
, data:
, blob:
ve filesystem:
düzenlerine sahip URL'lerle çerçevelere yerleştirmek istediğinde geçerlidir. Bu durumlarda URL, içerik komut dosyasının kalıbıyla eşleşmez (ve about:
ile data:
durumunda, üst URL'yi veya kaynağı URL'ye hiç dahil etmez, örneğin about:blank
veya data:text/html,<html>Hello, World!</html>
). Ancak bu çerçeveler yine de oluşturulan çerçeveyle ilişkilendirilebilir.
Uzantılar, bu çerçevelere yerleştirmek için manifest dosyasındaki içerik komut dosyası spesifikasyonunda "match_origin_as_fallback"
özelliğini belirtebilir.
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.google.com/*"],
"match_origin_as_fallback": true,
"js": ["contentScript.js"]
}
],
...
}
Belirtilip true
olarak ayarlandığında Chrome, çerçevenin eşleşip eşleşmediğini belirlemek için çerçevenin URL'sine değil, çerçevenin başlatıcısının kaynağına bakar. Bunun, hedef çerçevenin kaynağından (ör. data:
URL'lerin kaynağı boş).
Çerçevenin başlatıcısı, hedef çerçeveyi oluşturan veya hedef çerçeveye giden çerçevedir. Bu genellikle doğrudan üst veya açıcı olsa da olmayabilir (ör. bir çerçeve, bir iframe içindeki bir iframe'de gezinirken).
Bu, başlatıcı çerçevenin kaynağını karşılaştırdığı için başlatıcı çerçeve, bu kaynaktan herhangi bir yolda olabilir. Bu çıkarımı netleştirmek için Chrome, "match_origin_as_fallback"
ile belirtilen tüm içerik komut dosyalarının true
olarak ayarlanmasının yanı sıra *
yolunu da belirtmesini zorunlu kılar.
Hem "match_origin_as_fallback"
hem de "match_about_blank"
belirtildiğinde "match_origin_as_fallback"
öncelikli olur.
Yerleştirme sayfasıyla iletişim
İçerik komut dosyalarının ve bunları barındıran sayfaların yürütme ortamları birbirinden izole edilmiş olsa da sayfanın DOM'una erişimi paylaşırlar. Sayfa, içerik komut dosyasıyla veya içerik komut dosyası aracılığıyla uzantıyla iletişim kurmak istiyorsa bunu paylaşılan DOM üzerinden yapmalıdır.
window.postMessage()
kullanılarak bir örnek verilebilir:
content-script.js
var port = chrome.runtime.connect();
window.addEventListener("message", (event) => {
// We only accept messages from ourselves
if (event.source !== window) {
return;
}
if (event.data.type && (event.data.type === "FROM_PAGE")) {
console.log("Content script received: " + event.data.text);
port.postMessage(event.data.text);
}
}, false);
example.js
document.getElementById("theButton").addEventListener("click", () => {
window.postMessage(
{type : "FROM_PAGE", text : "Hello from the webpage!"}, "*");
}, false);
Uzantı olmayan sayfa (ör. example.html) kendi kendine mesaj gönderir. Bu ileti, içerik komut dosyası tarafından yakalanıp incelenir ve ardından uzantı sürecine gönderilir. Bu şekilde sayfa, uzantı işlemiyle iletişim hattı oluşturur. Benzer yöntemlerle tersi de mümkündür.
Uzantı dosyalarına erişme
Bir içerik komut dosyasından uzantı dosyasına erişmek için aşağıdaki örnekte (content.js
) gösterildiği gibi uzantı öğenizin mutlak URL'sini almak üzere chrome.runtime.getURL()
işlevini çağırabilirsiniz:
content-script.js
let image = chrome.runtime.getURL("images/my_image.png")
Bir CSS dosyasında yazı tiplerini veya resimleri kullanmak için aşağıdaki örnekte (content.css
) gösterildiği gibi bir URL oluşturmak üzere @@extension_id
kullanabilirsiniz:
content.css
body {
background-image:url('chrome-extension://__MSG_@@extension_id__/background.png');
}
@font-face {
font-family: 'Stint Ultra Expanded';
font-style: normal;
font-weight: 400;
src: url('chrome-extension://__MSG_@@extension_id__/fonts/Stint Ultra Expanded.woff') format('woff');
}
Tüm öğeler, manifest.json
dosyasında web'de erişilebilen kaynaklar olarak bildirilmelidir:
manifest.json
{
...
"web_accessible_resources": [
{
"resources": [ "images/*.png" ],
"matches": [ "https://example.com/*" ]
},
{
"resources": [ "fonts/*.woff" ],
"matches": [ "https://example.com/*" ]
}
],
...
}
İçerik Güvenliği Politikası
İzole edilmiş dünyalarda çalışan içerik komut dosyaları aşağıdaki İçerik Güvenliği Politikası'na (İGP) sahiptir:
script-src 'self' 'wasm-unsafe-eval' 'inline-speculation-rules' chrome-extension://abcdefghijklmopqrstuvwxyz/; object-src 'self';
Diğer uzantı bağlamlarına uygulanan kısıtlamalara benzer şekilde, bu kısıtlama eval()
kullanımını ve harici komut dosyalarının yüklenmesini engeller.
Paketi açılmış uzantılar için CSP, localhost'u da içerir:
script-src 'self' 'wasm-unsafe-eval' 'inline-speculation-rules' http://localhost:* http://127.0.0.1:* chrome-extension://abcdefghijklmopqrstuvwxyz/; object-src 'self';
Ana dünyaya bir içerik komut dosyası yerleştirildiğinde sayfanın CSP'si uygulanır.
Güvende kalın
İzole edilmiş dünyalar bir koruma katmanı sağlasa da içerik komut dosyalarının kullanılması, uzantıda ve web sayfasında güvenlik açıkları oluşturabilir. İçerik komut dosyası, fetch()
çağrısı gibi yöntemlerle ayrı bir web sitesinden içerik alıyorsa içeriği yerleştirmeden önce siteler arası komut dosyası çalıştırma saldırılarına karşı filtrelemeye dikkat edin. "man-in-the-middle" saldırılarını önlemek için yalnızca HTTPS üzerinden iletişim kurun.
Kötü amaçlı web sayfalarını filtrelediğinizden emin olun. Örneğin, aşağıdaki kalıplar tehlikelidir ve Manifest V3'te yasaktır:
content-script.js
const data = document.getElementById("json-data"); // WARNING! Might be evaluating an evil script! const parsed = eval("(" + data + ")");
content-script.js
const elmt_id = ... // WARNING! elmt_id might be '); ... evil script ... //'! window.setTimeout("animate(" + elmt_id + ")", 200);
Bunun yerine, komut dosyası çalıştırmayan daha güvenli API'leri tercih edin:
content-script.js
const data = document.getElementById("json-data") // JSON.parse does not evaluate the attacker's scripts. const parsed = JSON.parse(data);
content-script.js
const elmt_id = ... // The closure form of setTimeout does not evaluate scripts. window.setTimeout(() => animate(elmt_id), 200);