Если вы хотите получить информацию о геолокации в своем расширении Chrome, используйте тот же API веб-платформы navigator.geolocation
, который обычно используется на любом веб-сайте. Эта статья существует, поскольку расширения Chrome обрабатывают разрешения на доступ к конфиденциальным данным иначе, чем веб-сайты. Геолокация — это очень конфиденциальные данные, поэтому браузеры гарантируют, что пользователи полностью осведомлены и контролируют, когда и где передается их точное местоположение.
Используйте геолокацию в расширениях MV3
В Интернете браузеры защищают данные о геолокации пользователей, показывая запрос на предоставление этому конкретному источнику доступа к их местоположению. Одна и та же модель разрешений не всегда подходит для расширений.
Разрешения — не единственное отличие. Как упоминалось выше, 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"
не предоставит вам автоматически доступ к информации о геолокации пользователей. Вы можете увидеть это в примере сценария контента .