Pesan yang diteruskan

Karena skrip konten dijalankan dalam konteks halaman web dan bukan ekstensi, skrip konten sering kali memerlukan cara untuk berkomunikasi dengan seluruh ekstensi. Misalnya, ekstensi pembaca RSS mungkin menggunakan skrip konten untuk mendeteksi keberadaan feed RSS di halaman, lalu memberi tahu halaman latar belakang agar dapat menampilkan ikon tindakan halaman untuk halaman tersebut.

Komunikasi antara ekstensi dan skrip kontennya berfungsi dengan menggunakan penerusan pesan. Kedua keduanya dapat memproses pesan yang dikirim dari pihak lain, dan merespons di saluran yang sama. Pesan dapat berisi semua objek JSON yang valid (null, boolean, angka, string, array, atau objek). Ada API sederhana untuk permintaan satu kali dan API yang lebih kompleks yang memungkinkan Anda memiliki koneksi jangka panjang untuk bertukar beberapa pesan dengan konteks bersama. Anda juga dapat mengirim pesan ke ekstensi lain jika mengetahui ID-nya, yang dibahas dalam bagian pesan ekstensi silang.

Permintaan satu kali yang sederhana

Jika Anda hanya perlu mengirim satu pesan ke bagian lain ekstensi (dan jika ingin mendapatkan respons kembali), Anda harus menggunakan runtime.sendMessage atau tabs.sendMessage yang telah disederhanakan . Hal ini memungkinkan Anda mengirim pesan satu kali JSON yang dapat diserialisasi dari skrip konten ke ekstensi, atau sebaliknya . Parameter callback opsional memungkinkan Anda menangani respons dari sisi lain, jika ada.

Mengirim permintaan dari skrip konten akan terlihat seperti ini:

chrome.runtime.sendMessage({greeting: "hello"}, function(response) {
  console.log(response.farewell);
});

Mengirim permintaan dari ekstensi ke skrip konten akan terlihat sangat mirip, hanya saja Anda perlu menentukan tab mana yang akan dikirimi permintaan. Contoh ini menunjukkan pengiriman pesan ke skrip konten di tab yang dipilih.

chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
  chrome.tabs.sendMessage(tabs[0].id, {greeting: "hello"}, function(response) {
    console.log(response.farewell);
  });
});

Di sisi penerima, Anda perlu menyiapkan pemroses peristiwa runtime.onMessage untuk menangani pesan. Ini terlihat sama dari skrip konten atau laman ekstensi.

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

Pada contoh di atas, sendResponse dipanggil secara sinkron. Jika Anda ingin menggunakan sendResponse secara asinkron, tambahkan return true; ke pengendali peristiwa onMessage.

Catatan: Jika beberapa halaman memproses peristiwa onMessage, hanya halaman pertama yang memanggil sendResponse() untuk peristiwa tertentu yang akan berhasil mengirim respons. Semua respons lain terhadap acara tersebut akan diabaikan.
Catatan: Callback sendResponse hanya valid jika digunakan secara sinkron, atau jika pengendali peristiwa menampilkan true untuk menunjukkan bahwa callback tersebut akan merespons secara asinkron. Callback fungsi sendMessage akan dipanggil secara otomatis jika tidak ada pengendali yang menampilkan benar atau jika callback sendResponse telah dibersihkan sampah memorinya.

Koneksi berumur panjang

Terkadang, ada gunanya melakukan percakapan yang berlangsung lebih dari satu permintaan dan respons. Dalam hal ini, Anda dapat membuka saluran berdurasi panjang dari skrip konten ke halaman ekstensi, atau sebaliknya, menggunakan runtime.connect atau tabs.connect . Saluran dapat memiliki nama secara opsional, sehingga Anda dapat membedakan berbagai jenis koneksi.

Salah satu kasus penggunaan dapat berupa ekstensi pengisian formulir otomatis. Skrip konten dapat membuka saluran ke halaman ekstensi untuk login tertentu, dan mengirim pesan ke ekstensi untuk setiap elemen input pada halaman guna meminta pengisian data formulir. Koneksi bersama memungkinkan ekstensi untuk mempertahankan status bersama yang menautkan beberapa pesan yang berasal dari skrip konten.

Saat membuat koneksi, setiap ujung diberi objek runtime.Port yang digunakan untuk mengirim dan menerima pesan melalui koneksi tersebut.

Berikut ini cara membuka channel dari skrip konten, serta mengirim dan memproses pesan:

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

Mengirim permintaan dari ekstensi ke skrip konten akan terlihat sangat mirip, hanya saja Anda perlu menentukan tab mana yang akan dihubungkan. Cukup ganti panggilan untuk terhubung dalam contoh di atas dengan tabs.connect.

Untuk menangani koneksi masuk, Anda perlu menyiapkan pemroses peristiwa runtime.onConnect. Ini terlihat sama dari skrip konten atau halaman ekstensi. Saat bagian lain dari ekstensi Anda memanggil "connect()", peristiwa ini akan diaktifkan, bersama dengan objek runtime.Port yang dapat Anda gunakan untuk mengirim dan menerima pesan melalui koneksi. Berikut adalah langkah-langkah yang akan dilakukan untuk merespons koneksi masuk:

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

Masa pakai port

Port didesain sebagai metode komunikasi dua arah antara berbagai bagian ekstensi, dengan frame (level atas) dipandang sebagai bagian terkecil. Setelah memanggil tabs.connect, runtime.connect, atau runtime.connectNative, Port akan dibuat. Port ini dapat segera digunakan untuk mengirim pesan ke pihak lain melalui postMessage.

