कॉन्टेंट स्क्रिप्ट

कॉन्टेंट स्क्रिप्ट ऐसी फ़ाइलें होती हैं जो वेब पेजों के कॉन्टेक्स्ट में चलती हैं. स्टैंडर्ड डॉक्यूमेंट ऑब्जेक्ट मॉडल (DOM) का इस्तेमाल करके, ये एक्सटेंशन उन वेब पेजों की जानकारी पढ़ सकते हैं जिन पर ब्राउज़र जाता है. साथ ही, उनमें बदलाव कर सकते हैं और अपने पैरंट एक्सटेंशन को जानकारी भेज सकते हैं.

कॉन्टेंट स्क्रिप्ट की सुविधाओं के बारे में जानकारी

कॉन्टेंट स्क्रिप्ट, अपने पैरंट एक्सटेंशन के इस्तेमाल किए गए Chrome API को ऐक्सेस कर सकती हैं. इसके लिए, उन्हें एक्सटेंशन के साथ मैसेज शेयर करने होंगे. वे chrome.runtime.getURL() की मदद से, एक्सटेंशन की फ़ाइल के यूआरएल को भी ऐक्सेस कर सकते हैं. साथ ही, नतीजे का इस्तेमाल अन्य यूआरएल की तरह ही कर सकते हैं.

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

इसके अलावा, कॉन्टेंट स्क्रिप्ट सीधे तौर पर इन Chrome API को ऐक्सेस कर सकती है:

कॉन्टेंट स्क्रिप्ट, अन्य एपीआई को सीधे तौर पर ऐक्सेस नहीं कर सकतीं.

आइसोलेटेड दुनिया में काम करना

कॉन्टेंट स्क्रिप्ट, अलग-अलग एनवायरमेंट में काम करती हैं. इससे कॉन्टेंट स्क्रिप्ट, अपने JavaScript एनवायरमेंट में बदलाव कर सकती है. इससे पेज या अन्य कॉन्टेंट स्क्रिप्ट के साथ कोई टकराव नहीं होता.

एक्सटेंशन, किसी वेब पेज पर चल सकता है. इसके लिए, वेब पेज में नीचे दिए गए उदाहरण जैसा कोड होना चाहिए.

<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 की अनुमति दें. इससे, चालू साइट के होस्ट को सुरक्षित ऐक्सेस मिलता है. साथ ही, टैब के लिए अनुमति का अस्थायी ऐक्सेस मिलता है. इससे कॉन्टेंट स्क्रिप्ट, चालू टैब पर चल पाती है. इसके लिए, क्रॉस-ऑरिजिन अनुमतियां तय करने की ज़रूरत नहीं होती.

