Mesaj iletme

İçerik komut dosyaları, bunları çalıştıran uzantının değil, bir web sayfasının bağlamında çalıştığından, genellikle uzantının geri kalanıyla iletişim kurmanın yollarına ihtiyaç duyar. Örneğin, bir RSS okuyucu uzantısı, bir sayfada RSS özet akışının varlığını algılamak için içerik komut dosyalarını kullanabilir, ardından hizmet çalışanına söz konusu sayfa için bir işlem simgesi görüntülemesini bildirebilir.

Bu iletişimde mesaj aktarımı kullanılır. Bu sayede hem uzantılar hem de içerik komut dosyaları birbirlerinin mesajlarını dinleyebilir ve aynı kanalda yanıt verebilir. Bir mesaj, geçerli herhangi bir JSON nesnesi (boş, boole, sayı, dize, dizi veya nesne) içerebilir. İki mesaj aktarma API'si vardır: Tek seferlik istekler için bir API ve birden fazla mesajın gönderilmesine izin veren uzun süreli bağlantılar için daha karmaşık bir API. Uzantılar arasında mesaj gönderme hakkında bilgi edinmek için uzantılar arası mesajlar bölümüne bakın.

Tek seferlik istekler

Uzantınızın başka bir bölümüne tek bir mesaj göndermek ve isteğe bağlı olarak yanıt almak için runtime.sendMessage() veya tabs.sendMessage() numaralı telefonu arayın. Bu yöntemler, bir içerik komut dosyasından uzantıya veya uzantıdan bir içerik komut dosyasına tek seferlik JSON dizili mesaj göndermenize olanak tanır. Yanıtı işlemek için döndürülen sözü kullanın. Eski uzantılarla geriye dönük uyumluluk için son bağımsız değişken olarak geri çağırma işlevi gönderebilirsiniz. Aynı görüşmede hem söz hem de geri arama kullanamazsınız.

Geri çağırmaları vaatlere dönüştürme ve bunları uzantılarda kullanma hakkında bilgi için Manifest V3 taşıma rehberini inceleyin.

İçerik komut dosyasından istek gönderme işlemi şu şekilde görünür:

content-script.js:

(async () => {
  const response = await chrome.runtime.sendMessage({greeting: "hello"});
  // do something with response here, not outside the function
  console.log(response);
})();

Bir mesajı senkronize olarak yanıtlamak istiyorsanız yanıtı aldıktan sonra sendResponse işlevini çağırmanız ve işlemin tamamlandığını belirtmek için false döndürmeniz yeterlidir. Asynkron yanıt vermek için true döndürerek sendResponse geri çağırma işlevini kullanmaya hazır olana kadar etkin tutun. Desteklenmeyen işlevler, desteklenmeyen bir Promise döndürdükleri için desteklenmiyor.

Bir içerik komut dosyasına istek göndermek için isteğin hangi sekme için geçerli olduğunu aşağıdaki gibi belirtin. Bu örnek, hizmet çalışanları, pop-up'lar ve sekme olarak açılan chrome-extension:// sayfalarında çalışır.

(async () => {
  const [tab] = await chrome.tabs.query({active: true, lastFocusedWindow: true});
  const response = await chrome.tabs.sendMessage(tab.id, {greeting: "hello"});
  // do something with response here, not outside the function
  console.log(response);
})();

Mesajı almak için bir runtime.onMessage etkinlik işleyici ayarlayın. Aşağıdakiler hem uzantılarda hem de içerik komut dosyalarında aynı kodu kullanır:

content-script.js veya service-worker.js:

chrome.runtime.onMessage.addListener(
  function(request, sender, sendResponse) {
    console.log(sender.tab ?
                "from a content script:" + sender.tab.url :
                "from the extension");
    if (request.greeting === "hello")
      sendResponse({farewell: "goodbye"});
  }
);

