تمرير الرسالة

نظرًا لأن النصوص البرمجية للمحتوى تعمل في سياق صفحة ويب وليس في الإضافة، فإنها غالبًا ما تحتاج إلى طريقة ما للتواصل مع بقية الإضافة. على سبيل المثال، قد تستخدم إضافة قارئ 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.

ملاحظة: إذا كانت هناك صفحات متعددة تستمع إلى أحداث onMessage، لن ينجح إرسال الردّ إلّا أول صفحة تستدعي sendResponse() لحدث معيّنًا. وسيتم تجاهل جميع الردود الأخرى على هذا الحدث.
ملاحظة: لا يكون استدعاء 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(...);

إرسال رسائل من صفحات الويب

كما هو الحال مع المراسلة بين الإضافات، يمكن للتطبيق أو الإضافة تلقّي الرسائل من صفحات الويب العادية والرد عليها. لاستخدام هذه الميزة، عليك أولاً تحديد مواقع الويب التي تريد التواصل معها في ملف البيان.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 التواصل مع تطبيق أصلي. لمزيد من الأمثلة والمساعدة في عرض رمز المصدر، راجع النماذج.