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, sehingga browser memastikan bahwa pengguna sepenuhnya mengetahui dan mengontrol kapan dan di mana lokasi persis mereka dibagikan.

Menggunakan geolokasi di ekstensi MV3

Di web, {i>browser<i} melindungi data pengguna data geolokasi dengan menampilkan perintah yang meminta mereka untuk memberikan akses asal tertentu ke lokasi mereka. Model izin yang sama tidak selalu sesuai untuk ekstensi.

Screenshot dialog izin yang Anda lihat saat situs meminta akses ke API geolokasi
Dialog izin geolokasi

Izin bukanlah satu-satunya perbedaan. Seperti yang disebutkan di atas, navigator.geolocation adalah DOM API, yaitu sesuatu yang merupakan bagian dari API yang membentuk situs. Akibatnya, ekstensi tersebut tidak dapat diakses dalam konteks pekerja, seperti pekerja layanan ekstensi yang merupakan tulang punggung ekstensi manifes v3. Namun, Anda tetap dapat menggunakan geolocation. Hanya ada perbedaan kecil mengenai cara dan di mana 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 balik layar. Kasus ini tidak menggunakan konten halaman apa pun, sehingga file ini nyaris kosong. Kode tersebut 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 telah disebutkan, Anda memerlukan skrip yang disebut offscreen.js. Anda juga harus memaketkan ini dengan ekstensi Anda. Elemen ini akan menjadi sumber informasi geolokasi pekerja layanan. Anda dapat meneruskan pesan antara layanan dan pekerja layanan.

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 demikian, Anda kini 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',
});

Perlu diketahui bahwa saat mengakses dokumen di balik 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 oleh proses peninjauan Chrome Web Store untuk memastikan dokumen di balik layar digunakan untuk tujuan yang valid.

Setelah Anda memiliki referensi ke Dokumen di Luar Layar, Anda dapat mengirimkan pesan ke dokumen tersebut 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 sekarang, kapan pun Anda 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 dan karenanya memiliki akses ke DOM API biasa. Anda dapat mengakses navigator.geolocation secara langsung. Satu-satunya perbedaan dengan situs standar adalah Anda harus menggunakan kolom manifest.json "permission" untuk meminta izin "geolocation". Jika izin tidak disertakan, Anda akan tetap memiliki akses ke navigator.geolocation. Namun, setiap upaya untuk menggunakannya akan langsung menyebabkan error, sama seperti jika pengguna menolak permintaan. 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 yang normal. Itu berarti menambahkan "geolocation" ke "permissions" tidak akan otomatis memberi Anda akses ke informasi geolokasi. Anda dapat melihatnya di contoh skrip konten.