Önceki örnekte sendResponse() zaman uyumlu olarak çağrılmıştır. sendResponse() öğesini eşzamansız olarak kullanmak için onMessage etkinlik işleyiciye return true; ekleyin.

Birden fazla sayfa onMessage etkinliklerini dinliyorsa yalnızca belirli bir etkinlik için sendResponse() çağrısını ilk yapan sayfa yanıtı göndermeyi başarır. Bu etkinliğe verilen diğer tüm yanıtlar yoksayılır.

Uzun ömürlü bağlantılar

Yeniden kullanılabilir, uzun ömürlü bir mesaj iletme kanalı oluşturmak için mesajları içerik komut dosyasından bir uzantı sayfasına iletmek için runtime.connect() veya bir uzantı sayfasından içerik komut dosyasına mesaj iletmek için tabs.connect() çağrısı yapın. Farklı bağlantı türlerini ayırt etmek için kanalınızı adlandırabilirsiniz.

Uzun süreli bağlantının olası kullanım alanlarından biri, otomatik form doldurma uzantısıdır. İçerik komut dosyası, belirli bir giriş için uzantı sayfasına bir kanal açabilir ve sayfadaki her giriş öğesi için uzantıya bir mesaj göndererek doldurulacak form verilerini isteyebilir. Paylaşılan bağlantı, uzantının uzantı bileşenleri arasında durum paylaşmasına olanak tanır.

Bağlantı kurulurken her uç noktaya, bu bağlantı üzerinden mesaj göndermek ve almak için bir runtime.Port nesnesi atanır.

İçerik komut dosyasından bir kanal açmak, mesaj göndermek ve dinlemek için aşağıdaki kodu kullanın:

content-script.js:

var port = chrome.runtime.connect({name: "knockknock"});
port.postMessage({joke: "Knock knock"});
port.onMessage.addListener(function(msg) {
  if (msg.question === "Who's there?")
    port.postMessage({answer: "Madame"});
  else if (msg.question === "Madame who?")
    port.postMessage({answer: "Madame... Bovary"});
});

Uzantıdan bir içerik komut dosyasına istek göndermek için önceki örnekteki runtime.connect() çağrısını tabs.connect() ile değiştirin.

Bir içerik komut dosyası veya uzantı sayfası için gelen bağlantıları işlemek üzere bir runtime.onConnect etkinlik işleyicisi ayarlayın. Uzantınızın başka bir bölümü connect()'ü çağrdığında bu etkinlik ve runtime.Port nesnesi etkinleştirilir. Gelen bağlantılara yanıt verme kodu şu şekilde görünür:

service-worker.js:

chrome.runtime.onConnect.addListener(function(port) {
  console.assert(port.name === "knockknock");
  port.onMessage.addListener(function(msg) {
    if (msg.joke === "Knock knock")
      port.postMessage({question: "Who's there?"});
    else if (msg.answer === "Madame")
      port.postMessage({question: "Madame who?"});
    else if (msg.answer === "Madame... Bovary")
      port.postMessage({question: "I don't get it."});
  });
});

Bağlantı noktası ömrü

Bağlantı noktaları, uzantının farklı bölümleri arasında iki yönlü bir iletişim yöntemi olarak tasarlanmıştır. Üst düzey çerçeve, bir uzantının bağlantı noktasını kullanabilen en küçük kısmıdır. Bir uzantının bir kısmı tabs.connect(), runtime.connect() veya runtime.connectNative()'i çağrdığında postMessage() kullanarak anında mesaj gönderebilen bir Port oluşturur.

Bir sekmede birden fazla çerçeve varsa tabs.connect() çağrısı, sekmedeki her çerçeve için runtime.onConnect etkinliğini bir kez tetikler. Benzer şekilde, runtime.connect() çağrılırsa onConnect etkinliği, uzantı sürecindeki her kare için bir kez tetiklenebilir.

