Используйте геолокацию

Если вы хотите получить информацию о геолокации в своем расширении Chrome, используйте тот же API веб-платформы navigator.geolocation , который обычно используется на любом веб-сайте. Эта статья существует, поскольку расширения Chrome обрабатывают разрешения на доступ к конфиденциальным данным иначе, чем веб-сайты. Геолокация — это очень конфиденциальные данные, поэтому браузеры гарантируют, что пользователи полностью осведомлены и контролируют, когда и где передается их точное местоположение.

Используйте геолокацию в расширениях MV3

В Интернете браузеры защищают данные о геолокации пользователей, показывая запрос на предоставление этому конкретному источнику доступа к их местоположению. Одна и та же модель разрешений не всегда подходит для расширений.

Снимок экрана с запросом разрешения, который вы видите, когда веб-сайт запрашивает доступ к API геолокации.
Запрос на разрешение геолокации

Разрешения — не единственное отличие. Как упоминалось выше, navigator.geolocation — это DOM API, то есть часть API, из которых состоят веб-сайты. В результате он недоступен внутри рабочих контекстов, например , рабочий процесс службы расширений , который является основой расширений манифеста v3. Однако вы все равно можете использовать geolocation . Просто есть нюансы с тем, как и где его использовать.

Используйте геолокацию в сервис-воркерах

Внутри сервис-воркеров нет объекта navigator . Он доступен только внутри контекстов, имеющих доступ к объекту document страницы. Чтобы получить доступ внутри сервис-воркера, используйте Offscreen Document , который предоставляет доступ к HTML-файлу, который вы можете связать с вашим расширением.

Для начала добавьте "offscreen" в раздел "permissions" манифеста.

манифест.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, чтобы гарантировать, что закадровые документы используются в законных целях.

Получив ссылку на внеэкранный документ, вы можете отправить ему сообщение с просьбой предоставить вам обновленную информацию о геолокации.

сервис_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() 

Используйте геолокацию во всплывающем окне или на боковой панели

Использовать геолокацию во всплывающем окне или боковой панели очень просто. Всплывающие окна и боковые панели — это просто веб-документы, поэтому они имеют доступ к обычным API-интерфейсам DOM. Вы можете получить прямой доступ к navigator.geolocation . Единственное отличие от стандартных веб-сайтов заключается в том, что вам необходимо использовать поле "permission" в файле manifest.json для запроса разрешения "geolocation" . Если вы не включите разрешение, у вас все равно будет доступ к navigator.geolocation . Однако любая попытка его использования приведет к немедленной ошибке, как если бы пользователь отклонил запрос. Вы можете увидеть это в образце всплывающего окна .

Использование геолокации в скрипте контента

Как и всплывающее окно, сценарий контента имеет полный доступ к DOM API; однако пользователи будут проходить обычный процесс получения разрешений пользователя. Это означает, что добавление "geolocation" к вашим "permissions" не предоставит вам автоматически доступ к информации о геолокации пользователей. Вы можете увидеть это в примере сценария контента .