মেসেজিং এপিআই আপনাকে আপনার এক্সটেনশনের সাথে যুক্ত কনটেক্সটে চলমান বিভিন্ন স্ক্রিপ্টের মধ্যে যোগাযোগ করতে দেয়। এর মধ্যে আপনার সার্ভিস ওয়ার্কার, chrome-extension://pages এবং কন্টেন্ট স্ক্রিপ্টের মধ্যকার যোগাযোগ অন্তর্ভুক্ত। উদাহরণস্বরূপ, একটি আরএসএস রিডার এক্সটেনশন কোনো পেজে আরএসএস ফিডের উপস্থিতি শনাক্ত করতে কন্টেন্ট স্ক্রিপ্ট ব্যবহার করতে পারে, এবং তারপর সেই পেজের অ্যাকশন আইকন আপডেট করার জন্য সার্ভিস ওয়ার্কারকে অবহিত করতে পারে।
মেসেজ আদান-প্রদানের দুটি এপিআই রয়েছে: একটি এককালীন অনুরোধের জন্য, এবং অন্যটি দীর্ঘস্থায়ী সংযোগের জন্য যা দিয়ে একাধিক মেসেজ পাঠানো যায়।
এক্সটেনশনগুলোর মধ্যে বার্তা পাঠানোর বিষয়ে তথ্যের জন্য, ক্রস-এক্সটেনশন বার্তা বিভাগটি দেখুন।
এককালীন অনুরোধ
আপনার এক্সটেনশনের অন্য কোনো অংশে একটিমাত্র মেসেজ পাঠাতে এবং ঐচ্ছিকভাবে একটি রেসপন্স পেতে, runtime.sendMessage() অথবা tabs.sendMessage() কল করুন। এই মেথডগুলো আপনাকে একটি কন্টেন্ট স্ক্রিপ্ট থেকে এক্সটেনশনে, অথবা এক্সটেনশন থেকে একটি কন্টেন্ট স্ক্রিপ্টে একবারের জন্য JSON-সিরিয়ালাইজেবল মেসেজ পাঠাতে দেয়। উভয় API-ই একটি Promise রিটার্ন করে, যা প্রাপকের দেওয়া রেসপন্সে রিজলভ হয়।
কন্টেন্ট স্ক্রিপ্ট থেকে অনুরোধ পাঠানোর প্রক্রিয়াটি দেখতে এইরকম:
কন্টেন্ট-স্ক্রিপ্ট.জেএস:
(async () => {
const response = await browser.runtime.sendMessage({greeting: "hello"});
// do something with response here, not outside the function
console.log(response);
})();
প্রতিক্রিয়া
মেসেজ শোনার জন্য, browser.runtime.onMessage ইভেন্টটি ব্যবহার করুন:
// Event listener
function handleMessages(message, sender, sendResponse) {
if (message !== 'get-status') return;
fetch('https://example.com')
.then((response) => sendResponse({statusCode: response.status}))
// Since `fetch` is asynchronous, must return an explicit `true`
return true;
}
browser.runtime.onMessage.addListener(handleMessages);
// From the sender's context...
const {statusCode} = await browser.runtime.sendMessage('get-status');
যখন ইভেন্ট লিসেনারটি কল করা হয়, তখন তৃতীয় প্যারামিটার হিসেবে একটি sendResponse ফাংশন পাস করা হয়। এটি এমন একটি ফাংশন যা একটি প্রতিক্রিয়া প্রদানের জন্য কল করা যেতে পারে। ডিফল্টরূপে, sendResponse কলব্যাকটি অবশ্যই সিনক্রোনাসভাবে কল করতে হবে।
কোনো প্যারামিটার ছাড়া sendResponse কল করলে, প্রতিক্রিয়া হিসেবে null পাঠানো হয়।
অ্যাসিঙ্ক্রোনাসভাবে প্রতিক্রিয়া পাঠানোর জন্য আপনার কাছে দুটি বিকল্প রয়েছে: true রিটার্ন করা অথবা একটি 'promise' রিটার্ন করা।
true ফেরত দিন
sendResponse() ব্যবহার করে অ্যাসিঙ্ক্রোনাসভাবে সাড়া দিতে, ইভেন্ট লিসেনার থেকে একটি আক্ষরিক true (শুধু ট্রুথি ভ্যালু নয়) রিটার্ন করুন। এটি করলে sendResponse কল না করা পর্যন্ত অপর প্রান্তের সাথে মেসেজ চ্যানেলটি খোলা থাকবে, ফলে আপনি এটি পরে কল করতে পারবেন।
একটি প্রতিশ্রুতি ফেরত দিন
ক্রোম ১৪৮ থেকে, আপনি অ্যাসিঙ্ক্রোনাসভাবে সাড়া দেওয়ার জন্য একটি মেসেজ লিসেনার থেকে একটি প্রমিজ রিটার্ন করতে পারবেন। এই আপডেটটি পর্যায়ক্রমে চালু করা হচ্ছে, তাই আপনি দেখতে পারেন যে এটি এখনও সব ব্যবহারকারীর ব্রাউজারে উপলব্ধ নয়। এই সক্ষমতাটি চালু আছে কি না, তা আপনার এক্সটেনশনটি সামলাতে পারে কি না, তা আপনাকে নিশ্চিত করতে হবে। এই সক্ষমতাটি চালু থাকুক বা না থাকুক, অ্যাসিঙ্ক্রোনাস রেসপন্সের জন্য return true; ব্যবহার করা অব্যাহত থাকবে।
যদি প্রমিসটি রিজলভ হয়, তবে এর রিজলভড ভ্যালুটি রেসপন্স হিসেবে পাঠানো হয়।
প্রমিসটি প্রত্যাখ্যাত হলে, প্রেরকের sendMessage() কলটি ত্রুটির বার্তা সহ প্রত্যাখ্যাত হবে। আরও বিস্তারিত তথ্য ও উদাহরণের জন্য ত্রুটি পরিচালনা (error handling) বিভাগটি দেখুন।
এমন একটি প্রমিজ রিটার্ন করার উদাহরণ যা রিজলভ বা রিজেক্ট হতে পারে:
// Event listener
function handleMessages(message, sender, sendResponse) {
// Return a promise that wraps fetch
// If the response is OK, resolve with the status. If it's not OK then reject
// with the network error that prevents the fetch from completing.
return new Promise((resolve, reject) => {
fetch('https://example.com')
.then(response => {
if (!response.ok) {
reject(response);
} else {
resolve(response.status);
}
})
.catch(error => {
reject(error);
});
});
}
browser.runtime.onMessage.addListener(handleMessages);
প্রমিজ রিটার্ন করার জন্য আপনি একটি লিসেনারকে async হিসেবেও ডিক্লেয়ার করতে পারেন:
browser.runtime.onMessage.addListener(async function(message, sender) {
const response = await fetch('https://example.com');
if (!response.ok) {
// rejects the promise returned by `async function`.
throw new Error(`Fetch failed: ${response.status}`);
}
// resolves the promise returned by `async function`.
return {statusCode: response.status};
});
প্রমিজ রিটার্ন করা: async ফাংশনের সমস্যাসমূহ
মনে রাখবেন যে, একটি লিসেনার হিসেবে async ফাংশন return স্টেটমেন্ট ছাড়াও সর্বদা একটি প্রমিজ রিটার্ন করে। যদি কোনো async লিসেনার কোনো ভ্যালু রিটার্ন না করে, তবে এর প্রমিজটি ইমপ্লিসিটলি undefined এ রিজলভ হয়ে যায় এবং সেন্ডারের কাছে রেসপন্স হিসেবে null পাঠানো হয়। একাধিক লিসেনার থাকলে এটি অপ্রত্যাশিত আচরণের কারণ হতে পারে।
// content_script.js
function handleResponse(message) {
// The first listener promise resolves to `undefined` before the second
// listener can respond. When a listener responds with `undefined`, Chrome
// sends null as the response.
console.assert(message === null);
}
function notifyBackgroundPage() {
const sending = browser.runtime.sendMessage('test');
sending.then(handleResponse);
}
notifyBackgroundPage();
// background.js
browser.runtime.onMessage.addListener(async (message) => {
// This just causes the function to pause for a millisecond, but the promise
// is *not* returned from the listener so it doesn't act as a response.
await new Promise(resolve => {
setTimeout(resolve, 1, 'OK');
});
// `async` functions always return promises. So once we
// reach here there is an implicit `return undefined;`. Chrome translates
// `undefined` responses to `null`.
});
browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
return new Promise((resolve) => {
setTimeout(resolve, 1000, 'response');
});
});
ত্রুটি পরিচালনা
ক্রোম ১৪৬ থেকে, যদি কোনো onMessage লিসেনার কোনো এরর থ্রো করে (সিঙ্ক্রোনাসলি অথবা রিজেক্ট হওয়া কোনো প্রমিজ রিটার্ন করার মাধ্যমে অ্যাসিঙ্ক্রোনাসলি), তাহলে সেন্ডারের sendMessage() দ্বারা রিটার্ন করা প্রমিজটি এররের মেসেজসহ রিজেক্ট হয়ে যাবে। এই আপডেটটি পর্যায়ক্রমে চালু করা হচ্ছে, তাই আপনি দেখতে পারেন যে এই পরিবর্তনটি এখনও সব ব্যবহারকারীর ব্রাউজারে কার্যকর হয়নি। এই পরিবর্তনটি চালু আছে কি না, তা আপনার এক্সটেনশনটি সামলাতে পারবে কি না, তা আপনাকে নিশ্চিত করতে হবে।
যদি কোনো লিসেনার এমন একটি রেসপন্স রিটার্ন করার চেষ্টা করে যা সিরিয়ালাইজ করা যায় না (এবং এর ফলে সৃষ্ট TypeError টি ক্যাচ না করে), তবে এটিকেও লিসেনারের একটি এরর থ্রো করা হিসেবে গণ্য করা হবে।
যদি কোনো লিসেনার এমন একটি প্রমিজ রিটার্ন করে যা রিজেক্ট হয়, তবে সেন্ডারের কাছে সেই এরর মেসেজটি পৌঁছানোর জন্য সেটিকে অবশ্যই একটি Error ইনস্ট্যান্স দিয়ে রিজেক্ট করতে হবে। যদি প্রমিজটি অন্য কোনো ভ্যালু (যেমন null বা undefined ) দিয়ে রিজেক্ট করা হয়, তাহলে sendMessage() তার পরিবর্তে একটি জেনেরিক এরর মেসেজ দিয়ে রিজেক্ট হবে।
যদি onMessage জন্য একাধিক লিসেনার নিবন্ধিত থাকে, তবে শুধুমাত্র প্রথম যে লিসেনারটি সাড়া দেবে, প্রত্যাখ্যান করবে বা কোনো ত্রুটি দেখাবে, সেটিই প্রেরককে প্রভাবিত করবে; অন্য সব লিসেনার চালু হবে, কিন্তু তাদের ফলাফল উপেক্ষা করা হবে।
উদাহরণ
যদি কোনো লিসেনার এমন একটি প্রমিজ রিটার্ন করে যা রিজেক্ট হয়, তাহলে sendMessage() রিজেক্ট হয়ে যায়:
// sender.js
try {
await browser.runtime.sendMessage('test');
} catch (e) {
console.log(e.message); // "some error"
}
// listener.js
browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
return Promise.reject(new Error('some error'));
});
যদি কোনো লিসেনার এমন কোনো মান দিয়ে সাড়া দেয় যা সিরিয়ালাইজ করা যায় না, sendMessage() প্রত্যাখ্যাত হয়:
// sender.js
try {
await browser.runtime.sendMessage('test');
} catch (e) {
console.log(e.message); // "Error: Could not serialize message."
}
// listener.js
browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
sendResponse(() => {}); // Functions are not serializable
return true; // Keep channel open for async sendResponse
});
যদি অন্য কোনো লিসেনারের সাড়া দেওয়ার আগে কোনো লিসেনার সিনক্রোনাসভাবে একটি এরর থ্রো করে, তাহলে সেই লিসেনারের sendMessage() প্রমিসটি রিজেক্ট হয়ে যায়:
// sender.js
try {
await browser.runtime.sendMessage('test');
} catch (e) {
console.log(e.message); // "error!"
}
// listener.js
browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
throw new Error('error!');
});
তবে, যদি একজন লিসেনার অন্যজনের এরর দেওয়ার আগেই সাড়া দেয়, তাহলে sendMessage() সফল হয়:
// sender.js
const response = await browser.runtime.sendMessage('test');
console.log(response); // "OK"
// listener.js
browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
sendResponse('OK');
});
browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
throw new Error('error!');
});
দীর্ঘস্থায়ী সংযোগ
একটি পুনঃব্যবহারযোগ্য ও দীর্ঘস্থায়ী বার্তা আদান-প্রদান চ্যানেল তৈরি করতে, কল করুন:
- কন্টেন্ট স্ক্রিপ্ট থেকে এক্সটেনশন পেজে মেসেজ পাঠানোর জন্য
runtime.connect()করা হয়। - এক্সটেনশন পেজ থেকে কন্টেন্ট স্ক্রিপ্টে মেসেজ পাঠানোর জন্য
tabs.connect()ব্যবহার করা হয়।
বিভিন্ন ধরনের সংযোগের মধ্যে পার্থক্য করার জন্য, আপনি একটি name ' কী-সহ অপশনস প্যারামিটার পাস করে আপনার চ্যানেলের নামকরণ করতে পারেন:
const port = browser.runtime.connect({name: "example"});
একটি দীর্ঘস্থায়ী সংযোগের একটি সম্ভাব্য ব্যবহার হলো একটি স্বয়ংক্রিয় ফর্ম-পূরণকারী এক্সটেনশন। কন্টেন্ট স্ক্রিপ্টটি একটি নির্দিষ্ট লগইনের জন্য এক্সটেনশন পেজে একটি চ্যানেল খুলতে পারে এবং পূরণ করার জন্য ফর্মের ডেটা অনুরোধ করতে পেজের প্রতিটি ইনপুট এলিমেন্টের জন্য এক্সটেনশনটিতে একটি বার্তা পাঠাতে পারে। এই শেয়ার্ড সংযোগটি এক্সটেনশনটিকে এর কম্পোনেন্টগুলোর মধ্যে স্টেট শেয়ার করার সুযোগ দেয়।
সংযোগ স্থাপনের সময়, সেই সংযোগের মাধ্যমে বার্তা পাঠানো ও গ্রহণ করার জন্য উভয় প্রান্তকে একটি runtime.Port অবজেক্ট বরাদ্দ করা হয়।
একটি কন্টেন্ট স্ক্রিপ্ট থেকে চ্যানেল খুলতে এবং মেসেজ পাঠাতে ও শুনতে নিম্নলিখিত কোডটি ব্যবহার করুন:
কন্টেন্ট-স্ক্রিপ্ট.জেএস:
const port = browser.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:
browser.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."});
}
});
});
ক্রমিকীকরণ
ক্রোমে, মেসেজ পাসিং এপিআইগুলো JSON সিরিয়ালাইজেশন ব্যবহার করে। উল্লেখ্য যে, এটি অন্যান্য ব্রাউজার থেকে ভিন্ন, যেগুলো স্ট্রাকচার্ড ক্লোন অ্যালগরিদম দিয়ে একই এপিআইগুলো প্রয়োগ করে।
এর মানে হলো, একটি বার্তা (এবং প্রাপকদের দেওয়া উত্তর) যেকোনো বৈধ JSON.stringify() ভ্যালু ধারণ করতে পারে। অন্যান্য ভ্যালুগুলোকে সিরিয়ালাইজেবল ভ্যালুতে রূপান্তরিত করা হবে (বিশেষত undefined null হিসেবে সিরিয়ালাইজ করা হবে);
বার্তার আকারের সীমা
একটি বার্তার সর্বোচ্চ আকার ৬৪ এমআইবি।
বন্দরের জীবনকাল
একটি এক্সটেনশনের বিভিন্ন অংশের মধ্যে দ্বিমুখী যোগাযোগের মাধ্যম হিসেবে পোর্টগুলো ডিজাইন করা হয়েছে। যখন এক্সটেনশনের কোনো অংশ 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 a single request:
browser.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:
browser.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:
browser.runtime.sendMessage(laserExtensionId, {getTargetData: true},
function(response) {
if (targetInRange(response.targetData))
browser.runtime.sendMessage(laserExtensionId, {activateLasers: true});
}
);
// For a long-lived connection:
const port = browser.runtime.connect(laserExtensionId);
port.postMessage(...);
ওয়েব পেজ থেকে বার্তা পাঠান
এক্সটেনশনগুলি ওয়েব পেজ থেকে বার্তা গ্রহণ এবং তার উত্তরও দিতে পারে। একটি ওয়েব পেজ থেকে কোনো এক্সটেনশনে বার্তা পাঠাতে, আপনার manifest.json ফাইলে "externally_connectable" ম্যানিফেস্ট কী ব্যবহার করে নির্দিষ্ট করুন যে আপনি কোন কোন ওয়েবসাইট থেকে বার্তা পাঠানোর অনুমতি দিতে চান। উদাহরণস্বরূপ:
manifest.json
"externally_connectable": {
"matches": ["https://*.example.com/*"]
}
এর মাধ্যমে, আপনার নির্দিষ্ট করা ম্যাচ প্যাটার্নের সাথে মেলে এমন যেকোনো পৃষ্ঠার জন্য মেসেজিং এপিআই উন্মুক্ত হয়ে যায়।
কোনো নির্দিষ্ট এক্সটেনশনে বার্তা পাঠাতে 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 && browser.runtime) {
// Make a request:
browser.runtime.sendMessage(
editorExtensionId,
{
openUrlInEditor: url
},
(response) => {
if (!response.success) handleError(url);
}
);
}
আপনার এক্সটেনশন থেকে, ক্রস-এক্সটেনশন মেসেজিংয়ের মতোই runtime.onMessageExternal অথবা runtime.onConnectExternal API ব্যবহার করে ওয়েব পেজের মেসেজগুলো শুনুন। এখানে একটি উদাহরণ দেওয়া হলো:
সার্ভিস-ওয়ার্কার.জেএস
browser.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);
});
এক্সটেনশন থেকে কোনো ওয়েব পেজে বার্তা পাঠানো সম্ভব নয়।
স্থানীয় বার্তা
এক্সটেনশনগুলো সেইসব নেটিভ অ্যাপ্লিকেশনের সাথে বার্তা আদান-প্রদান করতে পারে , যেগুলো নেটিভ মেসেজিং হোস্ট হিসেবে নিবন্ধিত। এই বৈশিষ্ট্যটি সম্পর্কে আরও জানতে, নেটিভ মেসেজিং দেখুন।
নিরাপত্তা সংক্রান্ত বিবেচনা
মেসেজিং সম্পর্কিত কয়েকটি নিরাপত্তা বিষয় নিচে দেওয়া হলো।
কন্টেন্ট স্ক্রিপ্টগুলো কম নির্ভরযোগ্য।
এক্সটেনশন সার্ভিস ওয়ার্কারের তুলনায় কন্টেন্ট স্ক্রিপ্ট কম নির্ভরযোগ্য । উদাহরণস্বরূপ, একটি ক্ষতিকারক ওয়েব পেজ সেই রেন্ডারিং প্রক্রিয়াটিকে ক্ষতিগ্রস্ত করতে পারে যা কন্টেন্ট স্ক্রিপ্টগুলো চালায়। ধরে নিন যে একটি কন্টেন্ট স্ক্রিপ্ট থেকে আসা বার্তাগুলো কোনো আক্রমণকারী দ্বারা তৈরি করা হতে পারে এবং সমস্ত ইনপুট যাচাই ও জীবাণুমুক্ত করা নিশ্চিত করুন। ধরে নিন যে কন্টেন্ট স্ক্রিপ্টে পাঠানো যেকোনো ডেটা ওয়েব পেজে ফাঁস হয়ে যেতে পারে। কন্টেন্ট স্ক্রিপ্ট থেকে প্রাপ্ত বার্তা দ্বারা যে বিশেষাধিকারপ্রাপ্ত কাজগুলো করা যেতে পারে, তার পরিধি সীমিত রাখুন।
ক্রস-সাইট স্ক্রিপ্টিং
আপনার স্ক্রিপ্টগুলোকে ক্রস-সাইট স্ক্রিপ্টিং থেকে সুরক্ষিত রাখুন। ব্যবহারকারীর ইনপুট, কন্টেন্ট স্ক্রিপ্টের মাধ্যমে অন্য ওয়েবসাইট বা এপিআই-এর মতো কোনো অবিশ্বস্ত উৎস থেকে ডেটা গ্রহণ করার সময়, সেটিকে এইচটিএমএল (HTML) হিসেবে ব্যাখ্যা করা বা এমনভাবে ব্যবহার করা থেকে বিরত থাকুন, যার ফলে অপ্রত্যাশিত কোড চালু হতে পারে।
যথাসম্ভব এমন API ব্যবহার করুন যা স্ক্রিপ্ট চালায় না:
সার্ভিস-ওয়ার্কার.জেএস
browser.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) { // JSON.parse doesn't evaluate the attacker's scripts. const resp = JSON.parse(response.farewell); });
সার্ভিস-ওয়ার্কার.জেএস
browser.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) { // innerText does not let the attacker inject HTML elements. document.getElementById("resp").innerText = response.farewell; });
নিম্নলিখিত পদ্ধতিগুলো ব্যবহার করা থেকে বিরত থাকুন, যেগুলো আপনার এক্সটেনশনকে ঝুঁকিপূর্ণ করে তোলে:
সার্ভিস-ওয়ার্কার.জেএস
browser.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) { // WARNING! Might be evaluating a malicious script! const resp = eval(`(${response.farewell})`); });
সার্ভিস-ওয়ার্কার.জেএস
browser.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) { // WARNING! Might be injecting a malicious script! document.getElementById("resp").innerHTML = response.farewell; });
মেসেজিং এপিআই আপনাকে আপনার এক্সটেনশনের সাথে যুক্ত কনটেক্সটে চলমান বিভিন্ন স্ক্রিপ্টের মধ্যে যোগাযোগ করতে দেয়। এর মধ্যে আপনার সার্ভিস ওয়ার্কার, chrome-extension://pages এবং কন্টেন্ট স্ক্রিপ্টের মধ্যকার যোগাযোগ অন্তর্ভুক্ত। উদাহরণস্বরূপ, একটি আরএসএস রিডার এক্সটেনশন কোনো পেজে আরএসএস ফিডের উপস্থিতি শনাক্ত করতে কন্টেন্ট স্ক্রিপ্ট ব্যবহার করতে পারে, এবং তারপর সেই পেজের অ্যাকশন আইকন আপডেট করার জন্য সার্ভিস ওয়ার্কারকে অবহিত করতে পারে।
মেসেজ আদান-প্রদানের দুটি এপিআই রয়েছে: একটি এককালীন অনুরোধের জন্য, এবং অন্যটি দীর্ঘস্থায়ী সংযোগের জন্য যা দিয়ে একাধিক মেসেজ পাঠানো যায়।
এক্সটেনশনগুলোর মধ্যে বার্তা পাঠানোর বিষয়ে তথ্যের জন্য, ক্রস-এক্সটেনশন বার্তা বিভাগটি দেখুন।
এককালীন অনুরোধ
আপনার এক্সটেনশনের অন্য কোনো অংশে একটিমাত্র মেসেজ পাঠাতে এবং ঐচ্ছিকভাবে একটি রেসপন্স পেতে, runtime.sendMessage() অথবা tabs.sendMessage() কল করুন। এই মেথডগুলো আপনাকে একটি কন্টেন্ট স্ক্রিপ্ট থেকে এক্সটেনশনে, অথবা এক্সটেনশন থেকে একটি কন্টেন্ট স্ক্রিপ্টে একবারের জন্য JSON-সিরিয়ালাইজেবল মেসেজ পাঠাতে দেয়। উভয় API-ই একটি Promise রিটার্ন করে, যা প্রাপকের দেওয়া রেসপন্সে রিজলভ হয়।
কন্টেন্ট স্ক্রিপ্ট থেকে অনুরোধ পাঠানোর প্রক্রিয়াটি দেখতে এইরকম:
কন্টেন্ট-স্ক্রিপ্ট.জেএস:
(async () => {
const response = await browser.runtime.sendMessage({greeting: "hello"});
// do something with response here, not outside the function
console.log(response);
})();
প্রতিক্রিয়া
মেসেজ শোনার জন্য, browser.runtime.onMessage ইভেন্টটি ব্যবহার করুন:
// Event listener
function handleMessages(message, sender, sendResponse) {
if (message !== 'get-status') return;
fetch('https://example.com')
.then((response) => sendResponse({statusCode: response.status}))
// Since `fetch` is asynchronous, must return an explicit `true`
return true;
}
browser.runtime.onMessage.addListener(handleMessages);
// From the sender's context...
const {statusCode} = await browser.runtime.sendMessage('get-status');
যখন ইভেন্ট লিসেনারটি কল করা হয়, তখন তৃতীয় প্যারামিটার হিসেবে একটি sendResponse ফাংশন পাস করা হয়। এটি এমন একটি ফাংশন যা একটি প্রতিক্রিয়া প্রদানের জন্য কল করা যেতে পারে। ডিফল্টরূপে, sendResponse কলব্যাকটি অবশ্যই সিনক্রোনাসভাবে কল করতে হবে।
কোনো প্যারামিটার ছাড়া sendResponse কল করলে, প্রতিক্রিয়া হিসেবে null পাঠানো হয়।
অ্যাসিঙ্ক্রোনাসভাবে প্রতিক্রিয়া পাঠানোর জন্য আপনার কাছে দুটি বিকল্প রয়েছে: true রিটার্ন করা অথবা একটি 'promise' রিটার্ন করা।
true ফেরত দিন
sendResponse() ব্যবহার করে অ্যাসিঙ্ক্রোনাসভাবে সাড়া দিতে, ইভেন্ট লিসেনার থেকে একটি আক্ষরিক true (শুধু ট্রুথি ভ্যালু নয়) রিটার্ন করুন। এটি করলে sendResponse কল না করা পর্যন্ত অপর প্রান্তের সাথে মেসেজ চ্যানেলটি খোলা থাকবে, ফলে আপনি এটি পরে কল করতে পারবেন।
একটি প্রতিশ্রুতি ফেরত দিন
ক্রোম ১৪৮ থেকে, আপনি অ্যাসিঙ্ক্রোনাসভাবে সাড়া দেওয়ার জন্য একটি মেসেজ লিসেনার থেকে একটি প্রমিজ রিটার্ন করতে পারবেন। এই আপডেটটি পর্যায়ক্রমে চালু করা হচ্ছে, তাই আপনি দেখতে পারেন যে এটি এখনও সব ব্যবহারকারীর ব্রাউজারে উপলব্ধ নয়। এই সক্ষমতাটি চালু আছে কি না, তা আপনার এক্সটেনশনটি সামলাতে পারে কি না, তা আপনাকে নিশ্চিত করতে হবে। এই সক্ষমতাটি চালু থাকুক বা না থাকুক, অ্যাসিঙ্ক্রোনাস রেসপন্সের জন্য return true; ব্যবহার করা অব্যাহত থাকবে।
যদি প্রমিসটি রিজলভ হয়, তবে এর রিজলভড ভ্যালুটি রেসপন্স হিসেবে পাঠানো হয়।
প্রমিসটি প্রত্যাখ্যাত হলে, প্রেরকের sendMessage() কলটি ত্রুটির বার্তা সহ প্রত্যাখ্যাত হবে। আরও বিস্তারিত তথ্য ও উদাহরণের জন্য ত্রুটি পরিচালনা (error handling) বিভাগটি দেখুন।
এমন একটি প্রমিজ রিটার্ন করার উদাহরণ যা রিজলভ বা রিজেক্ট হতে পারে:
// Event listener
function handleMessages(message, sender, sendResponse) {
// Return a promise that wraps fetch
// If the response is OK, resolve with the status. If it's not OK then reject
// with the network error that prevents the fetch from completing.
return new Promise((resolve, reject) => {
fetch('https://example.com')
.then(response => {
if (!response.ok) {
reject(response);
} else {
resolve(response.status);
}
})
.catch(error => {
reject(error);
});
});
}
browser.runtime.onMessage.addListener(handleMessages);
প্রমিজ রিটার্ন করার জন্য আপনি একটি লিসেনারকে async হিসেবেও ডিক্লেয়ার করতে পারেন:
browser.runtime.onMessage.addListener(async function(message, sender) {
const response = await fetch('https://example.com');
if (!response.ok) {
// rejects the promise returned by `async function`.
throw new Error(`Fetch failed: ${response.status}`);
}
// resolves the promise returned by `async function`.
return {statusCode: response.status};
});
প্রমিজ রিটার্ন করা: async ফাংশনের সমস্যাসমূহ
মনে রাখবেন যে, একটি লিসেনার হিসেবে async ফাংশন return স্টেটমেন্ট ছাড়াও সর্বদা একটি প্রমিজ রিটার্ন করে। যদি কোনো async লিসেনার কোনো ভ্যালু রিটার্ন না করে, তবে এর প্রমিজটি ইমপ্লিসিটলি undefined এ রিজলভ হয়ে যায় এবং সেন্ডারের কাছে রেসপন্স হিসেবে null পাঠানো হয়। একাধিক লিসেনার থাকলে এটি অপ্রত্যাশিত আচরণের কারণ হতে পারে।
// content_script.js
function handleResponse(message) {
// The first listener promise resolves to `undefined` before the second
// listener can respond. When a listener responds with `undefined`, Chrome
// sends null as the response.
console.assert(message === null);
}
function notifyBackgroundPage() {
const sending = browser.runtime.sendMessage('test');
sending.then(handleResponse);
}
notifyBackgroundPage();
// background.js
browser.runtime.onMessage.addListener(async (message) => {
// This just causes the function to pause for a millisecond, but the promise
// is *not* returned from the listener so it doesn't act as a response.
await new Promise(resolve => {
setTimeout(resolve, 1, 'OK');
});
// `async` functions always return promises. So once we
// reach here there is an implicit `return undefined;`. Chrome translates
// `undefined` responses to `null`.
});
browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
return new Promise((resolve) => {
setTimeout(resolve, 1000, 'response');
});
});
ত্রুটি পরিচালনা
ক্রোম ১৪৬ থেকে, যদি কোনো onMessage লিসেনার কোনো এরর থ্রো করে (সিঙ্ক্রোনাসলি অথবা রিজেক্ট হওয়া কোনো প্রমিজ রিটার্ন করার মাধ্যমে অ্যাসিঙ্ক্রোনাসলি), তাহলে সেন্ডারের sendMessage() দ্বারা রিটার্ন করা প্রমিজটি এররের মেসেজসহ রিজেক্ট হয়ে যাবে। এই আপডেটটি পর্যায়ক্রমে চালু করা হচ্ছে, তাই আপনি দেখতে পারেন যে এই পরিবর্তনটি এখনও সব ব্যবহারকারীর ব্রাউজারে কার্যকর হয়নি। এই পরিবর্তনটি চালু আছে কি না, তা আপনার এক্সটেনশনটি সামলাতে পারবে কি না, তা আপনাকে নিশ্চিত করতে হবে।
যদি কোনো লিসেনার এমন একটি রেসপন্স রিটার্ন করার চেষ্টা করে যা সিরিয়ালাইজ করা যায় না (এবং এর ফলে সৃষ্ট TypeError টি ক্যাচ না করে), তবে এটিকেও লিসেনারের একটি এরর থ্রো করা হিসেবে গণ্য করা হবে।
যদি কোনো লিসেনার এমন একটি প্রমিজ রিটার্ন করে যা রিজেক্ট হয়, তবে সেন্ডারের কাছে সেই এরর মেসেজটি পৌঁছানোর জন্য সেটিকে অবশ্যই একটি Error ইনস্ট্যান্স দিয়ে রিজেক্ট করতে হবে। যদি প্রমিজটি অন্য কোনো ভ্যালু (যেমন null বা undefined ) দিয়ে রিজেক্ট করা হয়, তাহলে sendMessage() তার পরিবর্তে একটি জেনেরিক এরর মেসেজ দিয়ে রিজেক্ট হবে।
যদি onMessage জন্য একাধিক লিসেনার নিবন্ধিত থাকে, তবে শুধুমাত্র প্রথম যে লিসেনারটি সাড়া দেবে, প্রত্যাখ্যান করবে বা কোনো ত্রুটি দেখাবে, সেটিই প্রেরককে প্রভাবিত করবে; অন্য সব লিসেনার চালু হবে, কিন্তু তাদের ফলাফল উপেক্ষা করা হবে।
উদাহরণ
যদি কোনো লিসেনার এমন একটি প্রমিজ রিটার্ন করে যা রিজেক্ট হয়, তাহলে sendMessage() রিজেক্ট হয়ে যায়:
// sender.js
try {
await browser.runtime.sendMessage('test');
} catch (e) {
console.log(e.message); // "some error"
}
// listener.js
browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
return Promise.reject(new Error('some error'));
});
যদি কোনো লিসেনার এমন কোনো মান দিয়ে সাড়া দেয় যা সিরিয়ালাইজ করা যায় না, sendMessage() প্রত্যাখ্যাত হয়:
// sender.js
try {
await browser.runtime.sendMessage('test');
} catch (e) {
console.log(e.message); // "Error: Could not serialize message."
}
// listener.js
browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
sendResponse(() => {}); // Functions are not serializable
return true; // Keep channel open for async sendResponse
});
যদি অন্য কোনো লিসেনারের সাড়া দেওয়ার আগে কোনো লিসেনার সিনক্রোনাসভাবে একটি এরর থ্রো করে, তাহলে সেই লিসেনারের sendMessage() প্রমিসটি রিজেক্ট হয়ে যায়:
// sender.js
try {
await browser.runtime.sendMessage('test');
} catch (e) {
console.log(e.message); // "error!"
}
// listener.js
browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
throw new Error('error!');
});
তবে, যদি একজন লিসেনার অন্যজনের এরর দেওয়ার আগেই সাড়া দেয়, তাহলে sendMessage() সফল হয়:
// sender.js
const response = await browser.runtime.sendMessage('test');
console.log(response); // "OK"
// listener.js
browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
sendResponse('OK');
});
browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
throw new Error('error!');
});
দীর্ঘস্থায়ী সংযোগ
একটি পুনঃব্যবহারযোগ্য ও দীর্ঘস্থায়ী বার্তা আদান-প্রদান চ্যানেল তৈরি করতে, কল করুন:
- কন্টেন্ট স্ক্রিপ্ট থেকে এক্সটেনশন পেজে মেসেজ পাঠানোর জন্য
runtime.connect()করা হয়। - এক্সটেনশন পেজ থেকে কন্টেন্ট স্ক্রিপ্টে মেসেজ পাঠানোর জন্য
tabs.connect()ব্যবহার করা হয়।
বিভিন্ন ধরনের সংযোগের মধ্যে পার্থক্য করার জন্য, আপনি একটি name ' কী-সহ অপশনস প্যারামিটার পাস করে আপনার চ্যানেলের নামকরণ করতে পারেন:
const port = browser.runtime.connect({name: "example"});
একটি দীর্ঘস্থায়ী সংযোগের একটি সম্ভাব্য ব্যবহার হলো একটি স্বয়ংক্রিয় ফর্ম-পূরণকারী এক্সটেনশন। কন্টেন্ট স্ক্রিপ্টটি একটি নির্দিষ্ট লগইনের জন্য এক্সটেনশন পেজে একটি চ্যানেল খুলতে পারে এবং পূরণ করার জন্য ফর্মের ডেটা অনুরোধ করতে পেজের প্রতিটি ইনপুট এলিমেন্টের জন্য এক্সটেনশনটিতে একটি বার্তা পাঠাতে পারে। এই শেয়ার্ড সংযোগটি এক্সটেনশনটিকে এর কম্পোনেন্টগুলোর মধ্যে স্টেট শেয়ার করার সুযোগ দেয়।
সংযোগ স্থাপনের সময়, সেই সংযোগের মাধ্যমে বার্তা পাঠানো ও গ্রহণ করার জন্য উভয় প্রান্তকে একটি runtime.Port অবজেক্ট বরাদ্দ করা হয়।
একটি কন্টেন্ট স্ক্রিপ্ট থেকে চ্যানেল খুলতে এবং মেসেজ পাঠাতে ও শুনতে নিম্নলিখিত কোডটি ব্যবহার করুন:
কন্টেন্ট-স্ক্রিপ্ট.জেএস:
const port = browser.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:
browser.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."});
}
});
});
ক্রমিকীকরণ
ক্রোমে, মেসেজ পাসিং এপিআইগুলো JSON সিরিয়ালাইজেশন ব্যবহার করে। উল্লেখ্য যে, এটি অন্যান্য ব্রাউজার থেকে ভিন্ন, যেগুলো স্ট্রাকচার্ড ক্লোন অ্যালগরিদম দিয়ে একই এপিআইগুলো প্রয়োগ করে।
এর মানে হলো, একটি বার্তা (এবং প্রাপকদের দেওয়া উত্তর) যেকোনো বৈধ JSON.stringify() ভ্যালু ধারণ করতে পারে। অন্যান্য ভ্যালুগুলোকে সিরিয়ালাইজেবল ভ্যালুতে রূপান্তরিত করা হবে (বিশেষত undefined null হিসেবে সিরিয়ালাইজ করা হবে);
বার্তার আকারের সীমা
একটি বার্তার সর্বোচ্চ আকার ৬৪ এমআইবি।
বন্দরের জীবনকাল
একটি এক্সটেনশনের বিভিন্ন অংশের মধ্যে দ্বিমুখী যোগাযোগের মাধ্যম হিসেবে পোর্টগুলো ডিজাইন করা হয়েছে। যখন এক্সটেনশনের কোনো অংশ 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 a single request:
browser.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:
browser.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:
browser.runtime.sendMessage(laserExtensionId, {getTargetData: true},
function(response) {
if (targetInRange(response.targetData))
browser.runtime.sendMessage(laserExtensionId, {activateLasers: true});
}
);
// For a long-lived connection:
const port = browser.runtime.connect(laserExtensionId);
port.postMessage(...);
ওয়েব পেজ থেকে বার্তা পাঠান
এক্সটেনশনগুলি ওয়েব পেজ থেকে বার্তা গ্রহণ এবং তার উত্তরও দিতে পারে। একটি ওয়েব পেজ থেকে কোনো এক্সটেনশনে বার্তা পাঠাতে, আপনার manifest.json ফাইলে "externally_connectable" ম্যানিফেস্ট কী ব্যবহার করে নির্দিষ্ট করুন যে আপনি কোন কোন ওয়েবসাইট থেকে বার্তা পাঠানোর অনুমতি দিতে চান। উদাহরণস্বরূপ:
manifest.json
"externally_connectable": {
"matches": ["https://*.example.com/*"]
}
এর মাধ্যমে, আপনার নির্দিষ্ট করা ম্যাচ প্যাটার্নের সাথে মেলে এমন যেকোনো পৃষ্ঠার জন্য মেসেজিং এপিআই উন্মুক্ত হয়ে যায়।
কোনো নির্দিষ্ট এক্সটেনশনে বার্তা পাঠাতে 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 && browser.runtime) {
// Make a request:
browser.runtime.sendMessage(
editorExtensionId,
{
openUrlInEditor: url
},
(response) => {
if (!response.success) handleError(url);
}
);
}
আপনার এক্সটেনশন থেকে, ক্রস-এক্সটেনশন মেসেজিংয়ের মতোই runtime.onMessageExternal অথবা runtime.onConnectExternal API ব্যবহার করে ওয়েব পেজের মেসেজগুলো শুনুন। এখানে একটি উদাহরণ দেওয়া হলো:
সার্ভিস-ওয়ার্কার.জেএস
browser.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);
});
এক্সটেনশন থেকে কোনো ওয়েব পেজে বার্তা পাঠানো সম্ভব নয়।
স্থানীয় বার্তা
এক্সটেনশনগুলো সেইসব নেটিভ অ্যাপ্লিকেশনের সাথে বার্তা আদান-প্রদান করতে পারে , যেগুলো নেটিভ মেসেজিং হোস্ট হিসেবে নিবন্ধিত। এই বৈশিষ্ট্যটি সম্পর্কে আরও জানতে, নেটিভ মেসেজিং দেখুন।
নিরাপত্তা সংক্রান্ত বিবেচনা
মেসেজিং সম্পর্কিত কয়েকটি নিরাপত্তা বিষয় নিচে দেওয়া হলো।
কন্টেন্ট স্ক্রিপ্টগুলো কম নির্ভরযোগ্য।
এক্সটেনশন সার্ভিস ওয়ার্কারের তুলনায় কন্টেন্ট স্ক্রিপ্ট কম নির্ভরযোগ্য । উদাহরণস্বরূপ, একটি ক্ষতিকারক ওয়েব পেজ সেই রেন্ডারিং প্রক্রিয়াটিকে ক্ষতিগ্রস্ত করতে পারে যা কন্টেন্ট স্ক্রিপ্টগুলো চালায়। ধরে নিন যে একটি কন্টেন্ট স্ক্রিপ্ট থেকে আসা বার্তাগুলো কোনো আক্রমণকারী দ্বারা তৈরি করা হতে পারে এবং সমস্ত ইনপুট যাচাই ও জীবাণুমুক্ত করা নিশ্চিত করুন। ধরে নিন যে কন্টেন্ট স্ক্রিপ্টে পাঠানো যেকোনো ডেটা ওয়েব পেজে ফাঁস হয়ে যেতে পারে। কন্টেন্ট স্ক্রিপ্ট থেকে প্রাপ্ত বার্তা দ্বারা যে বিশেষাধিকারপ্রাপ্ত কাজগুলো করা যেতে পারে, তার পরিধি সীমিত রাখুন।
ক্রস-সাইট স্ক্রিপ্টিং
আপনার স্ক্রিপ্টগুলোকে ক্রস-সাইট স্ক্রিপ্টিং থেকে সুরক্ষিত রাখুন। ব্যবহারকারীর ইনপুট, কন্টেন্ট স্ক্রিপ্টের মাধ্যমে অন্য ওয়েবসাইট বা এপিআই-এর মতো কোনো অবিশ্বস্ত উৎস থেকে ডেটা গ্রহণ করার সময়, সেটিকে এইচটিএমএল (HTML) হিসেবে ব্যাখ্যা করা বা এমনভাবে ব্যবহার করা থেকে বিরত থাকুন, যার ফলে অপ্রত্যাশিত কোড চালু হতে পারে।
যথাসম্ভব এমন API ব্যবহার করুন যা স্ক্রিপ্ট চালায় না:
সার্ভিস-ওয়ার্কার.জেএস
browser.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) { // JSON.parse doesn't evaluate the attacker's scripts. const resp = JSON.parse(response.farewell); });
সার্ভিস-ওয়ার্কার.জেএস
browser.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) { // innerText does not let the attacker inject HTML elements. document.getElementById("resp").innerText = response.farewell; });
নিম্নলিখিত পদ্ধতিগুলো ব্যবহার করা থেকে বিরত থাকুন, যেগুলো আপনার এক্সটেনশনকে ঝুঁকিপূর্ণ করে তোলে:
সার্ভিস-ওয়ার্কার.জেএস
browser.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) { // WARNING! Might be evaluating a malicious script! const resp = eval(`(${response.farewell})`); });
সার্ভিস-ওয়ার্কার.জেএস
browser.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) { // WARNING! Might be injecting a malicious script! document.getElementById("resp").innerHTML = response.farewell; });
মেসেজিং এপিআই আপনাকে আপনার এক্সটেনশনের সাথে যুক্ত কনটেক্সটে চলমান বিভিন্ন স্ক্রিপ্টের মধ্যে যোগাযোগ করতে দেয়। এর মধ্যে আপনার সার্ভিস ওয়ার্কার, chrome-extension://pages এবং কন্টেন্ট স্ক্রিপ্টের মধ্যকার যোগাযোগ অন্তর্ভুক্ত। উদাহরণস্বরূপ, একটি আরএসএস রিডার এক্সটেনশন কোনো পেজে আরএসএস ফিডের উপস্থিতি শনাক্ত করতে কন্টেন্ট স্ক্রিপ্ট ব্যবহার করতে পারে, এবং তারপর সেই পেজের অ্যাকশন আইকন আপডেট করার জন্য সার্ভিস ওয়ার্কারকে অবহিত করতে পারে।
মেসেজ আদান-প্রদানের দুটি এপিআই রয়েছে: একটি এককালীন অনুরোধের জন্য, এবং অন্যটি দীর্ঘস্থায়ী সংযোগের জন্য যা দিয়ে একাধিক মেসেজ পাঠানো যায়।
এক্সটেনশনগুলোর মধ্যে বার্তা পাঠানোর বিষয়ে তথ্যের জন্য, ক্রস-এক্সটেনশন বার্তা বিভাগটি দেখুন।
এককালীন অনুরোধ
আপনার এক্সটেনশনের অন্য কোনো অংশে একটিমাত্র মেসেজ পাঠাতে এবং ঐচ্ছিকভাবে একটি রেসপন্স পেতে, runtime.sendMessage() অথবা tabs.sendMessage() কল করুন। এই মেথডগুলো আপনাকে একটি কন্টেন্ট স্ক্রিপ্ট থেকে এক্সটেনশনে, অথবা এক্সটেনশন থেকে একটি কন্টেন্ট স্ক্রিপ্টে একবারের জন্য JSON-সিরিয়ালাইজেবল মেসেজ পাঠাতে দেয়। উভয় API-ই একটি Promise রিটার্ন করে, যা প্রাপকের দেওয়া রেসপন্সে রিজলভ হয়।
কন্টেন্ট স্ক্রিপ্ট থেকে অনুরোধ পাঠানোর প্রক্রিয়াটি দেখতে এইরকম:
কন্টেন্ট-স্ক্রিপ্ট.জেএস:
(async () => {
const response = await browser.runtime.sendMessage({greeting: "hello"});
// do something with response here, not outside the function
console.log(response);
})();
প্রতিক্রিয়া
মেসেজ শোনার জন্য, browser.runtime.onMessage ইভেন্টটি ব্যবহার করুন:
// Event listener
function handleMessages(message, sender, sendResponse) {
if (message !== 'get-status') return;
fetch('https://example.com')
.then((response) => sendResponse({statusCode: response.status}))
// Since `fetch` is asynchronous, must return an explicit `true`
return true;
}
browser.runtime.onMessage.addListener(handleMessages);
// From the sender's context...
const {statusCode} = await browser.runtime.sendMessage('get-status');
যখন ইভেন্ট লিসেনারটি কল করা হয়, তখন তৃতীয় প্যারামিটার হিসেবে একটি sendResponse ফাংশন পাস করা হয়। এটি এমন একটি ফাংশন যা একটি প্রতিক্রিয়া প্রদানের জন্য কল করা যেতে পারে। ডিফল্টরূপে, sendResponse কলব্যাকটি অবশ্যই সিনক্রোনাসভাবে কল করতে হবে।
কোনো প্যারামিটার ছাড়া sendResponse কল করলে, প্রতিক্রিয়া হিসেবে null পাঠানো হয়।
অ্যাসিঙ্ক্রোনাসভাবে প্রতিক্রিয়া পাঠানোর জন্য আপনার কাছে দুটি বিকল্প রয়েছে: true রিটার্ন করা অথবা একটি 'promise' রিটার্ন করা।
true ফেরত দিন
sendResponse() ব্যবহার করে অ্যাসিঙ্ক্রোনাসভাবে সাড়া দিতে, ইভেন্ট লিসেনার থেকে একটি আক্ষরিক true (শুধু ট্রুথি ভ্যালু নয়) রিটার্ন করুন। এটি করলে sendResponse কল না করা পর্যন্ত অপর প্রান্তের সাথে মেসেজ চ্যানেলটি খোলা থাকবে, ফলে আপনি এটি পরে কল করতে পারবেন।
একটি প্রতিশ্রুতি ফেরত দিন
ক্রোম ১৪৮ থেকে, আপনি অ্যাসিঙ্ক্রোনাসভাবে সাড়া দেওয়ার জন্য একটি মেসেজ লিসেনার থেকে একটি প্রমিজ রিটার্ন করতে পারবেন। এই আপডেটটি পর্যায়ক্রমে চালু করা হচ্ছে, তাই আপনি দেখতে পারেন যে এটি এখনও সব ব্যবহারকারীর ব্রাউজারে উপলব্ধ নয়। এই সক্ষমতাটি চালু আছে কি না, তা আপনার এক্সটেনশনটি সামলাতে পারে কি না, তা আপনাকে নিশ্চিত করতে হবে। এই সক্ষমতাটি চালু থাকুক বা না থাকুক, অ্যাসিঙ্ক্রোনাস রেসপন্সের জন্য return true; ব্যবহার করা অব্যাহত থাকবে।
যদি প্রমিসটি রিজলভ হয়, তবে এর রিজলভড ভ্যালুটি রেসপন্স হিসেবে পাঠানো হয়।
প্রমিসটি প্রত্যাখ্যাত হলে, প্রেরকের sendMessage() কলটি ত্রুটির বার্তা সহ প্রত্যাখ্যাত হবে। আরও বিস্তারিত তথ্য ও উদাহরণের জন্য ত্রুটি পরিচালনা (error handling) বিভাগটি দেখুন।
এমন একটি প্রমিজ রিটার্ন করার উদাহরণ যা রিজলভ বা রিজেক্ট হতে পারে:
// Event listener
function handleMessages(message, sender, sendResponse) {
// Return a promise that wraps fetch
// If the response is OK, resolve with the status. If it's not OK then reject
// with the network error that prevents the fetch from completing.
return new Promise((resolve, reject) => {
fetch('https://example.com')
.then(response => {
if (!response.ok) {
reject(response);
} else {
resolve(response.status);
}
})
.catch(error => {
reject(error);
});
});
}
browser.runtime.onMessage.addListener(handleMessages);
প্রমিজ রিটার্ন করার জন্য আপনি একটি লিসেনারকে async হিসেবেও ডিক্লেয়ার করতে পারেন:
browser.runtime.onMessage.addListener(async function(message, sender) {
const response = await fetch('https://example.com');
if (!response.ok) {
// rejects the promise returned by `async function`.
throw new Error(`Fetch failed: ${response.status}`);
}
// resolves the promise returned by `async function`.
return {statusCode: response.status};
});
প্রমিজ রিটার্ন করা: async ফাংশনের সমস্যাসমূহ
মনে রাখবেন যে, একটি লিসেনার হিসেবে async ফাংশন return স্টেটমেন্ট ছাড়াও সর্বদা একটি প্রমিজ রিটার্ন করে। যদি কোনো async লিসেনার কোনো ভ্যালু রিটার্ন না করে, তবে এর প্রমিজটি ইমপ্লিসিটলি undefined এ রিজলভ হয়ে যায় এবং সেন্ডারের কাছে রেসপন্স হিসেবে null পাঠানো হয়। একাধিক লিসেনার থাকলে এটি অপ্রত্যাশিত আচরণের কারণ হতে পারে।
// content_script.js
function handleResponse(message) {
// The first listener promise resolves to `undefined` before the second
// listener can respond. When a listener responds with `undefined`, Chrome
// sends null as the response.
console.assert(message === null);
}
function notifyBackgroundPage() {
const sending = browser.runtime.sendMessage('test');
sending.then(handleResponse);
}
notifyBackgroundPage();
// background.js
browser.runtime.onMessage.addListener(async (message) => {
// This just causes the function to pause for a millisecond, but the promise
// is *not* returned from the listener so it doesn't act as a response.
await new Promise(resolve => {
setTimeout(resolve, 1, 'OK');
});
// `async` functions always return promises. So once we
// reach here there is an implicit `return undefined;`. Chrome translates
// `undefined` responses to `null`.
});
browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
return new Promise((resolve) => {
setTimeout(resolve, 1000, 'response');
});
});
ত্রুটি পরিচালনা
ক্রোম ১৪৬ থেকে, যদি কোনো onMessage লিসেনার কোনো এরর থ্রো করে (সিঙ্ক্রোনাসলি অথবা রিজেক্ট হওয়া কোনো প্রমিজ রিটার্ন করার মাধ্যমে অ্যাসিঙ্ক্রোনাসলি), তাহলে সেন্ডারের sendMessage() দ্বারা রিটার্ন করা প্রমিজটি এররের মেসেজসহ রিজেক্ট হয়ে যাবে। এই আপডেটটি পর্যায়ক্রমে চালু করা হচ্ছে, তাই আপনি দেখতে পারেন যে এই পরিবর্তনটি এখনও সব ব্যবহারকারীর ব্রাউজারে কার্যকর হয়নি। এই পরিবর্তনটি চালু আছে কি না, তা আপনার এক্সটেনশনটি সামলাতে পারবে কি না, তা আপনাকে নিশ্চিত করতে হবে।
যদি কোনো লিসেনার এমন একটি রেসপন্স রিটার্ন করার চেষ্টা করে যা সিরিয়ালাইজ করা যায় না (এবং এর ফলে সৃষ্ট TypeError টি ক্যাচ না করে), তবে এটিকেও লিসেনারের একটি এরর থ্রো করা হিসেবে গণ্য করা হবে।
যদি কোনো লিসেনার এমন একটি প্রমিজ রিটার্ন করে যা রিজেক্ট হয়, তবে সেন্ডারের কাছে সেই এরর মেসেজটি পৌঁছানোর জন্য সেটিকে অবশ্যই একটি Error ইনস্ট্যান্স দিয়ে রিজেক্ট করতে হবে। যদি প্রমিজটি অন্য কোনো ভ্যালু (যেমন null বা undefined ) দিয়ে রিজেক্ট করা হয়, তাহলে sendMessage() তার পরিবর্তে একটি জেনেরিক এরর মেসেজ দিয়ে রিজেক্ট হবে।
যদি onMessage জন্য একাধিক লিসেনার নিবন্ধিত থাকে, তবে শুধুমাত্র প্রথম যে লিসেনারটি সাড়া দেবে, প্রত্যাখ্যান করবে বা কোনো ত্রুটি দেখাবে, সেটিই প্রেরককে প্রভাবিত করবে; অন্য সব লিসেনার চালু হবে, কিন্তু তাদের ফলাফল উপেক্ষা করা হবে।
উদাহরণ
যদি কোনো লিসেনার এমন একটি প্রমিজ রিটার্ন করে যা রিজেক্ট হয়, তাহলে sendMessage() রিজেক্ট হয়ে যায়:
// sender.js
try {
await browser.runtime.sendMessage('test');
} catch (e) {
console.log(e.message); // "some error"
}
// listener.js
browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
return Promise.reject(new Error('some error'));
});
যদি কোনো লিসেনার এমন কোনো মান দিয়ে সাড়া দেয় যা সিরিয়ালাইজ করা যায় না, sendMessage() প্রত্যাখ্যাত হয়:
// sender.js
try {
await browser.runtime.sendMessage('test');
} catch (e) {
console.log(e.message); // "Error: Could not serialize message."
}
// listener.js
browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
sendResponse(() => {}); // Functions are not serializable
return true; // Keep channel open for async sendResponse
});
যদি অন্য কোনো লিসেনারের সাড়া দেওয়ার আগে কোনো লিসেনার সিনক্রোনাসভাবে একটি এরর থ্রো করে, তাহলে সেই লিসেনারের sendMessage() প্রমিসটি রিজেক্ট হয়ে যায়:
// sender.js
try {
await browser.runtime.sendMessage('test');
} catch (e) {
console.log(e.message); // "error!"
}
// listener.js
browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
throw new Error('error!');
});
তবে, যদি একজন লিসেনার অন্যজনের এরর দেওয়ার আগেই সাড়া দেয়, তাহলে sendMessage() সফল হয়:
// sender.js
const response = await browser.runtime.sendMessage('test');
console.log(response); // "OK"
// listener.js
browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
sendResponse('OK');
});
browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
throw new Error('error!');
});
দীর্ঘস্থায়ী সংযোগ
একটি পুনঃব্যবহারযোগ্য ও দীর্ঘস্থায়ী বার্তা আদান-প্রদান চ্যানেল তৈরি করতে, কল করুন:
- কন্টেন্ট স্ক্রিপ্ট থেকে এক্সটেনশন পেজে মেসেজ পাঠানোর জন্য
runtime.connect()করা হয়। - এক্সটেনশন পেজ থেকে কন্টেন্ট স্ক্রিপ্টে মেসেজ পাঠানোর জন্য
tabs.connect()ব্যবহার করা হয়।
বিভিন্ন ধরনের সংযোগের মধ্যে পার্থক্য করার জন্য, আপনি একটি name ' কী-সহ অপশনস প্যারামিটার পাস করে আপনার চ্যানেলের নামকরণ করতে পারেন:
const port = browser.runtime.connect({name: "example"});
একটি দীর্ঘস্থায়ী সংযোগের একটি সম্ভাব্য ব্যবহার হলো একটি স্বয়ংক্রিয় ফর্ম-পূরণকারী এক্সটেনশন। কন্টেন্ট স্ক্রিপ্টটি একটি নির্দিষ্ট লগইনের জন্য এক্সটেনশন পেজে একটি চ্যানেল খুলতে পারে এবং পূরণ করার জন্য ফর্মের ডেটা অনুরোধ করতে পেজের প্রতিটি ইনপুট এলিমেন্টের জন্য এক্সটেনশনটিতে একটি বার্তা পাঠাতে পারে। এই শেয়ার্ড সংযোগটি এক্সটেনশনটিকে এর কম্পোনেন্টগুলোর মধ্যে স্টেট শেয়ার করার সুযোগ দেয়।
সংযোগ স্থাপনের সময়, সেই সংযোগের মাধ্যমে বার্তা পাঠানো ও গ্রহণ করার জন্য উভয় প্রান্তকে একটি runtime.Port অবজেক্ট বরাদ্দ করা হয়।
একটি কন্টেন্ট স্ক্রিপ্ট থেকে চ্যানেল খুলতে এবং মেসেজ পাঠাতে ও শুনতে নিম্নলিখিত কোডটি ব্যবহার করুন:
কন্টেন্ট-স্ক্রিপ্ট.জেএস:
const port = browser.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:
browser.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."});
}
});
});
ক্রমিকীকরণ
ক্রোমে, মেসেজ পাসিং এপিআইগুলো JSON সিরিয়ালাইজেশন ব্যবহার করে। উল্লেখ্য যে, এটি অন্যান্য ব্রাউজার থেকে ভিন্ন, যেগুলো স্ট্রাকচার্ড ক্লোন অ্যালগরিদম দিয়ে একই এপিআইগুলো প্রয়োগ করে।
এর মানে হলো, একটি বার্তা (এবং প্রাপকদের দেওয়া উত্তর) যেকোনো বৈধ JSON.stringify() ভ্যালু ধারণ করতে পারে। অন্যান্য ভ্যালুগুলোকে সিরিয়ালাইজেবল ভ্যালুতে রূপান্তরিত করা হবে (বিশেষত undefined null হিসেবে সিরিয়ালাইজ করা হবে);
বার্তার আকারের সীমা
একটি বার্তার সর্বোচ্চ আকার ৬৪ এমআইবি।
বন্দরের জীবনকাল
একটি এক্সটেনশনের বিভিন্ন অংশের মধ্যে দ্বিমুখী যোগাযোগের মাধ্যম হিসেবে পোর্টগুলো ডিজাইন করা হয়েছে। যখন এক্সটেনশনের কোনো অংশ 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 a single request:
browser.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:
browser.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:
browser.runtime.sendMessage(laserExtensionId, {getTargetData: true},
function(response) {
if (targetInRange(response.targetData))
browser.runtime.sendMessage(laserExtensionId, {activateLasers: true});
}
);
// For a long-lived connection:
const port = browser.runtime.connect(laserExtensionId);
port.postMessage(...);
ওয়েব পেজ থেকে বার্তা পাঠান
Extensions can also receive and respond to messages from web pages. To send messages from a web page to an extension, specify in your manifest.json which websites you want to allow messages from using the "externally_connectable" manifest key. For example:
manifest.json
"externally_connectable": {
"matches": ["https://*.example.com/*"]
}
This exposes the messaging API to any page that matches the match patterns you specify.
Use the runtime.sendMessage() or runtime.connect() APIs to send a message to a specific extension. For example:
webpage.js
// The ID of the extension we want to talk to.
const editorExtensionId = 'abcdefghijklmnoabcdefhijklmnoabc';
// Check if extension is installed
if (chrome && browser.runtime) {
// Make a request:
browser.runtime.sendMessage(
editorExtensionId,
{
openUrlInEditor: url
},
(response) => {
if (!response.success) handleError(url);
}
);
}
From your extension, listen to messages from web pages using the runtime.onMessageExternal or runtime.onConnectExternal APIs as in cross-extension messaging . Here's an example:
service-worker.js
browser.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);
});
It is not possible to send a message from an extension to a web page.
Native messaging
Extensions can exchange messages with native applications that are registered as a native messaging host . To learn more about this feature, see Native messaging .
Security considerations
Here are a few security considerations related to messaging.
Content scripts are less trustworthy
Content scripts are less trustworthy than the extension service worker. For example, a malicious web page might be able to compromise the rendering process that runs the content scripts. Assume that messages from a content script might have been crafted by an attacker and make sure to validate and sanitize all input . Assume any data sent to the content script might leak to the web page. Limit the scope of privileged actions that can be triggered by messages received from content scripts.
Cross-site scripting
Make sure to protect your scripts against cross-site scripting . When receiving data from an untrusted source such as user input, other websites through a content script, or an API, take care to avoid interpreting this as HTML or using it in a way which could allow unexpected code to run.
Use APIs that don't run scripts whenever possible:
service-worker.js
browser.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) { // JSON.parse doesn't evaluate the attacker's scripts. const resp = JSON.parse(response.farewell); });
service-worker.js
browser.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) { // innerText does not let the attacker inject HTML elements. document.getElementById("resp").innerText = response.farewell; });
Avoid using the following methods that make your extension vulnerable:
service-worker.js
browser.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) { // WARNING! Might be evaluating a malicious script! const resp = eval(`(${response.farewell})`); });
service-worker.js
browser.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) { // WARNING! Might be injecting a malicious script! document.getElementById("resp").innerHTML = response.farewell; });