ใช้ตำแหน่งทางภูมิศาสตร์

หากต้องการรับข้อมูลตำแหน่งทางภูมิศาสตร์ในส่วนขยาย Chrome ให้ใช้ navigator.geolocation Web Platform API เดียวกันกับที่เว็บไซต์ใดๆ จะทําตามปกติ บทความนี้มีอยู่เนื่องจากส่วนขยาย Chrome จัดการสิทธิ์ในการเข้าถึงข้อมูลที่ละเอียดอ่อนแตกต่างจากเว็บไซต์ ตำแหน่งทางภูมิศาสตร์เป็นข้อมูลที่ละเอียดอ่อนมาก ดังนั้นเบราว์เซอร์จึงมั่นใจได้ว่าผู้ใช้รับรู้อย่างสมบูรณ์และสามารถควบคุมเวลาและสถานที่ที่แน่นอนได้

ใช้ตำแหน่งทางภูมิศาสตร์ในส่วนขยาย MV3

บนเว็บ เบราว์เซอร์จะปกป้องข้อมูลตำแหน่งทางภูมิศาสตร์ของผู้ใช้โดยการแสดงข้อความที่ขอให้ผู้ใช้ให้สิทธิ์เข้าถึงตำแหน่งแก่ต้นทางที่ระบุ รูปแบบสิทธิ์เดียวกันไม่เหมาะสำหรับส่วนขยายเสมอไป

ภาพหน้าจอของข้อความแจ้งสิทธิ์ที่คุณเห็นเมื่อเว็บไซต์ขอสิทธิ์เข้าถึง API ตำแหน่งทางภูมิศาสตร์
ข้อความแจ้งเกี่ยวกับสิทธิ์เข้าถึงตำแหน่งทางภูมิศาสตร์

สิทธิ์ไม่ได้มีเพียงความแตกต่างเท่านั้น ตามที่กล่าวไว้ข้างต้น navigator.geolocation คือ DOM API ซึ่งเป็นสิ่งที่เป็นส่วนหนึ่งของ API ที่ประกอบเป็นเว็บไซต์ ด้วยเหตุนี้ จึงไม่สามารถเข้าถึงภายในบริบทของผู้ปฏิบัติงาน เช่น โปรแกรมทำงานของบริการส่วนขยายที่เป็นกระดูกสันหลังของส่วนขยายไฟล์ Manifest V3 แต่คุณจะยังคงใช้ geolocation ได้เหมือนเดิม โดยมีความแตกต่างเล็กน้อยระหว่างวิธีและตำแหน่งที่คุณใช้

ใช้ตำแหน่งทางภูมิศาสตร์ใน Service Worker

ไม่มีออบเจ็กต์ navigator ภายใน Service Worker แต่ใช้ได้เฉพาะในบริบทที่มีสิทธิ์เข้าถึงออบเจ็กต์ document ของหน้าเท่านั้น หากต้องการเข้าถึงภายใน Service Worker ให้ใช้ Offscreen Document ซึ่งให้สิทธิ์เข้าถึงไฟล์ HTML ที่คุณจัดกลุ่มกับส่วนขยายได้

หากต้องการเริ่มต้นใช้งาน ให้เพิ่ม "offscreen" ลงในส่วน "permissions" ของไฟล์ Manifest

manifest.json:

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

หลังจากเพิ่มสิทธิ์ "offscreen" แล้ว ให้เพิ่มไฟล์ HTML ที่มีเอกสารนอกหน้าจอลงในส่วนขยาย กรณีนี้ไม่ได้ใช้เนื้อหาของหน้าเว็บ จึงอาจเป็นไฟล์ที่แทบจะว่างเปล่า โดยต้องเป็นไฟล์ HTML ขนาดเล็กที่โหลดในสคริปต์เท่านั้น

offscreen.html:

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

บันทึกไฟล์นี้ในรูทของโปรเจ็กต์เป็น offscreen.html

ดังที่กล่าวไว้ คุณต้องมีสคริปต์ชื่อว่า offscreen.js นอกจากนี้ คุณจะต้องรวมกลุ่มรายการนี้กับส่วนขยายด้วย ซึ่งจะเป็นแหล่งข้อมูลตำแหน่งทางภูมิศาสตร์ของโปรแกรมทำงาน คุณส่งข้อความระหว่างเซิร์ฟเวอร์และโปรแกรมทำงานของบริการได้

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

เท่านี้คุณก็พร้อมเข้าถึงเอกสารนอกจอภาพในโปรแกรมทำงานของบริการแล้ว

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

ใช้ตำแหน่งทางภูมิศาสตร์ในป๊อปอัปหรือแผงด้านข้าง

การใช้ตำแหน่งทางภูมิศาสตร์ภายในป๊อปอัปหรือแผงด้านข้างจะตรงไปตรงมามาก ป๊อปอัปและแผงด้านข้างเป็นเพียงเอกสารบนเว็บ ดังนั้นจึงสามารถเข้าถึง DOM API ปกติได้ คุณเข้าถึง navigator.geolocation ได้โดยตรง ความแตกต่างเพียงอย่างเดียวจากเว็บไซต์มาตรฐานคือ คุณต้องใช้ฟิลด์ manifest.json "permission" เพื่อขอสิทธิ์ "geolocation" หากไม่ได้รวมสิทธิ์ดังกล่าว คุณจะยังคงเข้าถึง navigator.geolocation ได้ อย่างไรก็ตาม การพยายามใช้ลิงก์ใดก็ตามจะทำให้เกิดข้อผิดพลาดทันที เช่นเดียวกับที่ผู้ใช้ปฏิเสธคำขอ ซึ่งดูได้ในตัวอย่างป๊อปอัป

การใช้ตำแหน่งทางภูมิศาสตร์ในสคริปต์เนื้อหา

สคริปต์เนื้อหามีสิทธิ์เข้าถึง DOM API อย่างเต็มรูปแบบเช่นเดียวกับป๊อปอัป แต่ผู้ใช้จะผ่านขั้นตอนการให้สิทธิ์จากผู้ใช้ตามปกติ ซึ่งหมายความว่าการเพิ่ม "geolocation" ใน "permissions" จะไม่ทำให้คุณเข้าถึงข้อมูลตำแหน่งทางภูมิศาสตร์ของผู้ใช้ได้โดยอัตโนมัติ คุณดูสิ่งนี้ได้ในตัวอย่างสคริปต์เนื้อหา