Jika ada beberapa frame di tab, memanggil tabs.connect akan menghasilkan beberapa pemanggilan peristiwa runtime.onConnect (satu kali untuk setiap frame di tab). Demikian pula, jika runtime.connect digunakan, peristiwa onConnect dapat diaktifkan beberapa kali (satu kali untuk setiap frame dalam proses ekstensi).

Anda mungkin ingin mengetahui kapan koneksi ditutup, misalnya jika Anda mempertahankan status terpisah untuk setiap port yang terbuka. Untuk itu, Anda dapat memproses peristiwa runtime.Port.onDisconnect. Peristiwa ini dipicu saat tidak ada port yang valid di sisi lain saluran. Hal ini terjadi dalam situasi berikut:

  • Tidak ada pemroses untuk runtime.onConnect di ujung lain.
  • Tab yang berisi port akan dihapus muatannya (misalnya, jika tab dibuka).
  • Frame tempat connect dipanggil telah menghapus muatannya.
  • Semua frame yang menerima port (melalui runtime.onConnect) telah dihapus muatannya.
  • runtime.Port.disconnect dipanggil oleh ujung lainnya. Perhatikan bahwa jika panggilan connect menghasilkan beberapa port di ujung penerima, dan disconnect() dipanggil di salah satu port ini, peristiwa onDisconnect hanya diaktifkan pada port pengirim, dan bukan pada port lain.

Pesan lintas ekstensi

Selain mengirim pesan antara berbagai komponen dalam ekstensi, Anda dapat menggunakan API pesan untuk berkomunikasi dengan ekstensi lain. Hal ini memungkinkan Anda mengekspos API publik yang dapat dimanfaatkan ekstensi lain.

Memproses koneksi dan permintaan masuk mirip dengan kasus internal, kecuali Anda menggunakan metode runtime.onMessageExternal atau runtime.onConnectExternal. Berikut ini contoh masing-masingnya:

// For simple requests:
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.
  });
});

Demikian juga, mengirim pesan ke ekstensi lain serupa dengan mengirim pesan dalam ekstensi Anda. Satu-satunya perbedaan adalah Anda harus meneruskan ID ekstensi yang ingin diajak berkomunikasi. Contoh:

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

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

// Start a long-running conversation:
var port = chrome.runtime.connect(laserExtensionId);
port.postMessage(...);

Mengirim pesan dari halaman web

Mirip dengan pesan lintas-ekstensi, aplikasi atau ekstensi Anda dapat menerima dan merespons pesan dari halaman web reguler. Untuk menggunakan fitur ini, pertama-tama Anda harus menentukan situs web mana yang ingin Anda ajak berkomunikasi di manifest.json. Contoh:

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

Tindakan ini akan mengekspos API pesan ke halaman apa pun yang cocok dengan pola URL yang Anda tentukan. Pola URL harus berisi setidaknya domain level kedua - yaitu, pola nama host seperti "*", "*.com", "*.co.uk", dan "*.appspot.com" dilarang. Dari halaman web, gunakan API runtime.sendMessage atau runtime.connect untuk mengirim pesan ke aplikasi atau ekstensi tertentu. Contoh:

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

Dari aplikasi atau ekstensi, Anda dapat memproses pesan dari halaman web melalui API runtime.onMessageExternal atau runtime.onConnectExternal, mirip dengan pesan lintas-ekstensi. Hanya halaman web yang dapat memulai koneksi. Berikut ini contohnya:

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

Pengiriman pesan native

Ekstensi dan aplikasi dapat bertukar pesan dengan aplikasi native yang terdaftar sebagai host pesan native. Untuk mempelajari fitur ini lebih lanjut, lihat Pesan native.

Pertimbangan keamanan

Skrip konten kurang dapat dipercaya

Skrip konten kurang tepercaya dibandingkan halaman latar belakang ekstensi (mis., halaman web berbahaya mungkin dapat membahayakan proses perender tempat skrip konten dijalankan). Asumsikan bahwa pesan dari skrip konten mungkin telah dibuat oleh penyerang dan pastikan untuk memvalidasi dan membersihkan semua input. Asumsikan bahwa data yang dikirim ke skrip konten mungkin bocor ke halaman web. Batasi cakupan tindakan dengan hak istimewa yang dapat dipicu oleh pesan yang diterima dari skrip konten.

Pembuatan skrip antarsitus

Saat menerima pesan dari skrip konten atau ekstensi lain, skrip Anda harus berhati-hati agar tidak menjadi korban pembuatan skrip lintas situs. Saran ini berlaku untuk skrip yang berjalan di dalam halaman latar belakang ekstensi serta skrip konten yang berjalan di dalam asal web lainnya. Khususnya, hindari penggunaan API berbahaya seperti di bawah ini:

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

Sebagai gantinya, pilih API yang lebih aman yang tidak menjalankan skrip:

chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
  // JSON.parse does not evaluate the attacker's scripts.
  var resp = JSON.parse(response.farewell);
});
chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
  // innerText does not let the attacker inject HTML elements.
  document.getElementById("resp").innerText = response.farewell;
});

Contoh

Anda dapat menemukan contoh komunikasi sederhana melalui pesan di direktori examples/api/messaging. Contoh pesan native menunjukkan cara aplikasi Chrome dapat berkomunikasi dengan aplikasi native. Untuk contoh lainnya dan untuk mendapatkan bantuan dalam melihat kode sumber, lihat Contoh.