สคริปต์เนื้อหา

Content Script คือไฟล์ที่ทำงานในบริบทของหน้าเว็บ การใช้Document Object Model (DOM) มาตรฐานทำให้ส่วนขยายสามารถอ่านรายละเอียดของหน้าเว็บที่เบราว์เซอร์เข้าชม ทำการเปลี่ยนแปลง และส่งข้อมูลไปยังส่วนขยายหลักได้

ทำความเข้าใจความสามารถของ Content Script

สคริปต์เนื้อหาสามารถเข้าถึง API ของส่วนขยายต่อไปนี้ได้โดยตรง

Content Script จะเข้าถึง API อื่นๆ โดยตรงไม่ได้ แต่จะเข้าถึงได้โดยอ้อมด้วยการแลกเปลี่ยนข้อความกับส่วนอื่นๆ ของส่วนขยาย

นอกจากนี้ คุณยังเข้าถึงไฟล์อื่นๆ ในส่วนขยายจาก Content Script ได้โดยใช้ API เช่น 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); 

การเปลี่ยนแปลงนี้จะทำให้การแจ้งเตือนทั้ง 2 รายการปรากฏตามลำดับเมื่อมีการคลิกปุ่ม

แทรกสคริปต์

ประกาศสคริปต์เนื้อหาได้แบบคงที่ แบบไดนามิก หรือแทรกโดยโปรแกรม

แทรกด้วยการประกาศแบบคงที่

ใช้การประกาศสคริปต์เนื้อหาแบบคงที่ใน manifest.json สำหรับสคริปต์ที่ควรเรียกใช้โดยอัตโนมัติในชุดหน้าเว็บที่รู้จักกันดี

สคริปต์ที่ประกาศแบบคงที่จะลงทะเบียนในไฟล์ Manifest ภายใต้คีย์ "content_scripts" ซึ่งอาจมีไฟล์ JavaScript, ไฟล์ CSS หรือทั้ง 2 อย่าง สคริปต์เนื้อหาที่ทำงานโดยอัตโนมัติทั้งหมดต้องระบุรูปแบบที่ตรงกัน

manifest.json

{  "name": "My extension",  ...  "content_scripts": [    {      "matches": ["https://*.nytimes.com/*"],      "css": ["my-styles.css"],      "js": ["content-script.js"]    }  ],  ... }  
ชื่อ ประเภท คำอธิบาย
matches อาร์เรย์ของสตริง ต้องระบุ ระบุหน้าเว็บที่จะแทรก Content Script นี้ ดูรายละเอียดเกี่ยวกับไวยากรณ์ของสตริงเหล่านี้ได้ที่รูปแบบการจับคู่ และดูข้อมูลเกี่ยวกับวิธียกเว้น URL ได้ที่รูปแบบการจับคู่และ Glob
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 ดูเพิ่มเติม ทำงานในโลกที่แยกจากกัน

แทรกด้วยการประกาศแบบไดนามิก

สคริปต์เนื้อหาแบบไดนามิกมีประโยชน์เมื่อไม่ทราบรูปแบบการจับคู่สำหรับสคริปต์เนื้อหา หรือเมื่อไม่ควรแทรกสคริปต์เนื้อหาในโฮสต์ที่รู้จักเสมอไป

การประกาศแบบไดนามิกซึ่งเปิดตัวใน Chrome 96 มีลักษณะคล้ายกับการประกาศแบบคงที่ แต่จะลงทะเบียนออบเจ็กต์ Content Script กับ Chrome โดยใช้วิธีการในเนมสเปซ chrome.scripting แทนที่จะอยู่ใน manifest.json นอกจากนี้ Scripting API ยังช่วยให้นักพัฒนาส่วนขยายทำสิ่งต่อไปนี้ได้ด้วย

การประกาศแบบไดนามิกสามารถมีไฟล์ JavaScript, ไฟล์ CSS หรือทั้ง 2 อย่างได้เช่นเดียวกับการประกาศแบบคงที่

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

แทรกโดยใช้โปรแกรม

ใช้การแทรกแบบเป็นโปรแกรมสำหรับ Content Script ที่ต้องทำงานเพื่อตอบสนองต่อเหตุการณ์หรือในโอกาสที่เฉพาะเจาะจง

หากต้องการแทรก Content Script โดยอัตโนมัติ ส่วนขยายของคุณต้องมีสิทธิ์เข้าถึงโฮสต์สำหรับ หน้าที่พยายามแทรกสคริปต์ คุณให้สิทธิ์ของโฮสต์ได้โดย ขอสิทธิ์ดังกล่าวเป็นส่วนหนึ่งของไฟล์ Manifest ของส่วนขยาย หรือใช้ "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"]   }); }); 

หรือจะแทรกและเรียกใช้เนื้อหาของฟังก์ชันเป็น Content Script ก็ได้

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()การเรียกใช้ ไม่ใช่ฟังก์ชันเดิม ด้วยเหตุนี้ เนื้อหาของฟังก์ชัน จึงต้องมีอยู่ในตัวของมันเอง การอ้างอิงถึงตัวแปรที่อยู่นอกฟังก์ชันจะทำให้ Content Script แสดง 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" ],   }); }); 

ยกเว้นการจับคู่และ Glob

หากต้องการปรับแต่งการจับคู่หน้าเว็บที่ระบุ ให้รวมช่องต่อไปนี้ในการลงทะเบียนแบบประกาศ

