javascript - Chrome扩展程序: How to show custom UI for a PDF file?

标签 javascript google-chrome google-chrome-extension pdf.js

我正在尝试编写用于显示PDF文件的Google Chrome扩展程序。一旦我检测到浏览器正在重定向到指向PDF文件的URL,我希望它停止加载默认的PDF查看器,而是开始显示我的UI。 UI将使用PDF.JS呈现PDF,并使用jQuery-ui显示其他内容。

问题:我该怎么做?阻止原始的PDF查看器非常重要,因为我不想通过显示两个文档实例来使内存消耗增加一倍。因此,我应该以某种方式将选项卡导航到我自己的 View 。

最佳答案

作为PDF.js Chrome extension的主要作者,我可以分享一些有关为Chrome构建PDF Viewer扩展程序背后的逻辑的见解。

如何检测PDF文件?

在理想情况下,每个网站都会提供标准application/pdf MIME类型的PDF文件。不幸的是,现实世界并不完美,实际上,有许多网站使用了错误的MIME类型。通过选择满足以下任一条件的请求,您将捕获大多数情况:

  • 资源由Content-Type: application/pdf响应 header 提供。
  • 资源由Content-Type: application/octet-stream响应 header 提供,其URL包含“.pdf”(不区分大小写)。

  • 除此之外,您还必须检测用户是否要查看PDF文件下载PDF文件。如果您不关心区别,那很简单:如果请求符合先前的任何条件,则拦截该请求。
    否则(这是我采用的方法),您需要检查Content-Disposition响应 header 是否存在,并且其值以“attachment”开头。

    如果您想支持PDF下载(例如通过您的UI),则需要添加Content-Disposition: attachment响应 header 。如果标题已经存在,则必须用“attachment”替换现有的处置类型(例如inline)。不要为试图解析完整的 header 值而烦恼,只需将第一部分剥离到第一个分号,然后在其前面放置“附件”即可。 (如果您真的想解析 header ,请阅读RFC 2616 (section 19.5.1)RFC 6266)。

    我应该使用哪些Chrome(扩展)API来拦截PDF文件?

    chrome.webRequest API可用于拦截和重定向请求。使用以下逻辑,您可以截取PDF并将其重定向到自定义查看器,该查看器从给定URL请求PDF文件。

    chrome.webRequest.onHeadersReceived.addListener(function(details) {
        if (/* TODO: Detect if it is not a PDF file*/)
            return; // Nope, not a PDF file. Ignore this request.
    
        var viewerUrl = chrome.extension.getURL('viewer.html') +
          '?file=' + encodeURIComponent(details.url);
        return { redirectUrl: viewerUrl };
    }, {
        urls: ["<all_urls>"],
        types: ["main_frame", "sub_frame"]
    }, ["responseHeaders", "blocking"]);
    

    (请参阅https://github.com/mozilla/pdf.js/blob/master/extensions/chromium/pdfHandler.js,以使用此答案顶部所述的逻辑进行PDF检测的实际实现)

    使用上面的代码,您可以拦截http和https URL上的任何PDF文件。
    如果要从本地文件系统和/或ftp查看PDF文件,则需要使用 chrome.webRequest.onBeforeRequest 事件而不是 onHeadersReceived 。幸运的是,您可以假设如果文件以“.pdf”结尾,则资源很可能是PDF文件。但是,想要使用扩展名来查看本地PDF文件的用户必须在扩展名设置页面上明确允许此操作。

    在Chrome操作系统上,使用 chrome.fileBrowserHandler API将扩展程序注册为PDF查看器(https://github.com/mozilla/pdf.js/blob/master/extensions/chromium/pdfHandler-vcros.js)。

    基于webRequest API的方法仅适用于顶级文档和框架中的PDF,不适用于通过<object><embed>嵌入的PDF。尽管它们很少见,但我仍然想为其提供支持,因此我想出了一种非常规的方法来在这些情况下检测并加载PDF查看器。可以在https://github.com/mozilla/pdf.js/pull/4549/files上查看实现。此方法依赖于以下事实:将元素放入文档中时,最终必须将其呈现。呈现时,将应用CSS样式。当我为CSS中的embed/object元素声明动画时,将触发动画事件。这些事件在文档中冒泡。然后,我可以为此事件添加一个监听器,并用加载我的PDF查看器的iframe替换object/embedded元素的内容。
    有几种替换元素或内容的方法,但是我使用Shadow DOM更改了显示的内容,而又不影响页面中的DOM。

    局限性和注意事项

    此处描述的方法有一些限制:
  • 从服务器至少两次请求PDF文件:首先是获取 header 的常规请求,当扩展名重定向到PDF Viewer时,该请求被中止。然后是另一个请求,以请求实际数据。
    因此,如果文件仅有效一次,则无法显示PDF(第一个请求使URL无效,第二个请求失败)。
  • 此方法仅适用于GET请求。没有公共(public)API可直接从Chrome扩展程序(crbug.com/104058)中的请求获取响应正文。
  • 使<object><embed>元素使用PDF的方法需要在每个页面上运行脚本。我已经分析了代码,发现对性能的影响可以忽略不计,但是如果您要更改逻辑,则仍然需要小心。
    (我首先尝试使用Mutation Observers进行检测,这使大型文档上的页面加载速度降低了3-20%,并在复杂的DOM基准测试中导致了1.5 GB的额外内存峰值)。
  • 用于检测<object>/<embed>标记的方法可能仍会导致加载任何基于NPAPI/PPAPI的PDF插件,因为该方法仅在已插入并呈现<embed>/<object>标记的内容时才对其进行替换。当标签处于非 Activity 状态,动画没有安排,因此动画事件的调度会显著被延迟。

  • 后记

    PDF.js是开源的,您可以在https://github.com/mozilla/pdf.js/tree/master/extensions/chromium上查看Chrome扩展程序的代码。如果您浏览源代码,您会发现代码比我在这里解释的要复杂一些。这是因为扩展程序无法在onHeadersReceived事件上重定向请求,直到几个月前我实现了它(crbug.com/280464,Chrome 35)。

    还有一些逻辑可以使多功能框中的URL看起来更好一些。

    PDF.js扩展会继续发展,因此,除非您想大幅更改PDF Viewer的UI,否则我建议要求用户安装PDF.js的官方PDF Viewer in the Chrome Web Store和/或在PDF.js's issue tracker上打开问题以提出合理的功能请求。

    关于javascript - Chrome扩展程序: How to show custom UI for a PDF file?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27770677/

    相关文章:

    javascript - Angular JS 组件绑定(bind)

    javascript - 将按钮值传递给模态中的 Url.Action

    javascript - 如何在整个 Bootstrap 中保持事件链接突出显示?

    jquery - Google Chrome 中使用 jQuery 的动画工件(红/蓝移)

    jquery - 不必要地加载或重新加载内容时,Safari 中的页面会闪烁白色

    javascript - addthis_widget.js 在 Android 和 Chrome 上抛出 Cannot read property '_pmh' of null 错误

    javascript - Chrome 在后台运行时动态站点更新的 Chrome 桌面通知

    javascript - 需要使用 Chrome 扩展程序替换网页中的特定 url 路径

    google-chrome - 如何在隐身模式下启用Chrome扩展程序?

    javascript - 到达 scrollspy 部分的末尾时强制停止滚动