النصوص البرمجية للمحتوى

نصوص برمجية للمحتوى هي ملفات يتم تشغيلها في سياق صفحات الويب. باستخدام نموذج Document Object Model (DOM) العادي، يمكن لهذه الإضافات قراءة تفاصيل صفحات الويب التي يزورها المتصفّح وإجراء تعديلات عليها ونقل المعلومات إلى الإضافة الرئيسية.

فهم إمكانات نصوص المحتوى

يمكن للنصوص البرمجية للمحتوى الوصول إلى واجهات برمجة تطبيقات الإضافات التالية مباشرةً:

لا يمكن للنصوص البرمجية للمحتوى الوصول إلى واجهات برمجة تطبيقات أخرى مباشرةً. ولكن يمكنهم الوصول إليها بشكل غير مباشر من خلال تبادل الرسائل مع أجزاء أخرى من إضافتك.

يمكنك أيضًا الوصول إلى ملفات أخرى في الإضافة من نص محتوى باستخدام واجهات برمجة تطبيقات مثل 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);