Mesaj iletme

İçerik komut dosyaları, onları çalıştıran uzantının bağlamında değil, bir web sayfasının bağlamında çalıştırıldığı için 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ışı olup olmadığını algılamak için içerik komut dosyalarını kullanabilir ve ardından hizmet çalışanını bilgilendirerek söz konusu sayfa için bir işlem simgesi gösterebilir.

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. Mesajlar, geçerli JSON nesnelerini (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 olanak tanıyan 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ıları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() tuşuna bası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 promise'i 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ı çağrıda bir promise ve geri çağırma işlevi kullanamazsınız.

Bir mesaj gönderdiğinizde, mesajı işleyen etkinlik işleyiciye isteğe bağlı üçüncü bir bağımsız değişken (sendResponse) iletilir. Bu, mesajı gönderen işlevin döndürülen değeri olarak kullanılan, JSON'a serileştirilebilir bir nesne alan bir işlevdir. Varsayılan olarak sendResponse geri çağırma işlevi eşzamanlı olarak çağrılmalıdır. sendResponse değerini almak için asenkron çalışma yapmak istiyorsanız etkinlik dinleyicisinden bir gerçek değer true (yalnızca doğru değer değil) döndürmeniz gerekir. Bu işlem, sendResponse çağrılana kadar mesaj kanalının diğer tarafa açık kalmasını sağlar.

// Event listener
function handleMessages(message, sender, sendResponse) {

  fetch(message.url)
    .then((response) => sendResponse({statusCode: response.status}))

  // Since `fetch` is asynchronous, must send an explicit `true`
  return true;
}

// Message sender
  const {statusCode} = await chrome.runtime.sendMessage({
    url: 'https://example.com'
  });

Geri çağırma işlevlerini Promise'lere dönüştürme ve bunları uzantılarda kullanma hakkında bilgi edinmek için Manifest V3 taşıma kılavuzuna bakın.

İç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. Asenkron yanıt vermek için true döndürerek sendResponse geri çağırma işlevini kullanmaya hazır olana kadar etkin tutun. Asynchronize işlevler, desteklenmeyen bir Promise döndürdüğü için desteklenmez.

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);
})();

Mesajları 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() adını asenkron olarak kullanmak için onMessage etkinlik işleyicisine 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 aktarma kanalı oluşturmak için bir içerik komut dosyasından uzantı sayfasına mesaj aktarmak üzere runtime.connect() veya bir uzantı sayfasından içerik komut dosyasına mesaj aktarmak için tabs.connect() işlevini çağırın. Farklı bağlantı türlerini ayırt etmek için kanalınızı adlandırabilirsiniz.

Uzun süreli bağlantıların olası kullanım alanlarından biri, otomatik form doldurma uzantıları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ı kullanabilen en küçük parçası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. Bunun nedeni aşağıdakilerden biri olabilir:

  • 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 (runtime.onConnect aracılığıyla) kaldırıldı.
  • runtime.Port.disconnect(), karşı taraf tarafından aranır. Bir connect() çağrısı, alıcı tarafında birden fazla bağlantı noktasıyla sonuçlanırsa ve bu bağlantı noktalarından herhangi birinde disconnect() çağrılırsa onDisconnect etkinliği yalnızca gönderen bağlantı noktasında tetiklenir, diğer bağlantı noktalarında tetiklenmez.

Uzantılar arası mesajlaşma

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 minimal 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. Bu değişiklik 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 uygulamaya 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.
const editorExtensionId = 'abcdefghijklmnoabcdefhijklmnoabc';

// Check if extension is installed
if (chrome && chrome.runtime) {
  // Make a request:
  chrome.runtime.sendMessage(
    editorExtensionId,
    {
      openUrlInEditor: url
    },
    (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ın yerel mesajlaşma ana makinesi olarak kayıtlı yerel uygulamalarla mesaj alışverişi yapması mümkündür. Bu özellik hakkında daha fazla bilgi edinmek için Doğal mesajlaşma başlıklı makaleyi inceleyin.

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 varsayın ve 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ızabileceğ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 dosyası çalıştırmayan API'leri 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;
});