סקריפטים של תוכן

סקריפטים של תוכן הם קבצים שפועלים בהקשר של דפי אינטרנט. באמצעות מודל אובייקט מסמך (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;

בנוסף, סקריפט תוכן יכול לגשת ישירות לממשקי ה-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 } array of strings חובה. מציין באילו דפים יוזרק סקריפט התוכן הזה. מידע נוסף על התחביר של המחרוזות האלה זמין במאמר תבניות התאמה. במאמר תבניות התאמה ו-glob אפשר לקרוא על אופן ההחרגה של כתובות URL.
css {: #css } array of strings אופציונלי. רשימת קובצי ה-CSS שיוזרקו לדפים תואמים. התגיות האלה מוזרקות לפי הסדר שבו הן מופיעות במערך הזה, לפני שנוצר או מוצג DOM כלשהו בדף.
js {: #js } array of strings אופציונלי. רשימת קובצי JavaScript שיוזרקו לדפים תואמים. הם מוזרקים לפי הסדר שבו הם מופיעים במערך הזה.
match_about_blank {: #match_about_blank } בוליאני אופציונלי. האם הסקריפט צריך להזריק למסגרת about:blank שבה מסגרת ההורה או מסגרת הפתיחה תואמת לאחד מהדפוסים שהוצהרו ב-matches. ברירת המחדל היא false.

החרגה של התאמות ושל תבניות glob

אפשר להתאים אישית את התאמת הדף שצוין על ידי הכללת השדות הבאים ברישום בקובץ המניפסט.

שם סוג תיאור
exclude_matches {: #exclude_matches } array of strings אופציונלי. החרגה של דפים שבהם סקריפט התוכן הזה אמור להיות מוזרק. פרטים נוספים על התחביר של המחרוזות האלה מופיעים במאמר תבניות התאמה.
include_globs {: #include_globs } array of strings אופציונלי. המסנן מופעל אחרי matches וכולל רק כתובות URL שתואמות גם ל-glob הזה. התכונה הזו נועדה לחקות את מילת המפתח @include של Greasemonkey.
exclude_globs {: #exclude_globs } array of string אופציונלי. התנאי מופעל אחרי matches כדי להחריג כתובות URL שתואמות ל-glob הזה. התכונה הזו נועדה לחקות את מילת המפתח @excludeGreasemonkey.

סקריפט התוכן יוזרק לדף אם כתובת ה-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:// 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"]
    }
  ],
  ...
}

זמן ריצה

השדה 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. אם המסגרת לא עומדת בדרישות, לא מתבצעת הזרקה למסגרות צאצא.

תקשורת עם הדף שבו מתבצעת ההטמעה

למרות שסביבות ההפעלה של סקריפטים של תוכן והדפים שמארחים אותם מבודדות זו מזו, יש להן גישה משותפת ל-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 כדי למנוע התקפות "man-in-the-middle".

חשוב לסנן דפי אינטרנט זדוניים. לדוגמה, הדפוסים הבאים מסוכנים:

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