Mesaj iletme

Mesajlaşma API'leri, uzantınızla ilişkili bağlamlarda çalışan farklı komut dosyaları arasında iletişim kurmanıza olanak tanır. Bu, hizmet çalışanınız, chrome-extension://pages ve içerik komut dosyaları arasındaki iletişimi içerir. Ö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 ve ardından hizmet çalışanını, söz konusu sayfanın işlem simgesini güncellemesi için bilgilendirebilir.

İki ileti geçirme API'si vardır: biri tek seferlik istekler için, diğeri ise birden fazla ileti gönderilmesine olanak tanıyan uzun süreli bağlantılar için daha karmaşıktır.

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() işlevini çağırın. Bu yöntemler, içerik komut dosyasından uzantıya veya uzantıdan içerik komut dosyasına tek seferlik JSON olarak serileştirilebilen bir mesaj göndermenize olanak tanır. Her iki API de alıcı tarafından sağlanan yanıta çözümlenen bir Promise döndürür.

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

Yanıtlar

Bir mesajı dinlemek için chrome.runtime.onMessage etkinliğini kullanın:

// Event listener
function handleMessages(message, sender, sendResponse) {
  fetch(message.url)
    .then((response) => sendResponse({statusCode: response.status}))

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

chrome.runtime.onMessage.addListener(handleMessages);

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

Etkinlik işleyicisi çağrıldığında üçüncü parametre olarak bir sendResponse işlevi iletilir. Bu, yanıt vermek için çağrılabilen bir işlevdir. Varsayılan olarak, sendResponse geri çağırma işlevi senkronize olarak çağrılmalıdır. sendResponse öğesine iletilen değeri almak için eşzamansız çalışma yapmak istiyorsanız etkinlik dinleyicisinden kesinlikle true değişmezini (yalnızca doğru değeri değil) döndürmelisiniz. Bu işlem, sendResponse aranana kadar mesaj kanalını diğer uç için açık tutar.

sendResponse işlevini parametre olmadan çağırırsanız yanıt olarak null gönderilir.

Birden fazla sayfa onMessage etkinliklerini dinliyorsa belirli bir etkinlik için sendResponse() işlevini çağıran yalnızca ilk sayfa yanıtı gönderebilir. Etkinliğe verilen diğer tüm yanıtlar yoksayılır.

Uzun süreli bağlantılar

Yeniden kullanılabilir, uzun ömürlü bir mesajlaşma kanalı oluşturmak için şunu çağırın:

  • runtime.connect() içerik komut dosyasından uzantı sayfasına mesaj iletmek için
  • tabs.connect(): Uzantı sayfasından içerik komut dosyasına mesaj iletmek için kullanılır.

Farklı bağlantı türlerini ayırt etmek için name anahtarıyla bir seçenek parametresi ileterek kanalınızı adlandırabilirsiniz:

const port = chrome.runtime.connect({name: "example"});

Uzun süreli 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ı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 bileşenler arasında durumu paylaşmasına olanak tanır.

Bağlantı oluşturulurken her uca, bu bağlantı üzerinden mesaj gönderme ve alma için bir runtime.Port nesnesi atanır.

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

content-script.js:

const port = chrome.runtime.connect({name: "knockknock"});
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"});
  }
});
port.postMessage({joke: "Knock knock"});

Uzantıdan içerik komut dosyasına istek göndermek için önceki örnekteki 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 runtime.onConnect etkinlik işleyici ayarlayın. Uzantınızın başka bir bölümü connect() işlevini ç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) {
  if (port.name !== "knockknock") {
    return;
  }
  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."});
    }
  });
});

Serileştirme

Chrome'da mesaj iletme API'leri JSON serileştirmeyi kullanır. Bu durum, aynı API'leri yapılandırılmış klonlama algoritmasıyla uygulayan diğer tarayıcılardan farklıdır.

Bu, bir mesajın (ve alıcılar tarafından sağlanan yanıtların) herhangi bir geçerli JSON.stringify() değeri içerebileceği anlamına gelir. Diğer değerler, serileştirilebilir değerlere dönüştürülür (özellikle undefined, null olarak serileştirilir).

İleti boyutu sınırları

Bir iletinin maksimum boyutu 64 MiB'tir.

