मैसेज पास करना

कॉन्टेंट स्क्रिप्ट, वेब पेज के संदर्भ में काम करती हैं, न कि उन्हें चलाने वाले एक्सटेंशन के संदर्भ में. इसलिए, अक्सर उन्हें बाकी एक्सटेंशन के साथ कम्यूनिकेट करने के तरीकों की ज़रूरत होती है. उदाहरण के लिए, कोई आरएसएस रीडर एक्सटेंशन, किसी पेज पर आरएसएस फ़ीड की मौजूदगी का पता लगाने के लिए कॉन्टेंट स्क्रिप्ट का इस्तेमाल कर सकता है. इसके बाद, उस पेज के लिए ऐक्शन आइकॉन दिखाने के लिए, सेवा वर्कर को सूचना दे सकता है.

इस बातचीत में मैसेज पास करने की सुविधा का इस्तेमाल किया जाता है. इससे एक्सटेंशन और कॉन्टेंट स्क्रिप्ट, दोनों एक-दूसरे के मैसेज सुन सकती हैं और एक ही चैनल पर जवाब दे सकती हैं. किसी मैसेज में, कोई भी मान्य JSON ऑब्जेक्ट (शून्य, बूलियन, संख्या, स्ट्रिंग, ऐरे या ऑब्जेक्ट) हो सकता है. मैसेज पास करने के लिए दो एपीआई उपलब्ध हैं: पहला, एक बार के अनुरोध के लिए और दूसरा, लंबे समय तक चलने वाले कनेक्शन के लिए. यह दूसरा एपीआई ज़्यादा जटिल है और इसकी मदद से एक से ज़्यादा मैसेज भेजे जा सकते हैं. एक्सटेंशन के बीच मैसेज भेजने के बारे में जानकारी पाने के लिए, एक से ज़्यादा एक्सटेंशन के बीच मैसेज भेजना सेक्शन देखें.

एक बार किए जाने वाले अनुरोध

अपने एक्सटेंशन के किसी दूसरे हिस्से पर एक मैसेज भेजने और जवाब पाने के लिए, runtime.sendMessage() या tabs.sendMessage() को कॉल करें. इन तरीकों की मदद से, कॉन्टेंट स्क्रिप्ट से एक्सटेंशन या एक्सटेंशन से कॉन्टेंट स्क्रिप्ट पर, एक बार में JSON-serializable मैसेज भेजा जा सकता है. रिस्पॉन्स को मैनेज करने के लिए, रिटर्न किए गए promise का इस्तेमाल करें. पुराने एक्सटेंशन के साथ काम करने के लिए, आखिरी आर्ग्युमेंट के तौर पर कॉलबैक पास किया जा सकता है. एक ही कॉल में, प्रॉमिस और कॉलबैक, दोनों का इस्तेमाल नहीं किया जा सकता.

मैसेज भेजने पर, मैसेज को मैनेज करने वाले इवेंट लिसनर को sendResponse नाम का तीसरा आर्ग्युमेंट भेजा जाता है. हालांकि, यह आर्ग्युमेंट ज़रूरी नहीं है. यह एक ऐसा फ़ंक्शन है जो JSON-serializable ऑब्जेक्ट लेता है. इसका इस्तेमाल, मैसेज भेजने वाले फ़ंक्शन की रिटर्न वैल्यू के तौर पर किया जाता है. डिफ़ॉल्ट रूप से, sendResponse callback को सिंक्रोनस तरीके से कॉल किया जाना चाहिए. अगर आपको sendResponse में पास की गई वैल्यू पाने के लिए, असिंक्रोनस तरीके से काम करना है, तो आपको इवेंट लिसनर से कोई लिटरल true (सिर्फ़ 'सही' वैल्यू नहीं) रिटर्न करना ज़रूरी है. ऐसा करने से, मैसेज चैनल दूसरे व्यक्ति के लिए तब तक खुला रहेगा, जब तक sendResponse को कॉल नहीं किया जाता.

// Event listener
function handleMessages(message, sender, sendResponse) {

  fetch(message.url)
    .then((response) => sendResponse({statusCode: response.status}))

  // Since `fetch` is asynchronous, must send an explicit `true`
  return true;
}

// Message sender
  const {statusCode} = await chrome.runtime.sendMessage({
    url: 'https://example.com'
  });

कॉलबैक को प्रॉमिस में बदलने और उन्हें एक्सटेंशन में इस्तेमाल करने के बारे में जानने के लिए, मेनिफ़ेस्ट V3 माइग्रेशन गाइड देखें.

कॉन्टेंट स्क्रिप्ट से अनुरोध भेजने का तरीका इस तरह का है:

content-script.js:

