Standortbestimmung verwenden

Wenn Sie Informationen zur Standortbestimmung in Ihrer Chrome-Erweiterung benötigen, verwenden Sie dieselbe navigator.geolocation Web Platform API wie jede andere Website. Dieser Artikel ist vorhanden, weil Berechtigungen für den Zugriff auf sensible Daten in Chrome-Erweiterungen anders gehandhabt werden als Websites. Die Standortbestimmung ist sehr sensibel. Daher stellen Browser sicher, dass die Nutzer vollständig wissen und die Kontrolle darüber haben, wann und wo ihr genauer Standort geteilt wird.

Standortbestimmung in MV3-Erweiterungen

Im Web schützen Browser die zur Standortbestimmung, indem es eine Aufforderung anzeigt, diesem Ursprung Zugriff auf seinen Standort zu gewähren. Nicht immer ist dasselbe Berechtigungsmodell für Erweiterungen geeignet.

<ph type="x-smartling-placeholder">
</ph> Screenshot der Berechtigungsaufforderung, die angezeigt wird, wenn eine Website Zugriff auf die API für die Standortbestimmung anfordert
Aufforderung zur Berechtigung zur Standortbestimmung

Berechtigungen sind nicht der einzige Unterschied. Wie oben erwähnt, ist navigator.geolocation eine DOM API, d. h. ein Teil der APIs, aus denen Websites bestehen. Daher ist der Zugriff innerhalb von Worker-Kontexten nicht möglich, wie z. B. der Extension Service Worker, der das Backbone von Manifest V3-Erweiterungen ist. Sie können geolocation aber trotzdem verwenden. Es gibt nur Nuancen darin, wie und wo man es nutzt.

Standortbestimmung in Service Workern

In den Service Workern befindet sich kein navigator-Objekt. Sie ist nur in Kontexten verfügbar, die Zugriff auf das document-Objekt einer Seite haben. Um innerhalb eines Service Workers Zugriff zu erhalten, verwenden Sie einen Offscreen Document. Dieser bietet Zugriff auf eine HTML-Datei, die Sie mit Ihrer Erweiterung bündeln können.

Füge zuerst "offscreen" zum Abschnitt "permissions" deines Manifests hinzu.

manifest.json:

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

Nachdem Sie die Berechtigung "offscreen" hinzugefügt haben, fügen Sie der Erweiterung eine HTML-Datei mit dem nicht sichtbaren Dokument hinzu. Bei diesem Fall wird kein Seiteninhalt verwendet, die Datei kann also fast leer sein. Es muss sich nur um eine kleine HTML-Datei handeln, die in Ihr Skript geladen wird.

offscreen.html:

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

Speichern Sie diese Datei im Stammverzeichnis Ihres Projekts als offscreen.html.

Wie bereits erwähnt, benötigen Sie ein Skript namens offscreen.js. Sie müssen dies außerdem mit Ihrer Erweiterung bündeln. Es wird die Quelle der Standortinformationen des Service Workers sein. Sie können Nachrichten zwischen diesem und Ihrem Service Worker austauschen.

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

Jetzt können Sie auf das Offscreen-Dokument im Service Worker zugreifen.

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

Hinweis: Wenn Sie auf ein nicht sichtbares Dokument zugreifen, müssen Sie ein reason einfügen. Da der Grund für die geolocation ursprünglich nicht verfügbar war, geben Sie als Fallback DOM_SCRAPING an und erklären Sie im Abschnitt justification, was der Code eigentlich tut. Diese Informationen werden bei der Überprüfung des Chrome Web Store verwendet, um sicherzustellen, dass nicht sichtbare Dokumente für einen legitimen Zweck verwendet werden.

Sobald Sie einen Verweis auf das nicht sichtbare Dokument haben, können Sie ihm eine Nachricht senden, um Sie um aktuelle Standortdaten zu bitten.

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

Wenn Sie also den Standort von Ihrem Service Worker abrufen möchten, müssen Sie nur noch Folgendes aufrufen:

const location = await getGeolocation()

Standortbestimmung in einem Pop-up oder einer Seitenleiste verwenden

Die Verwendung der Standortbestimmung in Pop-ups oder Seitenleisten ist ganz einfach. Pop-ups und Seitenleisten sind nur Webdokumente und haben daher Zugriff auf die normalen DOM-APIs. Sie können direkt auf navigator.geolocation zugreifen. Der einzige Unterschied zu Standardwebsites besteht darin, dass du das Feld manifest.json "permission" verwenden musst, um die Berechtigung "geolocation" anzufordern. Wenn du die Berechtigung nicht erteilst, haben Sie weiterhin Zugriff auf navigator.geolocation. Jeder Versuch, sie zu verwenden, führt jedoch zu einem sofortigen Fehler, genauso wie bei einer Ablehnung der Anfrage durch den Nutzer. Sie können dies im Pop-up-Beispiel sehen.

Standortbestimmung in einem Content-Skript

Genau wie ein Pop-up hat ein Inhaltsskript vollständigen Zugriff auf die DOM API. Nutzer durchlaufen jedoch den normalen Ablauf für Nutzerberechtigungen. Das bedeutet, dass Sie durch Hinzufügen von "geolocation" zu Ihrem "permissions" nicht automatisch Zugriff auf die zur Standortbestimmung. Ein Beispiel hierfür ist das Content-Script-Beispiel.