Bağlantı noktası ömrü

Bağlantı noktaları, bir uzantının farklı bölümleri arasında iki yönlü iletişim mekanizması olarak tasarlanmıştır. Bir uzantının parçası tabs.connect(), runtime.connect() veya runtime.connectNative() çağrısı yaptığında postMessage() kullanarak mesaj gönderebilen bir Port oluşturur.

Bir sekmede birden fazla çerçeve varsa tabs.connect() çağrıldığında sekmedeki her çerçeve için runtime.onConnect etkinliği bir kez tetiklenir. 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 nedenleri şunlar olabilir:

  • Diğer tarafta runtime.onConnect için dinleyici yok.
  • Bağlantı noktasını içeren sekme boşaltılır (örneğin, sekmede geziniliyorsa).
  • connect() öğesinin çağrıldığı çerçeve kaldırıldı.
  • Bağlantı noktasını (runtime.onConnect üzerinden) alan tüm çerçeveler kaldırıldı.
  • runtime.Port.disconnect(), karşı taraf tarafından aranıyor. Bir connect() araması, alıcının tarafında birden fazla bağlantı noktasıyla sonuçlanırsa ve bu bağlantı noktalarından herhangi birinde disconnect() aranı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

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

Diğer uzantılardan gelen istekleri ve bağlantıları dinlemek için runtime.onMessageExternal veya runtime.onConnectExternal yöntemlerini kullanın. Her biriyle ilgili bir örneği aşağıda görebilirsiniz:

service-worker.js

// For a single request:
chrome.runtime.onMessageExternal.addListener(
  function(request, sender, sendResponse) {
    if (sender.id !== allowlistedExtension) {
      return; // don't allow this extension access
    }
    if (request.getTargetData) {
      sendResponse({ targetData: targetData });
    } else if (request.activateLasers) {
      const 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.
const 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:
const port = chrome.runtime.connect(laserExtensionId);
port.postMessage(...);

Web sayfalarından mesaj gönderme

Uzantılar, web sayfalarından gelen mesajları da alıp yanıtlayabilir. Bir web sayfasından uzantıya mesaj göndermek için manifest.json dosyanızda, "externally_connectable" manifest anahtarını kullanarak hangi web sitelerinden mesajlara izin vermek istediğinizi belirtin. Örneğin:

manifest.json

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

Bu, mesajlaşma API'sini belirttiğiniz URL kalıplarıyla eşleşen tüm sayfalara sunar. URL kalıbı en az bir ikinci düzey alan içermelidir. Diğer bir deyişle, "*", "*.com", "*.co.uk" ve "*.appspot.com" gibi ana makine adı kalıpları desteklenmez. Tüm alanlara erişmek için <all_urls> kullanabilirsiniz.

Belirli bir 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ınızdan, runtime.onMessageExternal veya runtime.onConnectExternal API'lerini kullanarak web sayfalarından gelen mesajları uzantılar arası mesajlaşmada olduğu gibi dinleyin. 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);
  });

Bir uzantıdan bir web sayfasına mesaj göndermek mümkün değildir.

Yerel mesajlaşma

Uzantılar, mesaj alışverişi yapabilir. Yerel mesajlaşma ana makinesi olarak kaydedilen yerel uygulamalarla. Bu özellik hakkında daha fazla bilgi edinmek için Yerel mesajlaşma başlıklı makaleyi inceleyin.

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

Mesajlaşmayla ilgili birkaç güvenlik hususunu aşağıda bulabilirsiniz.

İçerik komut dosyaları daha az güvenilirdir

İçerik komut dosyaları, uzantı hizmeti çalışanına 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 iletilerin 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 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ırmaya karşı koruduğunuzdan emin olun. Kullanıcı girişi, içerik komut dosyası aracılığıyla diğer web siteleri veya API gibi güvenilmeyen bir kaynaktan veri alırken bunu HTML olarak yorumlamaktan veya beklenmedik kodların çalışmasına izin verecek ş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.
  const 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

Aşağıdaki yöntemleri kullanarak uzantınızı savunmasız hale getirmeyin:

service-worker.js

chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
  // WARNING! Might be evaluating a malicious script!
  const 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;
});