İçerik komut dosyaları

İçerik komut dosyaları, web sayfaları bağlamında çalışan dosyalardır. Standart Belge Nesnesi Modeli'ni (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 üst uzantılarına bilgi aktarabilirler.

İçerik komut dosyası özelliklerini anlama

İçerik komut dosyaları aşağıdaki uzantı API'lerine doğrudan erişebilir:

İçerik komut dosyaları diğer API'lere doğrudan erişemez. Ancak uzantınızın diğer bölümleriyle mesaj alışverişinde bulunarak bu bilgilere dolaylı olarak erişebilirler.

fetch() gibi API'leri kullanarak içerik komut dosyasından uzantınızdaki diğer dosyalara da erişebilirsiniz. Bunun için bu kaynakları web'den erişilebilir kaynaklar olarak belirtmeniz gerekir. Bu işlemin, kaynakları aynı sitede çalışan tüm birinci taraf veya üçüncü taraf komut dosyalarına da maruz bıraktığını unutmayın.

İzole edilmiş dünyalarda çalışma

İçerik komut dosyaları, izole bir dünyada bulunur. Bu sayede, içerik komut dosyaları sayfa veya diğer eklentilerin içerik komut dosyalarıyla çakışma olmadan JavaScript ortamında değişiklik yapabilir.

Bir uzantı, aşağıdaki örneğe benzer kod içeren bir web sayfası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 programlı olarak enjekte edilebilir.

Statik beyanlarla ekleme

İyi bilinen bir sayfa grubunda otomatik olarak çalıştırılması gereken komut dosyaları için manifest.json dosyasında statik içerik komut dosyası beyanları kullanın.

Statik olarak tanımlanan komut dosyaları, manifest dosyasında "content_scripts" anahtarı altında kaydedilir. Bunlar JavaScript dosyaları, CSS dosyaları veya her ikisi olabilir. Tüm otomatik çalıştırılan içerik komut dosyaları eşleme 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ı başlıklı makaleyi, URL'lerin nasıl hariç tutulacağı hakkında bilgi için Eşleşme kalıpları ve genel eşlemeler başlıklı makaleyi inceleyin.
css dize dizisi İsteğe bağlıdır. Eşleşen sayfalara eklenecek CSS dosyalarının listesi. Bunlar, sayfa için herhangi bir DOM oluşturulmadan veya görüntülenmeden önce bu dizgede göründükleri sırada eklenir.
js dize dizisi İsteğe bağlıdır. Eşleşen sayfalara eklenecek JavaScript dosyalarının listesi. Dosyalar bu dizgede göründükleri sırayla eklenir. Bu listedeki her dize, uzantının kök dizininde bulunan bir kaynağa yönlendiren göreli bir yol 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 ekleneceğ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 tanımlanan kalıplardan biriyle eşleştiği bir about:blank çerçevesine eklenip eklenmeyeceği. Varsayılan değer yanlıştı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şmeyebilecek çerçevelere eklenip eklenmeyeceği. Bunlara about:, data:, blob: ve filesystem: gibi farklı şemalara sahip çerçeveler dahildir. Ayrıca İlgili çerçevelere kod yerleştirme başlıklı makaleyi de inceleyin.
world ExecutionWorld İsteğe bağlıdır. Bir komut dosyasının çalışacağı JavaScript dünyası. Varsayılan olarak ISOLATED değerine ayarlanır. Ayrıca Bağımsız dünyalarda çalışma başlıklı makaleyi de inceleyin.

Dinamik beyanlarla ekleme yapma

Dinamik içerik komut dosyaları, içerik komut dosyalarının eşleşme kalıpları iyi bilinmediğinde veya içerik komut dosyalarının her zaman bilinen ana makinelere yerleştirilmemesi gerektiğinde kullanışlıdır.

Chrome 96'da kullanıma sunulan dinamik beyanlar statik beyanlara benzer ancak içerik komut dosyası nesnesi, manifest.json yerine chrome.scripting ad alanındaki yöntemler kullanılarak Chrome'a kaydedilir. Komut Dosyası API'si, uzantı geliştiricilerine şunları da sağlar:

Statik bildirimler gibi dinamik bildirimler de JavaScript dosyaları, CSS dosyaları veya her ikisi 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 veya belirli durumlarda çalıştırılması gereken içerik komut dosyaları için programatik enjeksiyon kullanın.

Bir içerik komut dosyasını programatik olarak yerleştirmek için eklentinizin, komut dosyası yerleştirmeye çalıştığı sayfa için barındırma izinlerine ihtiyacı vardır. Ana makine izinleri, uzantınızın manifest'i kapsamında isteyerek veya geçici olarak "activeTab" kullanarak verilebilir.