ชื่อ ประเภท คำอธิบาย
exclude_matches อาร์เรย์ของสตริง ไม่บังคับ ยกเว้นหน้าเว็บที่สคริปต์เนื้อหานี้จะแทรก ดูรายละเอียดไวยากรณ์ของสตริงเหล่านี้ได้ที่รูปแบบการจับคู่
include_globs อาร์เรย์ของสตริง ไม่บังคับ ใช้หลังจาก matches เพื่อรวมเฉพาะ URL ที่ตรงกับ Glob นี้ด้วย ซึ่งมีวัตถุประสงค์เพื่อเลียนแบบ@include คีย์เวิร์ด Greasemonkey
exclude_globs อาร์เรย์ของสตริง ไม่บังคับ ใช้หลังจาก matches เพื่อยกเว้น URL ที่ตรงกับ glob นี้ มีจุดประสงค์เพื่อเลียนแบบคีย์เวิร์ด @exclude Greasemonkey

ระบบจะแทรก Content Script ลงในหน้าเว็บหากทั้ง 2 ข้อต่อไปนี้เป็นจริง

  • 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" ], }]); 

พร็อพเพอร์ตี้ Glob ใช้ไวยากรณ์ที่แตกต่างกันและยืดหยุ่นกว่ารูปแบบที่ตรงกัน Glob string ที่ยอมรับคือ 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 แต่ก่อนที่จะสร้าง DOM อื่นๆ หรือเรียกใช้สคริปต์อื่นๆ
document_end สตริง ระบบจะแทรกสคริปต์ทันทีหลังจาก DOM เสร็จสมบูรณ์ แต่ก่อนที่ทรัพยากรย่อย เช่น รูปภาพและเฟรม จะโหลด

ระบุเฟรม

สำหรับสคริปต์เนื้อหาแบบประกาศที่ระบุไว้ในไฟล์ Manifest ฟิลด์ "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 จะไม่ตรงกับรูปแบบของ Content Script (และในกรณีของ about: และ data: จะไม่มีแม้แต่ URL หลักหรือต้นทางใน URL เลย ดังเช่นใน about:blank หรือ data:text/html,<html>Hello, World!</html>) อย่างไรก็ตาม เฟรมเหล่านี้ยังคงเชื่อมโยงกับเฟรมที่สร้างได้

หากต้องการแทรกในเฟรมเหล่านี้ ส่วนขยายสามารถระบุพร็อพเพอร์ตี้ "match_origin_as_fallback" ในข้อกำหนดของ Content Script ใน ไฟล์ Manifest

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" จะมีลำดับความสำคัญสูงกว่า

การสื่อสารกับหน้าเว็บที่ฝัง

แม้ว่าสภาพแวดล้อมการดำเนินการของ Content Script และหน้าเว็บที่โฮสต์ Content Script จะแยกออกจากกัน แต่ทั้ง 2 ส่วนก็แชร์สิทธิ์เข้าถึง DOM ของหน้าเว็บ หากหน้าเว็บต้องการสื่อสารกับ Content Script หรือกับส่วนขยายผ่าน Content Script หน้าเว็บจะต้องสื่อสารผ่าน 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 จะโพสต์ข้อความไปยังตัวมันเอง สคริปต์เนื้อหาจะดักจับและ ตรวจสอบข้อความนี้ แล้วโพสต์ไปยังกระบวนการของส่วนขยาย ด้วยวิธีนี้ หน้าเว็บจะ สร้างช่องทางการสื่อสารกับกระบวนการของส่วนขยาย คุณสามารถทำในทางกลับกันได้โดยใช้ วิธีการที่คล้ายกัน

เข้าถึงไฟล์ส่วนขยาย

หากต้องการเข้าถึงไฟล์ส่วนขยายจาก Content Script คุณสามารถเรียกใช้ 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/*" ]    }  ],  ... } 

นโยบายรักษาความปลอดภัยเนื้อหา

สคริปต์เนื้อหาที่ทํางานในโลกที่แยกจากกันมีนโยบายรักษาความปลอดภัยเนื้อหา (CSP) ดังนี้

script-src 'self' 'wasm-unsafe-eval' 'inline-speculation-rules' chrome-extension://abcdefghijklmopqrstuvwxyz/; object-src 'self'; 

ซึ่งคล้ายกับข้อจำกัดที่ใช้กับบริบทส่วนขยายอื่นๆ ซึ่งจะป้องกันการใช้ eval() รวมถึงการโหลดสคริปต์ภายนอก

สำหรับส่วนขยายที่ไม่ได้แพ็ก CSP จะรวม localhost ด้วย

script-src 'self' 'wasm-unsafe-eval' 'inline-speculation-rules' http://localhost:* http://127.0.0.1:* chrome-extension://abcdefghijklmopqrstuvwxyz/; object-src 'self'; 

เมื่อแทรก Content Script ลงในโลกหลัก CSP ของหน้าเว็บจะมีผล

ปลอดภัยอยู่เสมอ

แม้ว่าโลกที่แยกจากกันจะให้การปกป้องอีกชั้นหนึ่ง แต่การใช้สคริปต์เนื้อหาอาจสร้างช่องโหว่ในส่วนขยายและหน้าเว็บได้ หาก Content Script ได้รับเนื้อหาจากเว็บไซต์อื่น เช่น โดยการเรียกใช้ fetch() ให้ระมัดระวังในการกรองเนื้อหาเพื่อป้องกันการโจมตีแบบ Cross-Site Scripting ก่อนที่จะแทรกเนื้อหา สื่อสารผ่าน HTTPS เท่านั้นเพื่อหลีกเลี่ยงการโจมตีแบบ"man-in-the-middle"

อย่าลืมกรองหน้าเว็บที่เป็นอันตราย ตัวอย่างเช่น รูปแบบต่อไปนี้เป็นอันตรายและ ไม่อนุญาตใน Manifest V3

ไม่ควรทำ

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

แต่ให้ใช้ API ที่ปลอดภัยกว่าซึ่งไม่เรียกใช้สคริปต์แทน

ควรทำ

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