העברת ההודעה

מכיוון שסקריפטים של תוכן פועלים בהקשר של דף אינטרנט ולא בהקשר של התוסף, לעיתים קרובות נדרשים דרך התקשורת עם שאר התוסף. לדוגמה, תוסף של קורא RSS עשוי להשתמש סקריפטים של תוכן כדי לזהות את נוכחות של פיד RSS בדף, ולאחר מכן להודיע לדף הרקע ב כדי להציג סמל פעולה בדף עבור הדף הזה.

התקשורת בין תוספים לבין סקריפטים של התוכן שלהם פועלת באמצעות העברת הודעות. אחד משני הערכים הצד השני יכול להאזין להודעות שנשלחו מהצד השני ולהשיב באותו ערוץ. הודעה יכולה להכיל אובייקט JSON חוקי (null, בוליאני, מספר, מחרוזת, מערך או אובייקט). יש מודל פשוט ממשק API לבקשות חד-פעמיות וממשק API מורכב יותר שמאפשר לנהל לטווח ארוך להחלפת הודעות מרובות עם הקשר משותף. אפשר גם לשלוח הודעה לתוסף אחר, אם המזהה שלו ידוע לך, שמפורטת בתוספים השונים הודעות.

בקשות חד-פעמיות פשוטות

אם אתם צריכים לשלוח הודעה אחת רק לחלק אחר של התוסף (ואפשר לקבל גם תגובה חזרה), עליך להשתמש בשיטה הפשוטה של runtime.sendMessage או tabs.sendMessage . כך תהיה לך אפשרות לשלוח הודעה חד-פעמית עם יכולת JSON לבצע סריאליזציה מסקריפט תוכן לתוסף , או להיפך להיפך, בהתאמה . פרמטר אופציונלי של קריאה חוזרת מאפשר לטפל בתגובה מהצד השני בצד, אם יש כזה.

שליחת בקשה מסקריפט של תוכן נראית כך:

chrome.runtime.sendMessage({greeting: "hello"}, function(response) {
  console.log(response.farewell);
});

שליחת בקשה מהתוסף לסקריפט תוכן נראית דומה מאוד, מלבד העובדה שאתה צריך לציין לאיזו כרטיסייה הוא יישלח. בדוגמה הזו מוצגת שליחת הודעה לסקריפט התוכן. בכרטיסייה שנבחרה.

chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
  chrome.tabs.sendMessage(tabs[0].id, {greeting: "hello"}, function(response) {
    console.log(response.farewell);
  });
});

בצד המקבל, עליך להגדיר האזנה לאירוע runtime.onMessage שתטפל ב הודעה. זה נראה אותו דבר מסקריפט תוכן או מדף תוסף.

chrome.runtime.onMessage.addListener(
  function(request, sender, sendResponse) {
    console.log(sender.tab ?
                "from a content script:" + sender.tab.url :
                "from the extension");
    if (request.greeting == "hello")
      sendResponse({farewell: "goodbye"});
  }
);

בדוגמה שלמעלה, הפקודה sendResponse נקראה באופן סינכרוני. אם רוצים להשתמש באופן אסינכרוני sendResponse, מוסיפים את return true; לגורם המטפל באירועים של onMessage.

הערה: אם מספר דפים מאזינים לאירועי onMessage, רק הראשון שיקרא ל-sendResponse() לאירוע מסוים יצליח לשלוח את התגובה. המערכת תתעלם מכל שאר התגובות לאירוע הזה.
הערה: הקריאה החוזרת של sendResponse תקפה רק אם משתמשים בה באופן סינכרוני, או אם ה-handler של האירוע מחזיר true כדי לציין שהוא יגיב באופן אסינכרוני. הקריאה החוזרת (callback) של הפונקציה sendMessage תופעל באופן אוטומטי אם אין handlers שמחזירים ערך True או אם הקריאה החוזרת (callback) של sendResponse נאספים באשפה.

קשרים לטווח ארוך

לפעמים כדאי לנהל שיחה שנמשכת יותר מבקשה אחת ותגובה אחת. במקרה כזה, תוכלו לפתוח ערוץ לטווח ארוך מסקריפט התוכן אל דף של תוסף , או להפך, באמצעות runtime.connect או tabs.connect, בהתאמה . הערוץ יכול להם שם, באופן אופציונלי, שמאפשר להבחין בין סוגים שונים של חיבורים.

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

כשיוצרים חיבור, כל קצה מקבל אובייקט runtime.Port המשמש עבור שליחה וקבלה של הודעות דרך החיבור הזה.

כך פותחים ערוץ מסקריפט תוכן, שולחים הודעות ומאזינים להן:

var port = chrome.runtime.connect({name: "knockknock"});
port.postMessage({joke: "Knock knock"});
port.onMessage.addListener(function(msg) {
  if (msg.question == "Who's there?")
    port.postMessage({answer: "Madame"});
  else if (msg.question == "Madame who?")
    port.postMessage({answer: "Madame... Bovary"});
});

שליחת בקשה מהתוסף לסקריפט תוכן נראית דומה מאוד, מלבד העובדה שאתה צריך לציין לאיזו כרטיסייה רוצים להתחבר. צריך רק להחליף את הקריאה להתחבר בדוגמה שלמעלה tabs.connect.

כדי לטפל בחיבורים נכנסים, צריך להגדיר אירוע runtime.onConnect. Listener. זה נראה אותו דבר מסקריפט תוכן או מדף תוסף. כאשר חלק אחר של שהתוסף קורא "connect() ", האירוע הזה מופעל יחד עם האובייקט runtime.Port שניתן. שישמשו לשליחה ולקבלה של הודעות דרך החיבור. ככה נראה התגובה חיבורים נכנסים:

chrome.runtime.onConnect.addListener(function(port) {
  console.assert(port.name == "knockknock");
  port.onMessage.addListener(function(msg) {
    if (msg.joke == "Knock knock")
      port.postMessage({question: "Who's there?"});
    else if (msg.answer == "Madame")
      port.postMessage({question: "Madame who?"});
    else if (msg.answer == "Madame... Bovary")
      port.postMessage({question: "I don't get it."});
  });
});

משך החיים של השקע

יציאות תוכננו כשיטת תקשורת דו-כיוונית בין חלקים שונים של התוסף, מסגרת (ברמה העליונה) נתפסת כחלק הקטן ביותר. לאחר קריאה ל-tabs.connect, runtime.connect או runtime.connectNative, יש יציאה. נוצר. ניתן להשתמש ביציאה הזו באופן מיידי לשליחת הודעות לצד השני דרך postMessage

אם בכרטיסייה יש מספר פריימים, קריאה ל-tabs.connect תוביל למספר הפעלות של האירוע runtime.onConnect (פעם אחת לכל פריים בכרטיסייה). באופן דומה, אם נעשה שימוש ב-runtime.connect, אז האירוע onConnect עשוי להיות מופעל מספר פעמים (פעם אחת בכל ).

ייתכן שתרצו לברר מתי חיבור נסגר, לדוגמה, אם אתם מנהלים עבור כל יציאה פתוחה. כדי לעשות את זה, אפשר להאזין לאירוע runtime.Port.onDisconnect. הזה האירוע מופעל כשאין יציאות תקינות בצד השני של הערוץ. הפעולה הזו מתרחשת במצבים הבאים:

  • אין מאזינים ל-runtime.onConnect בצד השני.
  • הוסרו הנתונים שנטענו בכרטיסייה שמכילה את היציאה (לדוגמה, אם מתבצע ניווט בכרטיסייה).
  • הטעינה של המסגרת שממנה בוצעה קריאה אל connect הוסרה.
  • הטעינה של כל הפריימים שקיבלו את היציאה (דרך runtime.onConnect) הוסרה.
  • runtime.Port.disconnect נקרא על ידי הקצה השני. הערה: אם התקבלה שיחה מ-connect בכמה יציאות בצד של המקבל, ואז מתבצעת קריאה ל-disconnect() בכל אחת מהיציאות האלה, ואז האירוע onDisconnect מופעל רק ביציאה של השולח, ולא ביציאות האחרות.

העברת הודעות בין תוספים

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

ההאזנה לבקשות ולחיבורים נכנסים דומה להאזנה הפנימית, אבל צריך להשתמש שיטות runtime.onMessageExternal או runtime.onConnectExternal כאן דוגמה כל:

// For simple requests:
chrome.runtime.onMessageExternal.addListener(
  function(request, sender, sendResponse) {
    if (sender.id == blocklistedExtension)
      return;  // don't allow this extension access
    else if (request.getTargetData)
      sendResponse({targetData: targetData});
    else if (request.activateLasers) {
      var success = activateLasers();
      sendResponse({activateLasers: success});
    }
  });

// For long-lived connections:
chrome.runtime.onConnectExternal.addListener(function(port) {
  port.onMessage.addListener(function(msg) {
    // See other examples for sample onMessage handlers.
  });
});

בדומה לשליחת הודעה לתוסף אחר, שליחת הודעה אל תוסף אחר דומה לשליחת הודעה מתוך התוסף. ההבדל היחיד הוא שעליך להעביר את המזהה של התוסף שאיתו ברצונך לתקשר. לדוגמה:

// The ID of the extension we want to talk to.
var laserExtensionId = "abcdefghijklmnoabcdefhijklmnoabc";