Aşağıda, activeTab tabanlı bir uzantının farklı sürümleri gösterilmektedir.

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"]
  });
});

Ayrıca, bir işlev gövdesi içerik komut dosyası olarak enjekte edilebilir ve 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,
  });
});

Enjekte edilen işlevin, orijinal işlevin kendisi değil, chrome.scripting.executeScript() çağrısında referans verilen işlevin bir kopyası olduğunu unutmayın. Bu nedenle, işlevin gövdesi kendi kendine yetmelidir. İşlevin dışındaki değişkenlere yapılan referanslar, içerik komut dosyasının ReferenceError hatası vermesine neden olur.

İşlev olarak eklerken işleve bağımsız değişkenler de aktarabilirsiniz.

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 genel eşlemelerini hariç tutma

Belirtilen sayfa eşleşmesini özelleştirmek için aşağıdaki alanları açıklayıcı 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 yerleştirileceği sayfaları hariç tutar. Bu dizelerin söz dizimi hakkında ayrıntılı bilgi için Eşleşme Kalıpları başlıklı makaleyi inceleyin.
include_globs dize dizisi İsteğe bağlıdır. Yalnızca bu genel ifadeyle eşleşen URL'leri dahil etmek için matches'ten 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 genel ifadeyle eşleşen URL'leri hariç tutmak için matches'ten sonra uygulanır. @exclude Greasemonkey anahtar kelimesini taklit etmek için tasarlanmıştır.

İçerik komut dosyası, aşağıdakilerin her ikisi de doğruysa bir sayfaya yerleştirilir:

  • URL'si herhangi bir matches kalıbıyla ve herhangi bir include_globs kalıbıyla eşleşir.
  • URL, exclude_matches veya exclude_globs kalıplarıyla da eşleşmiyor. matches özelliği zorunlu olduğundan exclude_matches, include_globs ve exclude_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şleme kalıplarından farklı ve daha esnek bir söz dizimi kullanır. Kabul edilen genel dize, "joker karakter" yıldız işaretleri ve soru işaretleri içerebilen URL'lerdir. Yıldız işareti (*), boş dize dahil olmak üzere herhangi bir uzunluktaki dizelerle eşleşirken soru işareti (?) herhangi bir tek karakterle eşleşir.

Örneğin, https://???.example.com/foo/\* genel ifadesi aşağıdakilerden herhangi biriyle eşleşir:

  • https://www.example.com/foo/bar
  • https://the.example.com/foo/

Ancak aşağıdakilerle 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*'e enjekte eder ancak https://www.nytimes.com/sports/index.html'ye enjekte etmez:

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'e enjekte eder ancak https://science.nytimes.com veya https://www.nytimes.com/science'a enjekte etmez:

manifest.json

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["https://*.nytimes.com/*"],
      "exclude_globs": ["*science*"],
      "js": ["contentScript.js"]
    }
  ],
  ...
}

Doğru kapsama ulaşmak için bunlardan biri, tümü veya bazıları 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 ekleneceğ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ğinin hemen sonrasındaki zaman aralığında komut dosyaları yerleştirmek için bir zaman seçer. Tam enjeksiyon anı, belgenin ne kadar karmaşık olduğuna ve yüklenmesinin ne kadar sürdüğüne bağlıdır ve sayfa yükleme hızı için optimize edilir.

"document_idle" zamanında ç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 window.onload'ten sonra çalıştırılmasının kesinlikle gerekli olduğu durumlarda uzantı, document.readyState özelliğini kullanarak onload'un tetiklenip tetiklenmediğini kontrol edebilir.
document_start dize Komut dosyaları, css'deki dosyalardan sonra ancak başka bir DOM oluşturulmadan veya başka bir komut dosyası çalıştırılmadan önce eklenir.
document_end dize Komut dosyaları, DOM tamamlandıktan hemen sonra ancak resim ve çerçeve gibi alt kaynakların yüklenmesinden önce eklenir.

Çerçeveleri belirtme

Manifestte belirtilen açıklayıcı içerik komut dosyaları için "all_frames" alanı, uzantının JavaScript ve CSS dosyalarının belirtilen URL koşullarını karşılayan tüm çerçevelere mi yoksa yalnızca sekmedeki en üst çerçeveye mi ekleneceğini belirtmesine olanak tanır:

manifest.json

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["https://*.nytimes.com/*"],
      "all_frames": true,
      "js": ["contentScript.js"]
    }
  ],
  ...
}

