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

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

ทำความเข้าใจความสามารถของสคริปต์เนื้อหา

สคริปต์เนื้อหาจะเข้าถึงไฟล์ส่วนขยายได้หลังจากประกาศเป็นทรัพยากรที่เข้าถึงได้บนเว็บ โดยลูกค้าสามารถเข้าถึง API ส่วนขยายต่อไปนี้ได้โดยตรง

สคริปต์เนื้อหาไม่สามารถเข้าถึง API อื่นๆ โดยตรง แต่สามารถเข้าถึงส่วนขยายทางอ้อมโดยการแลกเปลี่ยนข้อความกับส่วนอื่นๆ ของส่วนขยายของคุณ

ทำงานในโลกที่โดดเดี่ยว

สคริปต์เนื้อหาอาศัยอยู่ในโลกที่แยกตัวออกมา ทำให้สคริปต์เนื้อหาสามารถเปลี่ยนแปลงสภาพแวดล้อม 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 สำหรับสคริปต์ที่ควรเรียกใช้โดยอัตโนมัติในชุดหน้าเว็บที่รู้จักกันดี

สคริปต์ที่ประกาศแบบคงที่จะได้รับการบันทึกในไฟล์ 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 อาร์เรย์ของสตริง จำเป็น ระบุหน้าที่จะแทรกสคริปต์เนื้อหานี้ ดูรายละเอียดเกี่ยวกับไวยากรณ์ของสตริงเหล่านี้และข้อมูลเกี่ยวกับวิธียกเว้น URL ได้ที่รูปแบบการจับคู่
css อาร์เรย์ของสตริง ไม่บังคับ รายการไฟล์ CSS ที่จะแทรกในหน้าที่ตรงกัน ระบบจะแทรกแท็กเหล่านี้ตามลำดับที่ปรากฏในอาร์เรย์นี้ ก่อนที่จะมีการสร้างหรือแสดง DOM สำหรับหน้าเว็บ
js อาร์เรย์ของสตริง ไม่บังคับ รายการไฟล์ JavaScript ที่จะแทรกในหน้าที่ตรงกัน ระบบจะแทรกไฟล์ตามลำดับที่ปรากฏในอาร์เรย์นี้ แต่ละสตริงในรายการนี้ต้องมีเส้นทางที่เกี่ยวข้องไปยังทรัพยากรในไดเรกทอรีรูทของส่วนขยาย ระบบจะตัดเครื่องหมายทับ ("/") ออกโดยอัตโนมัติ
run_at RunAt ไม่บังคับ ระบุเวลาที่ควรแทรกสคริปต์ลงในหน้าเว็บ ค่าเริ่มต้นคือ document_idle
match_about_blank boolean ไม่บังคับ ระบุว่าสคริปต์ควรแทรกลงในเฟรม about:blank ที่เฟรมระดับบนสุดหรือเฟรมเปิดตรงกับรูปแบบที่ประกาศใน matches หรือไม่ ค่าเริ่มต้นคือ "เท็จ"
match_origin_as_fallback boolean ไม่บังคับ สคริปต์ควรแทรกในเฟรมที่สร้างโดยต้นทางที่ตรงกัน แต่ URL หรือต้นทางอาจไม่ตรงกับรูปแบบโดยตรงหรือไม่ ซึ่งรวมถึงเฟรมที่มีรูปแบบต่างกัน เช่น about:, data:, blob: และ filesystem: ดูการแทรกในเฟรมที่เกี่ยวข้องเพิ่มเติม
world ExecutionWorld ไม่บังคับ โลกของ JavaScript สำหรับสคริปต์ที่เรียกใช้ภายใน ค่าเริ่มต้นคือ ISOLATED โปรดดูเพิ่มเติมในหัวข้อทำงานในโลกที่แยกกัน

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

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

ใน Chrome 96 การประกาศแบบไดนามิกนั้นคล้ายกับการประกาศแบบคงที่ แต่ออบเจ็กต์สคริปต์เนื้อหามีการลงทะเบียนกับ 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"));

แทรกแบบเป็นโปรแกรม

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

หากต้องการแทรกสคริปต์เนื้อหาแบบเป็นโปรแกรม ส่วนขยายของคุณต้องมีสิทธิ์โฮสต์สำหรับหน้าเว็บที่พยายามแทรกสคริปต์ คุณจะให้สิทธิ์โฮสต์ได้โดยการขอสิทธิ์เป็นส่วนหนึ่งของไฟล์ Manifest ของส่วนขยาย หรือใช้ "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 ที่ตรงกับ glob นี้ด้วย กระบวนการนี้มีไว้เพื่อจำลองคีย์เวิร์ด Greasemonkey @include
exclude_globs อาร์เรย์ของสตริง ไม่บังคับ ใช้หลังวันที่ matches เพื่อยกเว้น URL ที่ตรงกับ glob นี้ มีจุดประสงค์เพื่อจำลองคีย์เวิร์ด Greasemonkey @exclude