// Make a simple request:
chrome.runtime.sendMessage(laserExtensionId, {getTargetData: true},
  function(response) {
    if (targetInRange(response.targetData))
      chrome.runtime.sendMessage(laserExtensionId, {activateLasers: true});
  }
);

// Start a long-running conversation:
var port = chrome.runtime.connect(laserExtensionId);
port.postMessage(...);

שליחת הודעות מדפי אינטרנט

בדומה להודעות בין תוספים, האפליקציה או התוסף יכולים לקבל ולהגיב מדפי אינטרנט רגילים. כדי להשתמש בתכונה הזו, קודם צריך לציין בקובץman.json.json אתרי האינטרנט שאיתם אתם רוצים לתקשר. לדוגמה:

"externally_connectable": {
  "matches": ["*://*.example.com/*"]
}

הפעולה הזו תחשוף את ה-API להעברת הודעות לכל דף שתואם לתבניות ה-URL שציינתם. כתובת ה-URL חייב להכיל לפחות דומיין ברמה השנייה – כלומר, דפוסים של שמות מארחים כמו "*", ' *.com', ' *.co.uk' ו-' *.appspot.com' אסורות. מדף האינטרנט, משתמשים ממשקי API של runtime.sendMessage או runtime.connect לשליחת הודעה לאפליקציה ספציפית, לתוסף. לדוגמה:

// The ID of the extension we want to talk to.
var editorExtensionId = "abcdefghijklmnoabcdefhijklmnoabc";

// Make a simple request:
chrome.runtime.sendMessage(editorExtensionId, {openUrlInEditor: url},
  function(response) {
    if (!response.success)
      handleError(url);
  });

מהאפליקציה או מהתוסף, אפשר להאזין להודעות מדפי אינטרנט באמצעות ממשקי API של runtime.onMessageExternal או runtime.onConnectExternal, בדומה לממשקי API של תוספים שונים העברת הודעות. רק דף האינטרנט יכול ליזום חיבור. לדוגמה:

chrome.runtime.onMessageExternal.addListener(
  function(request, sender, sendResponse) {
    if (sender.url == blocklistedWebsite)
      return;  // don't allow this web page access
    if (request.openUrlInEditor)
      openUrl(request.openUrlInEditor);
  });

העברת הודעות במכשיר מקורי

תוספים ואפליקציות יכולים להחליף הודעות עם אפליקציות מקוריות שרשומות מארח של העברת הודעות מקומית. למידע נוסף על התכונה הזו, ראו העברת הודעות מותאמות.

שיקולי אבטחה

סקריפטים של תוכן פחות מהימנים

סקריפטים של תוכן פחות מהימנים מדף הרקע של התוסף (למשל, אתר זדוני עלול לסכן את תהליך כלי הרינדור, שבו פועלים הסקריפטים של התוכן). מניחים ייתכן שההודעות מסקריפט תוכן נוצרו על ידי תוקף, ועליך לוודא ש ניקוי כל הקלט. נניח שהנתונים שנשלחים לסקריפט התוכן עשויים להדליף לדף האינטרנט. צריך להגביל את היקף הפעולות ההרשאות שיכולות להיות מופעלות על ידי הודעות שמתקבלות מתוכן סקריפטים.

פרצת אבטחה XSS (cross-site scripting)

כשמקבלים הודעה מסקריפט תוכן או מתוסף אחר, יש להפעיל שיקול דעת לגבי הסקריפטים שלא ייפלו קורבן לכתיבת סקריפטים בין אתרים. עצה זו רלוונטית לסקריפטים שרצים בתוך לדף הרקע של התוסף וגם לסקריפטים של תוכן שפועלים במקורות אחרים באינטרנט. באופן ספציפי, אין להשתמש בממשקי API מסוכנים כמו בדוגמאות הבאות:

chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
  // WARNING! Might be evaluating an evil script!
  var resp = eval("(" + response.farewell + ")");
});
chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
  // WARNING! Might be injecting a malicious script!
  document.getElementById("resp").innerHTML = response.farewell;
});

במקום זאת, עדיף להשתמש בממשקי API בטוחים יותר שלא מריצים סקריפטים:

chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
  // JSON.parse does not evaluate the attacker's scripts.
  var resp = JSON.parse(response.farewell);
});
chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) {
  // innerText does not let the attacker inject HTML elements.
  document.getElementById("resp").innerText = response.farewell;
});

דוגמאות

דוגמאות פשוטות לתקשורת דרך הודעות זמינות בכתובת examples/api/messaging. הדוגמה של העברת הודעות מותאמות מדגימה איך אפליקציית Chrome יכולה לתקשר עם של אפליקציית נייטיב. לדוגמאות נוספות ולעזרה בהצגת קוד המקור, ראו דוגמאות.