(async () => {
  const response = await chrome.runtime.sendMessage({greeting: "hello"});
  // do something with response here, not outside the function
  console.log(response);
})();

अगर आपको किसी मैसेज का जवाब एक साथ देना है, तो जवाब तैयार होने के बाद sendResponse बोलें. इसके बाद, जवाब देने के बाद false बोलें. कॉलबैक के जवाब में, true को तब तक दिखाएं, जब तक आप इसका इस्तेमाल नहीं कर लेते.sendResponse इस फ़ंक्शन में असाइनिक फ़ंक्शन का इस्तेमाल नहीं किया जा सकता, क्योंकि ये एक प्रॉमिस रिटर्न करते हैं और प्रॉमिस का इस्तेमाल नहीं किया जा सकता.

किसी कॉन्टेंट स्क्रिप्ट के लिए अनुरोध भेजने के लिए, यह बताएं कि अनुरोध किस टैब पर लागू होता है, जैसा कि यहां दिखाया गया है. यह उदाहरण, टैब के तौर पर खोले गए सर्विस वर्कर, पॉप-अप, और chrome-extension:// पेजों में काम करता है.

(async () => {
  const [tab] = await chrome.tabs.query({active: true, lastFocusedWindow: true});
  const response = await chrome.tabs.sendMessage(tab.id, {greeting: "hello"});
  // do something with response here, not outside the function
  console.log(response);
})();

मैसेज पाने के लिए, runtime.onMessage इवेंट लिसनर सेट अप करें. ये एक्सटेंशन और कॉन्टेंट स्क्रिप्ट, दोनों में एक ही कोड का इस्तेमाल करते हैं:

content-script.js या service-worker.js:

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() का इस्तेमाल एक साथ करने के लिए, onMessage इवेंट हैंडलर में return true; जोड़ें.

अगर एक से ज़्यादा पेज onMessage इवेंट के लिए सुन रहे हैं, तो किसी खास इवेंट के लिए sendResponse() को कॉल करने वाला पहला पेज ही जवाब भेज पाएगा. उस इवेंट के लिए दिए गए अन्य सभी जवाबों को अनदेखा कर दिया जाएगा.

लंबे समय तक चलने वाले कनेक्शन

बार-बार इस्तेमाल किया जा सकने वाला, लंबे समय तक मैसेज भेजने वाला चैनल बनाने के लिए, कॉन्टेंट स्क्रिप्ट से एक्सटेंशन पेज पर मैसेज भेजने के लिए runtime.connect() और एक्सटेंशन पेज से कॉन्टेंट स्क्रिप्ट पर मैसेज भेजने के लिए tabs.connect() को कॉल करें. अलग-अलग तरह के कनेक्शन के बीच अंतर करने के लिए, अपने चैनल को नाम दिया जा सकता है.

लंबे समय तक चलने वाले कनेक्शन का इस्तेमाल, फ़ॉर्म अपने-आप भरने वाले एक्सटेंशन के लिए किया जा सकता है. कॉन्टेंट स्क्रिप्ट, किसी खास लॉगिन के लिए एक्सटेंशन पेज पर एक चैनल खोल सकती है. साथ ही, पेज पर मौजूद हर इनपुट एलिमेंट के लिए एक्सटेंशन को एक मैसेज भेज सकती है, ताकि फ़ॉर्म में भरने के लिए डेटा का अनुरोध किया जा सके. शेयर किए गए कनेक्शन की मदद से, एक्सटेंशन अपने कॉम्पोनेंट के बीच स्थिति शेयर कर सकता है.

कनेक्शन बनाते समय, हर एंड को एक runtime.Port ऑब्जेक्ट असाइन किया जाता है, ताकि उस कनेक्शन से मैसेज भेजे और पाए जा सकें.

कॉन्टेंट स्क्रिप्ट से चैनल खोलने और मैसेज भेजने और सुनने के लिए, नीचे दिए गए कोड का इस्तेमाल करें:

content-script.js:

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

एक्सटेंशन से कॉन्टेंट स्क्रिप्ट में अनुरोध भेजने के लिए, पिछले उदाहरण में runtime.connect() के कॉल को tabs.connect() से बदलें.

कॉन्टेंट स्क्रिप्ट या एक्सटेंशन पेज के लिए आने वाले कनेक्शन को हैंडल करने के लिए, runtime.onConnect इवेंट लिस्नर सेट अप करें. जब आपके एक्सटेंशन का कोई दूसरा हिस्सा connect() को कॉल करता है, तो यह इवेंट और runtime.Port ऑब्जेक्ट चालू हो जाता है. इनकमिंग कनेक्शन का जवाब देने वाला कोड कुछ ऐसा दिखता है:

service-worker.js:

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 तरीकों का इस्तेमाल करें. यहां हर एक का उदाहरण दिया गया है:

