Gunakan geolokasi

Jika Anda ingin mendapatkan informasi geolokasi di ekstensi Chrome, gunakan navigator.geolocation Web platform API yang sama seperti yang biasa digunakan situs mana pun. Artikel ini ada karena ekstensi Chrome menangani izin untuk mengakses data sensitif secara berbeda dengan situs. Geolokasi adalah data yang sangat sensitif, jadi browser memastikan bahwa pengguna sepenuhnya menyadari dan mengontrol kapan dan di mana lokasi persis mereka dibagikan.

Menggunakan geolokasi di ekstensi MV3

Di web, browser mengamankan data geolokasi pengguna dengan menampilkan perintah yang meminta mereka untuk memberikan akses asal tertentu tersebut ke lokasi mereka. Model izin yang sama tidak selalu sesuai untuk ekstensi.

Screenshot permintaan izin yang Anda lihat saat situs meminta akses ke geolokasi API
Prompt izin geolokasi

Izin bukan satu-satunya perbedaan. Seperti yang disebutkan di atas, navigator.geolocation adalah DOM API, yaitu sesuatu yang merupakan bagian dari API yang membentuk situs. Akibatnya, bagian tersebut tidak dapat diakses dalam konteks pekerja, seperti pekerja layanan ekstensi yang merupakan inti dari ekstensi manifes v3. Namun, Anda tetap dapat menggunakan geolocation. Namun, hanya ada sedikit perbedaan terkait cara dan tempat Anda menggunakannya.

Menggunakan geolokasi di pekerja layanan

Tidak ada objek navigator di dalam pekerja layanan. Class ini hanya tersedia dalam konteks yang memiliki akses ke objek document halaman. Untuk mendapatkan akses di dalam pekerja layanan, gunakan Offscreen Document, yang memberikan akses ke file HTML yang dapat dipaketkan dengan ekstensi Anda.

Untuk memulai, tambahkan "offscreen" ke bagian "permissions" manifes Anda.

manifest.json:

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

Setelah menambahkan izin "offscreen", tambahkan file HTML ke ekstensi yang menyertakan dokumen di luar layar. Kasus ini tidak menggunakan konten halaman, sehingga filenya bisa jadi hampir kosong. Itu hanya perlu berupa file HTML kecil yang dimuat di skrip Anda.

offscreen.html:

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

Simpan file ini di root project Anda sebagai offscreen.html.

Seperti yang disebutkan, Anda memerlukan skrip bernama offscreen.js. Anda juga harus memaketkannya dengan ekstensi Anda. Ini akan menjadi sumber informasi geolokasi bagi pekerja layanan. Anda bisa meneruskan pesan antara penyedia tersebut dan pekerja layanan Anda.

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

Dengan melakukannya, Anda sekarang siap untuk mengakses Dokumen Offscreen di pekerja layanan.

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

Perhatikan bahwa saat mengakses dokumen di luar layar, Anda perlu menyertakan reason. Alasan geolocation awalnya tidak tersedia, jadi tentukan penggantian DOM_SCRAPING, dan jelaskan di bagian justification apa yang sebenarnya dilakukan kode tersebut. Informasi ini digunakan dalam proses peninjauan Chrome Web Store untuk memastikan dokumen di luar layar digunakan untuk tujuan yang valid.

Setelah memiliki referensi ke Dokumen Offscreen, Anda dapat mengiriminya pesan untuk memintanya memberikan informasi geolokasi yang diperbarui.

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

Jadi, setiap kali ingin mendapatkan geolokasi dari pekerja layanan, Anda hanya perlu memanggil:

const location = await getGeolocation()

Menggunakan geolokasi di pop-up atau panel samping

Menggunakan geolokasi dalam popup atau panel samping sangat mudah. Pop-up dan panel samping hanyalah dokumen web sehingga memiliki akses ke DOM API normal. Anda dapat mengakses navigator.geolocation secara langsung. Satu-satunya perbedaan dari situs standar adalah Anda harus menggunakan kolom manifest.json "permission" untuk meminta izin "geolocation". Jika Anda tidak menyertakan izin tersebut, Anda akan tetap memiliki akses ke navigator.geolocation. Akan tetapi, setiap upaya untuk menggunakannya akan menyebabkan error langsung, sama seperti jika pengguna menolak permintaan tersebut. Anda dapat melihatnya di contoh pop-up.

Menggunakan geolokasi dalam skrip konten

Sama seperti pop-up, skrip konten memiliki akses penuh ke DOM API; namun, pengguna akan melalui alur izin pengguna seperti biasa. Artinya, menambahkan "geolocation" ke "permissions" Anda tidak akan otomatis memberi Anda akses ke informasi geolokasi pengguna. Anda dapat melihatnya di contoh skrip konten.