返回首页

Manifest V3 中无崩溃的 URL 跟踪清理

文章详细解析 Chrome 扩展 Manifest V3 中从跟踪器清理 URL 的实现。描述了搜索引擎白名单、transitionType 过滤、通过 content script 的 Clipboard API 等解决方案。提供了带示例的工作代码。

Manifest V3:不破坏浏览器的链接清理
Advertisement 728x90

Manifest V3 中清理 URL 跟踪参数的常见陷阱与解决方案

浏览器扩展开发者经常需要从 URL 中移除 UTM 标签、fbclid、gclid 等跟踪参数。使用正则表达式的简单实现在基础场景下可行,但在 Manifest V3 中却会导致问题:破坏搜索引擎功能、引发无限重定向、干扰 XHR 请求。让我们通过一个实际架构示例,探讨关键问题及其解决方案。

为搜索引擎设置白名单可以防止移除必要参数。谷歌、Yandex 等搜索引擎使用来源和媒介参数进行过滤(例如图片、视频)。若无例外处理,清理操作会使搜索功能失效。

const WHITELIST = [
  'google.com', 'bing.com', 'yandex.ru', 'duckduckgo.com' 
];

async function cleanUrl(url) {
  const urlObj = new URL(url);
  if (WHITELIST.some(site => urlObj.hostname.includes(site))) {
    return url; // 保留搜索引擎的原始 URL
  }
  // ... 清理逻辑
}

Manifest V3 中的导航:过滤过渡类型

Manifest V3 限制了请求拦截。declarativeNetRequest 无法动态解析参数,因此需使用 webNavigation.onBeforeNavigate。通过 chrome.tabs.update 进行简单重定向会导致问题:

Google AdInline article slot
  • 来自表单的 POST 请求会中断。
  • 用于 JSON 的 XHR/Fetch 请求会失败。
  • 重定向会重复触发事件,形成循环。

解决方案是严格检查 transitionType。仅处理 'link'(链接点击),忽略 typing、auto_subframe 和 form_submit。

chrome.webNavigation.onBeforeNavigate.addListener(async (details) => {
  const result = await chrome.storage.sync.get(['autoClean']);
  if (!result.autoClean) return;

  if (details.transitionType !== 'link') return;

  const cleanedUrl = await cleanUrl(details.url);

  if (details.url !== cleanedUrl) {
    chrome.tabs.update(details.tabId, { url: cleanedUrl });
  }
}, {
  url: [
    {hostContains: '.'},
    {schemes: ['http', 'https']}
  ]
});

hostContains: '.' 过滤器排除了 chrome-extension:// 协议,其中 new URL() 行为不可预测。

上下文菜单与剪贴板 API

对于手动链接清理,可添加上下文菜单。在 Manifest V3 Service Workers 中,剪贴板访问被阻止——需委托给内容脚本。

Google AdInline article slot

在 background.js 中:

chrome.contextMenus.onClicked.addListener((info, tab) => {
  if (info.menuItemId === "cleanCopy" && info.linkUrl) {
    chrome.tabs.sendMessage(tab.id, {
      action: "cleanAndCopy",
      url: info.linkUrl
    });
  }
});

内容脚本使用异步剪贴板 API 并显示通知:

function cleanAndCopyLink(url) {
  chrome.runtime.sendMessage({ action: "cleanLink", url: url }, (response) => {
    if (response && response.cleanedUrl) {
      navigator.clipboard.writeText(response.cleanedUrl).then(() => {
        showNotification(`链接已清理并复制!\n移除的跟踪器数量:${response.trackerCount}`);
      });
    }
  });
}

function showNotification(message) {
  let toast = document.getElementById('zerotail-toast');
  if (!toast) {
    toast = document.createElement('div');
    toast.id = 'zerotail-toast';
    toast.style.cssText = `
      position: fixed; bottom: 20px; right: 20px;
      background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
      color: white; padding: 12px 20px; border-radius: 8px;
      box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 999999;
      font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
      max-width: 320px; transition: opacity 0.3s ease;
    `;
    document.body.appendChild(toast);
  }
  toast.textContent = message;
  toast.style.opacity = '1';
  setTimeout(() => { toast.style.opacity = '0'; }, 3000);
}

样式模仿 Material Design:渐变背景、阴影、平滑淡出。

Google AdInline article slot

关键要点

  • 域名白名单:排除搜索引擎(如 google.com、yandex.ru),这些网站的参数对功能至关重要。
  • transitionType === 'link':防止干扰表单、XHR 和 URL 输入。
  • 协议过滤器:hostContains: '.' + http/https 避免与 extension:// 相关的错误。
  • 剪贴板委托:在 Manifest V3 中,Service Worker → 内容脚本。
  • 通知界面:简约通知,无弹窗,显示移除的跟踪器数量。

— Editorial Team

Advertisement 728x90

继续阅读