service-worker.js

// For a single request:
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.
  });
});

किसी दूसरे एक्सटेंशन को मैसेज भेजने के लिए, उस एक्सटेंशन का आईडी इस तरह पास करें जिसके साथ आपको बातचीत करनी है:

service-worker.js

// The ID of the extension we want to talk to.
var 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:
var port = chrome.runtime.connect(laserExtensionId);
port.postMessage(...);

वेब पेजों से मैसेज भेजना

एक्सटेंशन, दूसरे वेब पेजों से मैसेज भी पा सकते हैं और उनका जवाब भी दे सकते हैं. हालांकि, वे वेब पेजों पर मैसेज नहीं भेज सकते. किसी वेब पेज से एक्सटेंशन पर मैसेज भेजने के लिए, manifest.json में बताएं कि आपको "externally_connectable" मेनिफ़ेस्ट पासकोड का इस्तेमाल करके, किन वेबसाइटों से संपर्क करना है. उदाहरण के लिए:

manifest.json

"externally_connectable": {
  "matches": ["https://*.example.com/*"]
}

इससे, आपके तय किए गए यूआरएल पैटर्न से मैच करने वाले किसी भी पेज पर मैसेजिंग एपीआई का ऐक्सेस मिल जाता है. यूआरएल पैटर्न में कम से कम एक सेकंड-लेवल डोमेन होना चाहिए. इसका मतलब है कि "*", "*.com", "*.co.uk", और "*.appspot.com" जैसे होस्टनेम पैटर्न काम नहीं करते. Chrome 107 से, सभी डोमेन को ऐक्सेस करने के लिए <all_urls> का इस्तेमाल किया जा सकता है. ध्यान दें कि इस बदलाव का असर सभी होस्ट पर पड़ता है. इसलिए, Chrome Web Store पर उन एक्सटेंशन की समीक्षा में ज़्यादा समय लग सकता है जिनमें इस बदलाव का इस्तेमाल किया गया है.

किसी खास ऐप्लिकेशन या एक्सटेंशन पर मैसेज भेजने के लिए, 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);
  });

नेटिव मैसेज सेवा

एक्सटेंशन, नेटिव मैसेजिंग होस्ट के तौर पर रजिस्टर किए गए नेटिव ऐप्लिकेशन के साथ मैसेज एक्सचेंज कर सकते हैं. इस सुविधा के बारे में ज़्यादा जानने के लिए, नेटिव मैसेजिंग देखें.

सुरक्षा से जुड़ी बातें

मैसेजिंग से जुड़ी सुरक्षा से जुड़ी कुछ बातें यहां दी गई हैं.

कॉन्टेंट स्क्रिप्ट भरोसेमंद नहीं हैं

एक्सटेंशन सेवा वर्कर्स की तुलना में, कॉन्टेंट स्क्रिप्ट पर कम भरोसा किया जा सकता है. उदाहरण के लिए, नुकसान पहुंचाने वाला कोई वेब पेज, कॉन्टेंट स्क्रिप्ट को चलाने वाली रेंडरिंग प्रोसेस को नुकसान पहुंचा सकता है. मान लें कि किसी कॉन्टेंट स्क्रिप्ट के मैसेज, हमलावर ने बनाए हों. साथ ही, पक्का करें कि सभी इनपुट की पुष्टि की गई हो और उन्हें सुरक्षित किया गया हो. मान लें कि कॉन्टेंट स्क्रिप्ट में भेजा गया कोई भी डेटा, वेब पेज पर लीक हो सकता है. कॉन्टेंट स्क्रिप्ट से मिलने वाले मैसेज से ट्रिगर होने वाली, खास सुविधाओं वाली कार्रवाइयों के दायरे को सीमित करें.

क्रॉस-साइट स्क्रिप्टिंग

अपनी स्क्रिप्ट को क्रॉस-साइट स्क्रिप्टिंग से सुरक्षित रखना न भूलें. किसी भरोसेमंद सोर्स से डेटा मिलने पर, उसे एचटीएमएल के तौर पर न समझें. इसके अलावा, उसे इस तरह इस्तेमाल न करें कि अनचाहा कोड चलने लगे. भरोसेमंद सोर्स में, उपयोगकर्ता का इनपुट, कॉन्टेंट स्क्रिप्ट या एपीआई के ज़रिए मिली अन्य वेबसाइटों का डेटा शामिल है.

ज़्यादा सुरक्षित तरीके

जहां भी हो सके वहां ऐसे एपीआई का इस्तेमाल करें जो स्क्रिप्ट न चलाते हों:

service-worker.js

chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
  // JSON.parse doesn't evaluate the attacker's scripts.
  var 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!
  var 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;
});