Mesaj iletme

İçerik komut dosyaları, onları çalıştıran uzantı değil, web sayfası bağlamında çalıştığı için genellikle uzantının geri kalanıyla iletişim kurmak için yollara ihtiyaç duyarlar. Örneğin, bir RSS okuyucu uzantısı, içerik komut dosyalarını kullanarak bir sayfada RSS özet akışı olup olmadığını tespit edebilir ve ardından hizmet çalışanına söz konusu sayfa için bir işlem simgesi görüntülemesi gerektiğini bildirebilir.

Bu iletişimde mesaj iletme kullanılır. Bu sayede hem uzantılar hem de içerik komut dosyaları birbirlerinin mesajlarını dinleyebilir ve aynı kanaldan yanıt verebilir. Bir mesaj geçerli herhangi bir JSON nesnesini (boş, boole, sayı, dize, dizi veya nesne) içerebilir. İki mesaj iletme API'si vardır: biri tek seferlik istekler için, diğeri birden çok mesajın gönderilmesine izin veren uzun süreli bağlantılar için daha karmaşıktır. Uzantılar arasında mesaj gönderme hakkında bilgi 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 içerik komut dosyasına tek seferlik bir JSON serileştirilebilir mesajı gönderebilmenizi sağlar. Yanıtı işlemek için döndürülen sözü kullanın. Eski uzantılarla geriye dönük uyumluluk için, geri çağırmayı son bağımsız değişken olarak iletebilirsiniz. Söz verme ve geri çağırma özelliklerini aynı görüşmede kullanamazsınız.

Geri çağırmaları taahhütlere dönüştürme ve bunları uzantılarda kullanma hakkında bilgi edinmek için Manifest V3 taşıma rehberine bakın.

İçerik komut dosyasından istek şu şekilde gönderilir:

content-script.js:

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

İçerik komut dosyasına istek göndermek için isteğin hangi sekmeye uygulanacağını aşağıda gösterildiği 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. Bunlar 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() eşzamanlı olarak çağrıldı. sendResponse() öğesini eşzamansız olarak kullanmak için onMessage etkinlik işleyicisine return true; ekleyin.

onMessage etkinliklerini birden çok sayfa dinliyorsa yalnızca belirli bir etkinlik için sendResponse() yöntemini ilk çağıran kişi yanıt göndermede başarılı olur. 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ü mesaj iletme kanalı oluşturmak amacıyla mesajları içerik komut dosyasından uzantı sayfasına iletmek için runtime.connect() veya bir uzantı sayfasından içerik komut dosyasına mesaj iletmek için tabs.connect() numaralı telefonu arayın. Farklı bağlantı türlerini ayırt etmek için kanalınızı adlandırabilirsiniz.

Uzun süreli bir bağlantı için olası bir kullanım alanı otomatik form doldurma uzantısıdır. İçerik komut dosyası, belirli bir giriş için uzantı sayfasında bir kanal açabilir ve form verilerinin doldurulmasını istemek amacıyla sayfadaki her giriş öğesi için uzantıya bir mesaj gönderebilir. Paylaşılan bağlantı, uzantının uzantı bileşenleri arasında durum paylaşmasına olanak tanır.

Bağlantı kurulurken, bu bağlantı üzerinden mesaj gönderip almak için her iki uca bir runtime.Port nesnesi atanır.

İçerik komut dosyasından bir kanal açmak ve mesaj gönderip almak 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 içerik komut dosyasına bir istek göndermek için önceki örnekte bulunan runtime.connect() çağrısını tabs.connect() ile değiştirin.

İçerik komut dosyası veya uzantı sayfası için gelen bağlantıları işlemek üzere bir runtime.onConnect etkinlik işleyici ayarlayın. Uzantınızın başka bir parçası connect() yöntemini çağırdığında bu etkinliği ve runtime.Port nesnesini etkinleştirir. 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ı parçaları 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ı kullanabilen en küçük parçasıdır. Bir uzantının parçası tabs.connect(), runtime.connect() veya runtime.connectNative()'ı çağırdığında, postMessage() kullanarak mesajları hemen gönderebilen bir Bağlantı Noktası oluşturur.

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

