Utilizza geolocalizzazione

Se vuoi ricevere informazioni di geolocalizzazione nell'estensione di Chrome, utilizza la stessa API della piattaforma web navigator.geolocation che useresti normalmente. Questo articolo esiste perché le estensioni di Chrome gestiscono le autorizzazioni per accedere ai dati sensibili in modo diverso rispetto ai siti web. La geolocalizzazione è un dato molto sensibile, quindi i browser assicurano che gli utenti siano pienamente consapevoli e in controllo di quando e dove viene condivisa la loro posizione esatta.

Utilizza la geolocalizzazione nelle estensioni MV3

Sul web, i browser salvaguardano gli utenti i dati di geolocalizzazione mostrando un messaggio che chiede di concedere all'origine specifica l'accesso alla sua posizione. Lo stesso modello di autorizzazione non è sempre appropriato per le estensioni.

Uno screenshot della richiesta di autorizzazione visualizzata quando un sito web richiede l'accesso all'API di geolocalizzazione.
Richiesta di autorizzazione di geolocalizzazione

Le autorizzazioni non sono l'unica differenza. Come già detto, navigator.geolocation è un'API DOM, ossia qualcosa che fa parte delle API che compongono i siti web. Di conseguenza, non è accessibile all'interno dei contesti dei worker, come il service worker di estensione che è alla base delle estensioni manifest v3. Tuttavia, puoi comunque utilizzare geolocation. Ci sono solo sfumature di come e dove lo usi.

Utilizza la geolocalizzazione nei service worker

Non è presente alcun oggetto navigator all'interno dei service worker. È disponibile solo all'interno dei contesti che hanno accesso all'oggetto document di una pagina. Per ottenere l'accesso all'interno di un service worker, utilizza un'istanza Offscreen Document, che fornisce l'accesso a un file HTML che puoi raggruppare con la tua estensione.

Per iniziare, aggiungi "offscreen" alla sezione "permissions" del file manifest.

manifest.json:

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

Dopo aver aggiunto l'autorizzazione "offscreen", aggiungi all'estensione un file HTML che includa il documento fuori schermo. Questa richiesta non utilizza i contenuti della pagina, quindi il file potrebbe essere quasi vuoto. Deve essere un piccolo file HTML che viene caricato nello script.

offscreen.html:

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

Salva questo file nella directory principale del progetto con il nome offscreen.html.

Come accennato, hai bisogno di uno script chiamato offscreen.js. Dovrai anche raggrupparlo con l'estensione. Sarà la fonte di informazioni di geolocalizzazione del service worker. Puoi passare messaggi tra questo e il tuo 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)
    );
  });
}

Con questo, ora puoi accedere al documento fuori schermo nel service worker.

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

Tieni presente che quando accedi a un documento fuori schermo, devi includere un reason. Il motivo geolocation non era originariamente disponibile, quindi specifica un valore di riserva DOM_SCRAPING e spiega nella sezione justification cosa sta effettivamente facendo il codice. Queste informazioni vengono utilizzate dal processo di revisione del Chrome Web Store per garantire che i documenti fuori schermo vengano utilizzati per uno scopo valido.

Una volta ottenuto un riferimento al documento fuori schermo, puoi inviargli un messaggio per chiedergli di fornirti informazioni di geolocalizzazione aggiornate.

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

Quindi ora, ogni volta che vuoi ottenere la geolocalizzazione dal tuo service worker, devi solo chiamare:

const location = await getGeolocation()

Utilizzare la geolocalizzazione in un popup o in un riquadro laterale

L'utilizzo della geolocalizzazione all'interno di un popup o di un riquadro laterale è molto semplice. Popup e riquadri laterali sono solo documenti web e pertanto hanno accesso alle normali API DOM. Puoi accedere direttamente a navigator.geolocation. L'unica differenza rispetto ai siti web standard è che devi utilizzare il campo "permission" manifest.json per richiedere l'autorizzazione "geolocation". Se non includi l'autorizzazione, avrai comunque accesso a navigator.geolocation. Tuttavia, qualsiasi tentativo di utilizzo provocherà un errore immediato, come se l'utente rifiutasse la richiesta. Puoi verificarlo nell'esempio di popup.

Utilizzo della geolocalizzazione in uno script di contenuti

Proprio come un popup, uno script di contenuti ha accesso completo all'API DOM. ma seguiranno la normale procedura di autorizzazione dell'utente. Ciò significa che l'aggiunta di "geolocation" a "permissions" non ti consentirà di accedere automaticamente ai dati informazioni di geolocalizzazione. Puoi verificarlo nell'esempio di script dei contenuti.