Karena skrip konten berjalan dalam konteks halaman web, bukan ekstensi, skrip konten sering kali memerlukan cara untuk berkomunikasi dengan bagian ekstensi lainnya. Misalnya, ekstensi pembaca RSS dapat menggunakan skrip konten untuk mendeteksi keberadaan feed RSS di halaman, lalu memberi tahu halaman latar belakang untuk menampilkan ikon tindakan halaman untuk halaman tersebut.
Komunikasi antara ekstensi dan skrip kontennya berfungsi menggunakan pengiriman pesan. Setiap sisi dapat memproses pesan yang dikirim dari ujung lainnya, dan merespons di saluran yang sama. Pesan dapat berisi objek JSON valid apa pun (null, boolean, angka, string, array, atau objek). Ada API sederhana untuk permintaan satu kali dan API yang lebih kompleks yang memungkinkan Anda memiliki koneksiyang tahan lama untuk bertukar beberapa pesan dengan konteks bersama. Anda juga dapat mengirim pesan ke ekstensi lain jika mengetahui ID-nya, yang dibahas di bagian pesan lintas ekstensi.
Permintaan satu kali yang sederhana
Jika Anda hanya perlu mengirim satu pesan ke bagian lain ekstensi (dan secara opsional mendapatkan respons ), Anda harus menggunakan runtime.sendMessage atau tabs.sendMessage yang disederhanakan . Hal ini memungkinkan Anda mengirim pesan yang dapat di-serialisasi JSON satu kali dari skrip konten ke ekstensi, atau sebaliknya. Parameter callback opsional memungkinkan Anda menangani respons dari sisi lain, jika ada.
Mengirim permintaan dari skrip konten terlihat seperti ini:
chrome.runtime.sendMessage({greeting: "hello"}, function(response) {
console.log(response.farewell);
});
Mengirim permintaan dari ekstensi ke skrip konten terlihat sangat mirip, kecuali Anda harus menentukan tab mana yang akan dikirimi. Contoh ini menunjukkan cara mengirim 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 harus menyiapkan pemroses peristiwa runtime.onMessage untuk menangani pesan. Tampilannya sama dari skrip konten atau halaman 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 pemroses peristiwa onMessage.
sendResponse hanya valid jika digunakan secara sinkron, atau jika pemroses peristiwa menampilkan true untuk menunjukkan bahwa pemroses akan merespons secara asinkron. Callback fungsi sendMessage akan otomatis dipanggil jika tidak ada pemroses yang menampilkan nilai benar (true) atau jika callback sendResponse dikumpulkan sampah.Koneksi yang tahan lama
Terkadang, percakapan yang berlangsung lebih lama dari satu permintaan dan respons akan berguna. Dalam hal ini, Anda dapat membuka saluran yang tahan lama dari skrip konten ke halaman ekstensi , atau sebaliknya, menggunakan runtime.connect atau tabs.connect . Saluran ini dapat memiliki nama secara opsional, sehingga Anda dapat membedakan berbagai jenis koneksi.
Salah satu kasus penggunaan mungkin adalah ekstensi pengisian formulir otomatis. Skrip konten dapat membuka saluran ke halaman ekstensi untuk login tertentu, dan mengirim pesan ke ekstensi untuk setiap elemen input di halaman untuk meminta data formulir yang akan diisi. Koneksi bersama memungkinkan ekstensi 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 cara membuka saluran 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 terlihat sangat mirip, kecuali Anda harus menentukan tab mana yang akan disambungkan. Cukup ganti panggilan ke connect dalam contoh di atas dengan tabs.connect.
Untuk menangani koneksi masuk, Anda harus menyiapkan pemroses peristiwa runtime.onConnect. Tampilannya sama dari skrip konten atau halaman ekstensi. Saat bagian lain 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 tampilan respons terhadap 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 aktif port
Port didesain sebagai metode komunikasi dua arah antara berbagai bagian ekstensi, dengan frame (level teratas) dilihat sebagai bagian terkecil. Setelah memanggil tabs.connect, runtime.connect, atau runtime.connectNative, Port akan dibuat. Port ini dapat langsung digunakan untuk mengirim pesan ke ujung lainnya melalui postMessage.
Jika ada beberapa frame di tab, memanggil tabs.connect akan menghasilkan beberapa pemanggilan peristiwa runtime.onConnect (sekali untuk setiap frame di tab). Demikian pula, jika runtime.connect digunakan, peristiwa onConnect dapat diaktifkan beberapa kali (sekali untuk setiap frame dalam proses ekstensi).
Anda mungkin ingin mengetahui kapan koneksi ditutup, misalnya jika Anda mempertahankan status terpisah untuk setiap port terbuka. Untuk itu, Anda dapat memproses peristiwa runtime.Port.onDisconnect. Peristiwa ini diaktifkan jika tidak ada port yang valid di sisi lain saluran. Hal ini terjadi dalam situasi berikut:
- Tidak ada pemroses untuk runtime.onConnect di ujung lainnya.
- Tab yang berisi port tidak dimuat (misalnya, jika tab dinavigasi).
- Frame tempat
connectdipanggil telah tidak dimuat. - Semua frame yang menerima port (melalui runtime.onConnect) telah tidak dimuat.
- runtime.Port.disconnect dipanggil oleh ujung lainnya. Perhatikan bahwa jika panggilan
connectmenghasilkan beberapa port di ujung penerima, dandisconnect()dipanggil di salah satu port ini, maka peristiwaonDisconnecthanya akan diaktifkan di port pengirim, dan bukan di port lainnya.
Pengiriman pesan lintas ekstensi
Selain mengirim pesan antar-komponen yang berbeda dalam ekstensi, Anda dapat menggunakan Messaging API untuk berkomunikasi dengan ekstensi lain. Hal ini memungkinkan Anda mengekspos API publik yang dapat dimanfaatkan oleh ekstensi lain.
Memproses permintaan dan koneksi masuk mirip dengan kasus internal, kecuali Anda menggunakan metode runtime.onMessageExternal atau runtime.onConnectExternal. Berikut contoh masing-masing:
// 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 pula, mengirim pesan ke ekstensi lain mirip dengan mengirim pesan dalam ekstensi Anda. Satu-satunya perbedaan adalah Anda harus meneruskan ID ekstensi yang ingin Anda ajak 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 pengiriman pesan lintas ekstensi, aplikasi atau ekstensi Anda dapat menerima dan merespons pesan dari halaman web biasa. Untuk menggunakan fitur ini, Anda harus menentukan terlebih dahulu di manifest.json situs web mana yang ingin Anda ajak berkomunikasi. Contoh:
"externally_connectable": {
"matches": ["*://*.example.com/*"]
}
Tindakan ini akan mengekspos Messaging API ke halaman mana pun yang cocok dengan pola URL yang Anda tentukan. Pola URL harus berisi setidaknya domain tingkat kedua - yaitu, pola nama host seperti "*", "*.com", "*.co.uk", dan "*.appspot.com" dilarang. Dari halaman web, gunakan runtime.sendMessage atau runtime.connect API 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 runtime.onMessageExternal atau runtime.onConnectExternal API, mirip dengan pengiriman pesan lintas ekstsi. Hanya halaman web yang dapat memulai koneksi. Berikut 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);
});
Pesan native
Ekstensi dan aplikasi dapat bertukar pesan dengan aplikasi native yang terdaftar sebagai host pesan native. Untuk mengetahui selengkapnya tentang fitur ini, lihat Pesan native.
Pertimbangan keamanan
Skrip konten kurang tepercaya
Skrip konten kurang tepercaya dibandingkan halaman latar belakang ekstensi (misalnya, halaman web berbahaya mungkin dapat membahayakan proses perender tempat skrip konten berjalan). Asumsikan bahwa pesan dari skrip konten mungkin telah dibuat oleh penyerang dan pastikan untuk memvalidasi dan membersihkan semua input. Asumsikan bahwa data apa pun yang dikirim ke skrip konten mungkin bocor ke halaman web. Batasi cakupan tindakan khusus yang dapat dipicu oleh pesan yang diterima dari skrip konten.
Pembuatan skrip lintas situs
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. Secara khusus, hindari penggunaan API berbahaya seperti yang tercantum di bawah:
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, sebaiknya gunakan 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 sederhana komunikasi melalui pesan di direktori examples/api/messaging. Contoh pesan native menunjukkan cara aplikasi Chrome dapat berkomunikasi dengan aplikasi native. Untuk contoh lainnya dan bantuan dalam melihat kode sumber, lihat Contoh.