firefox扩展可以修改HTML文档的DOM,然后保存为HTML

can firefox extension modify DOM of HTML document then save as HTML?

本文关键字:HTML DOM 然后 保存 文档 扩展 修改 firefox      更新时间:2023-11-27

我正在创建一个firefox扩展,它允许操作员执行修改HTML文档内容的各种操作。操作员不编辑HTML,他们执行其他操作,我的扩展插件通过插入元素、添加属性等方式修改文档。

操作员完成后,他们需要能够将HTML文档保存为文件(或者让我的扩展名将其发送到互联网目的地,但这不是必需的,因为他们可以通过电子邮件发送保存的文件)。

我以为扩展中javascript代码所做的更改可能会反映在HTML文档中,但当我要求firefox浏览器在进行修改后"查看源代码"时,它会显示原始HTML文本。

我的问题是:

#1:对于操作员来说,保存HTML文档以及我的扩展名所做的所有更改最简单的方法是什么?

#2:我的扩展中的javascript代码处理HTML文档内容并写入本地磁盘上的HTML文件的最简单方法是什么?

#3:是否有任何有效的HTML内容无法在保存的文件中准确表示?

#4:TreeWalker是解决方案的一部分吗(见下文)?


到目前为止,我的研究有几个观察结果:

我读过关于TreeWalker对象的文章,它似乎为扩展提供了一种相当轻松的方式来遍历HTML文档中的所有内容(?或几乎所有内容?)。但是,它是否公开了所有内容,以便在不丢失任何重要内容的情况下保存原始内容(以及我的修改)中的所有内容?

TreeWalker是否按照"正确的顺序"遍历HTML文档——这是我的扩展生成原始和/或修改的HTML文档所必需的顺序?

这些问题有什么晦涩难懂的地方吗?

好的,所以我假设您可以访问页面DOM。你需要做的基本上是对dom进行更改,然后获得所有的dom代码并将其保存为一个文件。以下是如何下载页面的html代码。这将创建一个a标记,用户需要单击该标记才能下载文件。

var a = document.createElement('a'), code = document.querySelectorAll('html')[0].innerHTML;
a.setAttribute('download', 'filename.html');
a.setAttribute('href', 'data:text/html,' + code);

现在,您可以在DOM中的任何位置插入一个标记,当用户单击它时,文件就会下载

注意:这是一种黑客攻击,它将文件的整个html注入一个标签中,理论上它应该在任何最新的浏览器中工作(除了IE)。有一些更稳定、更不易破解的方法,比如将其存储在API文件系统中,然后下载该文件。

编辑:document.querySelectorAll行访问页面DOM。要使其工作,document必须是可访问的。您说您正在修改DOM,所以它应该已经存在了。请确保您在页面上添加的是代码,而不是扩展代码。此代码将与DOM修改代码位于同一位置,而不是不能访问DOM的扩展页。

至于a标签,它将被插入到页面中。我跳过了这些步骤,因为我认为您已经知道如何操作DOM,也因为我不知道您想在哪里添加链接。你也可以跳过用户点击链接的操作,但这是一种黑客行为,只适用于现代浏览器。您可以在原始页面中用户看不到的地方插入a标记,然后调用a.click()函数来模拟链接上的单击事件。但这不是一种合法的方式,我个人只在我的练习项目中使用它来呼叫点击事件听众。

我只能在chrome上测试这个,而不能在FF上测试,但尝试一下这个代码,这甚至不需要添加到DOM的链接。您需要将其添加到DOM操作代码旁边。如果运气好的话,这会起作用:)

var a = document.createElement('a'), code = document.querySelectorAll('html')[0].innerHTML;
a.setAttribute('download', 'filename.html');
a.setAttribute('href', 'data:text/html,' + code);
a.click();

只有web API才能实现这一点,至少当您想要一个不省略doctype或注释等内容的结果时。您仍然可以自己编写一个序列化程序,通过document.childNodes并根据节点类型(Element.outerHTMLComment.data等)进行序列化。

幸运的是,你正在编写一个Firefox插件,所以你可以访问更多(强大)的东西。

虽然仍然不是100%完美,但nsIDocumentEncoder实现将产生相当不错的结果,这最多只会在一些空白和显式字符集声明方面有所不同(其他都是错误)。以下是如何使用此组件的示例:

function serializeDocument(document) {
    const {
        classes: Cc,
        interfaces: Ci,
        utils: Cu
    } = Components;
    let encoder = Cc['@mozilla.org/layout/documentEncoder;1?type=text/html'].createInstance(Ci.nsIDocumentEncoder);
    encoder.init(document, 'text/html', Ci.nsIDocumentEncoder.OutputLFLineBreak | Ci.nsIDocumentEncoder.OutputRaw);
    encoder.setCharset("utf-8");
    return encoder.encodeToString();
}

如果你正在编写一个SDK插件,那么随着SDK抽象掉一些重要的东西,事情会变得更加复杂。您需要浏览chrome模块,还需要自己找出活动窗口和选项卡。像Services.wm.getMostRecentWindow("navigator:browser").content.document(Services.jsm)这样的东西应该可以做到这一点。

在XUL覆盖加载项中,content.document应该足以获取当前活动选项卡的文档,并且您已经拥有Components访问权限。

尽管如此,您还是需要让用户选择一个文件目的地,通常是通过nsIFilePicker,然后通过使用文件流或完全异步的OS.Fileneneneba API来实际写入文件。

多亏了mozilla#extdev IRC中的某个人,我似乎可以回答自己的问题了。

我完全被"视图源"骗了。当我在"查看源代码"显示的窗口中没有看到我的修改时,我认为浏览器不会提供信息。

然而,你猜怎么着?当我"file"===>>"将页面另存为…"时,请使用纯文本编辑器检查页面内容。。。果不其然,其中包含了我的firefox扩展所做的修改!惊喜

浏览器无法直接写入本地文件系统。它唯一的读取访问权限是当显式提供文件时://URL(见下面的注释1)

在您的案例中,我们明确讨论的是javascript,它可以读写cookie和本地存储。它还可以将内容发送回服务器并检索,例如使用AJAX。

你放在本地存储/cookie中的东西实际上是其他程序(如电子邮件客户端)无法访问的。

可以创建很长的mailto:URL(请参阅注释2),但只处理电子邮件中的内联内容,您将遇到各种尚未准备好处理的编码问题。

因此,我建议通过AJAX实现存储服务器端,并在整理/工作后查看本地存储。

注1:这并非完全正确。受信任的签名javascript可以访问其他功能,可能包括直接文件访问。

注2:(限制取决于浏览器电子邮件客户端-Lotus Notes截断了大量内容)