কন্টেন্ট স্ক্রিপ্ট হলো এমন ফাইল যা ওয়েব পেজের প্রেক্ষাপটে চলে। স্ট্যান্ডার্ড ডকুমেন্ট অবজেক্ট মডেল (DOM) ব্যবহার করে, এগুলি ব্রাউজার দ্বারা পরিদর্শিত ওয়েব পেজগুলির বিবরণ পড়তে, সেগুলিতে পরিবর্তন আনতে এবং তাদের প্যারেন্ট এক্সটেনশনে তথ্য পাঠাতে সক্ষম হয়।
কন্টেন্ট স্ক্রিপ্টের সক্ষমতা বুঝুন
কন্টেন্ট স্ক্রিপ্টগুলো সরাসরি নিম্নলিখিত এক্সটেনশন এপিআইগুলো অ্যাক্সেস করতে পারে:
-
dom -
i18n -
storage -
runtime.connect() -
runtime.getManifest() -
runtime.getURL() -
runtime.id -
runtime.onConnect -
runtime.onMessage -
runtime.sendMessage()
কন্টেন্ট স্ক্রিপ্টগুলো সরাসরি অন্য এপিআই অ্যাক্সেস করতে পারে না। কিন্তু আপনার এক্সটেনশনের অন্যান্য অংশের সাথে বার্তা আদান-প্রদানের মাধ্যমে তারা পরোক্ষভাবে সেগুলো অ্যাক্সেস করতে পারে।
আপনি fetch() এর মতো API ব্যবহার করে একটি কন্টেন্ট স্ক্রিপ্ট থেকে আপনার এক্সটেনশনের অন্যান্য ফাইলও অ্যাক্সেস করতে পারেন। এটি করার জন্য, আপনাকে সেগুলোকে ওয়েব-অ্যাক্সেসযোগ্য রিসোর্স হিসেবে ঘোষণা করতে হবে। মনে রাখবেন যে, এর ফলে একই সাইটে চলমান যেকোনো ফার্স্ট-পার্টি বা থার্ড-পার্টি স্ক্রিপ্টের কাছেও রিসোর্সগুলো উন্মুক্ত হয়ে যায়।
বিচ্ছিন্ন বিশ্বে কাজ করুন
কন্টেন্ট স্ক্রিপ্টগুলো একটি বিচ্ছিন্ন জগতে অবস্থান করে, যার ফলে এটি পেজ বা অন্যান্য এক্সটেনশনের কন্টেন্ট স্ক্রিপ্টের সাথে কোনো সংঘাত ছাড়াই তার জাভাস্ক্রিপ্ট পরিবেশে পরিবর্তন আনতে পারে।
একটি এক্সটেনশন কোনো ওয়েব পেজে নিম্নলিখিত উদাহরণের মতো কোড ব্যবহার করে চালানো যেতে পারে।
webPage.html
<html>
<button id="mybutton">click me</button>
<script>
var greeting = "hello, ";
var button = document.getElementById("mybutton");
button.person_name = "Bob";
button.addEventListener(
"click", () => alert(greeting + button.person_name + "."), false);
</script>
</html>
ওই এক্সটেনশনটি ' স্ক্রিপ্ট ইনজেক্ট করুন' বিভাগে বর্ণিত কৌশলগুলোর মধ্যে একটি ব্যবহার করে নিম্নলিখিত কন্টেন্ট স্ক্রিপ্টটি ইনজেক্ট করতে পারে।
কন্টেন্ট-স্ক্রিপ্ট.জেএস
var greeting = "hola, ";
var button = document.getElementById("mybutton");
button.person_name = "Roberto";
button.addEventListener(
"click", () => alert(greeting + button.person_name + "."), false);
এই পরিবর্তনের ফলে, বাটনটিতে ক্লিক করলে উভয় অ্যালার্ট ক্রমানুসারে প্রদর্শিত হবে।
স্ক্রিপ্ট ইনজেক্ট করুন
কন্টেন্ট স্ক্রিপ্ট স্ট্যাটিক্যালি , ডাইনামিক্যালি অথবা প্রোগ্রাম্যাটিকভাবে ইনজেক্ট করা যেতে পারে।
স্ট্যাটিক ডিক্লারেশন দিয়ে ইনজেক্ট করুন
যেসব স্ক্রিপ্ট সুপরিচিত কিছু পেজে স্বয়ংক্রিয়ভাবে চালানো উচিত, সেগুলোর জন্য manifest.json-এ স্ট্যাটিক কন্টেন্ট স্ক্রিপ্ট ডিক্লারেশন ব্যবহার করুন।
স্ট্যাটিক্যালি ডিক্লেয়ার করা স্ক্রিপ্টগুলো ম্যানিফেস্টে "content_scripts" কী-এর অধীনে রেজিস্টার করা হয়। এগুলোতে জাভাস্ক্রিপ্ট ফাইল, সিএসএস ফাইল অথবা উভয়ই অন্তর্ভুক্ত থাকতে পারে। স্বয়ংক্রিয়ভাবে চালু হওয়া সমস্ত কন্টেন্ট স্ক্রিপ্টে অবশ্যই ম্যাচ প্যাটার্ন উল্লেখ করতে হবে।
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"css": ["my-styles.css"],
"js": ["content-script.js"]
}
],
...
}
| নাম | প্রকার | বর্ণনা |
|---|---|---|
matches | স্ট্রিংগুলির অ্যারে | আবশ্যক। এটি নির্দিষ্ট করে যে এই কন্টেন্ট স্ক্রিপ্টটি কোন কোন পেজে যুক্ত করা হবে। এই স্ট্রিংগুলির সিনট্যাক্স সম্পর্কে বিস্তারিত জানতে ‘ম্যাচ প্যাটার্নস’ দেখুন এবং কীভাবে ইউআরএল বাদ দিতে হয় সে সম্পর্কে তথ্যের জন্য ‘ম্যাচ প্যাটার্নস অ্যান্ড গ্লোবস’ দেখুন। |
css | স্ট্রিংগুলির অ্যারে | ঐচ্ছিক। সংশ্লিষ্ট পেজগুলিতে ইনজেক্ট করার জন্য CSS ফাইলগুলির তালিকা। পেজের জন্য কোনো DOM তৈরি বা প্রদর্শিত হওয়ার আগে, এই অ্যারেতে ফাইলগুলি যে ক্রমে রয়েছে, সেই ক্রমেই ইনজেক্ট করা হয়। |
js | | ঐচ্ছিক। সংশ্লিষ্ট পেজগুলিতে ইনজেক্ট করার জন্য জাভাস্ক্রিপ্ট ফাইলগুলির তালিকা। ফাইলগুলি এই অ্যারেতে যে ক্রমে রয়েছে, সেই ক্রমেই ইনজেক্ট করা হয়। এই তালিকার প্রতিটি স্ট্রিং-এ অবশ্যই এক্সটেনশনের রুট ডিরেক্টরিতে থাকা কোনো রিসোর্সের একটি রিলেটিভ পাথ থাকতে হবে। শুরুর স্ল্যাশ (`/`) স্বয়ংক্রিয়ভাবে বাদ দেওয়া হয়। |
run_at | রানঅ্যাট | ঐচ্ছিক। স্ক্রিপ্টটি কখন পৃষ্ঠায় যুক্ত করা হবে তা নির্দিষ্ট করে। ডিফল্ট মান হলো document_idle । |
match_about_blank | বুলিয়ান | ঐচ্ছিক। স্ক্রিপ্টটি এমন একটি about:blank ফ্রেমে ইনজেক্ট করবে কিনা, যেখানে প্যারেন্ট বা ওপেনার ফ্রেমটি matches এ ঘোষিত প্যাটার্নগুলোর কোনো একটির সাথে মেলে। ডিফল্ট মান false। |
match_origin_as_fallback | বুলিয়ান | ঐচ্ছিক। স্ক্রিপ্টটি এমন ফ্রেমে ইনজেক্ট করবে কিনা, যেগুলো একটি মিলে যাওয়া অরিজিন দ্বারা তৈরি হয়েছে, কিন্তু যাদের URL বা অরিজিন সরাসরি প্যাটার্নের সাথে নাও মিলতে পারে। এর মধ্যে বিভিন্ন স্কিমের ফ্রেম অন্তর্ভুক্ত, যেমন about: , data: , blob: , এবং filesystem: । আরও দেখুন সম্পর্কিত ফ্রেমে ইনজেক্ট করা । |
world | এক্সিকিউশনওয়ার্ল্ড | ঐচ্ছিক। কোনো স্ক্রিপ্ট যে জাভাস্ক্রিপ্ট জগতের মধ্যে নির্বাহ হবে। ডিফল্ট হিসেবে এটি ISOLATED । আরও দেখুন: বিচ্ছিন্ন জগতে কাজ করা । |
ডকুমেন্ট লাইফসাইকেলের একটি নির্দিষ্ট পর্যায়ে , ম্যানিফেস্টে স্ট্যাটিক্যালি ঘোষিত কন্টেন্ট স্ক্রিপ্টগুলোই সর্বপ্রথম ইনজেক্ট করা হয়, অন্য কোনোভাবে রেজিস্টার করা কন্টেন্ট স্ক্রিপ্টগুলোর আগে। ম্যানিফেস্টে যে ক্রমে এগুলো নির্দিষ্ট করা থাকে, সেই ক্রমেই এগুলো ইনজেক্ট করা হয়।
ডাইনামিক ডিক্লারেশন দিয়ে ইনজেক্ট করুন
ডাইনামিক কন্টেন্ট স্ক্রিপ্ট তখন উপযোগী হয়, যখন কন্টেন্ট স্ক্রিপ্টের ম্যাচ প্যাটার্নগুলো সুপরিচিত না থাকে অথবা যখন কন্টেন্ট স্ক্রিপ্টগুলো সবসময় পরিচিত হোস্টগুলোতে ইনজেক্ট করার প্রয়োজন হয় না।
ক্রোম ৯৬-এ প্রবর্তিত ডাইনামিক ডিক্লারেশনগুলো স্ট্যাটিক ডিক্লারেশনের মতোই, তবে এক্ষেত্রে কন্টেন্ট স্ক্রিপ্ট অবজেক্টটি manifest.json- এর পরিবর্তে browser.scripting নেমস্পেসের মেথড ব্যবহার করে ক্রোমের সাথে রেজিস্টার করা হয়। স্ক্রিপ্টিং এপিআই এক্সটেনশন ডেভেলপারদেরকে আরও যেসব কাজ করার সুযোগ দেয়, সেগুলো হলো:
- কন্টেন্ট স্ক্রিপ্ট নিবন্ধন করুন ।
- নিবন্ধিত কন্টেন্ট স্ক্রিপ্টগুলোর একটি তালিকা পান ।
- নিবন্ধিত কন্টেন্ট স্ক্রিপ্টগুলোর তালিকা হালনাগাদ করুন ।
- নিবন্ধিত কন্টেন্ট স্ক্রিপ্টগুলো মুছে ফেলুন ।
স্ট্যাটিক ডিক্লারেশনের মতোই, ডাইনামিক ডিক্লারেশনেও জাভাস্ক্রিপ্ট ফাইল, সিএসএস ফাইল বা উভয়ই অন্তর্ভুক্ত থাকতে পারে।
সার্ভিস-ওয়ার্কার.জেএস
browser.scripting
.registerContentScripts([{
id: "session-script",
js: ["content.js"],
persistAcrossSessions: false,
matches: ["*://example.com/*"],
runAt: "document_start",
}])
.then(() => console.log("registration complete"))
.catch((err) => console.warn("unexpected error", err))
সার্ভিস-ওয়ার্কার.জেএস
browser.scripting
.updateContentScripts([{
id: "session-script",
excludeMatches: ["*://admin.example.com/*"],
}])
.then(() => console.log("registration updated"));
সার্ভিস-ওয়ার্কার.জেএস
browser.scripting
.getRegisteredContentScripts()
.then(scripts => console.log("registered content scripts", scripts));
সার্ভিস-ওয়ার্কার.জেএস
browser.scripting
.unregisterContentScripts({ ids: ["session-script"] })
.then(() => console.log("un-registration complete"));
প্রোগ্রাম্যাটিকভাবে ইনজেক্ট করুন
যেসব কন্টেন্ট স্ক্রিপ্ট ইভেন্টের প্রতিক্রিয়ায় বা নির্দিষ্ট উপলক্ষে চালানোর প্রয়োজন হয়, সেগুলোর জন্য প্রোগ্রাম্যাটিক ইনজেকশন ব্যবহার করুন।
প্রোগ্রাম্যাটিকভাবে একটি কন্টেন্ট স্ক্রিপ্ট ইনজেক্ট করতে, আপনার এক্সটেনশনটির সেই পৃষ্ঠার জন্য হোস্ট পারমিশন প্রয়োজন যেখানে এটি স্ক্রিপ্ট ইনজেক্ট করার চেষ্টা করছে। হোস্ট পারমিশন আপনার এক্সটেনশনের ম্যানিফেস্টের অংশ হিসাবে অনুরোধ করে অথবা সাময়িকভাবে "activeTab" ব্যবহার করে মঞ্জুর করা যেতে পারে।
নিম্নলিখিতগুলি হলো activeTab-ভিত্তিক এক্সটেনশনের বিভিন্ন সংস্করণ।
manifest.json:
{
"name": "My extension",
...
"permissions": [
"activeTab",
"scripting"
],
"background": {
"service_worker": "background.js"
},
"action": {
"default_title": "Action Button"
}
}
কন্টেন্ট স্ক্রিপ্টগুলো ফাইল হিসেবে ইনজেক্ট করা যেতে পারে।
কন্টেন্ট-স্ক্রিপ্ট.জেএস
document.body.style.backgroundColor = "orange";
service-worker.js:
browser.action.onClicked.addListener((tab) => {
browser.scripting.executeScript({
target: { tabId: tab.id },
files: ["content-script.js"]
});
});
অথবা, একটি ফাংশন বডিকে কন্টেন্ট স্ক্রিপ্ট হিসেবে ইনজেক্ট ও এক্সিকিউট করা যেতে পারে।
service-worker.js:
function injectedFunction() {
document.body.style.backgroundColor = "orange";
}
browser.action.onClicked.addListener((tab) => {
browser.scripting.executeScript({
target : {tabId : tab.id},
func : injectedFunction,
});
});
মনে রাখবেন যে ইনজেক্ট করা ফাংশনটি হলো browser.scripting.executeScript() কলে উল্লেখিত ফাংশনটির একটি অনুলিপি, এটি মূল ফাংশনটি নিজে নয়। ফলে, ফাংশনটির বডি অবশ্যই স্বয়ংসম্পূর্ণ হতে হবে; ফাংশনের বাইরের ভেরিয়েবলের রেফারেন্স দিলে কন্টেন্ট স্ক্রিপ্ট একটি ReferenceError থ্রো করবে।
ফাংশন হিসেবে ইনজেক্ট করার সময়, আপনি ফাংশনে আর্গুমেন্টও পাস করতে পারেন।
সার্ভিস-ওয়ার্কার.জেএস
function injectedFunction(color) {
document.body.style.backgroundColor = color;
}
browser.action.onClicked.addListener((tab) => {
browser.scripting.executeScript({
target : {tabId : tab.id},
func : injectedFunction,
args : [ "orange" ],
});
});
ম্যাচ এবং গ্লোব বাদ দিন
নির্দিষ্ট পৃষ্ঠা মেলানোর বিষয়টি কাস্টমাইজ করতে, একটি ডিক্লারেটিভ রেজিস্ট্রেশনে নিম্নলিখিত ফিল্ডগুলি অন্তর্ভুক্ত করুন।
| নাম | প্রকার | বর্ণনা |
|---|---|---|
exclude_matches | স্ট্রিংগুলির অ্যারে | ঐচ্ছিক। যেসব পৃষ্ঠায় অন্যথায় এই কন্টেন্ট স্ক্রিপ্টটি যুক্ত হতো, সেগুলোকে বাদ দেয়। এই স্ট্রিংগুলোর সিনট্যাক্স সম্পর্কে বিস্তারিত জানতে ম্যাচ প্যাটার্নস দেখুন। |
include_globs | স্ট্রিংগুলির অ্যারে | ঐচ্ছিক। matches পর এটি প্রয়োগ করা হয়, যাতে শুধুমাত্র সেইসব URL অন্তর্ভুক্ত হয় যেগুলো এই গ্লোবটির সাথেও মেলে। এর উদ্দেশ্য হলো Greasemonkey-এর @include কীওয়ার্ডটির অনুকরণ করা। |
exclude_globs | স্ট্রিং এর অ্যারে | ঐচ্ছিক। এই গ্লোবের সাথে মেলে এমন URL বাদ দেওয়ার জন্য matches পরে এটি প্রয়োগ করা হয়। এর উদ্দেশ্য হলো Greasemonkey-এর @exclude কীওয়ার্ডটির অনুকরণ করা। |
নিম্নলিখিত উভয় শর্তই সত্য হলে কন্টেন্ট স্ক্রিপ্টটি একটি পেজে যুক্ত করা হবে:
- এর URL যেকোনো
matchesপ্যাটার্ন এবং যেকোনোinclude_globsপ্যাটার্নের সাথে মেলে। - URL-টি
exclude_matchesবাexclude_globsপ্যাটার্নের সাথেও মেলে না। যেহেতুmatchesপ্রপার্টিটি আবশ্যক, তাইexclude_matches,include_globsএবংexclude_globsশুধুমাত্র কোন পৃষ্ঠাগুলো প্রভাবিত হবে তা সীমিত করতে ব্যবহার করা যেতে পারে।
নিম্নলিখিত এক্সটেনশনটি https://www.nytimes.com/health এ কন্টেন্ট স্ক্রিপ্ট ইনজেক্ট করে, কিন্তু https://www.nytimes.com/business এ করে না।
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"exclude_matches": ["*://*/*business*"],
"js": ["contentScript.js"]
}
],
...
}
সার্ভিস-ওয়ার্কার.জেএস
browser.scripting.registerContentScripts([{
id : "test",
matches : [ "https://*.nytimes.com/*" ],
excludeMatches : [ "*://*/*business*" ],
js : [ "contentScript.js" ],
}]);
ম্যাচ প্যাটার্নের চেয়ে গ্লোব প্রোপার্টির সিনট্যাক্স ভিন্ন এবং আরও নমনীয়। গ্রহণযোগ্য গ্লোব স্ট্রিং হলো ইউআরএল, যেগুলোতে 'ওয়াইল্ডকার্ড' অ্যাস্টারিস্ক এবং প্রশ্নবোধক চিহ্ন থাকতে পারে। অ্যাস্টারিস্ক ( * ) যেকোনো দৈর্ঘ্যের স্ট্রিংয়ের সাথে মেলে, এমনকি খালি স্ট্রিংয়ের সাথেও, আর প্রশ্নবোধক চিহ্ন ( ? ) যেকোনো একটি অক্ষরের সাথে মেলে।
উদাহরণস্বরূপ, https://???.example.com/foo/\* গ্লোবটি নিম্নলিখিত যেকোনোটির সাথে মিলে যায়:
-
https://www.example.com/foo/bar -
https://the.example.com/foo/
তবে, এটি নিম্নলিখিতগুলির সাথে মেলে না :
-
https://my.example.com/foo/bar -
https://example.com/foo/ -
https://www.example.com/foo
এই এক্সটেনশনটি কন্টেন্ট স্ক্রিপ্টটি https://www.nytimes.com/arts/index.html এবং https://www.nytimes.com/jobs/index.htm* -এ যুক্ত করে, কিন্তু https://www.nytimes.com/sports/index.html -এ করে না।
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"include_globs": ["*nytimes.com/???s/*"],
"js": ["contentScript.js"]
}
],
...
}
এই এক্সটেনশনটি কন্টেন্ট স্ক্রিপ্টটি https://history.nytimes.com এবং https://.nytimes.com/history তে যুক্ত করে, কিন্তু https://science.nytimes.com বা https://www.nytimes.com/science এ যুক্ত করে না।
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"exclude_globs": ["*science*"],
"js": ["contentScript.js"]
}
],
...
}
সঠিক পরিধি অর্জনের জন্য এগুলোর একটি, সবগুলো বা কয়েকটি অন্তর্ভুক্ত করা যেতে পারে।
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"exclude_matches": ["*://*/*business*"],
"include_globs": ["*nytimes.com/???s/*"],
"exclude_globs": ["*science*"],
"js": ["contentScript.js"]
}
],
...
}
রান টাইম
run_at ফিল্ডটি নিয়ন্ত্রণ করে কখন জাভাস্ক্রিপ্ট ফাইলগুলো ওয়েব পেজে যুক্ত হবে। এর পছন্দের এবং ডিফল্ট মান হলো "document_idle" । অন্যান্য সম্ভাব্য মানের জন্য `RunAt` টাইপটি দেখুন।
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"run_at": "document_idle",
"js": ["contentScript.js"]
}
],
...
}
সার্ভিস-ওয়ার্কার.জেএস
browser.scripting.registerContentScripts([{
id : "test",
matches : [ "https://*.nytimes.com/*" ],
runAt : "document_idle",
js : [ "contentScript.js" ],
}]);
| নাম | প্রকার | বর্ণনা |
|---|---|---|
document_idle | স্ট্রিং | পছন্দনীয়। সম্ভব হলে "document_idle" ব্যবহার করুন।ব্রাউজার "document_end" এবং window.onload ইভেন্টটি ফায়ার হওয়ার ঠিক পরের মুহূর্তের মধ্যে স্ক্রিপ্ট ইনজেক্ট করার জন্য একটি সময় বেছে নেয়। ইনজেকশনের সঠিক মুহূর্তটি নির্ভর করে ডকুমেন্টটি কতটা জটিল এবং এটি লোড হতে কত সময় নিচ্ছে তার উপর, এবং এটি পেজ লোডের গতি বাড়ানোর জন্য অপ্টিমাইজ করা হয়।"document_idle" অবস্থায় চলমান কন্টেন্ট স্ক্রিপ্টগুলোর window.onload ইভেন্টের জন্য অপেক্ষা করার প্রয়োজন নেই, কারণ DOM সম্পূর্ণ হওয়ার পরেই সেগুলো নিশ্চিতভাবে রান করে। যদি কোনো স্ক্রিপ্টকে window.onload পরে অবশ্যই রান করতে হয়, তবে এক্সটেনশনটি document.readyState প্রপার্টি ব্যবহার করে যাচাই করতে পারে যে onload ইতোমধ্যেই ফায়ার হয়েছে কি না। |
document_start | স্ট্রিং | স্ক্রিপ্টগুলো css এর যেকোনো ফাইলের পরে, কিন্তু অন্য কোনো DOM তৈরি হওয়ার বা অন্য কোনো স্ক্রিপ্ট চলার আগে ইনজেক্ট করা হয়। |
document_end | স্ট্রিং | DOM সম্পূর্ণ হওয়ার ঠিক পরেই, কিন্তু ছবি এবং ফ্রেমের মতো সাবরিসোর্সগুলো লোড হওয়ার আগে স্ক্রিপ্টগুলো ইনজেক্ট করা হয়। |
ফ্রেম নির্দিষ্ট করুন
ম্যানিফেস্টে নির্দিষ্ট করা ডিক্লারেটিভ কন্টেন্ট স্ক্রিপ্টগুলির জন্য, "all_frames" ফিল্ডটি এক্সটেনশনকে এটি নির্দিষ্ট করার সুযোগ দেয় যে, জাভাস্ক্রিপ্ট এবং সিএসএস ফাইলগুলি নির্দিষ্ট URL-এর শর্ত পূরণকারী সমস্ত ফ্রেমে ইনজেক্ট করা হবে, নাকি শুধুমাত্র একটি ট্যাবের সর্ববৃহৎ ফ্রেমে ইনজেক্ট করা হবে।
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"all_frames": true,
"js": ["contentScript.js"]
}
],
...
}
browser.scripting.registerContentScripts(...) ব্যবহার করে প্রোগ্রাম্যাটিকভাবে কন্টেন্ট স্ক্রিপ্ট রেজিস্টার করার সময়, allFrames প্যারামিটারটি ব্যবহার করে নির্দিষ্ট করা যায় যে কন্টেন্ট স্ক্রিপ্টটি নির্দিষ্ট URL-এর শর্ত পূরণকারী সমস্ত ফ্রেমে ইনজেক্ট করা হবে, নাকি শুধুমাত্র একটি ট্যাবের সবচেয়ে উপরের ফ্রেমে। এটি শুধুমাত্র tabId-এর সাথেই ব্যবহার করা যায় এবং frameIds বা documentIds নির্দিষ্ট করা থাকলে এটি ব্যবহার করা যায় না।
সার্ভিস-ওয়ার্কার.জেএস
browser.scripting.registerContentScripts([{
id: "test",
matches : [ "https://*.nytimes.com/*" ],
allFrames : true,
js : [ "contentScript.js" ],
}]);
সম্পর্কিত ফ্রেমে ইনজেক্ট করুন
এক্সটেনশনগুলো এমন ফ্রেমে স্ক্রিপ্ট চালাতে চাইতে পারে যা একটি মিলে যাওয়া ফ্রেমের সাথে সম্পর্কিত, কিন্তু ফ্রেমগুলো নিজেরা মিলে যায় না। এর একটি সাধারণ উদাহরণ হলো এমন ফ্রেম, যার ইউআরএলগুলো একটি মিলে যাওয়া ফ্রেম দ্বারা তৈরি হয়েছে, কিন্তু সেই ইউআরএলগুলো নিজেরা স্ক্রিপ্টের নির্দিষ্ট প্যাটার্নের সাথে মেলে না।
এটি তখন ঘটে যখন কোনো এক্সটেনশন about: , data: , blob: , এবং filesystem: স্কিমযুক্ত URL সহ ফ্রেমে ইনজেক্ট করতে চায়। এই ক্ষেত্রে, URL-টি কন্টেন্ট স্ক্রিপ্টের প্যাটার্নের সাথে মিলবে না (এবং, about: ও data: এর ক্ষেত্রে, URL-এ প্যারেন্ট URL বা অরিজিন একেবারেই অন্তর্ভুক্ত করবে না, যেমন about:blank বা data:text/html,<html>Hello, World!</html> )। তবে, এই ফ্রেমগুলোকেও সৃষ্টিকারী ফ্রেমের সাথে যুক্ত করা যেতে পারে।
এই ফ্রেমগুলিতে ইনজেক্ট করার জন্য, এক্সটেনশনগুলি ম্যানিফেস্টে থাকা কন্টেন্ট স্ক্রিপ্ট স্পেসিফিকেশনে "match_origin_as_fallback" প্রপার্টিটি নির্দিষ্ট করতে পারে।
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.google.com/*"],
"match_origin_as_fallback": true,
"js": ["contentScript.js"]
}
],
...
}
যখন এটি নির্দিষ্ট করে ' true সেট করা হয়, তখন Chrome ফ্রেমটি মেলে কিনা তা নির্ধারণ করতে ফ্রেমটির নিজস্ব URL-এর পরিবর্তে, ফ্রেমটির সূচনাকারীর অরিজিন দেখবে। উল্লেখ্য যে, এটি টার্গেট ফ্রেমের অরিজিন থেকেও ভিন্ন হতে পারে (যেমন, ' data: URL-গুলোর অরিজিন 'null' হয়)।
ফ্রেমটির সূচনাকারী হলো সেই ফ্রেম যা লক্ষ্য ফ্রেমটি তৈরি করেছে বা সেটিতে নেভিগেট করেছে। যদিও এটি সাধারণত সরাসরি প্যারেন্ট বা ওপেনার হয়ে থাকে, তবে তা নাও হতে পারে (যেমন একটি ফ্রেম যখন একটি আইফ্রেমের ভেতরে থাকা আরেকটি আইফ্রেমে নেভিগেট করে)।
যেহেতু এটি ইনিশিয়েটর ফ্রেমের অরিজিন তুলনা করে, তাই ইনিশিয়েটর ফ্রেমটি সেই অরিজিন থেকে যেকোনো পাথে চালু থাকতে পারে। এই বিষয়টি স্পষ্ট করার জন্য, Chrome-এর প্রয়োজন হয় যে, "match_origin_as_fallback" true সেট করা যেকোনো কন্টেন্ট স্ক্রিপ্টেও যেন * পাথটি নির্দিষ্ট করা থাকে।
যখন "match_origin_as_fallback" এবং "match_about_blank" উভয়ই নির্দিষ্ট করা থাকে, তখন "match_origin_as_fallback" অগ্রাধিকার পায়।
এম্বেডিং পৃষ্ঠার সাথে যোগাযোগ
যদিও কন্টেন্ট স্ক্রিপ্টের এক্সিকিউশন এনভায়রনমেন্ট এবং যে পেজগুলো সেগুলোকে হোস্ট করে, সেগুলো একে অপরের থেকে বিচ্ছিন্ন থাকে, তবুও তারা পেজের DOM-এ অ্যাক্সেস শেয়ার করে। যদি পেজটি কন্টেন্ট স্ক্রিপ্টের সাথে, অথবা কন্টেন্ট স্ক্রিপ্টের মাধ্যমে এক্সটেনশনের সাথে যোগাযোগ করতে চায়, তবে তাকে অবশ্যই শেয়ার করা DOM-এর মাধ্যমেই তা করতে হবে।
window.postMessage() ব্যবহার করে একটি উদাহরণ সম্পন্ন করা যেতে পারে:
কন্টেন্ট-স্ক্রিপ্ট.জেএস
var port = browser.runtime.connect();
window.addEventListener("message", (event) => {
// We only accept messages from ourselves
if (event.source !== window) {
return;
}
if (event.data.type && (event.data.type === "FROM_PAGE")) {
console.log("Content script received: " + event.data.text);
port.postMessage(event.data.text);
}
}, false);
example.js
document.getElementById("theButton").addEventListener("click", () => {
window.postMessage(
{type : "FROM_PAGE", text : "Hello from the webpage!"}, "*");
}, false);
নন-এক্সটেনশন পেজ example.html নিজের কাছেই বার্তা পাঠায়। এই বার্তাটি কন্টেন্ট স্ক্রিপ্ট দ্বারা গৃহীত ও পরীক্ষিত হয় এবং তারপর এক্সটেনশন প্রসেসে পাঠানো হয়। এইভাবে, পেজটি এক্সটেনশন প্রসেসের সাথে একটি যোগাযোগ ব্যবস্থা স্থাপন করে। অনুরূপ উপায়ে এর বিপরীতটিও সম্ভব।
এক্সটেনশন ফাইল অ্যাক্সেস করুন
কন্টেন্ট স্ক্রিপ্ট থেকে কোনো এক্সটেনশন ফাইল অ্যাক্সেস করতে, আপনি browser.runtime.getURL() কল করে আপনার এক্সটেনশন অ্যাসেটের অ্যাবসোলিউট URL পেতে পারেন, যেমনটি নিম্নলিখিত উদাহরণে ( content.js ) দেখানো হয়েছে:
কন্টেন্ট-স্ক্রিপ্ট.জেএস
let image = browser.runtime.getURL("images/my_image.png")
CSS ফাইলে ফন্ট বা ছবি ব্যবহার করতে, আপনি @@extension_id ব্যবহার করে একটি URL তৈরি করতে পারেন, যেমনটি নিচের উদাহরণে ( content.css ) দেখানো হয়েছে:
content.css
body {
background-image:url('chrome-extension://__MSG_@@extension_id__/background.png');
}
@font-face {
font-family: 'Stint Ultra Expanded';
font-style: normal;
font-weight: 400;
src: url('chrome-extension://__MSG_@@extension_id__/fonts/Stint Ultra Expanded.woff') format('woff');
}
manifest.json ফাইলে সমস্ত অ্যাসেটকে ওয়েব অ্যাক্সেসযোগ্য রিসোর্স হিসেবে ঘোষণা করতে হবে:
manifest.json
{
...
"web_accessible_resources": [
{
"resources": [ "images/*.png" ],
"matches": [ "https://example.com/*" ]
},
{
"resources": [ "fonts/*.woff" ],
"matches": [ "https://example.com/*" ]
}
],
...
}
বিষয়বস্তু নিরাপত্তা নীতি
বিচ্ছিন্ন বিশ্বে চলমান কন্টেন্ট স্ক্রিপ্টগুলির নিম্নলিখিত কন্টেন্ট সুরক্ষা নীতি (CSP) রয়েছে:
script-src 'self' 'wasm-unsafe-eval' 'inline-speculation-rules' chrome-extension://abcdefghijklmopqrstuvwxyz/; object-src 'self';
অন্যান্য এক্সটেনশন কনটেক্সটের উপর আরোপিত বিধিনিষেধের মতোই, এটি eval() এর ব্যবহার এবং বাহ্যিক স্ক্রিপ্ট লোড করাকে বাধা দেয়।
আনপ্যাক করা এক্সটেনশনগুলির জন্য, CSP-তে লোকালহোস্টও অন্তর্ভুক্ত থাকে:
script-src 'self' 'wasm-unsafe-eval' 'inline-speculation-rules' http://localhost:* http://127.0.0.1:* chrome-extension://abcdefghijklmopqrstuvwxyz/; object-src 'self';
যখন মূল অংশে কোনো কন্টেন্ট স্ক্রিপ্ট যুক্ত করা হয়, তখন পেজটির CSP (কন্টেন্ট স্ক্রিপ্ট পলিসি) প্রযোজ্য হয়।
সুরক্ষিত থাকুন
যদিও আইসোলেটেড ওয়ার্ল্ড এক ধরনের সুরক্ষা প্রদান করে, কন্টেন্ট স্ক্রিপ্ট ব্যবহার করলে একটি এক্সটেনশন এবং ওয়েব পেজ উভয়ের মধ্যেই দুর্বলতা তৈরি হতে পারে। যদি কন্টেন্ট স্ক্রিপ্ট কোনো আলাদা ওয়েবসাইট থেকে কন্টেন্ট গ্রহণ করে, যেমন fetch() কল করার মাধ্যমে, তবে তা ইনজেক্ট করার আগে ক্রস-সাইট স্ক্রিপ্টিং অ্যাটাকের বিরুদ্ধে কন্টেন্ট ফিল্টার করে নিতে সতর্ক থাকুন। "ম্যান-ইন-দ্য-মিডল" অ্যাটাক এড়ানোর জন্য শুধুমাত্র HTTPS-এর মাধ্যমেই যোগাযোগ করুন।
ক্ষতিকারক ওয়েব পেজগুলো ফিল্টার করতে ভুলবেন না। উদাহরণস্বরূপ, নিম্নলিখিত প্যাটার্নগুলো বিপজ্জনক এবং ম্যানিফেস্ট ভি৩-তে নিষিদ্ধ:
কন্টেন্ট-স্ক্রিপ্ট.জেএস
const data = document.getElementById("json-data"); // WARNING! Might be evaluating an evil script! const parsed = eval("(" + data + ")");
কন্টেন্ট-স্ক্রিপ্ট.জেএস
const elmt_id = ... // WARNING! elmt_id might be '); ... evil script ... //'! window.setTimeout("animate(" + elmt_id + ")", 200);
এর পরিবর্তে, এমন নিরাপদ এপিআই ব্যবহার করুন যেগুলো স্ক্রিপ্ট চালায় না:
কন্টেন্ট-স্ক্রিপ্ট.জেএস
const data = document.getElementById("json-data") // JSON.parse does not evaluate the attacker's scripts. const parsed = JSON.parse(data);
কন্টেন্ট-স্ক্রিপ্ট.জেএস
const elmt_id = ... // The closure form of setTimeout does not evaluate scripts. window.setTimeout(() => animate(elmt_id), 200);
কন্টেন্ট স্ক্রিপ্ট হলো এমন ফাইল যা ওয়েব পেজের প্রেক্ষাপটে চলে। স্ট্যান্ডার্ড ডকুমেন্ট অবজেক্ট মডেল (DOM) ব্যবহার করে, এগুলি ব্রাউজার দ্বারা পরিদর্শিত ওয়েব পেজগুলির বিবরণ পড়তে, সেগুলিতে পরিবর্তন আনতে এবং তাদের প্যারেন্ট এক্সটেনশনে তথ্য পাঠাতে সক্ষম হয়।
কন্টেন্ট স্ক্রিপ্টের সক্ষমতা বুঝুন
কন্টেন্ট স্ক্রিপ্টগুলো সরাসরি নিম্নলিখিত এক্সটেনশন এপিআইগুলো অ্যাক্সেস করতে পারে:
-
dom -
i18n -
storage -
runtime.connect() -
runtime.getManifest() -
runtime.getURL() -
runtime.id -
runtime.onConnect -
runtime.onMessage -
runtime.sendMessage()
কন্টেন্ট স্ক্রিপ্টগুলো সরাসরি অন্য এপিআই অ্যাক্সেস করতে পারে না। কিন্তু আপনার এক্সটেনশনের অন্যান্য অংশের সাথে বার্তা আদান-প্রদানের মাধ্যমে তারা পরোক্ষভাবে সেগুলো অ্যাক্সেস করতে পারে।
আপনি fetch() এর মতো API ব্যবহার করে একটি কন্টেন্ট স্ক্রিপ্ট থেকে আপনার এক্সটেনশনের অন্যান্য ফাইলও অ্যাক্সেস করতে পারেন। এটি করার জন্য, আপনাকে সেগুলোকে ওয়েব-অ্যাক্সেসযোগ্য রিসোর্স হিসেবে ঘোষণা করতে হবে। মনে রাখবেন যে, এর ফলে একই সাইটে চলমান যেকোনো ফার্স্ট-পার্টি বা থার্ড-পার্টি স্ক্রিপ্টের কাছেও রিসোর্সগুলো উন্মুক্ত হয়ে যায়।
বিচ্ছিন্ন বিশ্বে কাজ করুন
কন্টেন্ট স্ক্রিপ্টগুলো একটি বিচ্ছিন্ন জগতে অবস্থান করে, যার ফলে এটি পেজ বা অন্যান্য এক্সটেনশনের কন্টেন্ট স্ক্রিপ্টের সাথে কোনো সংঘাত ছাড়াই তার জাভাস্ক্রিপ্ট পরিবেশে পরিবর্তন আনতে পারে।
একটি এক্সটেনশন কোনো ওয়েব পেজে নিম্নলিখিত উদাহরণের মতো কোড ব্যবহার করে চালানো যেতে পারে।
webPage.html
<html>
<button id="mybutton">click me</button>
<script>
var greeting = "hello, ";
var button = document.getElementById("mybutton");
button.person_name = "Bob";
button.addEventListener(
"click", () => alert(greeting + button.person_name + "."), false);
</script>
</html>
ওই এক্সটেনশনটি ' স্ক্রিপ্ট ইনজেক্ট করুন' বিভাগে বর্ণিত কৌশলগুলোর মধ্যে একটি ব্যবহার করে নিম্নলিখিত কন্টেন্ট স্ক্রিপ্টটি ইনজেক্ট করতে পারে।
কন্টেন্ট-স্ক্রিপ্ট.জেএস
var greeting = "hola, ";
var button = document.getElementById("mybutton");
button.person_name = "Roberto";
button.addEventListener(
"click", () => alert(greeting + button.person_name + "."), false);
এই পরিবর্তনের ফলে, বাটনটিতে ক্লিক করলে উভয় অ্যালার্ট ক্রমানুসারে প্রদর্শিত হবে।
স্ক্রিপ্ট ইনজেক্ট করুন
কন্টেন্ট স্ক্রিপ্ট স্ট্যাটিক্যালি , ডাইনামিক্যালি অথবা প্রোগ্রাম্যাটিকভাবে ইনজেক্ট করা যেতে পারে।
স্ট্যাটিক ডিক্লারেশন দিয়ে ইনজেক্ট করুন
যেসব স্ক্রিপ্ট সুপরিচিত কিছু পেজে স্বয়ংক্রিয়ভাবে চালানো উচিত, সেগুলোর জন্য manifest.json-এ স্ট্যাটিক কন্টেন্ট স্ক্রিপ্ট ডিক্লারেশন ব্যবহার করুন।
স্ট্যাটিক্যালি ডিক্লেয়ার করা স্ক্রিপ্টগুলো ম্যানিফেস্টে "content_scripts" কী-এর অধীনে রেজিস্টার করা হয়। এগুলোতে জাভাস্ক্রিপ্ট ফাইল, সিএসএস ফাইল অথবা উভয়ই অন্তর্ভুক্ত থাকতে পারে। স্বয়ংক্রিয়ভাবে চালু হওয়া সমস্ত কন্টেন্ট স্ক্রিপ্টে অবশ্যই ম্যাচ প্যাটার্ন উল্লেখ করতে হবে।
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"css": ["my-styles.css"],
"js": ["content-script.js"]
}
],
...
}
| নাম | প্রকার | বর্ণনা |
|---|---|---|
matches | স্ট্রিংগুলির অ্যারে | আবশ্যক। এটি নির্দিষ্ট করে যে এই কন্টেন্ট স্ক্রিপ্টটি কোন কোন পেজে যুক্ত করা হবে। এই স্ট্রিংগুলির সিনট্যাক্স সম্পর্কে বিস্তারিত জানতে ‘ম্যাচ প্যাটার্নস’ দেখুন এবং কীভাবে ইউআরএল বাদ দিতে হয় সে সম্পর্কে তথ্যের জন্য ‘ম্যাচ প্যাটার্নস অ্যান্ড গ্লোবস’ দেখুন। |
css | স্ট্রিংগুলির অ্যারে | ঐচ্ছিক। সংশ্লিষ্ট পেজগুলিতে ইনজেক্ট করার জন্য CSS ফাইলগুলির তালিকা। পেজের জন্য কোনো DOM তৈরি বা প্রদর্শিত হওয়ার আগে, এই অ্যারেতে ফাইলগুলি যে ক্রমে রয়েছে, সেই ক্রমেই ইনজেক্ট করা হয়। |
js | | ঐচ্ছিক। সংশ্লিষ্ট পেজগুলিতে ইনজেক্ট করার জন্য জাভাস্ক্রিপ্ট ফাইলগুলির তালিকা। ফাইলগুলি এই অ্যারেতে যে ক্রমে রয়েছে, সেই ক্রমেই ইনজেক্ট করা হয়। এই তালিকার প্রতিটি স্ট্রিং-এ অবশ্যই এক্সটেনশনের রুট ডিরেক্টরিতে থাকা কোনো রিসোর্সের একটি রিলেটিভ পাথ থাকতে হবে। শুরুর স্ল্যাশ (`/`) স্বয়ংক্রিয়ভাবে বাদ দেওয়া হয়। |
run_at | রানঅ্যাট | ঐচ্ছিক। স্ক্রিপ্টটি কখন পৃষ্ঠায় যুক্ত করা হবে তা নির্দিষ্ট করে। ডিফল্ট মান হলো document_idle । |
match_about_blank | বুলিয়ান | ঐচ্ছিক। স্ক্রিপ্টটি এমন একটি about:blank ফ্রেমে ইনজেক্ট করবে কিনা, যেখানে প্যারেন্ট বা ওপেনার ফ্রেমটি matches এ ঘোষিত প্যাটার্নগুলোর কোনো একটির সাথে মেলে। ডিফল্ট মান false। |
match_origin_as_fallback | বুলিয়ান | ঐচ্ছিক। স্ক্রিপ্টটি এমন ফ্রেমে ইনজেক্ট করবে কিনা, যেগুলো একটি মিলে যাওয়া অরিজিন দ্বারা তৈরি হয়েছে, কিন্তু যাদের URL বা অরিজিন সরাসরি প্যাটার্নের সাথে নাও মিলতে পারে। এর মধ্যে বিভিন্ন স্কিমের ফ্রেম অন্তর্ভুক্ত, যেমন about: , data: , blob: , এবং filesystem: । আরও দেখুন সম্পর্কিত ফ্রেমে ইনজেক্ট করা । |
world | এক্সিকিউশনওয়ার্ল্ড | ঐচ্ছিক। কোনো স্ক্রিপ্ট যে জাভাস্ক্রিপ্ট জগতের মধ্যে নির্বাহ হবে। ডিফল্ট হিসেবে এটি ISOLATED । আরও দেখুন: বিচ্ছিন্ন জগতে কাজ করা । |
ডকুমেন্ট লাইফসাইকেলের একটি নির্দিষ্ট পর্যায়ে , ম্যানিফেস্টে স্ট্যাটিক্যালি ঘোষিত কন্টেন্ট স্ক্রিপ্টগুলোই সর্বপ্রথম ইনজেক্ট করা হয়, অন্য কোনোভাবে রেজিস্টার করা কন্টেন্ট স্ক্রিপ্টগুলোর আগে। ম্যানিফেস্টে যে ক্রমে এগুলো নির্দিষ্ট করা থাকে, সেই ক্রমেই এগুলো ইনজেক্ট করা হয়।
ডাইনামিক ডিক্লারেশন দিয়ে ইনজেক্ট করুন
ডাইনামিক কন্টেন্ট স্ক্রিপ্ট তখন উপযোগী হয়, যখন কন্টেন্ট স্ক্রিপ্টের ম্যাচ প্যাটার্নগুলো সুপরিচিত না থাকে অথবা যখন কন্টেন্ট স্ক্রিপ্টগুলো সবসময় পরিচিত হোস্টগুলোতে ইনজেক্ট করার প্রয়োজন হয় না।
ক্রোম ৯৬-এ প্রবর্তিত ডাইনামিক ডিক্লারেশনগুলো স্ট্যাটিক ডিক্লারেশনের মতোই, তবে এক্ষেত্রে কন্টেন্ট স্ক্রিপ্ট অবজেক্টটি manifest.json- এর পরিবর্তে browser.scripting নেমস্পেসের মেথড ব্যবহার করে ক্রোমের সাথে রেজিস্টার করা হয়। স্ক্রিপ্টিং এপিআই এক্সটেনশন ডেভেলপারদেরকে আরও যেসব কাজ করার সুযোগ দেয়, সেগুলো হলো:
- কন্টেন্ট স্ক্রিপ্ট নিবন্ধন করুন ।
- নিবন্ধিত কন্টেন্ট স্ক্রিপ্টগুলোর একটি তালিকা পান ।
- নিবন্ধিত কন্টেন্ট স্ক্রিপ্টগুলোর তালিকা হালনাগাদ করুন ।
- নিবন্ধিত কন্টেন্ট স্ক্রিপ্টগুলো মুছে ফেলুন ।
স্ট্যাটিক ডিক্লারেশনের মতোই, ডাইনামিক ডিক্লারেশনেও জাভাস্ক্রিপ্ট ফাইল, সিএসএস ফাইল বা উভয়ই অন্তর্ভুক্ত থাকতে পারে।
সার্ভিস-ওয়ার্কার.জেএস
browser.scripting
.registerContentScripts([{
id: "session-script",
js: ["content.js"],
persistAcrossSessions: false,
matches: ["*://example.com/*"],
runAt: "document_start",
}])
.then(() => console.log("registration complete"))
.catch((err) => console.warn("unexpected error", err))
সার্ভিস-ওয়ার্কার.জেএস
browser.scripting
.updateContentScripts([{
id: "session-script",
excludeMatches: ["*://admin.example.com/*"],
}])
.then(() => console.log("registration updated"));
সার্ভিস-ওয়ার্কার.জেএস
browser.scripting
.getRegisteredContentScripts()
.then(scripts => console.log("registered content scripts", scripts));
সার্ভিস-ওয়ার্কার.জেএস
browser.scripting
.unregisterContentScripts({ ids: ["session-script"] })
.then(() => console.log("un-registration complete"));
প্রোগ্রাম্যাটিকভাবে ইনজেক্ট করুন
যেসব কন্টেন্ট স্ক্রিপ্ট ইভেন্টের প্রতিক্রিয়ায় বা নির্দিষ্ট উপলক্ষে চালানোর প্রয়োজন হয়, সেগুলোর জন্য প্রোগ্রাম্যাটিক ইনজেকশন ব্যবহার করুন।
প্রোগ্রাম্যাটিকভাবে একটি কন্টেন্ট স্ক্রিপ্ট ইনজেক্ট করতে, আপনার এক্সটেনশনটির সেই পৃষ্ঠার জন্য হোস্ট পারমিশন প্রয়োজন যেখানে এটি স্ক্রিপ্ট ইনজেক্ট করার চেষ্টা করছে। হোস্ট পারমিশন আপনার এক্সটেনশনের ম্যানিফেস্টের অংশ হিসাবে অনুরোধ করে অথবা সাময়িকভাবে "activeTab" ব্যবহার করে মঞ্জুর করা যেতে পারে।
নিম্নলিখিতগুলি হলো activeTab-ভিত্তিক এক্সটেনশনের বিভিন্ন সংস্করণ।
manifest.json:
{
"name": "My extension",
...
"permissions": [
"activeTab",
"scripting"
],
"background": {
"service_worker": "background.js"
},
"action": {
"default_title": "Action Button"
}
}
কন্টেন্ট স্ক্রিপ্টগুলো ফাইল হিসেবে ইনজেক্ট করা যেতে পারে।
কন্টেন্ট-স্ক্রিপ্ট.জেএস
document.body.style.backgroundColor = "orange";
service-worker.js:
browser.action.onClicked.addListener((tab) => {
browser.scripting.executeScript({
target: { tabId: tab.id },
files: ["content-script.js"]
});
});
অথবা, একটি ফাংশন বডিকে কন্টেন্ট স্ক্রিপ্ট হিসেবে ইনজেক্ট ও এক্সিকিউট করা যেতে পারে।
service-worker.js:
function injectedFunction() {
document.body.style.backgroundColor = "orange";
}
browser.action.onClicked.addListener((tab) => {
browser.scripting.executeScript({
target : {tabId : tab.id},
func : injectedFunction,
});
});
মনে রাখবেন যে ইনজেক্ট করা ফাংশনটি হলো browser.scripting.executeScript() কলে উল্লেখিত ফাংশনটির একটি অনুলিপি, এটি মূল ফাংশনটি নিজে নয়। ফলে, ফাংশনটির বডি অবশ্যই স্বয়ংসম্পূর্ণ হতে হবে; ফাংশনের বাইরের ভেরিয়েবলের রেফারেন্স দিলে কন্টেন্ট স্ক্রিপ্ট একটি ReferenceError থ্রো করবে।
ফাংশন হিসেবে ইনজেক্ট করার সময়, আপনি ফাংশনে আর্গুমেন্টও পাস করতে পারেন।
সার্ভিস-ওয়ার্কার.জেএস
function injectedFunction(color) {
document.body.style.backgroundColor = color;
}
browser.action.onClicked.addListener((tab) => {
browser.scripting.executeScript({
target : {tabId : tab.id},
func : injectedFunction,
args : [ "orange" ],
});
});
ম্যাচ এবং গ্লোব বাদ দিন
নির্দিষ্ট পৃষ্ঠা মেলানোর বিষয়টি কাস্টমাইজ করতে, একটি ডিক্লারেটিভ রেজিস্ট্রেশনে নিম্নলিখিত ফিল্ডগুলি অন্তর্ভুক্ত করুন।
| নাম | প্রকার | বর্ণনা |
|---|---|---|
exclude_matches | স্ট্রিংগুলির অ্যারে | ঐচ্ছিক। যেসব পৃষ্ঠায় অন্যথায় এই কন্টেন্ট স্ক্রিপ্টটি যুক্ত হতো, সেগুলোকে বাদ দেয়। এই স্ট্রিংগুলোর সিনট্যাক্স সম্পর্কে বিস্তারিত জানতে ম্যাচ প্যাটার্নস দেখুন। |
include_globs | স্ট্রিংগুলির অ্যারে | ঐচ্ছিক। matches পর এটি প্রয়োগ করা হয়, যাতে শুধুমাত্র সেইসব URL অন্তর্ভুক্ত হয় যেগুলো এই গ্লোবটির সাথেও মেলে। এর উদ্দেশ্য হলো Greasemonkey-এর @include কীওয়ার্ডটির অনুকরণ করা। |
exclude_globs | স্ট্রিং এর অ্যারে | ঐচ্ছিক। এই গ্লোবের সাথে মেলে এমন URL বাদ দেওয়ার জন্য matches পরে এটি প্রয়োগ করা হয়। এর উদ্দেশ্য হলো Greasemonkey-এর @exclude কীওয়ার্ডটির অনুকরণ করা। |
নিম্নলিখিত উভয় শর্তই সত্য হলে কন্টেন্ট স্ক্রিপ্টটি একটি পেজে যুক্ত করা হবে:
- এর URL যেকোনো
matchesপ্যাটার্ন এবং যেকোনোinclude_globsপ্যাটার্নের সাথে মেলে। - URL-টি
exclude_matchesবাexclude_globsপ্যাটার্নের সাথেও মেলে না। যেহেতুmatchesপ্রপার্টিটি আবশ্যক, তাইexclude_matches,include_globsএবংexclude_globsশুধুমাত্র কোন পৃষ্ঠাগুলো প্রভাবিত হবে তা সীমিত করতে ব্যবহার করা যেতে পারে।
নিম্নলিখিত এক্সটেনশনটি https://www.nytimes.com/health এ কন্টেন্ট স্ক্রিপ্ট ইনজেক্ট করে, কিন্তু https://www.nytimes.com/business এ করে না।
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"exclude_matches": ["*://*/*business*"],
"js": ["contentScript.js"]
}
],
...
}
সার্ভিস-ওয়ার্কার.জেএস
browser.scripting.registerContentScripts([{
id : "test",
matches : [ "https://*.nytimes.com/*" ],
excludeMatches : [ "*://*/*business*" ],
js : [ "contentScript.js" ],
}]);
ম্যাচ প্যাটার্নের চেয়ে গ্লোব প্রোপার্টির সিনট্যাক্স ভিন্ন এবং আরও নমনীয়। গ্রহণযোগ্য গ্লোব স্ট্রিং হলো ইউআরএল, যেগুলোতে 'ওয়াইল্ডকার্ড' অ্যাস্টারিস্ক এবং প্রশ্নবোধক চিহ্ন থাকতে পারে। অ্যাস্টারিস্ক ( * ) যেকোনো দৈর্ঘ্যের স্ট্রিংয়ের সাথে মেলে, এমনকি খালি স্ট্রিংয়ের সাথেও, আর প্রশ্নবোধক চিহ্ন ( ? ) যেকোনো একটি অক্ষরের সাথে মেলে।
উদাহরণস্বরূপ, https://???.example.com/foo/\* গ্লোবটি নিম্নলিখিত যেকোনোটির সাথে মিলে যায়:
-
https://www.example.com/foo/bar -
https://the.example.com/foo/
তবে, এটি নিম্নলিখিতগুলির সাথে মেলে না :
-
https://my.example.com/foo/bar -
https://example.com/foo/ -
https://www.example.com/foo
এই এক্সটেনশনটি কন্টেন্ট স্ক্রিপ্টটি https://www.nytimes.com/arts/index.html এবং https://www.nytimes.com/jobs/index.htm* -এ যুক্ত করে, কিন্তু https://www.nytimes.com/sports/index.html -এ করে না।
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"include_globs": ["*nytimes.com/???s/*"],
"js": ["contentScript.js"]
}
],
...
}
এই এক্সটেনশনটি কন্টেন্ট স্ক্রিপ্টটি https://history.nytimes.com এবং https://.nytimes.com/history তে যুক্ত করে, কিন্তু https://science.nytimes.com বা https://www.nytimes.com/science এ যুক্ত করে না।
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"exclude_globs": ["*science*"],
"js": ["contentScript.js"]
}
],
...
}
সঠিক পরিধি অর্জনের জন্য এগুলোর একটি, সবগুলো বা কয়েকটি অন্তর্ভুক্ত করা যেতে পারে।
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"exclude_matches": ["*://*/*business*"],
"include_globs": ["*nytimes.com/???s/*"],
"exclude_globs": ["*science*"],
"js": ["contentScript.js"]
}
],
...
}
রান টাইম
run_at ফিল্ডটি নিয়ন্ত্রণ করে কখন জাভাস্ক্রিপ্ট ফাইলগুলো ওয়েব পেজে যুক্ত হবে। এর পছন্দের এবং ডিফল্ট মান হলো "document_idle" । অন্যান্য সম্ভাব্য মানের জন্য `RunAt` টাইপটি দেখুন।
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"run_at": "document_idle",
"js": ["contentScript.js"]
}
],
...
}
সার্ভিস-ওয়ার্কার.জেএস
browser.scripting.registerContentScripts([{
id : "test",
matches : [ "https://*.nytimes.com/*" ],
runAt : "document_idle",
js : [ "contentScript.js" ],
}]);
| নাম | প্রকার | বর্ণনা |
|---|---|---|
document_idle | স্ট্রিং | পছন্দনীয়। সম্ভব হলে "document_idle" ব্যবহার করুন।ব্রাউজার "document_end" এবং window.onload ইভেন্টটি ফায়ার হওয়ার ঠিক পরের মুহূর্তের মধ্যে স্ক্রিপ্ট ইনজেক্ট করার জন্য একটি সময় বেছে নেয়। ইনজেকশনের সঠিক মুহূর্তটি নির্ভর করে ডকুমেন্টটি কতটা জটিল এবং এটি লোড হতে কত সময় নিচ্ছে তার উপর, এবং এটি পেজ লোডের গতি বাড়ানোর জন্য অপ্টিমাইজ করা হয়।"document_idle" অবস্থায় চলমান কন্টেন্ট স্ক্রিপ্টগুলোর window.onload ইভেন্টের জন্য অপেক্ষা করার প্রয়োজন নেই, কারণ DOM সম্পূর্ণ হওয়ার পরেই সেগুলো নিশ্চিতভাবে রান করে। যদি কোনো স্ক্রিপ্টকে window.onload পরে অবশ্যই রান করতে হয়, তবে এক্সটেনশনটি document.readyState প্রপার্টি ব্যবহার করে যাচাই করতে পারে যে onload ইতোমধ্যেই ফায়ার হয়েছে কি না। |
document_start | স্ট্রিং | স্ক্রিপ্টগুলো css এর যেকোনো ফাইলের পরে, কিন্তু অন্য কোনো DOM তৈরি হওয়ার বা অন্য কোনো স্ক্রিপ্ট চলার আগে ইনজেক্ট করা হয়। |
document_end | স্ট্রিং | DOM সম্পূর্ণ হওয়ার ঠিক পরেই, কিন্তু ছবি এবং ফ্রেমের মতো সাবরিসোর্সগুলো লোড হওয়ার আগে স্ক্রিপ্টগুলো ইনজেক্ট করা হয়। |
ফ্রেম নির্দিষ্ট করুন
ম্যানিফেস্টে নির্দিষ্ট করা ডিক্লারেটিভ কন্টেন্ট স্ক্রিপ্টগুলির জন্য, "all_frames" ফিল্ডটি এক্সটেনশনকে এটি নির্দিষ্ট করার সুযোগ দেয় যে, জাভাস্ক্রিপ্ট এবং সিএসএস ফাইলগুলি নির্দিষ্ট URL-এর শর্ত পূরণকারী সমস্ত ফ্রেমে ইনজেক্ট করা হবে, নাকি শুধুমাত্র একটি ট্যাবের সর্ববৃহৎ ফ্রেমে ইনজেক্ট করা হবে।
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"all_frames": true,
"js": ["contentScript.js"]
}
],
...
}
browser.scripting.registerContentScripts(...) ব্যবহার করে প্রোগ্রাম্যাটিকভাবে কন্টেন্ট স্ক্রিপ্ট রেজিস্টার করার সময়, allFrames প্যারামিটারটি ব্যবহার করে নির্দিষ্ট করা যায় যে কন্টেন্ট স্ক্রিপ্টটি নির্দিষ্ট URL-এর শর্ত পূরণকারী সমস্ত ফ্রেমে ইনজেক্ট করা হবে, নাকি শুধুমাত্র একটি ট্যাবের সবচেয়ে উপরের ফ্রেমে। এটি শুধুমাত্র tabId-এর সাথেই ব্যবহার করা যায় এবং frameIds বা documentIds নির্দিষ্ট করা থাকলে এটি ব্যবহার করা যায় না।
সার্ভিস-ওয়ার্কার.জেএস
browser.scripting.registerContentScripts([{
id: "test",
matches : [ "https://*.nytimes.com/*" ],
allFrames : true,
js : [ "contentScript.js" ],
}]);
সম্পর্কিত ফ্রেমে ইনজেক্ট করুন
এক্সটেনশনগুলো এমন ফ্রেমে স্ক্রিপ্ট চালাতে চাইতে পারে যা একটি মিলে যাওয়া ফ্রেমের সাথে সম্পর্কিত, কিন্তু ফ্রেমগুলো নিজেরা মিলে যায় না। এর একটি সাধারণ উদাহরণ হলো এমন ফ্রেম, যার ইউআরএলগুলো একটি মিলে যাওয়া ফ্রেম দ্বারা তৈরি হয়েছে, কিন্তু সেই ইউআরএলগুলো নিজেরা স্ক্রিপ্টের নির্দিষ্ট প্যাটার্নের সাথে মেলে না।
এটি তখন ঘটে যখন কোনো এক্সটেনশন about: , data: , blob: , এবং filesystem: স্কিমযুক্ত URL সহ ফ্রেমে ইনজেক্ট করতে চায়। এই ক্ষেত্রে, URL-টি কন্টেন্ট স্ক্রিপ্টের প্যাটার্নের সাথে মিলবে না (এবং, about: ও data: এর ক্ষেত্রে, URL-এ প্যারেন্ট URL বা অরিজিন একেবারেই অন্তর্ভুক্ত করবে না, যেমন about:blank বা data:text/html,<html>Hello, World!</html> )। তবে, এই ফ্রেমগুলোকেও সৃষ্টিকারী ফ্রেমের সাথে যুক্ত করা যেতে পারে।
এই ফ্রেমগুলিতে ইনজেক্ট করার জন্য, এক্সটেনশনগুলি ম্যানিফেস্টে থাকা কন্টেন্ট স্ক্রিপ্ট স্পেসিফিকেশনে "match_origin_as_fallback" প্রপার্টিটি নির্দিষ্ট করতে পারে।
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.google.com/*"],
"match_origin_as_fallback": true,
"js": ["contentScript.js"]
}
],
...
}
যখন এটি নির্দিষ্ট করে ' true সেট করা হয়, তখন Chrome ফ্রেমটি মেলে কিনা তা নির্ধারণ করতে ফ্রেমটির নিজস্ব URL-এর পরিবর্তে, ফ্রেমটির সূচনাকারীর অরিজিন দেখবে। উল্লেখ্য যে, এটি টার্গেট ফ্রেমের অরিজিন থেকেও ভিন্ন হতে পারে (যেমন, ' data: URL-গুলোর অরিজিন 'null' হয়)।
ফ্রেমটির সূচনাকারী হলো সেই ফ্রেম যা লক্ষ্য ফ্রেমটি তৈরি করেছে বা সেটিতে নেভিগেট করেছে। যদিও এটি সাধারণত সরাসরি প্যারেন্ট বা ওপেনার হয়ে থাকে, তবে তা নাও হতে পারে (যেমন একটি ফ্রেম যখন একটি আইফ্রেমের ভেতরে থাকা আরেকটি আইফ্রেমে নেভিগেট করে)।
যেহেতু এটি ইনিশিয়েটর ফ্রেমের অরিজিন তুলনা করে, তাই ইনিশিয়েটর ফ্রেমটি সেই অরিজিন থেকে যেকোনো পাথে চালু থাকতে পারে। এই বিষয়টি স্পষ্ট করার জন্য, Chrome-এর প্রয়োজন হয় যে, "match_origin_as_fallback" true সেট করা যেকোনো কন্টেন্ট স্ক্রিপ্টেও যেন * পাথটি নির্দিষ্ট করা থাকে।
যখন "match_origin_as_fallback" এবং "match_about_blank" উভয়ই নির্দিষ্ট করা থাকে, তখন "match_origin_as_fallback" অগ্রাধিকার পায়।
এম্বেডিং পৃষ্ঠার সাথে যোগাযোগ
যদিও কন্টেন্ট স্ক্রিপ্টের এক্সিকিউশন এনভায়রনমেন্ট এবং যে পেজগুলো সেগুলোকে হোস্ট করে, সেগুলো একে অপরের থেকে বিচ্ছিন্ন থাকে, তবুও তারা পেজের DOM-এ অ্যাক্সেস শেয়ার করে। যদি পেজটি কন্টেন্ট স্ক্রিপ্টের সাথে, অথবা কন্টেন্ট স্ক্রিপ্টের মাধ্যমে এক্সটেনশনের সাথে যোগাযোগ করতে চায়, তবে তাকে অবশ্যই শেয়ার করা DOM-এর মাধ্যমেই তা করতে হবে।
window.postMessage() ব্যবহার করে একটি উদাহরণ সম্পন্ন করা যেতে পারে:
কন্টেন্ট-স্ক্রিপ্ট.জেএস
var port = browser.runtime.connect();
window.addEventListener("message", (event) => {
// We only accept messages from ourselves
if (event.source !== window) {
return;
}
if (event.data.type && (event.data.type === "FROM_PAGE")) {
console.log("Content script received: " + event.data.text);
port.postMessage(event.data.text);
}
}, false);
example.js
document.getElementById("theButton").addEventListener("click", () => {
window.postMessage(
{type : "FROM_PAGE", text : "Hello from the webpage!"}, "*");
}, false);
নন-এক্সটেনশন পেজ example.html নিজের কাছেই বার্তা পাঠায়। এই বার্তাটি কন্টেন্ট স্ক্রিপ্ট দ্বারা গৃহীত ও পরীক্ষিত হয় এবং তারপর এক্সটেনশন প্রসেসে পাঠানো হয়। এইভাবে, পেজটি এক্সটেনশন প্রসেসের সাথে একটি যোগাযোগ ব্যবস্থা স্থাপন করে। অনুরূপ উপায়ে এর বিপরীতটিও সম্ভব।
এক্সটেনশন ফাইল অ্যাক্সেস করুন
কন্টেন্ট স্ক্রিপ্ট থেকে কোনো এক্সটেনশন ফাইল অ্যাক্সেস করতে, আপনি browser.runtime.getURL() কল করে আপনার এক্সটেনশন অ্যাসেটের অ্যাবসোলিউট URL পেতে পারেন, যেমনটি নিম্নলিখিত উদাহরণে ( content.js ) দেখানো হয়েছে:
কন্টেন্ট-স্ক্রিপ্ট.জেএস
let image = browser.runtime.getURL("images/my_image.png")
CSS ফাইলে ফন্ট বা ছবি ব্যবহার করতে, আপনি @@extension_id ব্যবহার করে একটি URL তৈরি করতে পারেন, যেমনটি নিচের উদাহরণে ( content.css ) দেখানো হয়েছে:
content.css
body {
background-image:url('chrome-extension://__MSG_@@extension_id__/background.png');
}
@font-face {
font-family: 'Stint Ultra Expanded';
font-style: normal;
font-weight: 400;
src: url('chrome-extension://__MSG_@@extension_id__/fonts/Stint Ultra Expanded.woff') format('woff');
}
manifest.json ফাইলে সমস্ত অ্যাসেটকে ওয়েব অ্যাক্সেসযোগ্য রিসোর্স হিসেবে ঘোষণা করতে হবে:
manifest.json
{
...
"web_accessible_resources": [
{
"resources": [ "images/*.png" ],
"matches": [ "https://example.com/*" ]
},
{
"resources": [ "fonts/*.woff" ],
"matches": [ "https://example.com/*" ]
}
],
...
}
বিষয়বস্তু নিরাপত্তা নীতি
বিচ্ছিন্ন বিশ্বে চলমান কন্টেন্ট স্ক্রিপ্টগুলির নিম্নলিখিত কন্টেন্ট সুরক্ষা নীতি (CSP) রয়েছে:
script-src 'self' 'wasm-unsafe-eval' 'inline-speculation-rules' chrome-extension://abcdefghijklmopqrstuvwxyz/; object-src 'self';
অন্যান্য এক্সটেনশন কনটেক্সটের উপর আরোপিত বিধিনিষেধের মতোই, এটি eval() এর ব্যবহার এবং বাহ্যিক স্ক্রিপ্ট লোড করাকে বাধা দেয়।
আনপ্যাক করা এক্সটেনশনগুলির জন্য, CSP-তে লোকালহোস্টও অন্তর্ভুক্ত থাকে:
script-src 'self' 'wasm-unsafe-eval' 'inline-speculation-rules' http://localhost:* http://127.0.0.1:* chrome-extension://abcdefghijklmopqrstuvwxyz/; object-src 'self';
যখন মূল অংশে কোনো কন্টেন্ট স্ক্রিপ্ট যুক্ত করা হয়, তখন পেজটির CSP (কন্টেন্ট স্ক্রিপ্ট পলিসি) প্রযোজ্য হয়।
সুরক্ষিত থাকুন
যদিও আইসোলেটেড ওয়ার্ল্ড এক ধরনের সুরক্ষা প্রদান করে, কন্টেন্ট স্ক্রিপ্ট ব্যবহার করলে একটি এক্সটেনশন এবং ওয়েব পেজ উভয়ের মধ্যেই দুর্বলতা তৈরি হতে পারে। যদি কন্টেন্ট স্ক্রিপ্ট কোনো আলাদা ওয়েবসাইট থেকে কন্টেন্ট গ্রহণ করে, যেমন fetch() কল করার মাধ্যমে, তবে তা ইনজেক্ট করার আগে ক্রস-সাইট স্ক্রিপ্টিং অ্যাটাকের বিরুদ্ধে কন্টেন্ট ফিল্টার করে নিতে সতর্ক থাকুন। "ম্যান-ইন-দ্য-মিডল" অ্যাটাক এড়ানোর জন্য শুধুমাত্র HTTPS-এর মাধ্যমেই যোগাযোগ করুন।
ক্ষতিকারক ওয়েব পেজগুলো ফিল্টার করতে ভুলবেন না। উদাহরণস্বরূপ, নিম্নলিখিত প্যাটার্নগুলো বিপজ্জনক এবং ম্যানিফেস্ট ভি৩-তে নিষিদ্ধ:
কন্টেন্ট-স্ক্রিপ্ট.জেএস
const data = document.getElementById("json-data"); // WARNING! Might be evaluating an evil script! const parsed = eval("(" + data + ")");
কন্টেন্ট-স্ক্রিপ্ট.জেএস
const elmt_id = ... // WARNING! elmt_id might be '); ... evil script ... //'! window.setTimeout("animate(" + elmt_id + ")", 200);
এর পরিবর্তে, এমন নিরাপদ এপিআই ব্যবহার করুন যেগুলো স্ক্রিপ্ট চালায় না:
কন্টেন্ট-স্ক্রিপ্ট.জেএস
const data = document.getElementById("json-data") // JSON.parse does not evaluate the attacker's scripts. const parsed = JSON.parse(data);
কন্টেন্ট-স্ক্রিপ্ট.জেএস
const elmt_id = ... // The closure form of setTimeout does not evaluate scripts. window.setTimeout(() => animate(elmt_id), 200);
কন্টেন্ট স্ক্রিপ্ট হলো এমন ফাইল যা ওয়েব পেজের প্রেক্ষাপটে চলে। স্ট্যান্ডার্ড ডকুমেন্ট অবজেক্ট মডেল (DOM) ব্যবহার করে, এগুলি ব্রাউজার দ্বারা পরিদর্শিত ওয়েব পেজগুলির বিবরণ পড়তে, সেগুলিতে পরিবর্তন আনতে এবং তাদের প্যারেন্ট এক্সটেনশনে তথ্য পাঠাতে সক্ষম হয়।
কন্টেন্ট স্ক্রিপ্টের সক্ষমতা বুঝুন
কন্টেন্ট স্ক্রিপ্টগুলো সরাসরি নিম্নলিখিত এক্সটেনশন এপিআইগুলো অ্যাক্সেস করতে পারে:
-
dom -
i18n -
storage -
runtime.connect() -
runtime.getManifest() -
runtime.getURL() -
runtime.id -
runtime.onConnect -
runtime.onMessage -
runtime.sendMessage()
কন্টেন্ট স্ক্রিপ্টগুলো সরাসরি অন্য এপিআই অ্যাক্সেস করতে পারে না। কিন্তু আপনার এক্সটেনশনের অন্যান্য অংশের সাথে বার্তা আদান-প্রদানের মাধ্যমে তারা পরোক্ষভাবে সেগুলো অ্যাক্সেস করতে পারে।
আপনি fetch() এর মতো API ব্যবহার করে একটি কন্টেন্ট স্ক্রিপ্ট থেকে আপনার এক্সটেনশনের অন্যান্য ফাইলও অ্যাক্সেস করতে পারেন। এটি করার জন্য, আপনাকে সেগুলোকে ওয়েব-অ্যাক্সেসযোগ্য রিসোর্স হিসেবে ঘোষণা করতে হবে। মনে রাখবেন যে, এর ফলে একই সাইটে চলমান যেকোনো ফার্স্ট-পার্টি বা থার্ড-পার্টি স্ক্রিপ্টের কাছেও রিসোর্সগুলো উন্মুক্ত হয়ে যায়।
বিচ্ছিন্ন বিশ্বে কাজ করুন
কন্টেন্ট স্ক্রিপ্টগুলো একটি বিচ্ছিন্ন জগতে অবস্থান করে, যার ফলে এটি পেজ বা অন্যান্য এক্সটেনশনের কন্টেন্ট স্ক্রিপ্টের সাথে কোনো সংঘাত ছাড়াই তার জাভাস্ক্রিপ্ট পরিবেশে পরিবর্তন আনতে পারে।
একটি এক্সটেনশন কোনো ওয়েব পেজে নিম্নলিখিত উদাহরণের মতো কোড ব্যবহার করে চালানো যেতে পারে।
webPage.html
<html>
<button id="mybutton">click me</button>
<script>
var greeting = "hello, ";
var button = document.getElementById("mybutton");
button.person_name = "Bob";
button.addEventListener(
"click", () => alert(greeting + button.person_name + "."), false);
</script>
</html>
ওই এক্সটেনশনটি ' স্ক্রিপ্ট ইনজেক্ট করুন' বিভাগে বর্ণিত কৌশলগুলোর মধ্যে একটি ব্যবহার করে নিম্নলিখিত কন্টেন্ট স্ক্রিপ্টটি ইনজেক্ট করতে পারে।
কন্টেন্ট-স্ক্রিপ্ট.জেএস
var greeting = "hola, ";
var button = document.getElementById("mybutton");
button.person_name = "Roberto";
button.addEventListener(
"click", () => alert(greeting + button.person_name + "."), false);
এই পরিবর্তনের ফলে, বাটনটিতে ক্লিক করলে উভয় অ্যালার্ট ক্রমানুসারে প্রদর্শিত হবে।
স্ক্রিপ্ট ইনজেক্ট করুন
কন্টেন্ট স্ক্রিপ্ট স্ট্যাটিক্যালি , ডাইনামিক্যালি অথবা প্রোগ্রাম্যাটিকভাবে ইনজেক্ট করা যেতে পারে।
স্ট্যাটিক ডিক্লারেশন দিয়ে ইনজেক্ট করুন
যেসব স্ক্রিপ্ট সুপরিচিত কিছু পেজে স্বয়ংক্রিয়ভাবে চালানো উচিত, সেগুলোর জন্য manifest.json-এ স্ট্যাটিক কন্টেন্ট স্ক্রিপ্ট ডিক্লারেশন ব্যবহার করুন।
স্ট্যাটিক্যালি ডিক্লেয়ার করা স্ক্রিপ্টগুলো ম্যানিফেস্টে "content_scripts" কী-এর অধীনে রেজিস্টার করা হয়। এগুলোতে জাভাস্ক্রিপ্ট ফাইল, সিএসএস ফাইল অথবা উভয়ই অন্তর্ভুক্ত থাকতে পারে। স্বয়ংক্রিয়ভাবে চালু হওয়া সমস্ত কন্টেন্ট স্ক্রিপ্টে অবশ্যই ম্যাচ প্যাটার্ন উল্লেখ করতে হবে।
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"css": ["my-styles.css"],
"js": ["content-script.js"]
}
],
...
}
| নাম | প্রকার | বর্ণনা |
|---|---|---|
matches | স্ট্রিংগুলির অ্যারে | আবশ্যক। এটি নির্দিষ্ট করে যে এই কন্টেন্ট স্ক্রিপ্টটি কোন কোন পেজে যুক্ত করা হবে। এই স্ট্রিংগুলির সিনট্যাক্স সম্পর্কে বিস্তারিত জানতে ‘ম্যাচ প্যাটার্নস’ দেখুন এবং কীভাবে ইউআরএল বাদ দিতে হয় সে সম্পর্কে তথ্যের জন্য ‘ম্যাচ প্যাটার্নস অ্যান্ড গ্লোবস’ দেখুন। |
css | স্ট্রিংগুলির অ্যারে | ঐচ্ছিক। সংশ্লিষ্ট পেজগুলিতে ইনজেক্ট করার জন্য CSS ফাইলগুলির তালিকা। পেজের জন্য কোনো DOM তৈরি বা প্রদর্শিত হওয়ার আগে, এই অ্যারেতে ফাইলগুলি যে ক্রমে রয়েছে, সেই ক্রমেই ইনজেক্ট করা হয়। |
js | | ঐচ্ছিক। সংশ্লিষ্ট পেজগুলিতে ইনজেক্ট করার জন্য জাভাস্ক্রিপ্ট ফাইলগুলির তালিকা। ফাইলগুলি এই অ্যারেতে যে ক্রমে রয়েছে, সেই ক্রমেই ইনজেক্ট করা হয়। এই তালিকার প্রতিটি স্ট্রিং-এ অবশ্যই এক্সটেনশনের রুট ডিরেক্টরিতে থাকা কোনো রিসোর্সের একটি রিলেটিভ পাথ থাকতে হবে। শুরুর স্ল্যাশ (`/`) স্বয়ংক্রিয়ভাবে বাদ দেওয়া হয়। |
run_at | রানঅ্যাট | ঐচ্ছিক। স্ক্রিপ্টটি কখন পৃষ্ঠায় যুক্ত করা হবে তা নির্দিষ্ট করে। ডিফল্ট মান হলো document_idle । |
match_about_blank | বুলিয়ান | Optional. Whether the script should inject into an about:blank frame where the parent or opener frame matches one of the patterns declared in matches . Defaults to false. |
match_origin_as_fallback | বুলিয়ান | Optional. Whether the script should inject in frames that were created by a matching origin, but whose URL or origin may not directly match the pattern. These include frames with different schemes, such as about: , data: , blob: , and filesystem: . See also Injecting in related frames . |
world | ExecutionWorld | Optional. The JavaScript world for a script to execute within. Defaults to ISOLATED . See also Work in isolated worlds . |
Within a given stage of the document lifecycle, content scripts declared statically in the manifest are the first to be injected, before content scripts registered in any other way. They are injected in the order in which they are specified in the manifest.
Inject with dynamic declarations
Dynamic content scripts are useful when the match patterns for content scripts are not well known or when content scripts shouldn't always be injected on known hosts.
Introduced in Chrome 96, dynamic declarations are similar to static declarations , but the content script object is registered with Chrome using methods in the browser.scripting namespace rather than in manifest.json . The Scripting API also allows extension developers to:
- Register content scripts.
- Get a list of registered content scripts.
- Update the list of registered content scripts.
- Remove registered content scripts.
Like static declarations, dynamic declarations can include JavaScript files, CSS files, or both.
service-worker.js
browser.scripting
.registerContentScripts([{
id: "session-script",
js: ["content.js"],
persistAcrossSessions: false,
matches: ["*://example.com/*"],
runAt: "document_start",
}])
.then(() => console.log("registration complete"))
.catch((err) => console.warn("unexpected error", err))
service-worker.js
browser.scripting
.updateContentScripts([{
id: "session-script",
excludeMatches: ["*://admin.example.com/*"],
}])
.then(() => console.log("registration updated"));
service-worker.js
browser.scripting
.getRegisteredContentScripts()
.then(scripts => console.log("registered content scripts", scripts));
service-worker.js
browser.scripting
.unregisterContentScripts({ ids: ["session-script"] })
.then(() => console.log("un-registration complete"));
Inject programmatically
Use programmatic injection for content scripts that need to run in response to events or on specific occasions.
To inject a content script programmatically, your extension needs host permissions for the page it's trying to inject scripts into. Host permissions can either be granted by requesting them as part of your extension's manifest or temporarily using "activeTab" .
The following are different versions of an activeTab-based extension.
manifest.json:
{
"name": "My extension",
...
"permissions": [
"activeTab",
"scripting"
],
"background": {
"service_worker": "background.js"
},
"action": {
"default_title": "Action Button"
}
}
Content scripts can be injected as files.
content-script.js
document.body.style.backgroundColor = "orange";
service-worker.js:
browser.action.onClicked.addListener((tab) => {
browser.scripting.executeScript({
target: { tabId: tab.id },
files: ["content-script.js"]
});
});
Or, a function body can be injected and executed as a content script.
service-worker.js:
function injectedFunction() {
document.body.style.backgroundColor = "orange";
}
browser.action.onClicked.addListener((tab) => {
browser.scripting.executeScript({
target : {tabId : tab.id},
func : injectedFunction,
});
});
Be aware that the injected function is a copy of the function referenced in the browser.scripting.executeScript() call, not the original function itself. As a result, the function's body must be self contained; references to variables outside of the function will cause the content script to throw a ReferenceError .
When injecting as a function, you can also pass arguments to the function.
service-worker.js
function injectedFunction(color) {
document.body.style.backgroundColor = color;
}
browser.action.onClicked.addListener((tab) => {
browser.scripting.executeScript({
target : {tabId : tab.id},
func : injectedFunction,
args : [ "orange" ],
});
});
Exclude matches and globs
To customize specified page matching, include the following fields in a declarative registration.
| নাম | প্রকার | বর্ণনা |
|---|---|---|
exclude_matches | array of strings | Optional. Excludes pages that this content script would otherwise be injected into. See Match Patterns for details of the syntax of these strings. |
include_globs | array of strings | Optional. Applied after matches to include only those URLs that also match this glob. This is intended to emulate the @include Greasemonkey keyword. |
exclude_globs | array of string | Optional. Applied after matches to exclude URLs that match this glob. Intended to emulate the @exclude Greasemonkey keyword. |
The content script will be injected into a page if both of the following are true:
- Its URL matches any
matchespattern and anyinclude_globspattern. - The URL doesn't also match an
exclude_matchesorexclude_globspattern. Because thematchesproperty is required,exclude_matches,include_globs, andexclude_globscan only be used to limit which pages will be affected.
The following extension injects the content script into https://www.nytimes.com/health but not into https://www.nytimes.com/business .
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"exclude_matches": ["*://*/*business*"],
"js": ["contentScript.js"]
}
],
...
}
service-worker.js
browser.scripting.registerContentScripts([{
id : "test",
matches : [ "https://*.nytimes.com/*" ],
excludeMatches : [ "*://*/*business*" ],
js : [ "contentScript.js" ],
}]);
Glob properties follow a different, more flexible syntax than match patterns . Acceptable glob strings are URLs that may contain "wildcard" asterisks and question marks. The asterisk ( * ) matches any string of any length, including the empty string, while the question mark ( ? ) matches any single character.
For example, the glob https://???.example.com/foo/\* matches any of the following:
-
https://www.example.com/foo/bar -
https://the.example.com/foo/
However, it does not match the following:
-
https://my.example.com/foo/bar -
https://example.com/foo/ -
https://www.example.com/foo
This extension injects the content script into https://www.nytimes.com/arts/index.html and https://www.nytimes.com/jobs/index.htm* , but not into https://www.nytimes.com/sports/index.html :
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"include_globs": ["*nytimes.com/???s/*"],
"js": ["contentScript.js"]
}
],
...
}
This extension injects the content script into https://history.nytimes.com and https://.nytimes.com/history , but not into https://science.nytimes.com or https://www.nytimes.com/science :
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"exclude_globs": ["*science*"],
"js": ["contentScript.js"]
}
],
...
}
One, all, or some of these can be included to achieve the correct scope.
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"exclude_matches": ["*://*/*business*"],
"include_globs": ["*nytimes.com/???s/*"],
"exclude_globs": ["*science*"],
"js": ["contentScript.js"]
}
],
...
}
Run time
The run_at field controls when JavaScript files are injected into the web page. The preferred and default value is "document_idle" . See the RunAt type for other possible values.
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"run_at": "document_idle",
"js": ["contentScript.js"]
}
],
...
}
service-worker.js
browser.scripting.registerContentScripts([{
id : "test",
matches : [ "https://*.nytimes.com/*" ],
runAt : "document_idle",
js : [ "contentScript.js" ],
}]);
| নাম | প্রকার | বর্ণনা |
|---|---|---|
document_idle | স্ট্রিং | Preferred. Use "document_idle" whenever possible.The browser chooses a time to inject scripts between "document_end" and immediately after the window.onload event fires. The exact moment of injection depends on how complex the document is and how long it is taking to load, and is optimized for page load speed.Content scripts running at "document_idle" don't need to listen for the window.onload event, they are guaranteed to run after the DOM is complete. If a script definitely needs to run after window.onload , the extension can check if onload has already fired by using the document.readyState property. |
document_start | স্ট্রিং | Scripts are injected after any files from css , but before any other DOM is constructed or any other script is run. |
document_end | স্ট্রিং | Scripts are injected immediately after the DOM is complete, but before subresources like images and frames have loaded. |
Specify frames
For declarative content scripts specified in the manifest, the "all_frames" field allows the extension to specify if JavaScript and CSS files should be injected into all frames matching the specified URL requirements or only into the topmost frame in a tab:
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"all_frames": true,
"js": ["contentScript.js"]
}
],
...
}
When programmatically registering content scripts using browser.scripting.registerContentScripts(...) , the allFrames parameter can be used to specify if the content script should be injected into all frames matching the specified URL requirements or only into the topmost frame in a tab. This can only be used with tabId, and cannot be used if frameIds or documentIds are specified:
service-worker.js
browser.scripting.registerContentScripts([{
id: "test",
matches : [ "https://*.nytimes.com/*" ],
allFrames : true,
js : [ "contentScript.js" ],
}]);
Inject in to related frames
Extensions may want to run scripts in frames that are related to a matching frame, but don't themselves match. A common scenario when this is the case is for frames with URLs that were created by a matching frame, but whose URLs don't themselves match the script's specified patterns.
This is the case when an extension wants to inject in frames with URLs that have about: , data: , blob: , and filesystem: schemes. In these cases, the URL won't match the content script's pattern (and, in the case of about: and data: , don't even include the parent URL or origin in the URL at all, as in about:blank or data:text/html,<html>Hello, World!</html> ). However, these frames can still be associated with the creating frame.
To inject into these frames, extensions can specify the "match_origin_as_fallback" property on a content script specification in the manifest.
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.google.com/*"],
"match_origin_as_fallback": true,
"js": ["contentScript.js"]
}
],
...
}
When specified and set to true , Chrome will look at the origin of the initiator of the frame to determine whether the frame matches, rather than at the URL of the frame itself. Note that this might also be different than the target frame's origin (eg, data: URLs have a null origin).
The initiator of the frame is the frame that created or navigated the target frame. While this is commonly the direct parent or opener, it may not be (as in the case of a frame navigating an iframe within an iframe).
Because this compares the origin of the initiator frame, the initiator frame could be on at any path from that origin. To make this implication clear, Chrome requires any content scripts specified with "match_origin_as_fallback" set to true to also specify a path of * .
When both "match_origin_as_fallback" and "match_about_blank" are specified, "match_origin_as_fallback" takes priority.
Communication with the embedding page
Although the execution environments of content scripts and the pages that host them are isolated from each other, they share access to the page's DOM. If the page wishes to communicate with the content script, or with the extension through the content script, it must do so through the shared DOM.
An example can be accomplished using window.postMessage() :
content-script.js
var port = browser.runtime.connect();
window.addEventListener("message", (event) => {
// We only accept messages from ourselves
if (event.source !== window) {
return;
}
if (event.data.type && (event.data.type === "FROM_PAGE")) {
console.log("Content script received: " + event.data.text);
port.postMessage(event.data.text);
}
}, false);
example.js
document.getElementById("theButton").addEventListener("click", () => {
window.postMessage(
{type : "FROM_PAGE", text : "Hello from the webpage!"}, "*");
}, false);
The non-extension page, example.html, posts messages to itself. This message is intercepted and inspected by the content script and then posted to the extension process. In this way, the page establishes a line of communication to the extension process. The reverse is possible through similar means.
Access extension files
To access an extension file from a content script, you can call browser.runtime.getURL() to get the absolute URL of your extension asset as shown in the following example ( content.js ):
content-script.js
let image = browser.runtime.getURL("images/my_image.png")
To use fonts or images in a CSS file, you can use @@extension_id to construct a URL as shown in the following example ( content.css ):
content.css
body {
background-image:url('chrome-extension://__MSG_@@extension_id__/background.png');
}
@font-face {
font-family: 'Stint Ultra Expanded';
font-style: normal;
font-weight: 400;
src: url('chrome-extension://__MSG_@@extension_id__/fonts/Stint Ultra Expanded.woff') format('woff');
}
All assets must be declared as web accessible resources in the manifest.json file:
manifest.json
{
...
"web_accessible_resources": [
{
"resources": [ "images/*.png" ],
"matches": [ "https://example.com/*" ]
},
{
"resources": [ "fonts/*.woff" ],
"matches": [ "https://example.com/*" ]
}
],
...
}
Content Security Policy
Content scripts running in isolated worlds have the following Content Security Policy (CSP):
script-src 'self' 'wasm-unsafe-eval' 'inline-speculation-rules' chrome-extension://abcdefghijklmopqrstuvwxyz/; object-src 'self';
Similar to the restrictions applied to other extension contexts, this prevents the use of eval() as well as loading external scripts.
For unpacked extensions, the CSP also includes localhost:
script-src 'self' 'wasm-unsafe-eval' 'inline-speculation-rules' http://localhost:* http://127.0.0.1:* chrome-extension://abcdefghijklmopqrstuvwxyz/; object-src 'self';
When a content script is injected into the main world, the CSP of the page applies.
Stay secure
While isolated worlds provide a layer of protection, using content scripts can create vulnerabilities in an extension and the web page. If the content script receives content from a separate website, such as by calling fetch() , be careful to filter content against cross-site scripting attacks before injecting it. Only communicate over HTTPS in order to avoid "man-in-the-middle" attacks.
Be sure to filter for malicious web pages. For example, the following patterns are dangerous, and disallowed in Manifest V3:
content-script.js
const data = document.getElementById("json-data"); // WARNING! Might be evaluating an evil script! const parsed = eval("(" + data + ")");
content-script.js
const elmt_id = ... // WARNING! elmt_id might be '); ... evil script ... //'! window.setTimeout("animate(" + elmt_id + ")", 200);
Instead, prefer safer APIs that don't run scripts:
content-script.js
const data = document.getElementById("json-data") // JSON.parse does not evaluate the attacker's scripts. const parsed = JSON.parse(data);
content-script.js
const elmt_id = ... // The closure form of setTimeout does not evaluate scripts. window.setTimeout(() => animate(elmt_id), 200);