{
  "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" फ़ील्ड के तहत रजिस्टर की जाती हैं. इनमें JavaScript फ़ाइलें, सीएसएस फ़ाइलें या दोनों शामिल हो सकती हैं. अपने-आप चलने वाली सभी कॉन्टेंट स्क्रिप्ट में, मिलते-जुलते पैटर्न के बारे में बताना ज़रूरी है.

{
 "name": "My extension",
 ...
 "content_scripts": [
   {
     "matches": ["http://*.nytimes.com/*"],
     "css": ["myStyles.css"],
     "js": ["contentScript.js"]
   }
 ],
 ...
}
नाम टाइप ब्यौरा
matches {: #matches } स्ट्रिंग का कलेक्शन ज़रूरी है. इससे यह तय किया जाता है कि इस कॉन्टेंट स्क्रिप्ट को किन पेजों में इंजेक्ट किया जाएगा. इन स्ट्रिंग के सिंटैक्स के बारे में ज़्यादा जानकारी के लिए, मैच पैटर्न देखें. साथ ही, यूआरएल को बाहर रखने के तरीके के बारे में जानने के लिए, मैच पैटर्न और ग्लोब देखें.
css {: #css } स्ट्रिंग का कलेक्शन ज़रूरी नहीं. यह उन सीएसएस फ़ाइलों की सूची है जिन्हें मिलते-जुलते पेजों में इंजेक्ट किया जाना है. इन्हें इस ऐरे में दिखने के क्रम में डाला जाता है. ऐसा तब किया जाता है, जब पेज के लिए कोई डीओएम बनाया या दिखाया नहीं जाता है.
js {: #js } स्ट्रिंग का कलेक्शन ज़रूरी नहीं. यह उन JavaScript फ़ाइलों की सूची होती है जिन्हें मिलते-जुलते पेजों में इंजेक्ट किया जाना है. इन्हें इस ऐरे में दिए गए क्रम में इंजेक्ट किया जाता है.
match_about_blank {: #match_about_blank } बूलियन ज़रूरी नहीं. क्या स्क्रिप्ट को about:blank फ़्रेम में इंजेक्ट किया जाना चाहिए, जहां पैरंट या ओपनर फ़्रेम, matches में बताए गए किसी एक पैटर्न से मेल खाता हो. यह डिफ़ॉल्ट रूप से false पर सेट होता है.

मैच और ग्लोब को शामिल न करें

पेज मैचिंग की सुविधा को पसंद के मुताबिक बनाया जा सकता है. इसके लिए, मेनिफ़ेस्ट रजिस्ट्रेशन में ये फ़ील्ड शामिल करें.

नाम टाइप ब्यौरा
exclude_matches {: #exclude_matches } स्ट्रिंग का कलेक्शन ज़रूरी नहीं. यह उन पेजों को बाहर रखता है जिनमें यह कॉन्टेंट स्क्रिप्ट डाली जाती है. इन स्ट्रिंग के सिंटैक्स के बारे में ज़्यादा जानने के लिए, मैच पैटर्न देखें.
include_globs {: #include_globs } स्ट्रिंग का कलेक्शन ज़रूरी नहीं. matches के बाद लागू किया गया, ताकि सिर्फ़ उन यूआरएल को शामिल किया जा सके जो इस ग्लोब से भी मेल खाते हैं. इसका मकसद, Greasemonkey के @include कीवर्ड की तरह काम करना है.
exclude_globs {: #exclude_globs } array of string ज़रूरी नहीं. इस ग्लोब से मेल खाने वाले यूआरएल को बाहर रखने के लिए, matches के बाद लागू किया जाता है. इसका मकसद, @excludeGreasemonkey कीवर्ड की नकल करना है.

अगर किसी पेज का यूआरएल, matches और include_globs पैटर्न से मैच करता है, तो उस पेज में कॉन्टेंट स्क्रिप्ट इंजेक्ट की जाएगी. हालांकि, ऐसा तब ही होगा, जब यूआरएल, 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"]
    }
  ],
  ...
}

ग्लोब प्रॉपर्टी, मैच पैटर्न की तुलना में अलग और ज़्यादा सुविधाजनक सिंटैक्स का इस्तेमाल करती हैं. स्वीकार की जाने वाली ग्लोब स्ट्रिंग, ऐसे यूआरएल होते हैं जिनमें "वाइल्डकार्ड" तारे के निशान और सवाल के निशान हो सकते हैं. तारांकन * किसी भी लंबाई की स्ट्रिंग से मेल खाता है. इसमें खाली स्ट्रिंग भी शामिल है. वहीं, सवाल का निशान ? किसी भी एक वर्ण से मेल खाता है.

उदाहरण के लिए, ग्लोब http:// ??? .example.com/foo/ * इनमें से किसी भी यूआरएल से मेल खाता है:

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

हालांकि, यह इन बातों से मेल नहीं खाता:

  • http:// my .example.com/foo/bar
  • http:// example .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 फ़ील्ड से यह कंट्रोल किया जाता है कि JavaScript फ़ाइलों को वेब पेज में कब इंजेक्ट किया जाए. "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_idle" का इस्तेमाल करें.

ब्राउज़र, "document_end" और windowonload इवेंट के तुरंत बाद स्क्रिप्ट इंजेक्ट करने का समय चुनता है. कॉन्टेंट स्क्रिप्ट को इंजेक्ट करने का सही समय इस बात पर निर्भर करता है कि दस्तावेज़ कितना जटिल है और उसे लोड होने में कितना समय लग रहा है. साथ ही, इसे पेज लोड होने की स्पीड के लिए ऑप्टिमाइज़ किया जाता है.

"document_idle" पर चलने वाली कॉन्टेंट स्क्रिप्ट को window.onload इवेंट के लिए सुनने की ज़रूरत नहीं होती. ये DOM के पूरा होने के बाद ही चलती हैं. अगर किसी स्क्रिप्ट को window.onload के बाद चलाना ज़रूरी है, तो एक्सटेंशन यह देख सकता है कि onload पहले ही ट्रिगर हो चुका है या नहीं. इसके लिए, उसे document.readyState प्रॉपर्टी का इस्तेमाल करना होगा.
document_start {: #document_start } स्ट्रिंग स्क्रिप्ट, css की किसी भी फ़ाइल के बाद इंजेक्ट की जाती हैं. हालांकि, किसी अन्य डीओएम के बनने या किसी अन्य स्क्रिप्ट के चलने से पहले ऐसा किया जाता है.
document_end {: #document_end } स्ट्रिंग स्क्रिप्ट, DOM के पूरा होने के तुरंत बाद इंजेक्ट की जाती हैं. हालांकि, ऐसा इमेज और फ़्रेम जैसे सब-रिसोर्स लोड होने से पहले किया जाता है.

फ़्रेम तय करना

"all_frames" फ़ील्ड की मदद से एक्सटेंशन यह तय कर सकता है कि JavaScript और सीएसएस फ़ाइलों को, यूआरएल की ज़रूरी शर्तों को पूरा करने वाले सभी फ़्रेम में इंजेक्ट किया जाना चाहिए या सिर्फ़ टैब में सबसे ऊपर मौजूद फ़्रेम में.

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["http://*.nytimes.com/*"],
      "all_frames": true,
      "js": ["contentScript.js"]
    }
  ],
  ...
}
नाम टाइप ब्यौरा
all_frames {: #all_frames } बूलियन ज़रूरी नहीं. डिफ़ॉल्ट रूप से false पर सेट होता है. इसका मतलब है कि सिर्फ़ सबसे ऊपर वाले फ़्रेम को मैच किया जाता है.

अगर true के तौर पर सेट किया जाता है, तो यह सभी फ़्रेम में इंजेक्ट हो जाएगा. भले ही, फ़्रेम टैब में सबसे ऊपर वाला फ़्रेम न हो. यूआरएल से जुड़ी ज़रूरी शर्तों के लिए, हर फ़्रेम की अलग से जांच की जाती है. अगर यूआरएल से जुड़ी ज़रूरी शर्तें पूरी नहीं होती हैं, तो यह चाइल्ड फ़्रेम में नहीं डाला जाएगा.

एम्बेड करने वाले पेज से कम्यूनिकेशन

कॉन्टेंट स्क्रिप्ट और उन्हें होस्ट करने वाले पेजों के एक्ज़ीक्यूशन एनवायरमेंट एक-दूसरे से अलग होते हैं. हालांकि, वे पेज के डीओएम का ऐक्सेस शेयर करते हैं. अगर पेज को कॉन्टेंट स्क्रिप्ट या कॉन्टेंट स्क्रिप्ट के ज़रिए एक्सटेंशन से कम्यूनिकेट करना है, तो उसे शेयर किए गए 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 करना, तो कॉन्टेंट को इंजेक्ट करने से पहले, क्रॉस-साइट स्क्रिप्टिंग हमलों से बचाने के लिए, उसे फ़िल्टर करना न भूलें. सिर्फ़ एचटीटीपीएस पर कम्यूनिकेट करें, ताकि "man-in-the-middle" हमलों से बचा जा सके.

पक्का करें कि आपने नुकसान पहुंचाने वाले वेब पेजों को फ़िल्टर किया हो. उदाहरण के लिए, ये पैटर्न खतरनाक हैं:

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);

इसके बजाय, ऐसे सुरक्षित एपीआई का इस्तेमाल करें जो स्क्रिप्ट नहीं चलाते:

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);