You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自制Manifest V3 Chrome截图扩展无法在浏览器PDF中运行求助

Chrome截图扩展在PDF页面失效的问题解决

问题描述

我是首次提问的业余开发者,开发了一个基于Manifest V3的Chrome截图扩展,功能是在浏览器中绘制选区覆盖层,将选中区域绘制到Canvas后保存。扩展在网页上可正常运行,但在浏览器打开的PDF文件中完全无效——既无报错信息,也无法生成选区覆盖层。我需要它支持PDF,以便后续实现从PDF上传内容到Google表格的功能。尝试调整manifest.json的权限但没有效果,想知道这类扩展能否在浏览器打开的PDF中运行?该如何解决?

相关代码

foreground.js

var selectionOverlay = document.createElement('div');
selectionOverlay.style.position = 'absolute'; // Set overlay position to absolute
selectionOverlay.style.border = '2px solid red'; // Set border style
selectionOverlay.style.background = 'rgba(255, 0, 0, 0.3)'; // Set background color with alpha
selectionOverlay.style.pointerEvents = 'none'; // Make overlay ignore mouse events


// Initialize variables for tracking the selection
var selectionStarted = false;
var startX, startY, endX, endY;
var screenshotDataUrl = 0;

// Event handler for the start of the selection
function handleSelectionStart(event) {

  if (selectionStarted) {
  
    // Append the overlay to the body of the document
    document.body.appendChild(selectionOverlay);
    // Set the starting coordinates of the selection
    startX = event.clientX;
    startY = event.clientY;

    // Set initial position of the overlay
    updateOverlayPosition(startX, startY);
  }
}

// Event handler for the movement of the selection
function handleSelectionMove(event) {
  if (selectionStarted) {
    // Update overlay position as the mouse moves
    updateOverlayPosition(event.clientX, event.clientY);
  }
}

// Event handler for the end of the selection
function handleSelectionEnd(event) {
  console.log("Test2");
  if (selectionStarted) {
    // Reset the selection flag
    endX = event.clientX;
    endY = event.clientY;
    selectionStarted = false;
    console.log("Test2.5");
    // Get the selection range and its bounding rectangle
    var selection = window.getSelection();
    var range = selection.getRangeAt(0).getBoundingClientRect();
    captureSelectedArea(range);
    // Capture the selected area and send the data to the popup script
    // Remove the overlay after the selection is complete
    removeOverlay();
  }
}

// Update the overlay position based on mouse coordinates
function updateOverlayPosition(x, y) {
  selectionOverlay.style.left = Math.min(startX, x) + 'px';
  selectionOverlay.style.top = Math.min(startY, y) + 'px';
  selectionOverlay.style.width = Math.abs(startX - x) + 'px';
  selectionOverlay.style.height = Math.abs(startY - y) + 'px';
}

// Remove the overlay from the DOM
function removeOverlay() {
  selectionOverlay.parentNode.removeChild(selectionOverlay);
}

// Capture the selected area and send a message to the background script
function captureSelectedArea(range) {
  console.log("Test3");
  console.log(range);
  var img = new Image();
  img.src = screenshotDataUrl;
  img.onload = function () {
    var canvas = document.createElement('canvas');
    canvas.width = Math.abs(endX-startX);
    canvas.height = Math.abs(endY-startY);
  
    var context = canvas.getContext('2d');
    context.drawImage(img, startX-20, startY-20, canvas.width +20, canvas.height+20, 20, 20, canvas.width+5, canvas.height+20);
    
    var resultDataUrl = canvas.toDataURL('image/png');
    chrome.runtime.sendMessage({ action: 'downloadImage', dataUrl: resultDataUrl });
  }
};

// Add event listeners for mouse events
document.addEventListener('mousedown', handleSelectionStart);
document.addEventListener('mousemove', handleSelectionMove);
document.addEventListener('mouseup', handleSelectionEnd);

// Listen for messages from the background script
chrome.runtime.onMessage.addListener(function (message, sender, sendResponse) {
  if (message.action === 'startSelection') {
    selectionStarted = true;
    screenshotDataUrl = message.dataUrl;
    
    console.log("Reached here!");
  }
});

manifest.json

