APIهای پیامرسانی به شما این امکان را میدهند که بین اسکریپتهای مختلف در حال اجرا در زمینههای مرتبط با برنامه افزودنی خود ارتباط برقرار کنید. این شامل ارتباط بین کارمند خدمات شما، chrome-extension://pages و اسکریپتهای محتوا میشود. به عنوان مثال، یک برنامه افزودنی RSS reader ممکن است از اسکریپت های محتوا برای تشخیص وجود فید RSS در یک صفحه استفاده کند، سپس به کارگر سرویس اطلاع دهد تا نماد عمل آن صفحه را به روز کند.
دو API ارسال پیام وجود دارد: یکی برای درخواستهای یکباره و دیگری پیچیدهتر برای اتصالات طولانیمدت که امکان ارسال چندین پیام را فراهم میکند.
برای اطلاعات در مورد ارسال پیام بین برنامههای افزودنی، به بخش پیامهای پسوندی مراجعه کنید.
برای ارسال یک پیام واحد به قسمت دیگری از برنامه افزودنی خود و دریافت پاسخ به صورت اختیاری، با runtime.sendMessage()
یا tabs.sendMessage()
تماس بگیرید. این روشها به شما امکان میدهند یک پیام یکبار سریالسازی با JSON از یک اسکریپت محتوا به برنامه افزودنی یا از برنامه افزودنی به یک اسکریپت محتوا ارسال کنید. هر دو API یک Promise را برمی گردانند که به پاسخ ارائه شده توسط گیرنده حل می شود.
ارسال یک درخواست از یک اسکریپت محتوا به شکل زیر است:
content-script.js:
(async () => {
const response = await chrome.runtime.sendMessage({greeting: "hello"});
// do something with response here, not outside the function
console.log(response);
})();
برای گوش دادن به پیام، از رویداد chrome.runtime.onMessage
استفاده کنید:
// 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'
});
هنگامی که شنونده رویداد فراخوانی می شود، تابع sendResponse
به عنوان پارامتر سوم ارسال می شود. این تابعی است که می تواند برای ارائه پاسخ فراخوانی شود. به طور پیش فرض، sendResponse
callback باید به صورت همزمان فراخوانی شود. اگر میخواهید کار ناهمزمانی انجام دهید تا مقدار ارسال شده به sendResponse
را دریافت کنید، باید یک true
واقعی (نه فقط یک مقدار حقیقت) را از شنونده رویداد برگردانید. انجام این کار تا زمانی که sendResponse
فراخوانی نشود، کانال پیام تا انتهای دیگر باز می ماند.
اگر sendResponse
بدون هیچ پارامتری فراخوانی کنید، null
به عنوان پاسخ ارسال می شود.
اگر چندین صفحه به رویدادهای onMessage
گوش میدهند، تنها اولین کسی که sendResponse()
برای یک رویداد خاص فراخوانی میکند موفق به ارسال پاسخ میشود. تمام پاسخ های دیگر به آن رویداد نادیده گرفته می شود.
برای ایجاد یک کانال ارسال پیام طولانی مدت قابل استفاده مجدد، تماس بگیرید:
-
runtime.connect()
برای ارسال پیام از یک اسکریپت محتوا به یک صفحه افزونه -
tabs.connect()
برای ارسال پیام از یک صفحه افزونه به یک اسکریپت محتوا.
برای تمایز بین انواع مختلف اتصالات، میتوانید کانال خود را با ارسال یک پارامتر گزینه به همراه یک کلید name
نامگذاری کنید:
const port = chrome.runtime.connect({name: "example"});
یکی از موارد استفاده بالقوه برای اتصال طولانی مدت، فرمت پر کردن خودکار است. اسکریپت محتوا ممکن است کانالی را برای ورود به صفحه برنامه افزودنی باز کند و برای هر عنصر ورودی در صفحه پیامی به برنامه افزودنی ارسال کند تا دادههای فرم را پر کند. اتصال مشترک به برنامه افزودنی اجازه میدهد حالت را بین اجزای برنامه افزودنی به اشتراک بگذارد.
هنگام برقراری یک اتصال، به هر انتهای یک شیء runtime.Port
برای ارسال و دریافت پیام از طریق آن اتصال اختصاص داده می شود.
از کد زیر برای باز کردن یک کانال از اسکریپت محتوا و ارسال و گوش دادن به پیام ها استفاده کنید:
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"});
برای ارسال درخواست از برنامه افزودنی به یک اسکریپت محتوا، فراخوانی به runtime.connect()
در مثال قبلی را با tabs.connect()
جایگزین کنید.
برای مدیریت اتصالات ورودی برای یک اسکریپت محتوا یا یک صفحه افزونه، یک شنونده رویداد runtime.onConnect
راه اندازی کنید. هنگامی که بخش دیگری از برنامه افزودنی شما connect()
تماس می گیرد، این رویداد و شی runtime.Port
را فعال می کند. کد پاسخگویی به اتصالات ورودی به صورت زیر است:
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."});
}
});
});
در Chrome، APIهای ارسال پیام از سریالسازی JSON استفاده میکنند. این بدان معناست که یک پیام (و پاسخ های ارائه شده توسط گیرندگان) می تواند حاوی هر مقدار JSON معتبر (تهی، بولی، عدد، رشته، آرایه یا شی) باشد. مقادیر دیگر به مقادیر سریالسازی تبدیل میشوند.
قابل ذکر است، این با سایر مرورگرهایی که APIهای یکسانی را با الگوریتم کلون ساخت یافته پیاده سازی می کنند متفاوت است.
پورت ها به عنوان یک مکانیسم ارتباطی دو طرفه بین بخش های مختلف یک داخلی طراحی شده اند. هنگامی که بخشی از یک برنامه افزودنی tabs.connect()
، runtime.connect()
یا runtime.connectNative()
را فرا می خواند، پورتی ایجاد می کند که می تواند بلافاصله با استفاده از postMessage()
پیام ارسال کند.
اگر چندین فریم در یک برگه وجود داشته باشد، فراخوانی tabs.connect()
رویداد runtime.onConnect
را یک بار برای هر فریم در برگه فراخوانی می کند. به طور مشابه، اگر runtime.connect()
فراخوانی شود، رویداد onConnect
می تواند یک بار برای هر فریم در فرآیند افزونه فعال شود.
ممکن است بخواهید بدانید که چه زمانی یک اتصال بسته است، به عنوان مثال آیا حالت های جداگانه ای را برای هر پورت باز حفظ می کنید. برای انجام این کار، به رویداد runtime.Port.onDisconnect
گوش دهید. این رویداد زمانی فعال می شود که هیچ پورت معتبری در انتهای دیگر کانال وجود نداشته باشد، که می تواند یکی از دلایل زیر را داشته باشد:
- هیچ شنونده ای برای
runtime.onConnect
در انتهای دیگر وجود ندارد. - برگه حاوی پورت بارگیری می شود (به عنوان مثال، اگر برگه پیمایش شود).
- فریمی که در آن
connect()
فراخوانی شده بود بارگیری شد. - همه فریم هایی که پورت را دریافت کرده اند (از طریق
runtime.onConnect
) بارگیری شده اند. -
runtime.Port.disconnect()
توسط انتهای دیگر فراخوانی می شود. اگر فراخوانیconnect()
منجر به چندین پورت در انتهای گیرنده شود، وdisconnect()
روی هر یک از این پورت ها فراخوانی شود، رویدادonDisconnect
فقط در پورت ارسال کننده فعال می شود، نه در پورت های دیگر.
علاوه بر ارسال پیام بین اجزای مختلف در برنامه افزودنی خود، میتوانید از API پیامرسانی برای برقراری ارتباط با سایر برنامههای افزودنی استفاده کنید. این به شما امکان می دهد یک API عمومی را برای استفاده از برنامه های افزودنی دیگر در معرض دید قرار دهید.
برای گوش دادن به درخواستهای ورودی و اتصالات از برنامههای افزودنی دیگر، از روشهای runtime.onMessageExternal
یا runtime.onConnectExternal
استفاده کنید. در اینجا یک نمونه از هر کدام آورده شده است:
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.
});
});
برای ارسال پیام به افزونه دیگر، شناسه افزونه مورد نظر برای برقراری ارتباط را به صورت زیر ارسال کنید:
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(...);
برنامه های افزودنی همچنین می توانند پیام های صفحات وب را دریافت کرده و به آنها پاسخ دهند. برای ارسال پیامها از یک صفحه وب به یک برنامه افزودنی، در manifest.json
خود مشخص کنید که از کدام وبسایتها میخواهید با استفاده از کلید مانیفست "externally_connectable"
به پیامها اجازه دهید. به عنوان مثال:
manifest.json
"externally_connectable": {
"matches": ["https://*.example.com/*"]
}
این API پیامرسانی را در معرض هر صفحهای قرار میدهد که با الگوهای URL که شما مشخص کردهاید مطابقت داشته باشد. الگوی URL باید حداقل شامل یک دامنه سطح دوم باشد. یعنی الگوهای نام میزبان مانند «*»، «*.com»، «*.co.uk» و «*.appspot.com» پشتیبانی نمیشوند. برای دسترسی به همه دامنه ها می توانید از <all_urls>
استفاده کنید.
از APIهای runtime.sendMessage()
یا runtime.connect()
برای ارسال پیام به یک برنامه افزودنی خاص استفاده کنید. به عنوان مثال:
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);
}
);
}
از برنامه افزودنی خود، به پیامهای صفحات وب با استفاده از runtime.onMessageExternal
یا runtime.onConnectExternal
مانند پیامرسانی بین برنامههای افزودنی گوش دهید. در اینجا یک مثال است:
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);
});
امکان ارسال پیام از یک برنامه افزودنی به یک صفحه وب وجود ندارد.
برنامه های افزودنی می توانند پیام ها را با برنامه های کاربردی بومی که به عنوان میزبان پیام رسانی بومی ثبت شده اند مبادله کنند . برای کسب اطلاعات بیشتر در مورد این ویژگی، به پیامرسانی بومی مراجعه کنید.
در اینجا چند ملاحظات امنیتی مربوط به پیام رسانی وجود دارد.
اسکریپتهای محتوا نسبت به کارمند خدمات افزودنی کمتر قابل اعتماد هستند . به عنوان مثال، یک صفحه وب مخرب ممکن است بتواند فرآیند رندرینگ را که اسکریپت های محتوا را اجرا می کند، به خطر بیندازد. فرض کنید که پیامهای یک اسکریپت محتوا ممکن است توسط یک مهاجم ساخته شده باشد و مطمئن شوید که تمام ورودیها را اعتبارسنجی و پاکسازی میکنید . فرض کنید هرگونه داده ارسال شده به اسکریپت محتوا ممکن است به صفحه وب نشت کند. دامنه اقدامات ممتازی را که می تواند توسط پیام های دریافتی از اسکریپت های محتوا ایجاد شود، محدود کنید.
مطمئن شوید که از اسکریپت های خود در برابر اسکریپت های متقابل سایت محافظت می کنید. هنگام دریافت دادهها از یک منبع نامعتبر مانند ورودی کاربر، وبسایتهای دیگر از طریق یک اسکریپت محتوا یا یک API، مراقب باشید که از تفسیر آن بهعنوان HTML یا استفاده از آن بهگونهای که امکان اجرای کدهای غیرمنتظره را فراهم کند، خودداری کنید.
در صورت امکان از API هایی استفاده کنید که اسکریپت ها را اجرا نمی کنند:
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; });
از استفاده از روش های زیر که برنامه افزودنی شما را آسیب پذیر می کند خودداری کنید:
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; });