文章

浏览器插件获取剪贴板内容的方式

浏览器插件获取剪贴板内容的方式

在浏览器插件开发中,clipboardRead 是一个敏感权限,用于读取用户剪贴板内容。为了确保用户知情和控制,浏览器要求 必须由用户主动触发(如点击按钮)才能执行读取操作。以下是具体实现方法和用户触发方式:


1. 用户触发方式

方法 1:点击插件按钮(Popup 或工具栏图标)

1
2
3
4
5
6
7
8
9
10
11
// popup.js(插件的弹出页面)
document.getElementById('pasteBtn').addEventListener('click', async () => {
  try {
    const text = await navigator.clipboard.readText();
    console.log('剪贴板内容:', text);
    alert(`读取内容: ${text}`);
  } catch (err) {
    console.error('读取失败:', err);
    alert('请检查权限或手动粘贴!');
  }
});

用户操作
用户需主动点击插件的工具栏图标打开 Popup,再点击其中的按钮。


方法 2:内容脚本中的页面按钮

1
2
3
4
5
6
7
8
9
10
11
12
// content.js(注入到网页的脚本)
const btn = document.createElement('button');
btn.textContent = '读取剪贴板';
btn.addEventListener('click', async () => {
  try {
    const text = await navigator.clipboard.readText();
    console.log('剪贴板内容:', text);
  } catch (err) {
    console.error('读取失败:', err);
  }
});
document.body.appendChild(btn);

用户操作
用户需点击网页中由插件添加的按钮。


方法 3:右键菜单项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// background.js(后台脚本)
chrome.runtime.onInstalled.addListener(() => {
  chrome.contextMenus.create({
    id: 'readClipboard',
    title: '读取剪贴板',
    contexts: ['all'] // 所有场景显示右键菜单
  });
});

chrome.contextMenus.onClicked.addListener((info, tab) => {
  if (info.menuItemId === 'readClipboard') {
    chrome.scripting.executeScript({
      target: { tabId: tab.id },
      func: async () => {
        try {
          const text = await navigator.clipboard.readText();
          alert(`剪贴板内容: ${text}`);
        } catch (err) {
          alert('无法读取剪贴板!');
        }
      }
    });
  }
});

需在 manifest.json 中声明权限

1
2
3
{
  "permissions": ["clipboardRead", "contextMenus", "scripting"]
}

用户操作
用户在页面任意位置右键,选择插件添加的菜单项。


2. 权限声明与安全限制

manifest.json 配置

1
2
3
4
5
{
  "manifest_version": 3,
  "permissions": ["clipboardRead"], // 必须声明
  "host_permissions": ["<all_urls>"] // 如果要在内容脚本中使用
}

浏览器安全规则

  1. 用户手势要求
    • 调用 navigator.clipboard.readText() 必须由 clickkeypress 等用户交互事件直接触发。
    • 以下代码会失败(无用户手势):
      1
      2
      3
      4
      
      // 错误示例!
      setTimeout(async () => {
        const text = await navigator.clipboard.readText(); // 抛出异常
      }, 1000);
      
  2. HTTP 页面限制
    • 仅在 HTTPS 页面localhost 中可用,普通 HTTP 页面会拒绝访问。
  3. 控制台禁用
    • 开发者工具控制台中直接调用 navigator.clipboard.readText() 会失败,必须通过页面按钮触发。

3. 兼容性处理(旧版浏览器)

如果目标浏览器不支持现代 Clipboard API,可降级到 document.execCommand('paste')(需用户聚焦输入框):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 创建隐藏的输入框
const input = document.createElement('input');
input.style.position = 'fixed';
input.style.opacity = 0;
document.body.appendChild(input);
input.focus();

// 尝试粘贴(需用户手动触发)
document.addEventListener('click', () => {
  input.value = '';
  const success = document.execCommand('paste');
  if (success) {
    console.log('剪贴板内容:', input.value);
  }
});

4. 用户可见的权限提示

  • 首次触发时:浏览器会显示权限弹窗,用户需明确允许(如下图)。
    剪贴板权限提示
  • 已拒绝后:需用户手动在浏览器设置中重新授权(地址栏输入 chrome://settings/content/clipboard)。

5. 最佳实践

  1. 明确告知用户:在按钮附近添加说明(如“点击读取剪贴板”)。
  2. 错误处理:捕获异常并提示用户:
    1
    2
    3
    4
    5
    6
    7
    
    try {
      const text = await navigator.clipboard.readText();
    } catch (err) {
      if (err.name === 'NotAllowedError') {
        alert('请点击页面按钮并允许权限!');
      }
    }
    
  3. 备用方案:提供手动输入框,允许用户粘贴内容。

完整代码示例

1
2
3
4
<!-- popup.html -->
<button id="pasteBtn">读取剪贴板</button>
<p id="result"></p>
<script src="popup.js"></script>
1
2
3
4
5
6
7
8
9
// popup.js
document.getElementById('pasteBtn').addEventListener('click', async () => {
  try {
    const text = await navigator.clipboard.readText();
    document.getElementById('result').textContent = `内容: ${text}`;
  } catch (err) {
    document.getElementById('result').textContent = '失败: ' + err.message;
  }
});

通过以上方法,插件可以在用户主动操作的前提下安全读取剪贴板内容,符合浏览器安全策略。

本文由作者按照 CC BY 4.0 进行授权