העברת הודעות באפליקציות מקוריות

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

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

כדי לרשום מארח מקומי להעברת הודעות, האפליקציה צריכה לשמור קובץ שמגדיר את התצורה של המארח המקומי להעברת הודעות.

דוגמה לקובץ:

{
  "name": "com.my_company.my_application",
  "description": "My Application",
  "path": "C:\\Program Files\\My Application\\chrome_native_messaging_host.exe",
  "type": "stdio",
  "allowed_origins": ["chrome-extension://knldjmfmopnpolahpmmgbagdohdnhkik/"]
}

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

name
השם של מארח העברת ההודעות המקומית. הלקוחות מעבירים את המחרוזת הזו אל runtime.connectNative() או אל runtime.sendNativeMessage(). השם יכול להכיל רק תווים אלפאנומריים באותיות קטנות, קווים תחתונים ונקודות. השם לא יכול להתחיל או להסתיים בנקודה, ולא יכולות להיות שתי נקודות ברצף.
description
תיאור קצר של האפליקציה.
path
הנתיב לקובץ הבינארי של המארח להעברת הודעות נייטיב. ב-Linux וב-macOS הנתיב חייב להיות מוחלט. ב-Windows, הנתיב יכול להיות יחסי לספרייה שמכילה את קובץ המניפסט. תהליך המארח מתחיל כשספריית העבודה הנוכחית מוגדרת לספרייה שמכילה את קובץ ההפעלה של המארח. לדוגמה, אם הפרמטר הזה מוגדר כ-C:\Application\nm_host.exe, הוא יופעל עם הספרייה הנוכחית C:\Application.
type
סוג הממשק שמשמש לתקשורת עם מארח ההודעות המקומי. לפרמטר הזה יש ערך אפשרי אחד: stdio. היא מציינת ש-Chrome צריך להשתמש ב-stdin וב-stdout כדי לתקשר עם המארח.
allowed_origins
רשימת התוספים שצריכים לקבל גישה למארח של העברת הודעות באפליקציות מקומיות. הערכים של allowed-origins לא יכולים להכיל תווים כלליים לחיפוש.

מיקום של מארח להעברת הודעות נייטיב

המיקום של קובץ המניפסט תלוי בפלטפורמה.

ב-Windows, קובץ המניפסט יכול להיות ממוקם בכל מקום במערכת הקבצים. תוכנת ההתקנה של האפליקציה צריכה ליצור מפתח רישום, או HKEY_LOCAL_MACHINE\SOFTWARE\Google\Chrome\NativeMessagingHosts\com.my_company.my_application או HKEY_CURRENT_USER\SOFTWARE\Google\Chrome\NativeMessagingHosts\com.my_company.my_application, ולהגדיר את ערך ברירת המחדל של המפתח הזה לנתיב המלא לקובץ המניפסט. לדוגמה, משתמשים בפקודה הבאה:

REG ADD "HKCU\Software\Google\Chrome\NativeMessagingHosts\com.my_company.my_application" /ve /t REG_SZ /d "C:\path\to\nmh-manifest.json" /f

או באמצעות קובץ .reg הבא:

Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Google\Chrome\NativeMessagingHosts\com.my_company.my_application]
@="C:\\path\\to\\nmh-manifest.json"

כש-Chrome מחפש מארחים של העברת הודעות מקוריות, הוא קודם שולח שאילתה למרשם של 32 ביט, ואז למרשם של 64 ביט.

ב-macOS וב-Linux, המיקום של קובץ המניפסט של מארח ההודעות המקומי משתנה בהתאם לדפדפן (Google Chrome או Chromium). המערכת מחפשת את המארחים להעברת הודעות באפליקציות מקוריות במיקום קבוע, ואילו את המארחים להעברת הודעות באפליקציות מקוריות ברמת המשתמש היא מחפשת בספריית המשנה NativeMessagingHosts/ של ספריית פרופיל המשתמש.