İçerik komut dosyalarını chrome.scripting.registerContentScripts(...) kullanarak programatik olarak kaydederken allFrames parametresi, içerik komut dosyasının belirtilen URL koşullarını karşılayan tüm çerçevelere mi yoksa yalnızca bir sekmedeki en üst çerçeveye mi ekleneceğ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" ],
}]);

Uzantılar, eşleşen bir çerçeveyle ilişkili ancak eşleşmeyen çerçevelerde komut dosyaları çalıştırmak isteyebilir. Bu durumun yaşandığı yaygın bir senaryo, eşleşen bir çerçeve tarafından oluşturulan ancak URL'leri komut dosyasının belirtilen kalıplarıyla eşleşmeyen URL'lere sahip çerçevelerdir.

Bu durum, bir uzantının about:, data:, blob: ve filesystem: düzenleri içeren URL'leri çerçevelere eklemek istediğinde ortaya çıkar. Bu durumlarda, URL içerik komut dosyasının kalıbıyla eşleşmez (about: ve data: durumunda, about:blank veya data:text/html,<html>Hello, World!</html>'ta olduğu gibi URL'ye üst URL'yi veya kaynağı hiç dahil etmez). Ancak bu çerçeveler, oluşturan çerçeveyle ilişkilendirilmeye devam edebilir.

Uzantılar, bu çerçevelere içerik eklemek için manifest'teki bir içerik komut dosyası spesifikasyonunda "match_origin_as_fallback" mülkünü 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, karenin eşleşip eşleşmediğini belirlemek için karenin URL'si yerine kareyi başlatan öğenin kaynağına bakar. Bunun, hedef çerçevenin kökeninden (ör. data: URL'leri null bir kaynağa sahiptir).

Çerçevenin başlatıcısı, hedef çerçeveyi oluşturan veya bu çerçeveye giden çerçevedir. Bu genellikle doğrudan üst öğe veya açan öğe olsa da öyle olmayabilir (iframe içinde bir iframe'de gezinen bir çerçeve durumunda olduğu gibi).

Bu işlem, başlatıcı çerçevenin kaynağını karşılaştırdığından başlatıcı çerçeve, bu kaynaktan gelen herhangi bir yolda olabilir. Bu sonucun net olması 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" önceliklidir.

Yerleştirme sayfasıyla iletişim

İçerik komut dosyalarının yürütme ortamları ve bunları barındıran sayfalar birbirinden izole olsa da sayfanın DOM'una erişimi paylaşırlar. Sayfa, içerik komut dosyasıyla veya içerik komut dosyası üzerinden uzantı ile 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 example.html sayfası, kendisine mesaj gönderir. Bu ileti, içerik komut dosyası tarafından yakalanıp incelenir ve ardından uzantı işlemine gönderilir. Bu sayede sayfa, uzantı süreciyle iletişim hattı oluşturur. Benzer yöntemlerle tersini de yapabilirsiniz.

Uzatma 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")

CSS dosyasında yazı tipleri veya resimler kullanmak için aşağıdaki örnekte (content.css) gösterildiği gibi bir URL oluşturmak üzere @@extension_id seçeneğini 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'e erişilebilir kaynaklar olarak tanımlanmalıdır:

manifest.json

{
 ...
 "web_accessible_resources": [
   {
     "resources": [ "images/*.png" ],
     "matches": [ "https://example.com/*" ]
   },
   {
     "resources": [ "fonts/*.woff" ],
     "matches": [ "https://example.com/*" ]
   }
 ],
 ...
}

Güvende kalın

İzole dünyalar bir koruma katmanı sağlarken içerik komut dosyalarının kullanılması, uzantı ve web sayfasında güvenlik açıkları oluşturabilir. İçerik komut dosyası, fetch() çağrısı yaparak ayrı bir web sitesinden içerik alıyorsa içeriği eklemeden önce siteler arası komut dosyası çalıştırma saldırılarına karşı filtrelediğinizden emin olun. "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 izin verilmez:

Yapılmaması gerekenler:

content-script.js

const data = document.getElementById("json-data");
// WARNING! Might be evaluating an evil script!
const parsed = eval("(" + data + ")");
Yapılmaması gerekenler:

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:

Yapılması gerekenler

content-script.js

const data = document.getElementById("json-data")
// JSON.parse does not evaluate the attacker's scripts.
const parsed = JSON.parse(data);
Yapılması gerekenler

content-script.js

const elmt_id = ...
// The closure form of setTimeout does not evaluate scripts.
window.setTimeout(() => animate(elmt_id), 200);