我正在尝试编写用于显示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(第一个请求使URL无效,第二个请求失败)。
<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/