סקריפטים של תוכן הם קבצים שרצים בהקשר של דפי אינטרנט. באמצעות Document Object Model (DOM) הסטנדרטי, הם יכולים לקרוא פרטים של דפי האינטרנט שהדפדפן מבקר בהם, לבצע בהם שינויים ולהעביר מידע לתוסף ההורה שלהם.
הסבר על היכולות של סקריפטים של תוכן
סקריפטים של תוכן יכולים לגשת לממשקי ה-API של Chrome שבהם משתמש תוסף ההורה שלהם על ידי החלפת הודעות עם התוסף. הם יכולים גם לגשת לכתובת ה-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;
בנוסף, ל-content script יש גישה ישירה לממשקי ה-API הבאים של Chrome:
סקריפטים של תוכן לא יכולים לגשת ישירות לממשקי 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);
אם הלחצן יהיה לחוץ, יוצגו שתי ההתראות.
עולמות מבודדים לא מאפשרים לסקריפטים של תוכן, לתוסף ולדף האינטרנט לגשת למשתנים או לפונקציות שנוצרו על ידי האחרים. כך גם סקריפטים של תוכן יכולים להפעיל פונקציונליות שלא צריכה להיות נגישה לדף האינטרנט.
הזרקת סקריפטים
אפשר להחדיר סקריפטים של תוכן באופן פרוגרמטי או באופן מצהיר.
החדרה באופן פרוגרמטי
אפשר להשתמש בהחדרה פרוגרמטית לסקריפטים של תוכן שצריכים לפעול באירועים ספציפיים.
כדי להחדיר סקריפט תוכן פרוגרמטי, צריך לציין את ההרשאה activeTab במניפסט. מעניקה גישה מאובטחת למארח של האתר הפעיל וגישה זמנית לכרטיסיות הרשאה שמאפשרת לסקריפט התוכן לרוץ בכרטיסייה הפעילה הנוכחית מבלי לציין הרשאות בין מקורות.
{
"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'
});
}
});
החדרה באופן הצהרתי
שימוש בהחדרה הצהרתית לסקריפטים של תוכן שאמורים לרוץ באופן אוטומטי בדפים מסוימים.
סקריפטים שהוזרקו באופן דקלרטיבי רשומים במניפסט בשדה "content_scripts"
.
הם יכולים לכלול קובצי JavaScript, קובצי CSS או גם וגם. כל הסקריפטים של התוכן המופעלים באופן אוטומטי חייבים לציין
תבניות התאמה.
{
"name": "My extension",
...
"content_scripts": [
{
"matches": ["http://*.nytimes.com/*"],
"css": ["myStyles.css"],
"js": ["contentScript.js"]
}
],
...
}
שם | סוג | תיאור |
---|---|---|
matches {: #matches } |
חובה. מציין לאילו דפים סקריפט התוכן הזה יוחדר. מידע נוסף על התחביר של מחרוזות אלה זמין בקטע תבניות התאמה. מידע על החרגה של כתובות URL זמין במאמר תבניות התאמה וכדורי הארץ. | |
css {: #css } |
אופציונלי. רשימת קובצי ה-CSS שרוצים להחדיר לדפים התואמים. ה-DOM הזה מוחדר לפי הסדר שבו הן מופיעות במערך, לפני בניית ה-DOM או הצגתו עבור הדף. | |
js {: #js } |
אופציונלי. רשימת קובצי ה-JavaScript שיוזנו בדפים התואמים. הם מוחדרים לפי הסדר שבו הם מופיעים במערך הזה. | |
match_about_blank {: #match_about_blank } |
בוליאני | אופציונלי. האם הסקריפט צריך להכניס לשימוש במסגרת about:blank שבה מסגרת ההורה או המסגרת הפותח תואמת לאחד מהדפוסים שצוינו ב-matches . ברירת המחדל היא false . |
החרגה של התאמות וכדורי הארץ
כדי להתאים אישית את ההתאמה של דפים ספציפיים, צריך לכלול את השדות הבאים ברישום המניפסט.
שם | סוג | תיאור |
---|---|---|
exclude_matches {: #exclude_matches } |
אופציונלי. מחריגה דפים שסקריפט התוכן הזה היה מוחדר אליהם אחרת. ניתן למצוא פרטים נוספים על התחביר של מחרוזות אלה בקטע תבניות התאמה. | |
include_globs {: #include_globs } |
אופציונלי. המערכת החילה את ההגדרה אחרי matches כך שתכלול רק את כתובות ה-URL שתואמות גם לאזור הזה (glob). מיועדת לחקות את מילת המפתח @include ב-Greasemonkey. |
|
exclude_globs {: #exclude_globs } |
אופציונלי. המדיניות חלה אחרי matches כדי להחריג כתובות URL שתואמות ל-glob הזה. מיועדת לחקות את מילת המפתח @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 מקובל מחרוזות הן כתובות URL שעשויות להכיל תו כללי לחיפוש כוכביות וסימני שאלה. הכוכבית * תואמת לכל מחרוזת בכל אורך, כולל מחרוזת ריקה, בעוד שסימן השאלה ? תואם לכל תו יחיד.
לדוגמה, ה-glob http:// ... .example.com/foo/ * תואם לאחת מהאפשרויות הבאות:
- http:// www .example.com/foo /bar
- http:// the .example.com/foo /
עם זאת, הוא לא תואם את הקריטריונים הבאים:
- http:// my .example.com/foo/bar
- http:// example .com/foo/
- http://www.example.com/foo
תוסף זה יחדיר את סקריפט התוכן אל http:/www.nytimes.com/ Arts /index.html http://www.nytimes.com/ jobs /index.html אבל לא ב-http://www.nytimes.com/ Sports /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"]
}
],
...
}
זמן ריצה
כשקובצי JavaScript מוחדרים לדף האינטרנט, נשלטת על ידי השדה run_at
.
מראש ושדה ברירת המחדל הוא "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 צריכים להיות.
מוכנס לכל המסגרות שתואמות לדרישות כתובת האתר שצוינו או רק למסגרת העליונה
.
{
"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, חשוב לסנן את ההתקפות של סקריפטים חוצי-אתרים לפני ההזרקה. צריך לתקשר רק דרך 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);