ارسال پیام

از آنجایی که اسکریپت‌های محتوا در متن یک صفحه وب اجرا می‌شوند و نه برنامه افزودنی، اغلب به راهی برای برقراری ارتباط با بقیه برنامه‌های افزودنی نیاز دارند. به عنوان مثال، یک برنامه افزودنی RSS reader ممکن است از اسکریپت های محتوا برای تشخیص وجود فید RSS در یک صفحه استفاده کند، سپس صفحه پس زمینه را به منظور نمایش نماد عملکرد صفحه برای آن صفحه مطلع کند.

ارتباط بین برنامه های افزودنی و اسکریپت های محتوای آنها با استفاده از ارسال پیام کار می کند. هر یک از طرفین می توانند به پیام های ارسال شده از طرف دیگر گوش دهند و در همان کانال پاسخ دهند. یک پیام می‌تواند حاوی هر شیء JSON معتبر (تهی، بولی، عدد، رشته، آرایه یا شیء) باشد. یک API ساده برای درخواست‌های یک‌باره و یک API پیچیده‌تر وجود دارد که به شما امکان می‌دهد تا ارتباطات طولانی‌مدتی برای تبادل چند پیام با یک زمینه مشترک داشته باشید. همچنین در صورت اطلاع از شناسه آن که در قسمت پیام های متقاطع درج شده است، امکان ارسال پیام به داخلی دیگر وجود دارد.

درخواست های ساده یک بار مصرف

اگر فقط نیاز دارید یک پیام واحد را به قسمت دیگری از برنامه افزودنی خود ارسال کنید (و در صورت تمایل پاسخ را دریافت کنید)، باید از 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 را برگرداند تا نشان دهد که به صورت ناهمزمان پاسخ خواهد داد. اگر هیچ کنترل‌کننده‌ای درست را برنگرداند یا اگر پاسخ تماس sendResponse به صورت زباله جمع‌آوری شود، پاسخ تماس تابع sendMessage به‌طور خودکار فراخوانی می‌شود.

ارتباطات طولانی مدت

گاهی اوقات مفید است که مکالمه ای طولانی تر از یک درخواست و پاسخ داشته باشید. در این حالت، می‌توانید با استفاده از 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 فقط در پورت فرستنده اجرا می شود و نه در پورت های دیگر.

پیام رسانی متقاطع

علاوه بر ارسال پیام بین اجزای مختلف در برنامه افزودنی خود، می‌توانید از API پیام‌رسانی برای برقراری ارتباط با سایر برنامه‌های افزودنی استفاده کنید. این به شما امکان می‌دهد یک API عمومی را که سایر برنامه‌های افزودنی می‌توانند از آن استفاده کنند، در معرض دید قرار دهید.

گوش دادن به درخواست‌ها و اتصالات ورودی مشابه مورد داخلی است، با این تفاوت که از روش‌های 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.
  });
});

به همین ترتیب، ارسال پیام به برنامه افزودنی دیگر مشابه ارسال پیامی به داخل برنامه افزودنی شما است. تنها تفاوت این است که شما باید ID افزونه ای را که می خواهید با آن ارتباط برقرار کنید، ارسال کنید. مثلا:

// 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/*"]
}

این API پیام‌رسانی را در معرض هر صفحه‌ای قرار می‌دهد که با الگوهای URL که شما مشخص کرده‌اید مطابقت داشته باشد. الگوی URL باید حداقل دارای یک دامنه سطح دوم باشد - یعنی الگوهای نام میزبان مانند "*"، "*.com"، "*.co.uk" و "*.appspot.com" ممنوع هستند. از صفحه وب، از APIهای 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 API گوش دهید، شبیه به پیام‌رسانی بین برنامه‌های افزودنی . فقط صفحه وب می تواند اتصال را آغاز کند. به عنوان مثال:

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 های خطرناک مانند موارد زیر اجتناب کنید:

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

در عوض، API های امن تری را ترجیح دهید که اسکریپت ها را اجرا نمی کنند:

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 می‌تواند با یک برنامه بومی ارتباط برقرار کند. برای مثال‌های بیشتر و کمک به مشاهده کد منبع، به نمونه‌ها مراجعه کنید.