نصوص برمجية للمحتوى هي ملفات يتم تشغيلها في سياق صفحات الويب. باستخدام نموذج Document Object Model (DOM) العادي، يمكن لهذه الإضافات قراءة تفاصيل صفحات الويب التي يزورها المتصفّح وإجراء تعديلات عليها ونقل المعلومات إلى الإضافة الرئيسية.
فهم إمكانات نصوص المحتوى
يمكن للنصوص البرمجية للمحتوى الوصول إلى واجهات برمجة تطبيقات الإضافات التالية مباشرةً:
dom
i18n
storage
runtime.connect()
runtime.getManifest()
runtime.getURL()
runtime.id
runtime.onConnect
runtime.onMessage
runtime.sendMessage()
لا يمكن للنصوص البرمجية للمحتوى الوصول إلى واجهات برمجة تطبيقات أخرى مباشرةً. ولكن يمكنهم الوصول إليها بشكل غير مباشر من خلال تبادل الرسائل مع أجزاء أخرى من إضافتك.
يمكنك أيضًا الوصول إلى ملفات أخرى في الإضافة من نص محتوى باستخدام
واجهات برمجة تطبيقات مثل fetch()
. لإجراء ذلك، عليك الإفصاح عنها على أنّها
موارد يمكن الوصول إليها من الويب. يُرجى العِلم أنّ هذا الإجراء يعرّض أيضًا الموارد لأي
نصوص برمجية تابعة للطرف الأول أو لجهة خارجية يتم تشغيلها على الموقع الإلكتروني نفسه.
العمل في عوالم معزولة
تعمل نصوص المحتوى البرمجية في بيئة معزولة، ما يسمح لها بإجراء تغييرات على بيئة JavaScript بدون الاصطدام بنصوص المحتوى البرمجية للصفحات أو الإضافات الأخرى.
يمكن تشغيل إضافة في صفحة ويب باستخدام رمز مشابه للمثال التالي.
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>
يمكن أن تُدخل هذه الإضافة نص المحتوى التالي باستخدام إحدى الأساليب الموضّحة في قسم إدخال النصوص البرمجية.
content-script.js
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"
.
ويمكن أن تتضمّن ملفات JavaScript أو ملفات CSS أو كليهما. يجب أن تحدّد كل نصوص المحتوى البرمجية التي يتم تشغيلها تلقائيًا
أنماط المطابقة.
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"css": ["my-styles.css"],
"js": ["content-script.js"]
}
],
...
}
الاسم | النوع | الوصف |
---|---|---|
matches |
صفيف من السلاسل | مطلوبة تُستخدَم لتحديد الصفحات التي سيتم إدراج نص المحتوى هذا فيها. اطّلِع على أنماط المطابقة للحصول على تفاصيل حول بنية هذه السلاسل وأنماط المطابقة والعناصر الشاملة للحصول على معلومات عن كيفية استبعاد عناوين URL. |
css |
صفيف من السلاسل | اختياري قائمة ملفات CSS التي سيتم إدراجها في الصفحات المطابقة ويتم ضخّ هذه العناصر بالترتيب الذي تظهر به في هذه الصفيف، قبل إنشاء أيّ عنصر DOM أو عرضه للصفحة. |
js |
|
اختياري قائمة ملفات JavaScript التي سيتم إدراجها في الصفحات المطابقة يتم إدراج الملفات بالترتيب الذي تظهر به في هذه الصفيف. يجب أن تحتوي كل سلسلة في هذه القائمة على مسار نسبي إلى مورد في الدليل الجذر للإضافة. يتم تلقائيًا اقتطاع الشَرطات المائلة التي تأتي في بداية العنوان. |
run_at |
RunAt | اختياري تحدد هذه السمة وقت إدخال النص البرمجي في الصفحة. الإعداد التلقائي هو
document_idle . |
match_about_blank |
قيمة منطقية | اختياري ما إذا كان يجب إدراج النص البرمجي في إطار about:blank
حيث يتطابق الإطار الرئيسي أو الإطار الافتتاحي مع أحد الأنماط المحدّدة في
matches . القيمة التلقائية هي false. |
match_origin_as_fallback |
قيمة منطقية |
اختياري ما إذا كان يجب إدراج النص البرمجي في الإطارات التي تم
إنشاؤها من مصدر مطابق، ولكن قد لا يتطابق عنوان URL أو مصدرها
مباشرةً مع النمط. وتشمل هذه الإطارات إطارات ذات مخطّطات مختلفة، مثل
about: وdata: وblob: و
filesystem: . يُرجى الاطّلاع أيضًا على
الإدراج في اللقطات ذات الصلة.
|
world |
ExecutionWorld |
اختياري بيئة JavaScript التي سيتم تنفيذ نص برمجي فيها الإعداد التلقائي هو ISOLATED . راجِع أيضًا
العمل في عوالم معزولة.
|
الإدراج باستخدام تعريفات ديناميكية
تكون نصوص المحتوى الديناميكية مفيدة عندما تكون أنماط المطابقة لنصوص المحتوى غير معروفة جيدًا أو عندما لا يجب حقن نصوص المحتوى دائمًا على مضيفين معروفين.
تمّ تقديم التعريفات الديناميكية في الإصدار 96 من Chrome، وهي تشبه التعريفات static، ولكن يتم تسجيل عنصر نص المحتوى في Chrome باستخدام methods في مساحة الاسم chrome.scripting
بدلاً من
manifest.json. تسمح واجهة برمجة التطبيقات Scripting API أيضًا لمطوّري الإضافات بالآتي:
- تسجيل النصوص البرمجية للمحتوى
- الحصول على قائمة بنصوص المحتوى البرمجية المسجّلة
- تعديل قائمة نصوص المحتوى المسجّلة
- أزِل نصوص المحتوى البرمجية المسجّلة.
مثل البيانات الثابتة، يمكن أن تتضمّن البيانات الديناميكية ملفات JavaScript أو ملفات CSS أو كليهما.
service-worker.js
chrome.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
chrome.scripting
.updateContentScripts([{
id: "session-script",
excludeMatches: ["*://admin.example.com/*"],
}])
.then(() => console.log("registration updated"));
service-worker.js
chrome.scripting
.getRegisteredContentScripts()
.then(scripts => console.log("registered content scripts", scripts));
service-worker.js
chrome.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"
}
}
يمكن إدخال النصوص البرمجية للمحتوى كملفات.
content-script.js
document.body.style.backgroundColor = "orange";
service-worker.js:
chrome.action.onClicked.addListener((tab) => {
chrome.scripting.executeScript({
target: { tabId: tab.id },
files: ["content-script.js"]
});
});
أو يمكن إدخال نص الدالة وتنفيذه كنص برمجي للمحتوى.
service-worker.js:
function injectedFunction() {
document.body.style.backgroundColor = "orange";
}
chrome.action.onClicked.addListener((tab) => {
chrome.scripting.executeScript({
target : {tabId : tab.id},
func : injectedFunction,
});
});
يُرجى العِلم أنّ الدالة المُحقَّقة هي نسخة من الدالة المُشار إليها في دعوة
chrome.scripting.executeScript()
، وليس الدالة الأصلية نفسها. نتيجةً لذلك، يجب أن يكون محتوى الدالة مكتفيًا ذاتيًا، لأنّ الإشارات إلى المتغيّرات خارج الدالة ستؤدي إلى ظهور ReferenceError
في محتوى الرمز البرمجي.
عند الحقن كدالّة، يمكنك أيضًا تمرير وسيطات إلى الدالة.
service-worker.js
function injectedFunction(color) {
document.body.style.backgroundColor = color;
}
chrome.action.onClicked.addListener((tab) => {
chrome.scripting.executeScript({
target : {tabId : tab.id},
func : injectedFunction,
args : [ "orange" ],
});
});
استبعاد المطابقات والنطاقات الشاملة
لتخصيص مطابقة صفحة محدّدة، أدرِج الحقول التالية في تسجيل декларاتيف.
الاسم | النوع | الوصف |
---|---|---|
exclude_matches |
صفيف من السلاسل | اختياري يستبعد الصفحات التي سيتم إدراج نص برمجي للمحتوى فيها بخلاف ذلك. اطّلِع على أنماط المطابقة لمعرفة تفاصيل بنية هذه السلاسل. |
include_globs |
صفيف من السلاسل | اختياري يتم تطبيقها بعد matches لتضمين عناوين URL التي تطابق أيضًا
هذا النطاق الشامل. يهدف ذلك إلى محاكاة الكلمة الرئيسية @include
في Greasemonkey. |
exclude_globs |
صفيف من السلاسل | اختياري يتم تطبيقها بعد matches لاستبعاد عناوين URL التي تتطابق مع هذا
التعبير العادي. يهدف إلى محاكاة الكلمة الرئيسية @exclude
في Greasemonkey. |
سيتمّ إدراج نصّ المحتوى في صفحة إذا كان كلا الشرطَين التاليَين صحيحَين:
- يتطابق عنوان 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"]
}
],
...
}
service-worker.js
chrome.scripting.registerContentScripts([{
id : "test",
matches : [ "https://*.nytimes.com/*" ],
excludeMatches : [ "*://*/*business*" ],
js : [ "contentScript.js" ],
}]);
تتبع سمات النطاقات بنية مختلفة أكثر مرونة من نماذج المطابقة. إنّ سلاسل التجميع المقبولة هي عناوين URL التي قد تحتوي على علامات النجمة وعلامات الاستفهام "أحرف البدل". تتطابق علامة النجمة (*
)
مع أي سلسلة بأي طول، بما في ذلك السلسلة الفارغة، في حين تتطابق علامة الاستفهام (?
)
مع أي حرف مفرد.
على سبيل المثال، يتطابق نطاق البحث الشامل 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
في وقت إدخال ملفات JavaScript في صفحة الويب. القيمة المفضّلة
والتلقائية هي "document_idle"
. راجِع نوع RunAt للاطّلاع على قيم
أخرى محتملة.
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"run_at": "document_idle",
"js": ["contentScript.js"]
}
],
...
}
service-worker.js
chrome.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 ، يمكن للإضافة التحقّق مما إذا كان
onload قد تم تشغيله من خلال استخدام السمة document.readyState . |
document_start |
سلسلة | يتمّ إدراج النصوص البرمجية بعد أيّ ملفات من css ، ولكن قبل
إنشاء أيّ عنصر تحكم في نموذج البيانات أو تنفيذ أيّ نص برمجي آخر. |
document_end |
سلسلة | يتمّ حقن النصوص البرمجية فور اكتمال عنصر DOM، ولكن قبل تحميل الموارد الفرعية، مثل الصور والأُطر. |
تحديد الإطارات
بالنسبة إلى نصوص المحتوى التعريفية المحدّدة في البيان، يسمح الحقل "all_frames"
للإضافة بتحديد ما إذا كان يجب إدراج ملفات JavaScript وCSS في جميع اللقطات التي تتطابق مع متطلبات عنوان URL المحدّد أو في اللقطة العلوية فقط في
علامة تبويب:
manifest.json
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["https://*.nytimes.com/*"],
"all_frames": true,
"js": ["contentScript.js"]
}
],
...
}
عند التسجيل الآلي لنصوص برمجية للمحتوى باستخدام chrome.scripting.registerContentScripts(...)
، يمكن استخدام المَعلمة allFrames
لتحديد ما إذا كان يجب إدراج نصّ البرمجة للمحتوى في جميع الإطارات التي تتطابق مع متطلبات عنوان URL المحدّد أو في الإطار العلوي فقط في علامة تبويب. لا يمكن استخدام هذا الإجراء إلا مع tabId، ولا يمكن استخدامه في حال تحديد frameIds أو documentIds:
service-worker.js
chrome.scripting.registerContentScripts([{
id: "test",
matches : [ "https://*.nytimes.com/*" ],
allFrames : true,
js : [ "contentScript.js" ],
}]);
إدراجها في الإطارات ذات الصلة
قد تحتاج الإضافات إلى تشغيل النصوص البرمجية في الإطارات المرتبطة بإطار مطابق، ولكنّها لا تتطابق مع بعضها. ومن الأمثلة الشائعة على ذلك الإطارات التي تحتوي على عناوين URL تم إنشاؤها بواسطة إطار مطابق، ولكن عناوين URL الخاصة بها لا تتطابق مع الأنماط المحدّدة في النص البرمجي.
يحدث ذلك عندما تريد إحدى الإضافات إدراج إطارات تحتوي على عناوين URL
تتضمّن مخطّطات about:
وdata:
وblob:
وfilesystem:
. في هذه الحالات، لن يتطابق عنوان
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 على مصدر غير صالح).
عنصر بدء الإطار هو الإطار الذي أنشأ الإطار المستهدف أو تنقّل إليه. على الرغم من أنّ هذا العنصر يكون عادةً العنصر الرئيسي أو العنصر الذي فتح الإطار، إلا أنّه قد لا يكون كذلك (كما هو الحال في حالة إطار يتنقل في إطار iframe ضمن إطار iframe).
وبما أنّ هذا الإجراء يقارن المصدر لإطار المشغِّل، يمكن أن يكون إطار المشغِّل
مفعَّلاً في أي مسار من هذا المصدر. لتوضيح هذا التأثير، يتطلب Chrome
تحديد مسار *
أيضًا في أي نصوص برمجية للمحتوى تم تحديدها باستخدام "match_origin_as_fallback"
مع ضبطها على true
.
عند تحديد كل من "match_origin_as_fallback"
و"match_about_blank"
،
يكون الخيار "match_origin_as_fallback"
هو الخيار المُفضَّل.
التواصل مع صفحة التضمين
على الرغم من أنّ بيئات تنفيذ النصوص البرمجية للمحتوى والصفحات التي تستضيفها معزولة عن بعضها، إلا أنّها تشترك في الوصول إلى نموذج DOM للصفحة. إذا أرادت الصفحة التواصل مع نص المحتوى، أو مع الإضافة من خلال نص المحتوى، يجب أن تفعل ذلك من خلال نموذج DOM المشترَك.
يمكن تنفيذ مثال باستخدام window.postMessage()
:
content-script.js
var port = chrome.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، الرسائل إلى نفسها. يتم اعتراض هذه الرسالة وتفتيشها من خلال النص البرمجي للمحتوى، ثم يتم نشرها في عملية إضافة البيانات. بهذه الطريقة، تُنشئ الصفحة قناة تواصل مع عملية التمديد. ويمكن إجراء العكس من خلال وسائل مشابهة.
الوصول إلى ملفات البيانات الموسّعة
للوصول إلى ملف إضافة من نص برمجي للمحتوى، يمكنك استدعاء
chrome.runtime.getURL()
للحصول على عنوان URL المطلق لمادة عرض الإضافة كما هو موضّح في المثال التالي (content.js
):
content-script.js
let image = chrome.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/*" ]
}
],
...
}
الحفاظ على أمانك
على الرغم من أنّ العوالم المعزولة توفّر طبقة حماية، يمكن أن يؤدي استخدام النصوص البرمجية للمحتوى إلى تعريض الإضافة وصفحة الويب
للثغرات الأمنية. إذا كان نص المحتوى يتلقّى محتوى من
موقع إلكتروني منفصل، مثل الاتصال بـ fetch()
، يجب الانتباه إلى فلترة المحتوى للحماية من هجمات البرمجة النصية عبر المواقع قبل حقنه. يجب التواصل عبر HTTPS فقط لتجنُّب "man-in-the-middle".
احرص على الفلترة بحثًا عن صفحات الويب الضارّة. على سبيل المثال، الأنماط التالية خطيرة ومُهمَلة في الإصدار 3 من ملف البيان:
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);
بدلاً من ذلك، استخدِم واجهات برمجة تطبيقات أكثر أمانًا لا تعمل على تنفيذ النصوص البرمجية:
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);