استخدام الموقع الجغرافي

إذا أردت الحصول على معلومات الموقع الجغرافي في إضافة Chrome، استخدِم navigator.geolocation Web Platform API نفسها التي يستخدمها أي موقع إلكتروني عادةً. تتوفّر هذه المقالة لأنّ إضافات Chrome تتعامل مع إذن الوصول إلى البيانات الحسّاسة بطريقة مختلفة عن المواقع الإلكترونية. يعتبر رصد الموقع الجغرافي بيانات حسّاسة للغاية، لذلك تضمن المتصفِّحات أن يكون المستخدمون على دراية كاملة بموقعهم الجغرافي وأن يتحكموا في وقت ومكان مشاركة الموقع الجغرافي الدقيق.

استخدام ميزة رصد الموقع الجغرافي في إضافات MV3

على الويب، تحمي المتصفّحات بيانات رصد الموقع الجغرافي للمستخدمين من خلال عرض رسالة مطالبة تطلب منهم منح إذن الوصول المحدّد إلى موقعهم الجغرافي. نموذج الأذونات نفسه غير مناسب دائمًا للإضافات.

لقطة شاشة لمطالبة الإذن التي تظهر لك عندما يطلب موقع إلكتروني الوصول إلى واجهة برمجة تطبيقات رصد الموقع الجغرافي
طلب إذن رصد الموقع الجغرافي

الأذونات ليست الفرق الوحيد. كما هو موضح أعلاه، navigator.geolocation هي واجهة برمجة تطبيقات DOM، وهذا جزء من واجهات برمجة التطبيقات التي تتألف منها المواقع الإلكترونية. ونتيجةً لذلك، لا يمكن الوصول إليه في سياقات العاملين، مثل عامل خدمات الإضافات الذي يُعد الأساس للإضافات المستنِدة إلى إصدار Manifest v3. مع ذلك، لا يزال بإمكانك استخدام geolocation. هناك فقط بعض الفروق الطفيفة في طريقة استخدام هذا التطبيق ومكان استخدامه.

استخدام ميزة رصد الموقع الجغرافي لدى مشغّلي الخدمات

ما مِن عنصر navigator داخل مشغِّلي الخدمات. ولا تتوفّر هذه الميزة إلا داخل السياقات التي يمكنها الوصول إلى عنصر document للصفحة. للوصول إلى داخل أحد مشغّلي الخدمات، استخدِم Offscreen Document، الذي يوفّر إمكانية الوصول إلى ملف HTML يمكنك إرفاقه مع الإضافة.

للبدء، أضِف "offscreen" إلى قسم "permissions" في البيان.

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

والآن، عندما تريد الحصول على الموقع الجغرافي من عامل الخدمة لديك، ما عليك سوى الاتصال بـ:

const location = await getGeolocation()

استخدام رصد الموقع الجغرافي في نافذة منبثقة أو لوحة جانبية

يعد استخدام رصد الموقع الجغرافي داخل النافذة المنبثقة أو اللوحة الجانبية أمرًا بسيطًا للغاية. النوافذ المنبثقة واللوحات الجانبية ما هي إلا مستندات ويب، وبالتالي يمكنها الوصول إلى واجهات برمجة تطبيقات DOM العادية. يمكنك الوصول إلى "navigator.geolocation" مباشرةً. الفرق الوحيد عن المواقع الإلكترونية العادية هو أنّك تحتاج إلى استخدام الحقل "permission" manifest.json لطلب إذن "geolocation". في حال عدم تضمين الإذن، سيظل بإمكانك الوصول إلى navigator.geolocation. ومع ذلك، ستؤدي أي محاولة لاستخدامه إلى حدوث خطأ فوري، كما لو كان المستخدم رفض الطلب. يمكنك رؤية ذلك في عيّنة النافذة المنبثقة.

استخدام رصد الموقع الجغرافي في نص برمجي للمحتوى

تمامًا مثل النافذة المنبثقة، يحصل النص البرمجي للمحتوى على إمكانية الوصول الكامل إلى واجهة برمجة تطبيقات DOM، إلا أنّ المستخدمين سينتقلون إلى مسار أذونات المستخدم العادي. وهذا يعني أنّ إضافة "geolocation" إلى "permissions" لن تتيح لك تلقائيًا الوصول إلى معلومات رصد الموقع الجغرافي للمستخدمين. يمكنك الاطّلاع على ذلك في نموذج النص البرمجي للمحتوى.