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

תוספים ואפליקציות יכולים להחליף הודעות עם אפליקציות מקוריות באמצעות ממשק 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 וב-OSX, הנתיב חייב להיות מוחלט. ב-Windows, הקובץ יכול להיות יחסי לספרייה שבה נמצא קובץ המניפסט. תהליך המארח מתחיל כשהספרייה הנוכחית מוגדרת לספרייה שמכילה את הקובץ הבינארי של המארח. לדוגמה, אם הפרמטר הזה מוגדר לערך C:\Application\nm_host.exe, הוא יתחיל בספרייה הנוכחית C:\Application\.
typeסוג הממשק שמשמש לתקשורת עם המארח המקומי של העברת ההודעות. כרגע יש רק ערך אחד אפשרי לפרמטר הזה: stdio. המדיניות מציינת ש-Chrome צריך להשתמש ב-stdin וב-stdout כדי לתקשר עם המארח.
allowed_originsרשימת תוספים שאמורים לקבל גישה למארח העברת ההודעות המקומית. אסור להשתמש בתווים כלליים לחיפוש כמו chrome-extension://*/*.

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

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

ב-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 ביט.

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

  • OS X (כלל המערכת)
    • Google Chrome: /Library/Google/Chrome/NativeMessagingHosts/_com.my_company.my_application_.json
    • כרום: /Library/Application Support/Chromium/NativeMessagingHosts/_com.my_company.my_application_.json
  • OS X (נתיב ברירת מחדל ספציפי למשתמש)
    • Google Chrome: ~/Library/Application Support/Google/Chrome/NativeMessagingHosts/_com.my_company.my_application_.json
    • כרום: ~/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
    • כרום: /etc/chromium/native-messaging-hosts/_com.my_company.my_application_.json
  • Linux (נתיב ברירת מחדל ספציפי למשתמש)
    • Google Chrome: ~/.config/google-chrome/NativeMessagingHosts/_com.my_company.my_application_.json
    • כרום: ~/.config/chromium/NativeMessagingHosts/_com.my_company.my_application_.json

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

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

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

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

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

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

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

בדוגמה הבאה נוצר אובייקט 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);
  });

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

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

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

  • נכשלה ההפעלה של מארח העברת ההודעות המקורי.
    • צריך לבדוק אם יש לך הרשאות מספיקות להפעלת הקובץ.
  • צוין שם מארח לא חוקי להעברת הודעות באפליקציות מקוריות.
    • בדקו אם השם מכיל תווים לא חוקיים. מותר להשתמש רק בתווים אלפאנומריים, בקווים תחתונים ובנקודות. שם לא יכול להתחיל או להסתיים בנקודה, ולא יכולה להופיע נקודה נוספת אחריה.
  • בוצעה יציאה מהמארח המקורי.
    • תהליך ההגדרה של מארח העברת ההודעות המקורי נותק לפני ש-Chrome קרא את ההודעה. סביר להניח שהבקשה הזו נשלחה ממארח העברת ההודעות המקומי.
  • לא נמצא מארח העברת הודעות מקומי שצוין.
    • האם השם מאוית נכון בסיומת ובקובץ המניפסט?
    • האם המניפסט הוצב בספרייה הנכונה עם השם הנכון? הפורמטים הצפויים מפורטים במיקום של מארח העברת ההודעות המותאמת.
    • האם קובץ המניפסט בפורמט הנכון? באופן ספציפי, האם תחביר ה-JSON נכון, והאם הערכים תואמים להגדרה של מניפסט של מארח העברת הודעות מותאמת?
    • האם הקובץ שצוין ב-path קיים? ב-Windows, הנתיבים יכולים להיות יחסיים, אבל ב-OS X וב-Linux הנתיבים חייבים להיות מוחלטים.
  • שם המארח של מארח העברת ההודעות המקורי לא רשום. (Windows בלבד)
  • הגישה למארח העברת ההודעות המקומי שצוין אסורה.
    • האם מקור התוסף מופיע בallowed_origins?
  • אירעה שגיאה בתקשורת עם המארח המקומי של העברת ההודעות.
    • זוהי שגיאה נפוצה מאוד שמצביעה על הטמעה שגויה של פרוטוקול התקשורת במארח העברת ההודעות המקומי.
    • יש לוודא שכל הפלט ב-stdout תואם לפרוטוקול של העברת הודעות מותאמות. אם רוצים להדפיס נתונים מסוימים למטרות ניפוי באגים, צריך לכתוב אל stderr.
    • חשוב לוודא שאורך ההודעה של 32 ביט הוא בפורמט המספר השלם המקורי של הפלטפורמה (Little-endian / large-endian).
    • אורך ההודעה לא יכול לחרוג מ-1024*1024.
    • גודל ההודעה חייב להיות שווה למספר הבייטים בהודעה. הוא עשוי להיות שונה מה"אורך" של מחרוזת, מכיוון שתווים עשויים להיות מיוצגים על ידי מספר בייטים.
    • Windows בלבד: מוודאים שמצב הקלט/פלט של התוכנה מוגדר לערך O_BINARY. כברירת מחדל, מצב קלט/פלט (I/O) הוא O_TEXT, והוא פוגם בפורמט ההודעה כמעברי שורה (\n = 0A) בסיומות בסגנון Windows ( \r\n = 0D 0A). ניתן להגדיר את מצב קלט/פלט באמצעות __setmode.