Розблокування доступу до буфера обміну

7 хв. читання

Протягом останніх декількох років для взаємодії з буфером обміну браузери об'єднувало використання document.execCommand. Приємно мати один широко підтримуваний спосіб інтегрувати копіювання та вставку у веб-застосунки, але все не так просто: доступ до буфера обміну синхронний і може читати й писати тільки в DOM.

Синхронні копіювання та вставка можуть здаватися прекрасними для невеликих фрагментів тексту, але є ряд випадків, коли блокування сторінки для передачі даних із буфера обміну призводить до проблем. Перед тим, як вміст може бути безпечно вставлений, може знадобитися багато часу на включення коду або декодування. Браузеру може знадобитися завантажити пов'язані ресурси зі вставленого документа, а це заблокує сторінку на час очікування диску чи мережі. Також додайте до цього всього блокування сторінки для запиту на отримання доступ до буфера обміну.

Водночас, дозволи, які існують навколо document.execCommand для взаємодії з буфером обміну, слабо визначені й варіюються поміж браузерами. Тож, як може виглядати виділений API буфера обміну, якщо ми хочемо вирішити проблеми з блокуванням та дозволами?

Новий Asynchronous Clipboard API. Це заміна для копіювання та вставлення на основі execCommand, яка має добре визначену модель дозволів і не блокує сторінку. API обіцяє спростити події буфера обміну й поставити їх в один ряд з HTML Drag and Drop API.

Копіювання: запис тексту у буфер обміну

Текст може бути скопійований у буфер обміну за допомогою виклику writeText(). Оскільки цей API асинхронний, функція writeText() повертає проміс, що буде дозволений або відхилений в залежності від того, чи успішно скопійований текст, який ми передаємо:

navigator.clipboard.writeText('Text to be copied')
  .then(() => {
    console.log('Text copied to clipboard');
  })
  .catch(err => {
    // Це може статися, якщо користувач відхилить дозволи буфера обміну:
    console.error('Could not copy text: ', err);
  });

Аналогічно, ми можемо записати це як асинхронну функцію, а потім чекати повертання writeText():

async function copyPageUrl() {
  try {
    await navigator.clipboard.writeText(location.href);
    console.log('Page URL copied to clipboard');
  } catch (err) {
    console.error('Failed to copy: ', err);
  }
}

Вставка: читання тексту з буфера обміну

Подібно до копіювання, текст може бути отриманий з буфера обміну за допомогою виклику readText() й очікування того, що проміс, що повертається, буде дозволений разом з текстом:

navigator.clipboard.readText()
  .then(text => {
    console.log('Pasted content: ', text);
  })
  .catch(err => {
    console.error('Failed to read clipboard contents: ', err);
  });

Для узгодженості, ось еквівалентна асинхронна функція:

async function getClipboardContents() {
  try {
    const text = await navigator.clipboard.readText();
    console.log('Pasted content: ', text);
  } catch (err) {
    console.error('Failed to read clipboard contents: ', err);
  }
}

Обробка подій вставки

Планується ввести нову подію для виявлення змін у буфері обміну, але наразі найкраще використовувати подію paste. Вона добре працює з новими асинхронними методами для читання тексту з буфера обміну:

document.addEventListener('paste', event => {
  event.preventDefault();
  navigator.clipboard.getText().then(text => {
    console.log('Pasted text: ', text);
  });
});

Безпека та дозволи

Доступ до буфера обміну завжди представляв проблему безпеки для браузерів. Без належних дозволів, сторінка могла непомітно скопіювати увесь шкідливий контент у користувацький буфер обміну, що призводило до катастрофічних результатів при вставці. Уявіть веб-сторінку, яка непомітно копіює rm -rf / або образ zip-бомби у ваш буфер обміну.

Додавання веб-сторінкам необмеженого доступу до читання з буфера обміну завдає навіть ще більше клопоту. Користувачі регулярно копіюють у буфер конфіденційну інформацію, таку як паролі та особисті дані, яка потім може бути прочитана без їх відома будь-якою сторінкою.

