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

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

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

एक बार के अनुरोध

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

कॉलबैक को प्रॉमिस में बदलने और एक्सटेंशन में उनका इस्तेमाल करने के बारे में जानकारी पाने के लिए, मेनिफ़ेस्ट 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 कॉलबैक चालू रहेगा. इस फ़ंक्शन में असाइनिक फ़ंक्शन का इस्तेमाल नहीं किया जा सकता, क्योंकि ये एक Promise रिटर्न करते हैं.

किसी कॉन्टेंट स्क्रिप्ट के लिए अनुरोध भेजने के लिए, यह बताएं कि अनुरोध किस टैब पर लागू होता है, जैसा कि यहां दिखाया गया है. यह उदाहरण, टैब के तौर पर खोले गए सर्विस वर्कर, पॉप-अप, और 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 simple 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.
var editorExtensionId = "abcdefghijklmnoabcdefhijklmnoabc";

// Make a simple request:
chrome.runtime.sendMessage(editorExtensionId, {openUrlInEditor: url},
  function(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;
});