ระบบจะแทรกสคริปต์เนื้อหาลงในหน้าเว็บหากเงื่อนไข 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 ที่ยอมรับได้คือ URL ที่อาจมีเครื่องหมายดอกจันและเครื่องหมายคำถามเป็น "ไวลด์การ์ด" เครื่องหมายดอกจัน (*) จะจับคู่สตริงที่มีความยาวเท่าใดก็ได้รวมถึงสตริงว่าง ส่วนเครื่องหมายคำถาม (?) จะจับคู่อักขระเดี่ยว

เช่น glob 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"]
    }
  ],
  ...
}

โดยคุณสามารถรวมสิ่งเหล่านี้ไว้ 1 รายการ ทั้งหมด หรือบางส่วนเพื่อให้บรรลุขอบเขตที่ถูกต้องได้

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 string แนะนำ ใช้ "document_idle" ทุกครั้งที่ทำได้

เบราว์เซอร์จะเลือกเวลาที่จะแทรกสคริปต์ระหว่าง "document_end" และทันทีที่เหตุการณ์ window.onload เริ่มทำงาน ช่วงเวลาที่แน่นอนของการแทรกจะขึ้นอยู่กับความซับซ้อนของเอกสารและระยะเวลาที่ใช้ในการโหลด และได้รับการเพิ่มประสิทธิภาพเพื่อความเร็วในการโหลดหน้าเว็บ

สคริปต์เนื้อหาที่ทำงานที่ "document_idle" ไม่จำเป็นต้องรับเหตุการณ์ window.onload เนื่องจากรับประกันได้ว่าจะทำงานหลังจากที่ DOM ทำงานเสร็จสมบูรณ์ หากสคริปต์จำเป็นต้องทำงานหลังจาก window.onload ส่วนขยายจะตรวจสอบว่า onload เริ่มทำงานแล้วหรือไม่โดยใช้พร็อพเพอร์ตี้ document.readyState
document_start string ระบบจะแทรกสคริปต์หลังไฟล์จาก css แต่ก่อนที่จะมีการสร้าง DOM อื่นหรือเรียกใช้สคริปต์อื่น
document_end string ระบบจะแทรกสคริปต์ทันทีหลังจาก DOM เสร็จสมบูรณ์ แต่ก่อนที่ทรัพยากรย่อย เช่น รูปภาพและเฟรมจะโหลด

ระบุเฟรม

ช่อง "all_frames" ช่วยให้ส่วนขยายระบุได้ว่าควรแทรกไฟล์ JavaScript และ CSS ลงในเฟรมทั้งหมดที่ตรงกับข้อกำหนดของ URL ที่ระบุ หรือลงในเฟรมระดับบนสุดในแท็บเท่านั้น

manifest.json

{
  "name": "My extension",
  ...
  "content_scripts": [
    {
      "matches": ["https://*.nytimes.com/*"],
      "all_frames": true,
      "js": ["contentScript.js"]
    }
  ],
  ...
}

service-worker.js

chrome.scripting.registerContentScripts([{
  id: "test",
  matches : [ "https://*.nytimes.com/*" ],
  allFrames : true,
  js : [ "contentScript.js" ],
}]);
ชื่อ ประเภท คำอธิบาย
all_frames boolean ไม่บังคับ ค่าเริ่มต้นคือ false ซึ่งหมายความว่าระบบจะจับคู่เฉพาะเฟรมด้านบนเท่านั้น

หากระบุ true ระบบจะแทรกเฟรมทั้งหมดเข้าไป แม้ว่าเฟรมจะไม่ใช่เฟรมบนสุดในแท็บก็ตาม แต่ละเฟรมจะได้รับการตรวจสอบแยกกันสำหรับข้อกำหนด URL และจะไม่แทรกลงในเฟรมย่อยหาก URL ไม่เป็นไปตามข้อกำหนด

ส่วนขยายอาจต้องการเรียกใช้สคริปต์ในเฟรมที่เกี่ยวข้องกับเฟรมที่ตรงกัน แต่ไม่ตรงกัน สถานการณ์ทั่วไปในกรณีนี้คือเฟรมที่มี 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 ได้

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() โปรดใช้ความระมัดระวังในการกรองเนื้อหาเพื่อป้องกันการโจมตีแบบ 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);