Örneğin, her açık bağlantı noktası için ayrı durumlar tutuyorsanız bir bağlantının ne zaman kapatıldığını öğrenmek isteyebilirsiniz. Bunu yapmak için runtime.Port.onDisconnect etkinliğini dinleyin. Bu etkinlik, kanalın diğer ucunda geçerli bağlantı noktası olmadığında tetiklenir. Bu durum aşağıdakilerden herhangi birinden kaynaklanabilir:

  • Diğer tarafta runtime.onConnect için dinleyici yok.
  • Bağlantı noktasını içeren sekme kaldırılır (örneğin, sekmeye gidilirse).
  • connect() işlevinin çağrıldığı çerçeve kaldırıldı.
  • Bağlantı noktasını alan tüm çerçeveler kaldırıldı (runtime.onConnect üzerinden).
  • runtime.Port.disconnect(), karşı taraf tarafından aranır. connect() çağrısı, alıcının ucunda birden fazla bağlantı noktasıyla sonuçlanırsa ve disconnect() bu bağlantı noktalarından herhangi birinde çağrılırsa onDisconnect etkinliği diğer bağlantı noktalarında değil, yalnızca gönderen bağlantı noktasında tetiklenir.

Çapraz uzantılar mesajlaşması

Mesajlaşma API'sini, uzantınızdaki farklı bileşenler arasında mesaj göndermenin yanı sıra diğer uzantılarla iletişim kurmak için de kullanabilirsiniz. Bu sayede, diğer uzantıların kullanabileceği herkese açık bir API sunabilirsiniz.

Diğer uzantılardan gelen isteklerini ve bağlantılarını dinlemek için runtime.onMessageExternal veya runtime.onConnectExternal yöntemlerini kullanın. Her biri için bir örnek aşağıda verilmiştir:

service-worker.js

// For a single request:
chrome.runtime.onMessageExternal.addListener(
  function(request, sender, sendResponse) {
    if (sender.id === blocklistedExtension)
      return;  // don't allow this extension access
    else if (request.getTargetData)
      sendResponse({targetData: targetData});
    else if (request.activateLasers) {
      var success = activateLasers();
      sendResponse({activateLasers: success});
    }
  });

// For long-lived connections:
chrome.runtime.onConnectExternal.addListener(function(port) {
  port.onMessage.addListener(function(msg) {
    // See other examples for sample onMessage handlers.
  });
});

Başka bir uzantıya mesaj göndermek için iletişim kurmak istediğiniz uzantının kimliğini aşağıdaki gibi iletin:

service-worker.js

// The ID of the extension we want to talk to.
var laserExtensionId = "abcdefghijklmnoabcdefhijklmnoabc";

// For a simple request:
chrome.runtime.sendMessage(laserExtensionId, {getTargetData: true},
  function(response) {
    if (targetInRange(response.targetData))
      chrome.runtime.sendMessage(laserExtensionId, {activateLasers: true});
  }
);

// For a long-lived connection:
var port = chrome.runtime.connect(laserExtensionId);
port.postMessage(...);

Web sayfalarından mesaj gönderme

Uzantılar, diğer web sayfalarından gelen mesajları da alıp yanıtlayabilir ancak web sayfalarına mesaj gönderemez. Bir web sayfasından uzantıya mesaj göndermek için manifest.json dosyanızda "externally_connectable" manifest anahtarını kullanarak hangi web siteleriyle iletişim kurmak istediğinizi belirtin. Örneğin:

manifest.json

"externally_connectable": {
  "matches": ["https://*.example.com/*"]
}

Bu işlem, Mesajlaşma API'sini belirttiğiniz URL kalıplarıyla eşleşen tüm sayfalara gösterir. URL kalıbı en az bir ikinci düzey alan içermelidir. Yani "*", "*.com", "*.co.uk" ve "*.appspot.com" gibi ana makine adı kalıpları desteklenmez. Chrome 107'den itibaren tüm alanlara erişmek için <all_urls> kullanabilirsiniz. Tüm ana makineleri etkilediği için bu özelliği kullanan uzantıların Chrome Web Mağazası incelemelerinin daha uzun sürebileceğini unutmayın.

