بما أنّ نصوص البرامج تعمل في سياق صفحة ويب وليس في سياق الإضافة، فإنّها غالبًا ما تحتاج إلى طريقة للتواصل مع بقية الإضافة. على سبيل المثال، قد تستخدم إضافة قارئ RSS نصوصًا برمجية خاصة بالمحتوى لرصد توفّر خلاصة RSS على صفحة، ثم إرسال إشعار إلى صفحة الخلفية من أجل عرض رمز إجراء الصفحة لتلك الصفحة.
يتم التواصل بين الإضافات ونصوص المحتوى البرمجية من خلال تمرير الرسائل. يمكن لأي من الطرفين الاستماع إلى الرسائل المرسَلة من الطرف الآخر والردّ عليها على القناة نفسها. يمكن أن تحتوي الرسالة على أي عنصر JSON صالح (قيمة فارغة أو منطقية أو رقم أو سلسلة أو مصفوفة أو عنصر). تتوفّر واجهة برمجة تطبيقات بسيطة للطلبات لمرة واحدة وواجهة برمجة تطبيقات أكثر تعقيدًا تتيح لك إنشاء اتصالات طويلة الأمد لتبادل عدة رسائل في سياق مشترك. يمكنك أيضًا إرسال رسالة إلى إضافة أخرى إذا كنت تعرف رقم تعريفها، وهو ما يتم تناوله في قسم الرسائل بين الإضافات.
طلبات بسيطة لمرة واحدة
إذا كنت بحاجة فقط إلى إرسال رسالة واحدة إلى جزء آخر من الإضافة (ويمكنك اختياريًا تلقّي رد)، عليك استخدام runtime.sendMessage أو tabs.sendMessage المبسّطَين . يتيح لك ذلك إرسال رسالة يمكن تحويلها إلى سلسلة JSON لمرة واحدة من نص برمجي خاص بالمحتوى إلى الإضافة، أو العكس. تتيح لك مَعلمة ردّ الاتصال الاختيارية التعامل مع الردّ من الجهة الأخرى، إذا كان هناك ردّ.
يبدو إرسال طلب من نص برمجي خاص بالمحتوى على النحو التالي:
chrome.runtime.sendMessage({greeting: "hello"}, function(response) {
console.log(response.farewell);
});
يبدو إرسال طلب من الإضافة إلى نص برمجي خاص بالمحتوى مشابهًا جدًا، باستثناء أنّه عليك تحديد علامة التبويب التي تريد إرسال الطلب إليها. يوضّح هذا المثال كيفية إرسال رسالة إلى البرنامج النصي للمحتوى في علامة التبويب المحدّدة.
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {greeting: "hello"}, function(response) {
console.log(response.farewell);
});
});
في الطرف المتلقّي، عليك إعداد متتبِّع الأحداث runtime.onMessage للتعامل مع الرسالة. يبدو هذا الإجراء مماثلاً من نص برمجي للمحتوى أو صفحة إضافة.
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"});
}
);
في المثال أعلاه، تم استدعاء sendResponse بشكل متزامن. إذا كنت تريد استخدام sendResponse بشكل غير متزامن، أضِف return true; إلى معالج الحدث onMessage.
sendResponse صالحًا إلا إذا تم استخدامه بشكل متزامن، أو إذا كان معالج الأحداث يعرض true للإشارة إلى أنّه سيردّ بشكل غير متزامن. سيتم استدعاء دالة رد الاتصال الخاصة بالدالة sendMessage تلقائيًا إذا لم تعرض أي من معالجات الأحداث القيمة "صحيح" أو إذا تم جمع البيانات غير الضرورية لدالة رد الاتصال sendResponse.الاتصالات الطويلة الأمد
في بعض الأحيان، يكون من المفيد إجراء محادثة تستمر لفترة أطول من طلب واحد وردّ واحد. في هذه الحالة، يمكنك فتح قناة طويلة الأمد من نص برمجي للمحتوى إلى صفحة إضافة، أو العكس، باستخدام runtime.connect أو tabs.connect على التوالي . يمكن أن تتضمّن القناة اسمًا اختياريًا، ما يتيح لك التمييز بين أنواع الاتصالات المختلفة.
قد تكون إحدى حالات الاستخدام هي إضافة لملء النماذج تلقائيًا. يمكن لبرنامج نصي للمحتوى فتح قناة إلى صفحة الإضافة لتسجيل دخول معيّن، وإرسال رسالة إلى الإضافة لكل عنصر إدخال في الصفحة لطلب ملء بيانات النموذج. يسمح الاتصال المشترك للإضافة بالاحتفاظ بحالة مشتركة تربط الرسائل المتعددة الواردة من البرنامج النصي للمحتوى.
عند إنشاء اتصال، يتم منح كل طرف كائن runtime.Port يُستخدَم في إرسال الرسائل وتلقّيها من خلال هذا الاتصال.
إليك كيفية فتح قناة من نص برمجي للمحتوى وإرسال الرسائل والاستماع إليها:
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"});
});
يبدو إرسال طلب من الإضافة إلى نص برمجي للمحتوى مشابهًا جدًا، باستثناء أنّه عليك تحديد علامة التبويب التي تريد الاتصال بها. ما عليك سوى استبدال طلب الاتصال في المثال أعلاه بـ tabs.connect.
للتعامل مع الاتصالات الواردة، عليك إعداد أداة معالجة الأحداث runtime.onConnect. يبدو هذا الإجراء مماثلاً من نص برمجي للمحتوى أو صفحة إضافة. عندما يستدعي جزء آخر من الإضافة الدالة "connect()"، يتم تنشيط هذا الحدث، بالإضافة إلى عنصر runtime.Port الذي يمكنك استخدامه لإرسال الرسائل وتلقّيها من خلال الاتصال. في ما يلي الشكل الذي يظهر به الرد على طلبات الاتصال الواردة:
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."});
});
});
مدة صلاحية المنفذ
تم تصميم المنافذ كطريقة تواصل ثنائية الاتجاه بين أجزاء مختلفة من الإضافة، حيث يتم اعتبار الإطار (المستوى الأعلى) هو الجزء الأصغر. عند استدعاء 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فقط على منفذ المرسِل، وليس على المنافذ الأخرى.
المراسلة بين الإضافات
بالإضافة إلى إرسال الرسائل بين المكوّنات المختلفة في الإضافة، يمكنك استخدام واجهة برمجة التطبيقات الخاصة بالمراسلة للتواصل مع إضافات أخرى. يتيح لك ذلك عرض واجهة برمجة تطبيقات عامة يمكن أن تستفيد منها الإضافات الأخرى.
تشبه عملية الاستماع إلى الطلبات والاتصالات الواردة العملية الداخلية، باستثناء أنّك تستخدم الطريقتَين runtime.onMessageExternal أو runtime.onConnectExternal. في ما يلي مثال على كلّ منهما:
// 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.
});
});
وبالمثل، فإنّ إرسال رسالة إلى رقم هاتف آخر يشبه إرسال رسالة إلى رقم هاتف ضمن نطاقك. والاختلاف الوحيد هو أنّه يجب تمرير معرّف الإضافة التي تريد التواصل معها. على سبيل المثال:
// 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(...);
إرسال الرسائل من صفحات الويب
على غرار الرسائل المتبادلة بين الإضافات، يمكن لتطبيقك أو إضافتك تلقّي الرسائل والردّ عليها من صفحات الويب العادية. لاستخدام هذه الميزة، يجب أولاً تحديد المواقع الإلكترونية التي تريد التواصل معها في ملف manifest.json. على سبيل المثال:
"externally_connectable": {
"matches": ["*://*.example.com/*"]
}
سيؤدي ذلك إلى إتاحة واجهة برمجة التطبيقات الخاصة بالمراسلة لأي صفحة تتطابق مع أنماط عناوين URL التي تحدّدها. يجب أن يحتوي نمط عنوان URL على نطاق من المستوى الثاني على الأقل، أي أنّ أنماط أسماء المضيفين مثل "*" و"*.com" و"*.co.uk" و "*.appspot.com" محظورة. من صفحة الويب، استخدِم واجهات برمجة التطبيقات runtime.sendMessage أو runtime.connect لإرسال رسالة إلى تطبيق أو إضافة معيّنة. على سبيل المثال:
// 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);
});
من خلال تطبيقك أو إضافتك، يمكنك الاستماع إلى الرسائل الواردة من صفحات الويب عبر واجهتَي برمجة التطبيقات runtime.onMessageExternal أو runtime.onConnectExternal، على غرار ميزة المراسلة بين الإضافات. يمكن لصفحة الويب فقط بدء عملية الربط. يُرجى الاطّلاع على المثال أدناه:
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);
});
المراسلة الأصلية
يمكن للإضافات والتطبيقات تبادل الرسائل مع التطبيقات الأصلية المسجّلة كمضيف المراسلة مع التطبيقات الأصلية. لمزيد من المعلومات عن هذه الميزة، اطّلِع على مقالة الرسائل الأصلية.
الاعتبارات الأمنية
النصوص البرمجية للمحتوى أقل موثوقية
النصوص البرمجية للمحتوى أقل جدارة بالثقة من صفحة الخلفية الخاصة بالإضافة (على سبيل المثال، قد تتمكّن صفحة ويب ضارة من اختراق عملية العرض التي يتم فيها تنفيذ النصوص البرمجية للمحتوى). افترض أنّ الرسائل الواردة من نص برمجي للمحتوى قد يكون قد صمّمها مهاجم، وتأكّد من التحقّق من صحة جميع المدخلات وتنظيفها. افترِض أنّ أي بيانات يتم إرسالها إلى نص برمجي للمحتوى قد يتم تسريبها إلى صفحة الويب. تحديد نطاق الإجراءات ذات الامتيازات التي يمكن أن يتم تشغيلها من خلال الرسائل الواردة من نصوص برمجية خاصة بالمحتوى
هجوم البرمجة عبر المواقع الإلكترونية
عند تلقّي رسالة من نص برمجي خاص بالمحتوى أو إضافة أخرى، يجب أن تحرص النصوص البرمجية على عدم الوقوع ضحية البرمجة النصية على مواقع متعددة. تنطبق هذه النصيحة على النصوص البرمجية التي يتم تشغيلها داخل صفحة الخلفية الخاصة بالإضافة، بالإضافة إلى النصوص البرمجية للمحتوى التي يتم تشغيلها داخل مصادر الويب الأخرى. على وجه التحديد، تجنَّب استخدام واجهات برمجة التطبيقات الخطيرة، مثل تلك الموضّحة أدناه:
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;
});
بدلاً من ذلك، ننصحك باستخدام واجهات برمجة تطبيقات أكثر أمانًا لا تشغّل نصوصًا برمجية:
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;
});
أمثلة
يمكنك العثور على أمثلة بسيطة على التواصل عبر الرسائل في الدليل examples/api/messaging. يوضّح نموذج المراسلة الأصلية كيف يمكن لتطبيق Chrome التواصل مع تطبيق أصلي. لمزيد من الأمثلة وللحصول على مساعدة في عرض رمز المصدر، راجِع الأمثلة.