สคริปต์เนื้อหาคือไฟล์ที่ทำงานในบริบทของหน้าเว็บ โดยใช้ Document Object Model (DOM) มาตรฐาน สคริปต์เหล่านี้จะอ่านรายละเอียดของหน้าเว็บที่เบราว์เซอร์เข้าชม ทำ การเปลี่ยนแปลง และส่งข้อมูลไปยังส่วนขยายหลักได้
ทำความเข้าใจความสามารถของสคริปต์เนื้อหา
สคริปต์เนื้อหาสามารถเข้าถึง API ของส่วนขยายต่อไปนี้ได้โดยตรง
domi18nstorageruntime.connect()runtime.getManifest()runtime.getURL()runtime.idruntime.onConnectruntime.onMessageruntime.sendMessage()
สคริปต์เนื้อหาไม่สามารถเข้าถึง API อื่นๆ ได้โดยตรง แต่สามารถเข้าถึงได้โดยอ้อมด้วยการแลกเปลี่ยนข้อความกับส่วนอื่นๆ ของส่วนขยาย
นอกจากนี้ คุณยังเข้าถึงไฟล์อื่นๆ ในส่วนขยายจากสคริปต์เนื้อหาได้โดยใช้ 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 |
อาร์เรย์ของสตริง | จำเป็น ระบุหน้าเว็บที่จะแทรกสคริปต์เนื้อหานี้ ดูรายละเอียดเกี่ยวกับไวยากรณ์ของสตริงเหล่านี้ได้ที่รูปแบบการจับคู่และดูข้อมูลเกี่ยวกับวิธียกเว้น URL ได้ที่รูปแบบการจับคู่และ Glob |
css |
อาร์เรย์ของสตริง | ไม่บังคับ รายการไฟล์ CSS ที่จะแทรกลงในหน้าเว็บที่ตรงกัน ระบบจะแทรกไฟล์เหล่านี้ตามลำดับที่ปรากฏในอาร์เรย์นี้ ก่อนที่จะสร้างหรือแสดง DOM ใดๆ สำหรับหน้าเว็บ |
js |
|
ไม่บังคับ รายการไฟล์ JavaScript ที่จะแทรกลงในหน้าเว็บที่ตรงกัน ระบบจะแทรกไฟล์ ตามลำดับที่ปรากฏในอาร์เรย์นี้ สตริงแต่ละรายการในรายการนี้ต้องมี เส้นทางที่สัมพันธ์กับแหล่งข้อมูลในไดเรกทอรีรากของส่วนขยาย ระบบจะตัดเครื่องหมายทับ (`/`) ที่นำหน้าออกโดยอัตโนมัติ |
run_at |
RunAt | ไม่บังคับ ระบุเวลาที่ควรแทรกสคริปต์ลงในหน้าเว็บ ค่าเริ่มต้นคือ
document_idle. |
match_about_blank |
บูลีน | ไม่บังคับ ระบุว่าควรแทรกสคริปต์ลงในเฟรม about:blank
ที่เฟรมหลักหรือเฟรมที่เปิดอยู่ตรงกับรูปแบบใดรูปแบบหนึ่งที่ประกาศไว้ใน matches หรือไม่ ค่าเริ่มต้นคือ "เท็จ" |
match_origin_as_fallback |
บูลีน |
ไม่บังคับ ระบุว่าควรแทรกสคริปต์ลงในเฟรมที่สร้างขึ้นโดยต้นทางที่ตรงกัน แต่ URL หรือต้นทางอาจไม่ตรงกับรูปแบบโดยตรงหรือไม่ ซึ่งรวมถึงเฟรมที่มีรูปแบบต่างๆ เช่น
about:, data:, blob: และ
filesystem: ดูเพิ่มเติมที่
การแทรกในเฟรมที่เกี่ยวข้อง
|
world |
ExecutionWorld |
ไม่บังคับ โลก JavaScript ที่สคริปต์จะดำเนินการภายใน ค่าเริ่มต้นคือ ISOLATED ดูเพิ่มเติมที่
ทำงานในโลกที่แยกจากกัน
|
ภายในระยะหนึ่งของ ขั้นตอนของวงจรชีวิตของเอกสาร สคริปต์เนื้อหา ที่ประกาศแบบคงที่ในไฟล์ Manifest จะเป็นสคริปต์แรกที่แทรก ก่อนสคริปต์เนื้อหา ที่ลงทะเบียนด้วยวิธีอื่นๆ ระบบจะแทรกสคริปต์ตามลำดับที่ระบุไว้ในไฟล์ Manifest
แทรกด้วยการประกาศแบบไดนามิก
สคริปต์เนื้อหาแบบไดนามิกมีประโยชน์เมื่อไม่ทราบรูปแบบการจับคู่สำหรับสคริปต์เนื้อหา หรือเมื่อไม่ควรแทรกสคริปต์เนื้อหาในโฮสต์ที่รู้จักเสมอไป
การประกาศแบบไดนามิกที่เปิดตัวใน 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"ชั่วคราว
ต่อไปนี้เป็นส่วนขยายเวอร์ชันต่างๆ ที่อิงตาม 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" ],
});
});
ยกเว้นการจับคู่และ Glob
หากต้องการปรับแต่งการจับคู่หน้าเว็บที่ระบุ ให้ใส่ช่องต่อไปนี้ในการลงทะเบียนแบบประกาศ
| ชื่อ | ประเภท | คำอธิบาย |
|---|---|---|
exclude_matches |
อาร์เรย์ของสตริง | ไม่บังคับ ยกเว้นหน้าเว็บที่จะแทรกสคริปต์เนื้อหานี้ ดูรายละเอียดเกี่ยวกับไวยากรณ์ของสตริงเหล่านี้ได้ที่ รูปแบบการจับคู่ |
include_globs |
อาร์เรย์ของสตริง | ไม่บังคับ ใช้หลังจาก matches เพื่อรวมเฉพาะ URL ที่ตรงกับ Glob นี้ด้วย
มีวัตถุประสงค์เพื่อจำลองคีย์เวิร์ด @include
ของ Greasemonkey |
exclude_globs |
อาร์เรย์ของสตริง | ไม่บังคับ ใช้หลังจาก matches เพื่อยกเว้น URL ที่ตรงกับ
Glob นี้ มีวัตถุประสงค์เพื่อจำลอง@exclude
คีย์เวิร์ดของ Greasemonkey |
ระบบจะแทรกสคริปต์เนื้อหาลงในหน้าเว็บหากเงื่อนไขทั้ง 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/barhttps://the.example.com/foo/
แต่จะไม่จับคู่กับรายการต่อไปนี้
https://my.example.com/foo/barhttps://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 จะไม่ตรงกับรูปแบบของสคริปต์เนื้อหา (และในกรณีของ about: และ
data: URL จะไม่มี 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 ของเฟรมเอง โปรดทราบว่าต้นทางนี้อาจแตกต่างจาก ต้นทาง ของเฟรมเป้าหมายด้วย (เช่น URL data: มีต้นทางเป็น null)
ผู้เริ่มเฟรมคือเฟรมที่สร้างหรือนำทางเฟรมเป้าหมาย แม้ว่าโดยทั่วไปแล้วจะเป็นเฟรมหลักหรือเฟรมที่เปิดอยู่โดยตรง แต่ก็อาจไม่ใช่เช่นนั้น (เช่น กรณีที่เฟรมนำทาง iframe ภายใน iframe)
เนื่องจากเป็นการเปรียบเทียบ ต้นทาง ของเฟรมผู้เริ่ม เฟรมผู้เริ่มจึงอาจอยู่ในเส้นทางใดก็ได้จากต้นทางนั้น เพื่อให้การดำเนินการนี้ชัดเจน Chrome
กำหนดให้สคริปต์เนื้อหาใดก็ตามที่ระบุด้วย "match_origin_as_fallback"
ที่ตั้งค่าเป็น true ต้องระบุเส้นทางเป็น * ด้วย
เมื่อระบุทั้ง "match_origin_as_fallback" และ "match_about_blank" แล้ว
"match_origin_as_fallback" จะมีความสำคัญมากกว่า
การสื่อสารกับหน้าเว็บที่ฝัง
แม้ว่าสภาพแวดล้อมการดำเนินการของสคริปต์เนื้อหาและหน้าเว็บที่โฮสต์สคริปต์เนื้อหาจะแยกจากกัน แต่ทั้ง 2 อย่างก็แชร์การเข้าถึง 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() เพื่อรับ Absolute 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';
เมื่อแทรกสคริปต์เนื้อหาลงในโลกหลัก ระบบจะใช้นโยบายรักษาความปลอดภัยเนื้อหาของหน้าเว็บ
ปลอดภัยอยู่เสมอ
แม้ว่าโลกที่แยกจากกันจะให้การป้องกันในระดับหนึ่ง แต่การใช้สคริปต์เนื้อหาอาจทำให้เกิดช่องโหว่ในส่วนขยายและหน้าเว็บได้ หากสคริปต์เนื้อหารับเนื้อหาจากเว็บไซต์อื่น เช่น โดยการเรียก 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);