macOS (כלל המערכת)
Google Chrome: /Library/Google/Chrome/NativeMessagingHosts/com.my_company.my_application.json
Chromium: /Library/Application Support/Chromium/NativeMessagingHosts/com.my_company.my_application.json
‫macOS (ספציפי למשתמש, נתיב ברירת המחדל)
Google Chrome: ~/Library/Application Support/Google/Chrome/NativeMessagingHosts/com.my_company.my_application.json
Chromium: ~/Library/Application Support/Chromium/NativeMessagingHosts/com.my_company.my_application.json
‫Linux (כלל המערכת)
Google Chrome: /etc/opt/chrome/native-messaging-hosts/com.my_company.my_application.json
Chromium: /etc/chromium/native-messaging-hosts/com.my_company.my_application.json
‫Linux (נתיב ברירת המחדל, ספציפי למשתמש)
Google Chrome: ~/.config/google-chrome/NativeMessagingHosts/com.my_company.my_application.json
Chromium: ~/.config/chromium/NativeMessagingHosts/com.my_company.my_application.json

פרוטוקול להעברת הודעות נייטיב

‫Chrome מפעיל כל מארח של העברת הודעות מקוריות בתהליך נפרד, ומתקשר איתו באמצעות קלט רגיל (stdin) ופלט רגיל (stdout). נעשה שימוש באותו פורמט לשליחת הודעות בשני הכיוונים. כל הודעה עוברת סריאליזציה באמצעות JSON, מוצפנת ב-UTF-8 ומקדימה אותה אורך ההודעה ב-32 ביט בסדר הבתים המקורי. הגודל המקסימלי של הודעה יחידה ממארח ההודעות המקומי הוא ‎1MB, בעיקר כדי להגן על Chrome מפני אפליקציות מקומיות שמתנהגות בצורה לא תקינה. הגודל המקסימלי של ההודעה שנשלחת למארח של העברת הודעות באפליקציות מקוריות הוא 64MiB.

הארגומנט הראשון למארח של העברת הודעות מקורית הוא המקור של המתקשר, בדרך כלל chrome-extension://[ID of allowed extension]. כך מארחים להעברת הודעות יכולים לזהות את המקור של ההודעה כשמציינים כמה תוספים במפתח allowed_origins במניפסט של מארח להעברת הודעות.

ב-Windows, מארח ההודעות הנייטיב מקבל גם ארגומנט של שורת פקודה עם נקודת אחיזה לחלון הנייטיב של Chrome שקורא לו: --parent-window=<decimal handle value>. כך מארח ההודעות הנייטיב יכול ליצור חלונות של ממשק משתמש נייטיב עם היררכיית אב-צאצא נכונה. הערה: הערך הזה יהיה 0 אם ההקשר של הקריאה הוא service worker.

כשיוצרים יציאת העברת הודעות באמצעות runtime.connectNative(),‏ Chrome מתחיל תהליך של מארח להעברת הודעות מקומיות וממשיך להפעיל אותו עד שהיציאה נהרסת. לעומת זאת, כששולחים הודעה באמצעות runtime.sendNativeMessage(), בלי ליצור יציאת העברת הודעות, Chrome מתחיל תהליך חדש של מארח להעברת הודעות מקוריות לכל הודעה. במקרה כזה, ההודעה הראשונה שנוצרת על ידי תהליך המארח מטופלת כתגובה לבקשה המקורית, ו-Chrome מעביר אותה לקריאה החוזרת של התגובה שצוינה כשמתבצעת קריאה ל-runtime.sendNativeMessage(). המערכת מתעלמת מכל שאר ההודעות שנוצרו על ידי המארח המקומי להעברת הודעות במקרה הזה.

התחברות לאפליקציית נייטיב

שליחה וקבלה של הודעות לאפליקציה מקומית וממנה דומות מאוד לשליחת הודעות בין תוספים. ההבדל העיקרי הוא שבמקום runtime.connect() משתמשים ב-runtime.connectNative(), ובמקום runtime.sendMessage() משתמשים ב-runtime.sendNativeMessage().

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

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

בדוגמה הבאה נוצר אובייקט runtime.Port שמחובר למארח של העברת הודעות באפליקציות מקוריות com.my_company.my_application, מתחיל להאזין להודעות מהיציאה הזו ושולח הודעה יוצאת אחת:

var port = chrome.runtime.connectNative('com.my_company.my_application');
port.onMessage.addListener(function (msg) {
  console.log('Received' + msg);
});
port.onDisconnect.addListener(function () {
  console.log('Disconnected');
});
port.postMessage({text: 'Hello, my_application'});

משתמשים ב-runtime.sendNativeMessage כדי לשלוח הודעה לאפליקציה המקורית בלי ליצור יציאה, למשל:

chrome.runtime.sendNativeMessage(
  'com.my_company.my_application',
  {text: 'Hello'},
  function (response) {
    console.log('Received ' + response);
  }
);

ניפוי באגים בהעברת הודעות מקומית

כשמתרחשות כשלים מסוימים בהעברת הודעות מקוריות, הפלט נכתב ביומן השגיאות של Chrome. זה כולל מקרים שבהם המארח של העברת ההודעות באפליקציות מקוריות לא מצליח להתחיל, כותב ל-stderr או מפר את פרוטוקול התקשורת. ב-Linux וב-macOS, אפשר לגשת ליומן הזה על ידי הפעלת Chrome משורת הפקודה ומעקב אחרי הפלט שלו במסוף. ב-Windows, משתמשים ב---enable-logging כמו שמוסבר במאמר איך מפעילים רישום ביומן.

ריכזנו כאן כמה שגיאות נפוצות וטיפים לפתרון שלהן:

ההפעלה של מארח ההודעות המקומי נכשלה.

בודקים אם יש לכם הרשאות מספיקות להפעלת קובץ המארח של העברת ההודעות המקורית.

צוין שם מארח לא תקין של העברת הודעות מקורית.

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

המארח המקומי יצא.

הצינור למארח המקומי להעברת הודעות נשבר לפני שההודעה נקראה על ידי Chrome. הפעולה הזו כנראה מתבצעת מהמארח להעברת הודעות נייטיב.

המארח המקומי להעברת הודעות שצוין לא נמצא.

בדוק את הפרטים הבאים:

  • האם האיות של השם נכון בתוסף ובקובץ המניפסט?
  • האם קובץ המניפסט נמצא בספרייה הנכונה עם השם הנכון? במאמר מיקום של מארח להעברת הודעות באפליקציות מקוריות מפורטים הפורמטים הצפויים.
  • האם קובץ המניפסט בפורמט הנכון? בפרט, האם ה-JSON תקין ומעוצב היטב, והאם הערכים תואמים להגדרה של מניפסט של מארח העברת הודעות מקורי?
  • האם הקובץ שצוין ב-path קיים? ב-Windows, הנתיבים יכולים להיות יחסיים, אבל ב-macOS וב-Linux, הנתיבים חייבים להיות מוחלטים.

המארח להעברת הודעות באפליקציות מקוריות שם המארח לא רשום. (Windows בלבד)

המארח להעברת הודעות באפליקציות מקוריות לא נמצא במרשם של Windows. כדי לוודא שהמפתח נוצר באמת ושהוא בפורמט הנדרש, כפי שמתואר במיקום של מארח הודעות מקורי, משתמשים ב-regedit.

הגישה למארח המקומי להעברת הודעות שצוין אסורה.

האם המקור של התוסף מופיע ב-allowed_origins?

שגיאה בתקשורת עם המארח של העברת ההודעות המקורית.

השגיאה הזו מציינת הטמעה שגויה של פרוטוקול התקשורת במארח של העברת הודעות באפליקציות מקומיות.

  • מוודאים שכל הפלט ב-stdout תואם לפרוטוקול ההודעות המקומיות. אם רוצים להדפיס נתונים מסוימים לצורך ניפוי באגים, אפשר לכתוב אל stderr.
  • צריך לוודא שאורך ההודעה של 32 ביט הוא בפורמט המספר השלם המקורי של הפלטפורמה (little-endian / big-endian).
  • אורך ההודעה לא יכול להיות יותר מ-1,048,576 תווים.
  • גודל ההודעה צריך להיות שווה למספר הבייטים בהודעה. יכול להיות שהערך הזה יהיה שונה מהערך של 'אורך' של מחרוזת, כי יכול להיות שתווים ייוצגו על ידי כמה בייטים.
  • ב-Windows בלבד: מוודאים שמצב הקלט/פלט של התוכנית מוגדר ל-O_BINARY. כברירת מחדל, מצב הקלט/פלט הוא O_TEXT, שגורם לשיבוש בפורמט ההודעה כי מעברי שורה (\n = 0A) מוחלפים בסיומי שורה בסגנון Windows (\r\n = 0D 0A). אפשר להגדיר את מצב הקלט/פלט באמצעות __setmode.