اسکریپت های محتوا

اسکریپت‌های محتوا فایل‌هایی هستند که در چارچوب صفحات وب اجرا می‌شوند. آن‌ها با استفاده از مدل شیءگرای سند (DOM) استاندارد، قادر به خواندن جزئیات صفحات وبی هستند که مرورگر بازدید می‌کند، تغییراتی در آن‌ها ایجاد می‌کند و اطلاعات را به افزونه والد خود منتقل می‌کند.

قابلیت‌های اسکریپت محتوا را درک کنید

اسکریپت‌های محتوا می‌توانند با تبادل پیام با افزونه، به APIهای کروم مورد استفاده افزونه والد خود دسترسی پیدا کنند. آن‌ها همچنین می‌توانند با استفاده از chrome.runtime.getURL() به آدرس اینترنتی فایل یک افزونه دسترسی پیدا کنند و از نتیجه آن مانند سایر URLها استفاده کنند.

// Code for displaying EXTENSION_DIR/images/myimage.png:
var imgURL = chrome.runtime.getURL("images/myimage.png");
document.getElementById("someImage").src = imgURL;

علاوه بر این، اسکریپت محتوا می‌تواند مستقیماً به APIهای کروم زیر دسترسی داشته باشد:

اسکریپت‌های محتوا نمی‌توانند مستقیماً به APIهای دیگر دسترسی داشته باشند.

کار در جهان‌های منزوی

اسکریپت‌های محتوا در یک دنیای ایزوله قرار دارند و به یک اسکریپت محتوا اجازه می‌دهند بدون ایجاد تداخل با صفحه یا اسکریپت‌های محتوای اضافی، در محیط جاوا اسکریپت خود تغییراتی ایجاد کند.

یک افزونه ممکن است در یک صفحه وب با کدی مشابه مثال زیر اجرا شود.

<html>
  <button id="mybutton">click me</button>
  <script>
    var greeting = "hello, ";
    var button = document.getElementById("mybutton");
    button.person_name = "Bob";
    button.addEventListener("click", function() {
      alert(greeting + button.person_name + ".");
    }, false);
  </script>
</html>

آن افزونه می‌تواند اسکریپت محتوای زیر را تزریق کند.

var greeting = "hola, ";
var button = document.getElementById("mybutton");
button.person_name = "Roberto";
button.addEventListener("click", function() {
  alert(greeting + button.person_name + ".");
}, false);

اگر دکمه فشرده می‌شد، هر دو هشدار ظاهر می‌شدند.

جهان‌های ایزوله به اسکریپت‌های محتوا، افزونه و صفحه وب اجازه دسترسی به متغیرها یا توابع ایجاد شده توسط دیگران را نمی‌دهند. این امر همچنین به اسکریپت‌های محتوا این امکان را می‌دهد که عملکردهایی را فعال کنند که نباید در صفحه وب قابل دسترسی باشند.

تزریق اسکریپت‌ها

اسکریپت‌های محتوا می‌توانند به صورت برنامه‌نویسی شده یا اعلانی تزریق شوند.

تزریق به صورت برنامه‌نویسی‌شده

از تزریق برنامه‌ریزی‌شده برای اسکریپت‌های محتوایی که باید در مواقع خاص اجرا شوند، استفاده کنید.

برای تزریق یک اسکریپت محتوای برنامه‌ریزی‌شده، مجوز activeTab را در مانیفست ارائه دهید. این امر دسترسی امن به میزبان سایت فعال و دسترسی موقت به مجوز tabs را فراهم می‌کند و اسکریپت محتوا را قادر می‌سازد تا بدون تعیین مجوزهای cross-origin ، روی تب فعال فعلی اجرا شود.

{
  "name": "My extension",
  ...
  "permissions": [
    "activeTab"
  ],
  ...
}

اسکریپت‌های محتوا می‌توانند به صورت کد تزریق شوند.

chrome.runtime.onMessage.addListener(
  function(message, callback) {
    if (message == "changeColor"){
      chrome.tabs.executeScript({
        code: 'document.body.style.backgroundColor="orange"'
      });
    }
  });

