สคริปต์เนื้อหาเป็นไฟล์ที่ทำงานในบริบทของหน้าเว็บ การใช้ Document Object Model (DOM) มาตรฐานช่วยให้ส่วนขยายอ่านรายละเอียดของหน้าเว็บที่เบราว์เซอร์เข้าชม ทำการเปลี่ยนแปลง และส่งข้อมูลไปยังส่วนขยายหลักได้
ทำความเข้าใจความสามารถของสคริปต์เนื้อหา
สคริปต์เนื้อหาเข้าถึง Chrome API ที่ใช้โดยส่วนขยายหลักได้โดยแลกเปลี่ยนข้อความกับส่วนขยาย นอกจากนี้ ยังเข้าถึง URL ของไฟล์ส่วนขยายได้ด้วย chrome.runtime.getURL()
และใช้ผลลัพธ์ได้เช่นเดียวกับ URL อื่นๆ
// Code for displaying EXTENSION_DIR/images/myimage.png:
var imgURL = chrome.runtime.getURL("images/myimage.png");
document.getElementById("someImage").src = imgURL;
นอกจากนี้ สคริปต์เนื้อหายังเข้าถึง Chrome API ต่อไปนี้ได้โดยตรง
สคริปต์เนื้อหาไม่สามารถเข้าถึง 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);
การแจ้งเตือนทั้ง 2 รายการจะปรากฏขึ้นหากมีการกดปุ่ม
โลกที่โดดเดี่ยวไม่อนุญาตให้มีสคริปต์เนื้อหา ส่วนขยาย และหน้าเว็บในการเข้าถึง ตัวแปรหรือฟังก์ชันที่ผู้อื่นสร้างขึ้น นอกจากนี้ ยังช่วยให้สคริปต์เนื้อหาเปิดใช้ฟังก์ชันการทำงานที่หน้าเว็บไม่ควรเข้าถึงได้
แทรกสคริปต์
คุณสามารถแทรกสคริปต์เนื้อหาแบบเป็นโปรแกรมหรือแทรกแบบประกาศก็ได้
แทรกแบบเป็นโปรแกรม
ใช้การแทรกแบบเป็นโปรแกรมสำหรับสคริปต์เนื้อหาที่ต้องทํางานในบางโอกาส
หากต้องการแทรกสคริปต์เนื้อหาแบบเป็นโปรแกรม ให้ระบุสิทธิ์ activeTab ในไฟล์ Manifest ซึ่งจะมอบสิทธิ์เข้าถึงที่ปลอดภัยในโฮสต์ของเว็บไซต์ที่ใช้งานอยู่และสิทธิ์เข้าถึงแท็บชั่วคราว ซึ่งช่วยให้สคริปต์เนื้อหาทำงานในแท็บที่ใช้งานอยู่ในปัจจุบันได้โดยไม่ต้องระบุสิทธิ์ข้ามแหล่งที่มา
{
"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'
});
}
});
แทรกแบบประกาศ
ใช้การแทรกแบบประกาศสำหรับสคริปต์เนื้อหาที่ควรเรียกใช้โดยอัตโนมัติในหน้าที่ระบุ
สคริปต์ที่แทรกแบบประกาศจะลงทะเบียนในไฟล์ Manifest ในส่วนฟิลด์ "content_scripts"
โดยจะรวมไฟล์ JavaScript, ไฟล์ CSS หรือทั้ง 2 อย่างก็ได้ สคริปต์เนื้อหาที่ทำงานอัตโนมัติทั้งหมดต้องระบุรูปแบบการจับคู่
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["http://*.nytimes.com/*"],
"css": ["myStyles.css"],
"js": ["contentScript.js"]
}
],
...
}
ชื่อ | ประเภท | คำอธิบาย |
---|---|---|
matches {: #matches } |
ต้องระบุ ระบุหน้าที่จะแทรกสคริปต์เนื้อหานี้ โปรดดูที่รูปแบบการจับคู่สำหรับรายละเอียดเพิ่มเติมเกี่ยวกับไวยากรณ์ของสตริงเหล่านี้ และรูปแบบการจับคู่และ glob สำหรับข้อมูลเกี่ยวกับวิธียกเว้น URL | |
css {: #css } |
ไม่บังคับ รายการไฟล์ CSS ที่จะแทรกลงในหน้าที่ตรงกัน ข้อมูลเหล่านี้จะแทรกตามลำดับที่ปรากฏในอาร์เรย์นี้ ก่อนที่จะมีการสร้างหรือแสดง DOM สำหรับหน้าเว็บ | |
js {: #js } |
ไม่บังคับ รายการไฟล์ JavaScript ที่จะแทรกลงในหน้าที่ตรงกัน ซึ่งจะแทรกตามลำดับที่ปรากฏในอาร์เรย์ | |
match_about_blank {: #match_about_blank } |
บูลีน | ไม่บังคับ กำหนดว่าสคริปต์ควรแทรกลงในเฟรม about:blank เมื่อเฟรมหลักหรือเฟรมเปิดตรงกับรูปแบบใดรูปแบบหนึ่งที่ประกาศไว้ใน matches หรือไม่ ค่าเริ่มต้นคือ false |
ไม่รวมการแข่งขันและ glob
คุณสามารถปรับแต่งการจับคู่หน้าเว็บที่ระบุได้โดยใส่ช่องต่อไปนี้ในการลงทะเบียนไฟล์ Manifest
ชื่อ | ประเภท | คำอธิบาย |
---|---|---|
exclude_matches {: #exclude_matches } |
ไม่บังคับ ยกเว้นหน้าที่สคริปต์เนื้อหานี้จะแทรกเข้าไป ดูรายละเอียดเพิ่มเติมเกี่ยวกับไวยากรณ์ของสตริงเหล่านี้ได้ที่รูปแบบการจับคู่ | |
include_globs {: #include_globs } |
ไม่บังคับ ใช้หลังวันที่ matches เพื่อรวมเฉพาะ URL ที่ตรงกับ glob นี้ด้วย มีวัตถุประสงค์เพื่อจำลองคีย์เวิร์ด Greasemonkey @include |
|
exclude_globs {: #exclude_globs } |
ไม่บังคับ ใช้หลังจาก matches เพื่อยกเว้น URL ที่ตรงกับนิพจน์ทั่วไปนี้ มีไว้เพื่อจำลองคีย์เวิร์ด @exclude Greasemonkey |
ระบบจะแทรกสคริปต์เนื้อหาลงในหน้าเว็บหาก URL ของหน้าตรงกับรูปแบบ matches
และรูปแบบ include_globs
ใดก็ได้ ตราบใดที่ URL นั้นไม่ตรงกับรูปแบบ 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"]
}
],
...
}
พร็อพเพอร์ตี้ Glob มีไวยากรณ์ที่ยืดหยุ่นและแตกต่างจากรูปแบบการจับคู่ glob ที่ยอมรับ สตริงคือ URL ที่อาจมี "ไวลด์การ์ด" เครื่องหมายดอกจันและเครื่องหมายคำถาม เครื่องหมายดอกจัน * จะจับคู่สตริงใดก็ได้ที่มีความยาว รวมทั้งสตริงว่าง ขณะที่เครื่องหมายคำถาม ? ค่าที่ตรงกัน อักขระเดี่ยวๆ
ตัวอย่างเช่น glob http:// ??? .example.com/foo/ * ตรงกับรายการใดก็ได้ต่อไปนี้
- http:// www .example.com/foo /bar
- http:// ไฟล์ .example.com/foo /
แต่จะไม่ตรงกับเงื่อนไขต่อไปนี้
- http:// my .example.com/foo/bar
- http:// example .com/foo/
- http://www.example.com/foo
ส่วนขยายนี้จะแทรกสคริปต์เนื้อหาลงใน http:/www.nytimes.com/ art /index.html และ http://www.nytimes.com/ งาน /index.html แต่ไม่ใช่ใน http://www.nytimes.com/ กีฬา /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 แต่ก่อนที่จะมีการสร้าง DOM อื่นๆ หรือเรียกใช้สคริปต์อื่นๆ |
document_end {: #document_end } |
สตริง | ระบบจะแทรกสคริปต์ทันทีหลังจากที่ DOM เสร็จสมบูรณ์ แต่ก่อนที่ทรัพยากรย่อย เช่น รูปภาพและเฟรมจะโหลด |
ระบุเฟรม
ช่อง "all_frames"
ช่วยให้ส่วนขยายระบุได้ว่าควรแนบไฟล์ JavaScript และ CSS หรือไม่
แทรกลงในเฟรมทั้งหมดที่ตรงกับข้อกำหนดของ URL ที่ระบุ หรือเฉพาะในเฟรมระดับบนสุดใน
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["http://*.nytimes.com/*"],
"all_frames": true,
"js": ["contentScript.js"]
}
],
...
}
ชื่อ | ประเภท | คำอธิบาย |
---|---|---|
all_frames {: #all_frames } |
บูลีน | ไม่บังคับ ค่าเริ่มต้นคือ false ซึ่งหมายความว่าระบบจะจับคู่เฉพาะเฟรมบนสุดหากระบุ true ระบบจะแทรกลงในเฟรมทั้งหมด แม้ว่าเฟรมนั้นจะไม่ใช่เฟรมบนสุดในแท็บก็ตาม แต่ละเฟรมจะได้รับการตรวจสอบแยกกันสำหรับข้อกำหนดของ URL ระบบจะไม่แทรกลงในเฟรมย่อยหากไม่เป็นไปตามข้อกำหนดของ URL |
การสื่อสารกับหน้าเว็บที่ฝัง
แม้ว่าสภาพแวดล้อมการเรียกใช้สคริปต์เนื้อหาและหน้าเว็บที่โฮสต์สคริปต์จะแยกจากกัน แต่สคริปต์และหน้าเว็บจะแชร์สิทธิ์เข้าถึง DOM ของหน้าเว็บ หากหน้าเว็บต้องการสื่อสารกับ สคริปต์เนื้อหา หรือส่วนขยายผ่านสคริปต์เนื้อหา จะต้องดำเนินการผ่าน 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 โปรดระวังการกรองเนื้อหาข้ามเว็บไซต์ Scripting ก่อนแทรกไฟล์ สื่อสารผ่าน HTTPS เท่านั้นเพื่อหลีกเลี่ยงการโจมตี"คนกลาง"
อย่าลืมกรองหาหน้าเว็บที่เป็นอันตราย ตัวอย่างเช่น รูปแบบต่อไปนี้เป็นอันตราย:
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);
แต่ให้ใช้ API ที่ปลอดภัยกว่าซึ่งไม่เรียกใช้สคริปต์แทน ดังนี้
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);