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

หากคุณต้องการรับข้อมูลตำแหน่งทางภูมิศาสตร์ในส่วนขยาย 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 และคุณจะต้องรวมกลุ่มนี้เข้ากับส่วนขยายด้วย ซึ่งจะเป็นแหล่งที่มาของข้อมูลตำแหน่งทางภูมิศาสตร์ของ Service Worker คุณจะส่งข้อความระหว่างไฟล์ดังกล่าวและ 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)
    );
  });
}

เมื่อดำเนินการดังกล่าว คุณก็พร้อมที่จะเข้าถึงเอกสารนอกหน้าจอใน Service Worker แล้ว

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

ดังนั้น คราวนี้เมื่อใดก็ตามที่คุณต้องการค้นหาตำแหน่งทางภูมิศาสตร์จากโปรแกรมทำงานของบริการ คุณเพียงแค่ต้องเรียก:

const location = await getGeolocation()

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

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

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

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