یا می‌توان کل یک فایل را تزریق کرد.

chrome.runtime.onMessage.addListener(
  function(message, callback) {
    if (message == "runContentScript"){
      chrome.tabs.executeScript({
        file: 'contentScript.js'
      });
    }
  });

تزریق به صورت اعلانی

از تزریق اعلانی برای اسکریپت‌های محتوایی که باید به طور خودکار در صفحات مشخص شده اجرا شوند، استفاده کنید.

اسکریپت‌های تزریق‌شده به صورت اعلانی در فایل مانیفست، زیر فیلد "content_scripts" ثبت می‌شوند. آن‌ها می‌توانند شامل فایل‌های جاوا اسکریپت، فایل‌های CSS یا هر دو باشند. همه اسکریپت‌های محتوای خودکار باید الگوهای منطبق را مشخص کنند.

{
 "name": "My extension",
 ...
 "content_scripts": [
   {
     "matches": ["http://*.nytimes.com/*"],
     "css": ["myStyles.css"],
     "js": ["contentScript.js"]
   }
 ],
 ...
}
نام نوع توضیحات
matches {: #matches } آرایه‌ای از رشته‌ها الزامی. مشخص می‌کند که این اسکریپت محتوا به کدام صفحات تزریق خواهد شد. برای جزئیات بیشتر در مورد سینتکس این رشته‌ها به Match Patterns و برای اطلاعات بیشتر در مورد نحوه‌ی حذف URLها به Match patterns and globs مراجعه کنید.
css {: #سی‌اس‌اس } آرایه‌ای از رشته‌ها اختیاری. فهرست فایل‌های CSS که باید به صفحات منطبق تزریق شوند. این فایل‌ها به ترتیبی که در این آرایه ظاهر می‌شوند، قبل از ساخت یا نمایش هرگونه DOM برای صفحه، تزریق می‌شوند.
js {: #جی‌اس } آرایه‌ای از رشته‌ها اختیاری. فهرست فایل‌های جاوا اسکریپتی که باید به صفحات منطبق تزریق شوند. این فایل‌ها به ترتیبی که در این آرایه ظاهر می‌شوند، تزریق می‌شوند.
match_about_blank {: #match_about_blank } بولی اختیاری. اینکه آیا اسکریپت باید در یک قاب about:blank تزریق شود که قاب والد یا بازکننده با یکی از الگوهای اعلام شده در matches مطابقت داشته باشد. پیش‌فرض روی false است.

حذف تطابق‌ها و جانشین‌ها

تطبیق صفحه مشخص شده با درج فیلدهای زیر در ثبت مانیفست قابل تنظیم است.

نام نوع توضیحات
exclude_matches {: #حذف_مطابقت‌ها } آرایه‌ای از رشته‌ها اختیاری. صفحاتی را که این اسکریپت محتوا در غیر این صورت به آنها تزریق می‌شد، شامل نمی‌شود. برای جزئیات بیشتر در مورد نحو این رشته‌ها، به الگوهای تطبیق مراجعه کنید.
include_globs {: #شامل_گلوب‌ها } آرایه‌ای از رشته‌ها اختیاری. بعد از matches اعمال می‌شود تا فقط URLهایی را که با این glob مطابقت دارند، شامل شود. هدف از این کار شبیه‌سازی کلمه کلیدی @include Greasemonkey است.
exclude_globs {: #حذف_گلوب‌ها } آرایه‌ای از رشته اختیاری. پس از matches اعمال می‌شود تا URLهایی که با این glob مطابقت دارند، حذف شوند. هدف از این کار شبیه‌سازی کلمه کلیدی @exclude Greasemonkey است.

اسکریپت محتوا در صورتی به صفحه تزریق می‌شود که URL آن با هر الگوی matches و هر الگوی include_globs مطابقت داشته باشد، البته تا زمانی که URL با الگوی exclude_matches یا exclude_globs نیز مطابقت نداشته باشد.

از آنجا که ویژگی matches الزامی است، exclude_matches ، include_globs و exclude_globs فقط می‌توانند برای محدود کردن صفحاتی که تحت تأثیر قرار می‌گیرند، استفاده شوند.

افزونه‌ی زیر اسکریپت محتوا را به http://www.nytimes.com/health تزریق می‌کند اما به http://www.nytimes.com/business تزریق نمی‌کند.

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.nytimes.com/*"],
      "exclude_matches": ["*://*/*business*"],
      "js": ["contentScript.js"]
    }
  ],
  ...
}

ویژگی‌های جانشین (glob) از سینتکس (syntax) متفاوت و انعطاف‌پذیرتری نسبت به الگوهای تطبیق (match patterns ) پیروی می‌کنند. رشته‌های جانشین قابل قبول، URLهایی هستند که ممکن است حاوی ستاره‌های "wildcard" و علامت سوال باشند. ستاره * با هر رشته‌ای با هر طولی، از جمله رشته خالی، مطابقت دارد، در حالی که علامت سوال ? با هر کاراکتر واحدی مطابقت دارد.

برای مثال، عبارت glob `http://???.example.com/foo/` با هر یک از موارد زیر مطابقت دارد:

  • http://www.example.com/foo/bar
  • http:// .example.com/foo /

با این حال، با موارد زیر مطابقت ندارد :

  • http:// من .example.com/foo/bar
  • http:// مثال .com/foo/
  • http://www.example.com/foo

این افزونه اسکریپت محتوا را به http://www.nytimes.com/ arts /index.html و http://www.nytimes.com/ jobs /index.html تزریق می‌کند، اما به http://www.nytimes.com/ sports /index.html تزریق نمی‌کند.

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.nytimes.com/*"],
      "include_globs": ["*nytimes.com/???s/*"],
      "js": ["contentScript.js"]
    }
  ],
  ...
}

این افزونه اسکریپت محتوا را به http:// history .nytimes.com و http://.nytimes.com/ history تزریق می‌کند، اما نه به http:// science .nytimes.com یا http://www.nytimes.com/ science .

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.nytimes.com/*"],
      "exclude_globs": ["*science*"],
      "js": ["contentScript.js"]
    }
  ],
  ...
}

برای دستیابی به محدوده صحیح، می‌توان یکی، همه یا برخی از این موارد را در نظر گرفت.

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.nytimes.com/*"],
      "exclude_matches": ["*://*/*business*"],
      "include_globs": ["*nytimes.com/???s/*"],
      "exclude_globs": ["*science*"],
      "js": ["contentScript.js"]
    }
  ],
  ...
}

زمان اجرا

وقتی فایل‌های جاوا اسکریپت به صفحه وب تزریق می‌شوند، توسط فیلد run_at کنترل می‌شوند. فیلد ترجیحی و پیش‌فرض "document_idle" است، اما در صورت نیاز می‌توان آن را به صورت "document_start" یا "document_end" نیز مشخص کرد.

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.nytimes.com/*"],
      "run_at": "document_idle",
      "js": ["contentScript.js"]
    }
  ],
  ...
}
نام نوع توضیحات
document_idle {: #سند_بیکار } رشته ترجیح داده می‌شود. هر زمان که ممکن است از "document_idle" استفاده کنید.

مرورگر زمانی را برای تزریق اسکریپت‌ها بین "document_end" و بلافاصله پس از اجرای رویداد windowonload انتخاب می‌کند. لحظه دقیق تزریق به پیچیدگی سند و مدت زمان بارگذاری آن بستگی دارد و برای سرعت بارگذاری صفحه بهینه شده است.

اسکریپت‌های محتوایی که در "document_idle" اجرا می‌شوند، نیازی به گوش دادن به رویداد window.onload ندارند، آنها تضمین می‌کنند که پس از تکمیل DOM اجرا شوند. اگر یک اسکریپت قطعاً نیاز به اجرا پس از window.onload داشته باشد، افزونه می‌تواند با استفاده از ویژگی document.readyState بررسی کند که آیا onload قبلاً اجرا شده است یا خیر.
document_start {: #شروع_سند } رشته اسکریپت‌ها بعد از هر فایلی از css تزریق می‌شوند، اما قبل از اینکه هر DOM دیگری ساخته شود یا هر اسکریپت دیگری اجرا شود.
document_end {: #پایان_سند } رشته اسکریپت‌ها بلافاصله پس از تکمیل DOM تزریق می‌شوند، اما قبل از اینکه منابع فرعی مانند تصاویر و فریم‌ها بارگذاری شوند.

فریم‌ها را مشخص کنید

فیلد "all_frames" به افزونه اجازه می‌دهد تا مشخص کند که آیا فایل‌های جاوا اسکریپت و CSS باید به تمام فریم‌هایی که با الزامات URL مشخص شده مطابقت دارند تزریق شوند یا فقط به بالاترین فریم در یک تب.

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.nytimes.com/*"],
      "all_frames": true,
      "js": ["contentScript.js"]
    }
  ],
  ...
}
نام نوع توضیحات
all_frames {: #همه_فریم‌ها } بولی اختیاری. پیش‌فرض false است، به این معنی که فقط فریم بالایی تطبیق داده می‌شود.

اگر مقدار آن true باشد، به تمام فریم‌ها تزریق می‌شود، حتی اگر فریم، بالاترین فریم در تب نباشد. هر فریم به طور مستقل برای الزامات URL بررسی می‌شود و اگر الزامات URL رعایت نشده باشد، به فریم‌های فرزند تزریق نمی‌شود.

ارتباط با صفحه جاسازی

اگرچه محیط‌های اجرای اسکریپت‌های محتوا و صفحاتی که میزبان آنها هستند از یکدیگر جدا هستند، اما به DOM صفحه دسترسی مشترک دارند. اگر صفحه بخواهد با اسکریپت محتوا یا با افزونه از طریق اسکریپت محتوا ارتباط برقرار کند، باید این کار را از طریق DOM مشترک انجام دهد.

یک مثال را می‌توان با استفاده از window.postMessage انجام داد:

var port = chrome.runtime.connect();

window.addEventListener("message", function(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);
document.getElementById("theButton").addEventListener("click",
    function() {
  window.postMessage({ type: "FROM_PAGE", text: "Hello from the webpage!" }, "*");
}, false);

صفحه غیر افزونه، example.html، پیام‌هایی را برای خودش ارسال می‌کند. این پیام توسط اسکریپت محتوا رهگیری و بررسی شده و سپس به فرآیند افزونه ارسال می‌شود. به این ترتیب، صفحه یک خط ارتباطی با فرآیند افزونه ایجاد می‌کند. عکس این عمل نیز از طریق روش‌های مشابه امکان‌پذیر است.

ایمن بمانید

اگرچه جهان‌های ایزوله لایه‌ای از محافظت ایجاد می‌کنند، اما استفاده از اسکریپت‌های محتوا می‌تواند باعث ایجاد آسیب‌پذیری در افزونه و صفحه وب شود. اگر اسکریپت محتوا از یک وب‌سایت جداگانه محتوا دریافت می‌کند، مانند ایجاد یک درخواست XMLHttpRequest ، قبل از تزریق آن، مراقب باشید که حملات اسکریپت‌نویسی بین‌سایتی محتوا را فیلتر کنید. برای جلوگیری از حملات "مرد میانی" فقط از طریق HTTPS ارتباط برقرار کنید.

حتماً صفحات وب مخرب را فیلتر کنید. به عنوان مثال، الگوهای زیر خطرناک هستند:

var data = document.getElementById("json-data")
// WARNING! Might be evaluating an evil script!
var parsed = eval("(" + data + ")")
var elmt_id = ...
// WARNING! elmt_id might be "); ... evil script ... //"!
window.setTimeout("animate(" + elmt_id + ")", 200);

در عوض، API های امن‌تری را ترجیح دهید که اسکریپت اجرا نمی‌کنند:

var data = document.getElementById("json-data")
// JSON.parse does not evaluate the attacker's scripts.
var parsed = JSON.parse(data);
var elmt_id = ...
// The closure form of setTimeout does not evaluate scripts.
window.setTimeout(function() {
  animate(elmt_id);
}, 200);