שימוש במיקום גיאוגרפי

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

שימוש במיקום גיאוגרפי בתוספי MV3

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

צילום מסך של בקשת ההרשאה שמוצגת כשאתר מבקש גישה לממשק ה-API של המיקום הגיאוגרפי
בקשת ההרשאה למיקום גיאוגרפי

הרשאות הן לא ההבדל היחיד. כפי שצוין למעלה, navigator.geolocation הוא DOM API, כלומר חלק מממשקי ה-API שמהם מורכבת האתרים. כתוצאה מכך, לא ניתן לגשת אליו בהקשרים של עובדים, כמו קובץ השירות של ההרחבה שהוא עמוד השדרה של תוספי Manifest V3. עם זאת, אתה עדיין יכול להשתמש ב-geolocation. יש ניואנסים שונים לגבי האופן והמקום שבהם משתמשים בה.

שימוש במיקום גיאוגרפי ב-Service Workers

אין אובייקט navigator בתוך קובצי שירות (service worker). היא זמינה רק בהקשרים שיש להם גישה לאובייקט document של דף. כדי לקבל גישה לתוך קובץ שירות (service worker), צריך להשתמש ב-Offscreen Document, שמספק גישה לקובץ HTML שאפשר לארוז יחד עם התוסף.

כדי להתחיל, יש להוסיף את "offscreen" לקטע "permissions" של המניפסט.

manifest.json:

{
  "name": "My extension",
    ...
  "permissions": [
    ...
   "offscreen"
  ],
  ...
}

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

offscreen.html:

<!doctype html>
<title>offscreenDocument</title>
<script src="offscreen.js"></script>

שומרים את הקובץ ברמה הבסיסית (root) של הפרויקט בתור offscreen.html.

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

offscreen.js:

chrome.runtime.onMessage.addListener(handleMessages);
function handleMessages(message, sender, sendResponse) {
  // Return early if this message isn't meant for the offscreen document.
  if (message.target !== 'offscreen') {
    return;
  }

  if (message.type !== 'get-geolocation') {
    console.warn(`Unexpected message type received: '${message.type}'.`);
    return;
  }

  // You can directly respond to the message from the service worker with the
  // provided `sendResponse()` callback. But in order to be able to send an async
  // response, you need to explicitly return `true` in the onMessage handler
  // As a result, you can't use async/await here. You'd implicitly return a Promise.
  getLocation().then((loc) => sendResponse(loc));

  return true;
}

// getCurrentPosition() returns a prototype-based object, so the properties
// end up being stripped off when sent to the service worker. To get
// around this, create a deep clone.
function clone(obj) {
  const copy = {};
  // Return the value of any non true object (typeof(null) is "object") directly.
  // null will throw an error if you try to for/in it. Just return
  // the value early.
  if (obj === null || !(obj instanceof Object)) {
    return obj;
  } else {
    for (const p in obj) {
      copy[p] = clone(obj[p]);
    }
  }
  return copy;
}

async function getLocation() {
  // Use a raw Promise here so you can pass `resolve` and `reject` into the
  // callbacks for getCurrentPosition().
  return new Promise((resolve, reject) => {
    navigator.geolocation.getCurrentPosition(
      (loc) => resolve(clone(loc)),
      // in case the user doesnt have/is blocking `geolocation`
      (err) => reject(err)
    );
  });
}

עכשיו תוכלו לגשת למסמך מחוץ למסך ב-Service Worker.

chrome.offscreen.createDocument({
  url: 'offscreen.html',
  reasons: [chrome.offscreen.Reason.GEOLOCATION || chrome.offscreen.Reason.DOM_SCRAPING],
  justification: 'geolocation access',
});

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

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

service_worker.js:

const OFFSCREEN_DOCUMENT_PATH = '/offscreen.html';
let creating; // A global promise to avoid concurrency issues

chrome.runtime.onMessage.addListener(handleMessages);

async function getGeolocation() {
  await setupOffscreenDocument(OFFSCREEN_DOCUMENT_PATH);
  const geolocation = await chrome.runtime.sendMessage({
    type: 'get-geolocation',
    target: 'offscreen'
  });
  await closeOffscreenDocument();
  return geolocation;
}

async function hasDocument() {
  // Check all windows controlled by the service worker to see if one
  // of them is the offscreen document with the given path
  const offscreenUrl = chrome.runtime.getURL(OFFSCREEN_DOCUMENT_PATH);
  const matchedClients = await clients.matchAll();

  return matchedClients.some(c => c.url === offscreenUrl)
}

async function setupOffscreenDocument(path) {
  //if we do not have a document, we are already setup and can skip
  if (!(await hasDocument())) {
    // create offscreen document
    if (creating) {
      await creating;
    } else {
      creating = chrome.offscreen.createDocument({
        url: path,
        reasons: [chrome.offscreen.Reason.GEOLOCATION || chrome.offscreen.Reason.DOM_SCRAPING],
        justification: 'add justification for geolocation use here',
      });

      await creating;
      creating = null;
    }
  }
}

async function closeOffscreenDocument() {
  if (!(await hasDocument())) {
    return;
  }
  await chrome.offscreen.closeDocument();
}

עכשיו בכל פעם שתרצו לקבל את המיקום הגיאוגרפי מה-Service Worker, עליכם רק להתקשר:

const location = await getGeolocation()

שימוש במיקום גיאוגרפי בחלון קופץ או בחלונית צדדית

השימוש במיקום גיאוגרפי בתוך חלון קופץ או חלונית צדדית מאוד פשוט. חלונות קופצים וחלוניות צדדיות הם רק מסמכי אינטרנט, ולכן יש להם גישה לממשקי ה-API הרגילים של DOM. אפשר לגשת ישירות אל navigator.geolocation. ההבדל היחיד בין אתרים רגילים הוא שעליך להשתמש בשדה "permission" של manifest.json כדי לבקש את ההרשאה "geolocation". אם לא תכלול את ההרשאה, עדיין תהיה לך גישה אל navigator.geolocation. עם זאת, כל ניסיון להשתמש בו יגרום לשגיאה מיידית, בדיוק כמו במקרה שהמשתמש דחה את הבקשה. תוכלו לראות את זה בדוגמה של חלון קופץ.

שימוש במיקום גיאוגרפי בסקריפט תוכן

בדיוק כמו בחלון קופץ, לסקריפט תוכן יש גישה מלאה ל-DOM API. עם זאת, המשתמשים עוברים את התהליך הרגיל של הרשאות משתמש. פירוש הדבר שהוספת "geolocation" ל-"permissions" לא תעניק לך באופן אוטומטי גישה לפרטי המיקום הגיאוגרפי של המשתמשים. ניתן לראות זאת בדוגמה של סקריפט תוכן.