Belirli bir uygulama veya uzantıya mesaj göndermek için runtime.sendMessage() ya da runtime.connect() API'lerini kullanın. Örneğin:

webpage.js

// The ID of the extension we want to talk to.
var editorExtensionId = "abcdefghijklmnoabcdefhijklmnoabc";

// Make a simple request:
chrome.runtime.sendMessage(editorExtensionId, {openUrlInEditor: url},
  function(response) {
    if (!response.success)
      handleError(url);
  });

Uzantıdan, uzantılar arası mesajlaşmada olduğu gibi runtime.onMessageExternal veya runtime.onConnectExternal API'lerini kullanarak web sayfalarından gelen mesajları dinleyebilirsiniz. Aşağıda bununla ilgili bir örnek verilmiştir:

service-worker.js

chrome.runtime.onMessageExternal.addListener(
  function(request, sender, sendResponse) {
    if (sender.url === blocklistedWebsite)
      return;  // don't allow this web page access
    if (request.openUrlInEditor)
      openUrl(request.openUrlInEditor);
  });

Yerel mesajlaşma

Uzantılar, yerel mesajlaşma sunucusu olarak kayıtlı yerel uygulamalarla mesaj alışverişi yapabilir. Bu özellik hakkında daha fazla bilgi edinmek için Yerel mesajlaşma bölümüne bakın.

Güvenlikle ilgili olarak göz önünde bulundurulması gerekenler

Mesajlaşmayla ilgili güvenlikle ilgili birkaç noktayı aşağıda bulabilirsiniz.

İçerik komut dosyaları daha az güvenilirdir.

İçerik komut dosyaları, uzantı hizmet işçisine kıyasla daha az güvenilirdir. Örneğin, kötü amaçlı bir web sayfası, içerik komut dosyalarını çalıştıran oluşturma sürecini tehlikeye atabilir. İçerik komut dosyasından gelen mesajların bir saldırgan tarafından hazırlanmış olabileceğini varsayarak tüm girişleri doğruladığınızdan ve temizlediğinizden emin olun. İçerik komut dosyasına gönderilen tüm verilerin web sayfasına sızdırılabileceğini varsayın. İçerik komut dosyalarından alınan mesajlarla tetiklenebilecek ayrıcalıklı işlemlerin kapsamını sınırlayın.

Siteler arası komut dosyası çalıştırma

Komut dosyalarınızı siteler arası komut dosyası çalıştırma işlemlerine karşı koruduğunuzdan emin olun. Kullanıcı girişi, içerik komut dosyası veya API aracılığıyla diğer web siteleri gibi güvenilmeyen bir kaynaktan veri alırken bu verileri HTML olarak yorumlamaktan veya beklenmedik kodun çalıştırılmasına izin verebilecek bir şekilde kullanmaktan kaçının.

Daha güvenli yöntemler

Mümkün olduğunda komut dosyalarını çalıştırmayan API'ler kullanın:

service-worker.js

chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
  // JSON.parse doesn't evaluate the attacker's scripts.
  var resp = JSON.parse(response.farewell);
});

service-worker.js

chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
  // innerText does not let the attacker inject HTML elements.
  document.getElementById("resp").innerText = response.farewell;
});
Güvenli olmayan yöntemler

Uzantılarınızın güvenliğini ihlal eden aşağıdaki yöntemleri kullanmaktan kaçının:

service-worker.js

chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
  // WARNING! Might be evaluating a malicious script!
  var resp = eval(`(${response.farewell})`);
});

service-worker.js

chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
  // WARNING! Might be injecting a malicious script!
  document.getElementById("resp").innerHTML = response.farewell;
});