{ // Comments are allowed even though it's
    // Manifest version 3 is specified for the Chrome extension.
    "manifest_version": 3,

    // Extension name.
    "name": "Chrome Extension v3 Starter",

    // Description of the extension.
    "description": "A minimal example of a chrome extension using manifest v3",

    // Version of the extension.
    "version": "0.0.1",

    // Icons for different sizes.
    "icons": {
        "16": "logo/logo-16.png",
        "48": "logo/logo-48.png",
        "128": "logo/logo-128.png"
    },

    // Options page for settings.
    "options_page": "settings/settings.html",

    // Action details for the extension.
    "action": {
        "default_title": "Chrome Addon v3 Starter",
        "default_popup": "popup/popup.html"
    },

    // Permissions required by the extension.
    "permissions": ["activeTab","downloads","storage","tabs"],

    // Host permissions for the extension, allowing access to all URLs.
    "host_permissions": [
        "*://*/*",
        "file://*/*.pdf"
    ],

    // Background service worker for handling background tasks.
    "background": {
        "service_worker": "service-worker.js"
    },

    // Content scripts to be injected into specific matches (e.g., GitHub pages).
    "content_scripts": [{
        "js": ["foreground.js"],
        "matches": ["<all_urls>", "file://*/*.pdf","*://*/*.pdf"],
        "run_at": "document_end"
    }]
}

问题原因与解决方法

原因分析

Chrome的内置PDF阅读器是独立的沙盒渲染环境,普通内容脚本无法直接注入到PDF页面的DOM中。你的manifest.json里虽然配置了PDF的match规则,但脚本实际上并没有被加载到PDF的上下文,所以选区逻辑完全无法执行,也不会有报错(因为脚本根本没跑)。

可行解决方案

方案一:使用chrome.debugger API实现全页截图+选区裁剪

这个方案不需要向PDF页面注入脚本,而是通过调试API获取全页截图,再在扩展的popup中处理选区和裁剪:

  1. 更新manifest权限:添加debugger权限
    "permissions": ["activeTab","downloads","storage","tabs", "debugger"],
    
  2. 修改后台逻辑:在service-worker.js中,当用户触发截图时,连接到当前标签页的调试会话,获取全页截图:
    chrome.action.onClicked.addListener(async (tab) => {
      try {
        await chrome.debugger.attach({ tabId: tab.id }, '1.3');
        const screenshotResponse = await chrome.debugger.sendCommand({ tabId: tab.id }, 'Page.captureScreenshot');
        // 将截图数据传递给popup
        chrome.runtime.sendMessage({ action: 'showScreenshot', dataUrl: screenshotResponse.data });
        chrome.debugger.detach({ tabId: tab.id });
      } catch (err) {
        console.error('Debugger attach failed:', err);
      }
    });
    
  3. 在popup中实现选区:popup页面加载后接收截图数据,创建Canvas显示截图,监听鼠标事件绘制选区,最后裁剪对应区域保存。

方案二:使用desktopCapture API捕获屏幕区域

这个方案让用户直接选择屏幕上的PDF区域进行捕获,更直接:

  1. 更新manifest权限:添加desktopCapture权限
    "permissions": ["activeTab","downloads","storage","tabs", "desktopCapture"],
    
  2. 调用桌面捕获:在popup或后台中触发捕获请求:
    chrome.desktopCapture.chooseDesktopMedia(['screen', 'window'], (streamId) => {
      if (streamId) {
        navigator.mediaDevices.getUserMedia({
          video: {
            mandatory: {
              chromeMediaSource: 'desktop',
              chromeMediaSourceId: streamId
            }
          }
        }).then(mediaStream => {
          // 将MediaStream绘制到Canvas,后续处理保存逻辑
          const video = document.createElement('video');
          video.srcObject = mediaStream;
          video.onloadedmetadata = () => {
            const canvas = document.createElement('canvas');
            canvas.width = video.videoWidth;
            canvas.height = video.videoHeight;
            const ctx = canvas.getContext('2d');
            ctx.drawImage(video, 0, 0);
            // 这里可以添加选区裁剪逻辑
          };
        });
      }
    });
    

现有代码的调整建议

你的现有选区逻辑可以复用在方案一的popup中,不需要再依赖页面注入的content script。移除manifest中content_scripts里的PDF匹配规则,避免无效配置。

内容的提问来源于stack exchange,提问作者111y111y

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 18:55:06