Örneğin bir bağlantının ne zaman kapatıldığını öğrenmek isteyebilirsiniz. Örneğin, her açık bağlantı noktası için ayrı durum bilgileri kullanıyorsanız bunu öğrenebilirsiniz. Bunun için runtime.Port.onDisconnect etkinliğini dinleyin. Bu etkinlik, kanalın diğer ucunda aşağıdaki nedenlerden herhangi birine sahip olabilecek geçerli bağlantı noktaları olmadığında tetiklenir:

  • Diğer uçta runtime.onConnect için işleyici yok.
  • Bağlantı noktasını içeren sekme yüklenmemişse (örneğin, sekmede geziniliyorsa).
  • connect() öğesinin çağrıldığı çerçevenin yüklemesi kaldırıldı.
  • Bağlantı noktasını alan tüm kareler (runtime.onConnect üzerinden) kaldırıldı.
  • runtime.Port.disconnect() diğer uç tarafından çağrılır. connect() çağrısı, alıcının ucunda birden çok bağlantı noktasıyla sonuçlanırsa ve bu bağlantı noktalarından herhangi birinde disconnect() çağrılırsa onDisconnect etkinliği diğer bağlantı noktalarında değil, yalnızca gönderen bağlantı noktasında tetiklenir.

Uzantılar arası mesajlaşma

Mesajlaşma API'sini, uzantınızdaki farklı bileşenler arasında mesaj göndermeye ek olarak diğer uzantılarla iletişim kurmak için de kullanabilirsiniz. Bu, diğer uzantıların kullanması için herkese açık bir API sunmanıza olanak tanır.

Diğer uzantılardan gelen istekleri ve bağlantıları dinlemek için runtime.onMessageExternal veya runtime.onConnectExternal yöntemlerini kullanın. Aşağıda bunların her birine bir örnek 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 şekilde 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 ileti gönderme

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

manifest.json

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

Bu durumda mesajlaşma API'si, belirttiğiniz URL kalıplarıyla eşleşen tüm sayfalarda görünür. URL kalıbı en az ikinci düzey bir 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ğinden, bu özelliği kullanan uzantılarla ilgili 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() veya 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ınızdan, uzantılar arası mesajlaşmada olduğu gibi runtime.onMessageExternal veya runtime.onConnectExternal API'lerini kullanarak web sayfalarından gelen mesajları dinleyin. Aşağıda 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 ana makinesi olarak kayıtlı yerel uygulamalarla mesaj alışverişi yapabilir. Bu özellik hakkında daha fazla bilgi edinmek için Yerel mesajlaşma konusuna bakın.

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

Aşağıda, mesajlaşmayla ilgili olarak güvenlikle ilgili dikkat edilmesi gereken birkaç nokta verilmiştir.

İçerik komut dosyalarının daha az güvenilir olması

İçerik komut dosyaları, uzantı hizmet çalışanına göre daha az güvenilirdir. Örneğin kötü amaçlı bir web sayfası, içerik komut dosyalarını çalıştıran oluşturma sürecinin güvenliğini ihlal edebilir. Bir içerik komut dosyasından gelen mesajların bir saldırgan tarafından oluşturulmuş olabileceğini varsayın ve tüm girişleri doğrulayıp temizlediğinizden emin olun. İçerik komut dosyasına gönderilen verilerin web sayfasına sızdırılabileceğini varsayın. İçerik komut dosyalarından alınan iletiler tarafından 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ına 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 bunları HTML olarak yorumlamaktan veya beklenmeyen kodun çalışmasına izin verebilecek şekilde kullanmaktan kaçının.

Daha güvenli yöntemler

Mümkün olduğunda komut dosyası ç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ınızın güvenlik açığına neden olacak 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;
});