মেসেজিং এপিআই আপনাকে আপনার এক্সটেনশনের সাথে সম্পর্কিত প্রসঙ্গে চলমান বিভিন্ন স্ক্রিপ্টের মধ্যে যোগাযোগ করতে দেয়। এর মধ্যে আপনার পরিষেবা কর্মী, chrome-extension://pages এবং সামগ্রী স্ক্রিপ্টের মধ্যে যোগাযোগ অন্তর্ভুক্ত। উদাহরণস্বরূপ, একটি RSS রিডার এক্সটেনশন কোনও পৃষ্ঠায় RSS ফিডের উপস্থিতি সনাক্ত করতে সামগ্রী স্ক্রিপ্ট ব্যবহার করতে পারে, তারপরে পরিষেবা কর্মীকে সেই পৃষ্ঠার জন্য অ্যাকশন আইকন আপডেট করার জন্য অবহিত করতে পারে।
দুটি বার্তা প্রেরণকারী API আছে: একটি এককালীন অনুরোধের জন্য, এবং একটি আরও জটিল দীর্ঘস্থায়ী সংযোগের জন্য যা একাধিক বার্তা প্রেরণের অনুমতি দেয়।
এক্সটেনশনগুলির মধ্যে বার্তা পাঠানোর বিষয়ে তথ্যের জন্য, ক্রস-এক্সটেনশন বার্তা বিভাগটি দেখুন।
এককালীন অনুরোধ
আপনার এক্সটেনশনের অন্য অংশে একটি বার্তা পাঠাতে এবং ঐচ্ছিকভাবে একটি প্রতিক্রিয়া পেতে, runtime.sendMessage() অথবা tabs.sendMessage() কল করুন। এই পদ্ধতিগুলি আপনাকে একটি কন্টেন্ট স্ক্রিপ্ট থেকে এক্সটেনশনে, অথবা এক্সটেনশন থেকে একটি কন্টেন্ট স্ক্রিপ্টে এককালীন JSON-serializable বার্তা পাঠাতে দেয়। উভয় API একটি প্রতিশ্রুতি প্রদান করে যা প্রাপকের দ্বারা প্রদত্ত প্রতিক্রিয়ার সমাধান করে।
একটি কন্টেন্ট স্ক্রিপ্ট থেকে একটি অনুরোধ পাঠানো এইরকম দেখাচ্ছে:
কন্টেন্ট-স্ক্রিপ্ট.জেএস:
(async () => {
const response = await chrome.runtime.sendMessage({greeting: "hello"});
// do something with response here, not outside the function
console.log(response);
})();
প্রতিক্রিয়া
বার্তা শুনতে, chrome.runtime.onMessage ইভেন্টটি ব্যবহার করুন:
// Event listener
function handleMessages(message, sender, sendResponse) {
fetch(message.url)
.then((response) => sendResponse({statusCode: response.status}))
// Since `fetch` is asynchronous, must return an explicit `true`
return true;
}
chrome.runtime.onMessage.addListener(handleMessages);
// From the sender's context...
const {statusCode} = await chrome.runtime.sendMessage({
url: 'https://example.com'
});
যখন ইভেন্ট লিসেনার কল করা হয়, তখন একটি sendResponse ফাংশন তৃতীয় প্যারামিটার হিসেবে পাস করা হয়। এটি এমন একটি ফাংশন যা একটি প্রতিক্রিয়া প্রদানের জন্য কল করা যেতে পারে। ডিফল্টরূপে, sendResponse কলব্যাকটি সিঙ্ক্রোনাসভাবে কল করতে হবে। যদি আপনি sendResponse এ মান পাস করার জন্য অ্যাসিঙ্ক্রোনাস কাজ করতে চান, তাহলে আপনাকে ইভেন্ট লিসেনার থেকে একটি আক্ষরিক true (শুধুমাত্র একটি truthy মান নয়) ফেরত দিতে হবে । এটি করার ফলে sendResponse কল না হওয়া পর্যন্ত বার্তা চ্যানেলটি অন্য প্রান্তে খোলা থাকবে।
যদি আপনি কোনও প্যারামিটার ছাড়াই sendResponse কল করেন, তাহলে null একটি প্রতিক্রিয়া হিসাবে পাঠানো হবে।
যদি একাধিক পৃষ্ঠা onMessage ইভেন্টের জন্য লিসেনিং করে, তাহলে শুধুমাত্র একটি নির্দিষ্ট ইভেন্টের জন্য sendResponse() কে প্রথমে কল করা পৃষ্ঠাটিই প্রতিক্রিয়া পাঠাতে সফল হবে। সেই ইভেন্টের অন্যান্য সমস্ত প্রতিক্রিয়া উপেক্ষা করা হবে।
দীর্ঘস্থায়ী সংযোগ
একটি পুনঃব্যবহারযোগ্য দীর্ঘস্থায়ী বার্তা প্রেরণকারী চ্যানেল তৈরি করতে, কল করুন:
-
runtime.connect()একটি কন্টেন্ট স্ক্রিপ্ট থেকে একটি এক্সটেনশন পৃষ্ঠায় বার্তা প্রেরণ করতে -
tabs.connect()ব্যবহার করে একটি এক্সটেনশন পৃষ্ঠা থেকে একটি কন্টেন্ট স্ক্রিপ্টে বার্তা প্রেরণ করা যায়।
বিভিন্ন ধরণের সংযোগের মধ্যে পার্থক্য করার জন্য একটি name কী সহ একটি বিকল্প প্যারামিটার পাস করে আপনি আপনার চ্যানেলের নামকরণ করতে পারেন:
const port = chrome.runtime.connect({name: "example"});
দীর্ঘস্থায়ী সংযোগের জন্য একটি সম্ভাব্য ব্যবহারের ক্ষেত্রে হল একটি স্বয়ংক্রিয় ফর্ম-ফিলিং এক্সটেনশন। কন্টেন্ট স্ক্রিপ্ট একটি নির্দিষ্ট লগইনের জন্য এক্সটেনশন পৃষ্ঠায় একটি চ্যানেল খুলতে পারে এবং পৃষ্ঠার প্রতিটি ইনপুট উপাদানের জন্য এক্সটেনশনে একটি বার্তা পাঠাতে পারে যাতে ফর্ম ডেটা পূরণ করার জন্য অনুরোধ করা হয়। শেয়ার্ড সংযোগ এক্সটেনশনটিকে এক্সটেনশন উপাদানগুলির মধ্যে অবস্থা ভাগ করে নেওয়ার অনুমতি দেয়।
সংযোগ স্থাপনের সময়, প্রতিটি প্রান্তকে সেই সংযোগের মাধ্যমে বার্তা প্রেরণ এবং গ্রহণের জন্য একটি runtime.Port অবজেক্ট বরাদ্দ করা হয়।
একটি কন্টেন্ট স্ক্রিপ্ট থেকে একটি চ্যানেল খুলতে এবং বার্তা পাঠাতে এবং শুনতে নিম্নলিখিত কোডটি ব্যবহার করুন:
কন্টেন্ট-স্ক্রিপ্ট.জেএস:
const port = chrome.runtime.connect({name: "knockknock"});
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"});
}
});
port.postMessage({joke: "Knock knock"});
এক্সটেনশন থেকে একটি কন্টেন্ট স্ক্রিপ্টে একটি অনুরোধ পাঠাতে, পূর্ববর্তী উদাহরণে runtime.connect() এ কলটি tabs.connect() দিয়ে প্রতিস্থাপন করুন।
কন্টেন্ট স্ক্রিপ্ট অথবা এক্সটেনশন পৃষ্ঠার জন্য ইনকামিং সংযোগ পরিচালনা করতে, একটি runtime.onConnect ইভেন্ট লিসেনার সেট আপ করুন। যখন আপনার এক্সটেনশনের অন্য অংশ connect() কল করে, তখন এটি এই ইভেন্ট এবং runtime.Port অবজেক্ট সক্রিয় করে। ইনকামিং সংযোগগুলিতে সাড়া দেওয়ার কোডটি এইরকম দেখাচ্ছে:
service-worker.js:
chrome.runtime.onConnect.addListener(function(port) {
if (port.name !== "knockknock") {
return;
}
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."});
}
});
});
ক্রমিকীকরণ
ক্রোমে, বার্তা প্রেরণকারী API গুলি JSON সিরিয়ালাইজেশন ব্যবহার করে। উল্লেখযোগ্যভাবে, এটি অন্যান্য ব্রাউজারগুলির থেকে আলাদা যারা স্ট্রাকচার্ড ক্লোন অ্যালগরিদমের সাথে একই API গুলি বাস্তবায়ন করে।
এর অর্থ হল একটি বার্তা (এবং প্রাপকদের দ্বারা প্রদত্ত প্রতিক্রিয়া) যেকোনো বৈধ JSON.stringify() মান ধারণ করতে পারে। অন্যান্য মানগুলিকে জোর করে সিরিয়ালাইজেবল মানগুলিতে রূপান্তরিত করা হবে (উল্লেখযোগ্যভাবে undefined null হিসাবে সিরিয়ালাইজ করা হবে);
বার্তার আকারের সীমা
একটি বার্তার সর্বোচ্চ আকার 64 MiB।
পোর্ট লাইফটাইম
পোর্টগুলি একটি এক্সটেনশনের বিভিন্ন অংশের মধ্যে দ্বিমুখী যোগাযোগ ব্যবস্থা হিসেবে ডিজাইন করা হয়েছে। যখন একটি এক্সটেনশনের অংশ 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 a single request:
chrome.runtime.onMessageExternal.addListener(
function(request, sender, sendResponse) {
if (sender.id !== allowlistedExtension) {
return; // don't allow this extension access
}
if (request.getTargetData) {
sendResponse({ targetData: targetData });
} else if (request.activateLasers) {
const 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.
const 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:
const port = chrome.runtime.connect(laserExtensionId);
port.postMessage(...);
ওয়েব পৃষ্ঠাগুলি থেকে বার্তা পাঠান
এক্সটেনশনগুলি ওয়েব পৃষ্ঠাগুলি থেকে বার্তা গ্রহণ এবং প্রতিক্রিয়া জানাতে পারে। একটি ওয়েব পৃষ্ঠা থেকে একটি এক্সটেনশনে বার্তা পাঠাতে, আপনার manifest.json এ "externally_connectable" ম্যানিফেস্ট কী ব্যবহার করে কোন ওয়েবসাইটগুলি থেকে বার্তাগুলি মঞ্জুর করতে চান তা উল্লেখ করুন। উদাহরণস্বরূপ:
ম্যানিফেস্ট.জেসন
"externally_connectable": {
"matches": ["https://*.example.com/*"]
}
এটি আপনার নির্দিষ্ট করা URL প্যাটার্নের সাথে মেলে এমন যেকোনো পৃষ্ঠায় মেসেজিং API প্রকাশ করে। URL প্যাটার্নে কমপক্ষে একটি দ্বিতীয়-স্তরের ডোমেন থাকতে হবে; অর্থাৎ, "*", "*.com", "*.co.uk", এবং "*.appspot.com" এর মতো হোস্টনেম প্যাটার্ন সমর্থিত নয়। আপনি সমস্ত ডোমেন অ্যাক্সেস করতে <all_urls> ব্যবহার করতে পারেন।
একটি নির্দিষ্ট এক্সটেনশনে বার্তা পাঠাতে runtime.sendMessage() অথবা runtime.connect() API ব্যবহার করুন। উদাহরণস্বরূপ:
ওয়েবপেজ.জেএস
// 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 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-এর মতো অবিশ্বস্ত উৎস থেকে ডেটা গ্রহণ করার সময়, এটিকে HTML হিসাবে ব্যাখ্যা করা বা এমনভাবে ব্যবহার করা এড়িয়ে চলুন যাতে অপ্রত্যাশিত কোড চালানোর সুযোগ না পায়।
যখনই সম্ভব স্ক্রিপ্ট চালায় না এমন API ব্যবহার করুন:
সার্ভিস-ওয়ার্কার.জেএস
chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) { // JSON.parse doesn't evaluate the attacker's scripts. const 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; });
আপনার এক্সটেনশনকে দুর্বল করে তোলে এমন নিম্নলিখিত পদ্ধতিগুলি ব্যবহার করা এড়িয়ে চলুন:
সার্ভিস-ওয়ার্কার.জেএস
chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) { // WARNING! Might be evaluating a malicious script! const 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; });