Як і в багатьох сучасних API, navigator.clipboard підтримується тільки для сторінок, які використовують HTTPS. Щоб запобігти зловживанням, доступ до буфера обміну дозволяється тільки в тому випадку, коли сторінка є активною вкладкою. Сторінки в активних вкладках можуть записувати дані в буфер обміну без запиту дозволу, але читання з буфера завжди потребує дозволу.

Щоб спростити задачу, в Permissions API були додані два нових дозволи для копіювання та вставки. Дозвіл clipboard-write автоматично надається сторінкам, коли вони є активними вкладками. А дозвіл clipboard-read необхідно запросити, що ви можете зробити, намагаючись прочитати дані з буфера обміну.

{ name: 'clipboard-read' }
{ name: 'clipboard-write' }

Async Clipboard API

Як і в будь-якому випадку використання Permissions API, можна перевірити, чи має дозвіл ваш застосунок для взаємодії з буфером обміну:

navigator.permissions.query({
  name: 'clipboard-read'
}).then(permissionStatus => {
  // Буде 'granted', 'denied' або 'prompt':
  console.log(permissionStatus.state);

  // Відстеження внесення змін до стану дозволу
  permissionStatus.onchange = () => {
    console.log(permissionStatus.state);
  };
});

Ось де асинхронна частина Async Clipboard API дійсно знадобиться: спроба читання або запису даних буде автоматично запитувати у користувача дозвіл, якщо він ще не був наданий. Оскільки API базується на промісах, все повністю прозоро, і користувач, який відмовляє в дозволі буферу обміну, відхиляє також і проміс, тож сторінка реагуватиме належним чином.

Оскільки Chrome дозволяє доступ до буфера обміну лише коли сторінка є активною вкладкою, деякі приклади не працюватимуть правильно, якщо будуть вставлені прямо у DevTools, бо DevTools сам по собі є активною вкладкою. Проте, є трюк: потрібно відкласти доступ до буфера обміну, використовуючи setTimeout, а потім швидко натиснути всередині сторінки, щоб сфокусувати її перед викликом функцій.

setTimeout(async () => {
  const text = await navigator.clipboard.readText();
  console.log(text);
}, 2000);

Озираючись назад

В браузерах була суміш різноманітних реалізацій копіювання та вставки до впровадження Async Clipboard API.

У більшості браузерів власне копіювання та вставка можуть запускатися за допомогою використання document.execCommand('copy') та document.execCommand('paste'). Якщо текст, який потрібно скопіювати, являє собою рядок, відсутній у DOM, нам потрібно його ввести й вибрати:

button.addEventListener('click', e => {
  const input = document.createElement('input');
  document.body.appendChild(input);
  input.value = text;
  input.focus();
  input.select();
  const result = document.execCommand('copy');
  if (result === 'unsuccessful') {
    console.error('Failed to copy text.');
  }
})

Точно так само ви можете обробляти вставлений вміст у браузери, що не підтримують новий Async Clipboard API:

document.addEventListener('paste', e => {
  const text = e.clipboardData.getData('text/plain');
  console.log('Got pasted text: ', text);
})

В Internet Explorer також можна отримати доступ до буфера обміну через window.clipboardData. Якщо доступ здійснюється в межах дій користувача, таких як подія клацання — частина перевірки сумісності – жодний запит дозволів не відобразиться.

Виявлення та запасний варіант

Щоб зберегти підтримку всіх браузерів і водночас скористатися перевагами Async Clipboard API, хорошою ідеєю є виконувати перевірку підтримки функцій (feature detection). Ви можете виявити підтримку, перевіривши наявність navigator.clipboard:

document.addEventListener('paste', async e => {
  let text;
  if (navigator.clipboard) {
    text = await navigator.clipboard.readText()
  }
  else {
    text = e.clipboardData.getData('text/plain');
  }
  console.log('Got pasted text: ', text);
});

Що чекає далі на Async Clipboard API?

Як ви могли помітити, цей пост покриває тільки текстову частину navigator.clipboard. В специфікації є більш загальні методи read() та write(), але вони мають додаткову складність реалізації та проблеми безпеки. Наразі, Chrome впроваджує більш прості текстові частини API.

Більше інформації

Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Codeguida 5.6K
Приєднався: 8 місяців тому
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація