Chrome 扩展程序

作者: Reuven Lerner

使用标准 Web 技术:HTML、CSS 和 JavaScript 在 Chrome 浏览器内部创建应用程序。

当 Netscape 在高科技世界冉冉升起时,联合创始人马克·安德森宣布,浏览器是一种新型操作系统,人们可以在其中创建应用程序。我们不是为 Windows、Macintosh 甚至 Linux(在当时看来是可笑的想法)编写应用程序,而是为浏览器编写应用程序。这在当时似乎是遥不可及的想法,但显然已成为现实。如今,谈论“Web 应用程序”已成为常态,这意味着通过浏览器交付的东西,但其代码位于服务器上。当有人说“Web 应用程序”时,我就是这样想的,而且我已经很久没有认真考虑过甚至编写桌面应用程序了。

话虽如此,使用桌面应用程序肯定有优势。它们与其他应用程序配合更流畅;它们可以与文件系统交互,并且它们具有更自然的外观和感觉。这种情况正在发生变化,特别是考虑到 HTML5 为我们带来的功能以及浏览器正变得融入整体用户体验的方式,而不是成为计算机上运行的众多应用程序之一。

我真正喜欢的是创建 Web 应用程序的相对简单性,包括使用 Web 的核心技术——HTML、CSS 和 JavaScript——而且我每天都在使用这些技术,至少用于客户端开发。

长期以来,Firefox 一直为开发人员提供编写扩展程序的机会。但是,我必须承认,我对学习一种全新的语言和范例(Mozilla 的 XUL)的想法并不感到兴奋。Firefox 的 Greasemonkey 扩展程序长期以来一直是我的最爱,它使我可以对各种网站进行客户端更改和自定义。但是,它并没有完全集成到浏览器中,并且它需要的安装和配置超出了大多数人愿意接受的范围。

相比之下,Google Chrome(或开源 Chromium)中的扩展程序使用 Web 技术,并内置于浏览器中,通过加载 HTML、CSS 和 JavaScript 包,真正有可能以多种不同的方式扩展浏览器。

本月,我将研究您可以使用 Chrome 编写的不同类型的扩展程序,并考虑什么时候编写扩展程序比 Web 应用程序更好,并展示如何开发您自己的简单扩展程序。

创建扩展程序

正如我之前提到的,Chrome 扩展程序是 HTML、CSS 和 JavaScript 的组合。扩展程序有不同的类型;现在,让我们专注于浏览器扩展程序,它会在浏览器的右上角放置一个图标,该图标会生成一个弹出窗口,并且还可以与浏览器窗口的内容进行交互。

创建扩展程序实际上非常简单,可以从计算机上的任何目录中完成。创建一个新目录,并在其中创建一个名为 manifest.json 的文件。正如文件扩展名所示,此文件(向 Chrome 提供有关您的扩展程序的信息)以 JSON(JavaScript 对象表示法)编写,对于任何熟悉 JavaScript 的人来说,JSON 自然且易于上手。manifest 告诉 Chrome 如何加载扩展程序、它应该具有哪些权限以及应该在浏览器窗口中显示哪些元素。

例如,这是我为本文构建的扩展程序的简单扩展程序 manifest


{
  "name": "ATF sample extension",
  "version": "1.0",
  "manifest_version": 2,
  "description": "Description of my ATF sample extension",
  "browser_action": {
    "default_icon": "atf.png"
  }
}

正如您所看到的,manifest.json 包含许多名称-值对,正如您从 JSON 或 JavaScript 对象中期望的那样。名称由 Chrome 扩展程序标准文档设置,尽管大多数值都是字符串,但在某些情况下,它们将包含数字(例如,manifest_version)、对象(例如,browser_action)甚至数组。

根据标准文档(请参阅“资源”),manifest.json 中唯一必需的字段是“name”(包含扩展程序名称)和“version”(指示扩展程序版本)。但是,Google 也表示,从 Chrome 18 开始,“开发人员应将版本号指定为 2”,这在我看来似乎是一个合理的想法。

由于此扩展程序是浏览器操作,因此您需要指定此名称-值对,将“browser_action”指定为名称,并将 JSON 对象指定为其值。该值可以(并且将)包含几个额外的名称-值对,目前只有一个,即“default_icon”,它指示应在 Chrome 工具栏中地址栏右侧显示的图标。default_icon 是一个字符串,其中包含文件名,该文件名应是正确大小 (19x19) 的 PNG 图形,代表您的扩展程序。

创建 manifest.json 后,创建(或下载)一个 19x19 PNG 图标,并将其与文件名 atf.png 放在扩展程序文件夹中。有了扩展程序目录、manifest.json 和图标,您现在就可以将扩展程序加载到 Chrome 中了。在浏览器中打开 chrome://chrome/extensions/——一个用于扩展程序管理的特殊 URL——并确保选中“开发者模式”复选框,以便您可以加载扩展程序,而无需 Google 的许可,并且可以从本地磁盘加载。完成此操作后,应该可以使用“加载已解压的扩展程序”按钮。单击该按钮,然后使用文件选择对话框选择扩展程序目录。(不要选择目录中的文件,而是选择目录本身。)

完成此操作后,您的扩展程序应该会显示在 Chrome 中,其中包含扩展程序名称、描述和版本号。如果您更新了扩展程序,并且在更新后,您可以通过单击扩展程序名称下的重新加载链接来告诉 Chrome 重新加载它。

使扩展程序有用

现在您已经创建了一个基本扩展程序,让我们尝试使其有用。扩展程序可以执行许多操作,从提供其他站点的快照,到与整体浏览器环境交互,再到与当前正在查看的页面交互。

让我们首先创建一个弹出窗口。manifest.json 文件已经指示了 popup_icon 将是什么。让我们向其中添加一个 HTML 文件,然后在您单击扩展程序的图标时显示该文件。为此,只需将 manifest.json 中的“default_popup”值设置为扩展程序目录中 HTML 文件的名称即可。然后,使用该名称创建一个 HTML 文件。例如


{
    "name": "ATF sample extension",
    "version": "1.1",
    "manifest_version": 2,
    "description": "Description of my ATF sample extension",
    "browser_action": {
        "default_icon": "icon.png",
        "default_popup": "popup.html"
    }
}

请注意,我还增加了扩展程序的版本号,以确保我可以跟踪创建的版本。(如果您使用的是版本控制系统(例如 Git),则可以保留一个标记或提交注释来指示您何时更新了版本号。)

既然我已经告诉 Chrome,每当我单击扩展程序的图标时,它都应该加载 popup.html,那么我真的应该创建一个名为 popup.html 的 HTML 文件。这是一个您可以包含的简单示例


<!doctype html>
<html>
  <head>
    <h3>ATF extension</h3>
  </head>
  <body>
    <h1>Extension headline</h1>
    <p>I am an extension paragraph.</p>
  </body>
</html>

现在,如果这个文件对您来说看起来很简单,那就是重点。扩展程序可能会变得复杂,但它们不必如此,特别是如果您正在做一些简单的事情。将新版本的 manifest.json 和 popup.html 保存在扩展程序目录中,重新加载扩展程序,然后单击扩展程序图标。您应该会看到文本弹出,尽管是在一个难看的黑白窗口中。

如果您想为 popup.html 添加一些漂亮的样式,可以使用 CSS 来完成。创建一个文件 popup.css,并将其(当然)放在扩展程序目录中


h1 {
   color: blue;
}

现在,在 popup.html 中添加一个指向该样式表的链接


<!doctype html>
<html>
  <head>
    <h3>ATF extension</h3>
    <link rel="stylesheet" type="text/css" href="popup.css">
  </head>
  <body>
    <h1>Extension headline</h1>
    <p>I am an extension paragraph.</p>
  </body>
</html>

果然,h1 标题现在变成了蓝色。

扩展程序中的 JavaScript

所有这些看起来都很简单——而且确实如此。但是,如果您认为您可以像在普通的 HTML 文件中那样,只在 HTML 文件中插入一些 JavaScript 并使其执行……那么,这就是事情变得有点棘手和不同的地方。出于安全原因(我承认我不太明白),JavaScript 需要放在一个单独的文件中,并从 HTML 文件中引用。在这种情况下,该文件看起来像这样


<!doctype html>
<html>
  <head>
    <h3>ATF extension</h3>
    <script src="popup.js"></script>
    <link rel="stylesheet" type="text/css" href="popup.css">
  </head>
  <body>
    <h1>Extension headline</h1>
    <p>I am an extension paragraph.</p>
  </body>
</html>

您的 popup.js 中可以包含什么?实际上,任何您想要的东西。这是一个非常简单(且烦人!)的例子


alert("You have loaded the popup!");

现在,单击扩展程序图标将产生一个 JavaScript 警报。当您通过单击“确定”按钮将其关闭后,您将获得格式精美的 HTML 页面,位于 popup.html 中。

您可以在 popup.js 中做什么?说实话,您几乎可以做任何您想做的事情——修改文本、从其他站点检索内容、计算事物并将信息发送到其他地方。如果您可以在 JavaScript 中执行此操作,那么您很可能可以在浏览器中执行此操作。您甚至可以使用库,例如 jQuery,只要您的 jQuery 副本已引用并从扩展程序目录中加载。

因此,让我们尝试一些更大胆的事情。让我们使用 jQuery 从网站检索数据并将其插入到弹出窗口中。为了做到这一点,您需要稍微修改您的 popup.html


<!doctype html>
<html>
  <head>
    <h3>ATF extension</h3>
    <script src="jquery.js"></script>
    <script src="popup.js"></script>
    <link rel="stylesheet" type="text/css" href="popup.css">
  </head>
  <body>
    <h1>Extension headline</h1>
    <p id="paragraph">I am an extension paragraph.</p>
  </body>
</html>

请注意,我如何在扩展程序目录中添加了引用 jquery.js 的行。您还可以引用 Google 或其他公司在线放置的副本之一,以提高缓存和下载速度。我还为 HTML 中的“p”标记赋予了“paragraph”的 ID 属性,这将使它更容易抓取段落并对其进行一些操作。

最大的区别将在 popup.js 中。您不再只是在其中调用 alert()。相反,您实际上将使用 jQuery 的 Ajax 功能从网站检索信息并将其粘贴到弹出窗口中。您将在这里以一种丑陋的、蛮力的方式来完成它,以便更明显地看到结果,但是您可以轻松地想象一个示例,该示例可以更优雅地浏览网站的内容(或者,也许是它的 RSS/Atom 提要),挑选出有用的信息,然后显示它。例如,您可以创建一个浏览器扩展程序来显示当前天气。

对于此示例,让我们只是让浏览器转到网站,检索其内容并将原始内容粘贴到弹出窗口的“p”标记中。这是更新后的 popup.js


alert("Popup -- before");

function showText(data) {
    $("#paragraph").text(data);
};

$.get('http://lerner.co.il/', showText);

alert("Popup -- after");

现在,我放入“before”和“after”警报的原因不是因为我喜欢惹恼我的用户,而是因为我发现了解异步 Ajax 世界中事情何时发生很有启发性。(提示:在向您的用户发布这个惊人的扩展程序之前,请删除对“alert”的调用。)您定义了一个函数 showText,它符合 jQuery 对函数外观的定义,即它接受(至少)一个名为“data”的参数,其中包含您尝试检索的 URL 的内容。这就是您将在此处执行的所有操作,使用“text”方法粘贴到 HTML 源代码中。这意味着最终用户将看到源代码;如果您想要更美观的东西,可以使用 html() 方法而不是 text() 方法。

但是,showText 不是直接调用的。相反,它是作为回调函数调用的,当您调用的 $.get() 函数(在后台(即异步)执行)返回网站的内容时执行。这可能需要一秒或十秒,但在大多数情况下,它会非常快。但是,回调几乎肯定会在您第二次调用 alert() 后才调用。也就是说,您将看到第一个 alert() 调用、第二个 alert() 调用,然后是段落内容的变化。这种基于事件的编码在 JavaScript 世界中很常见,可能需要一些时间才能适应它。请注意,第二个参数是 showText,即函数本身,然后在成功的 Ajax 调用后调用该函数。

如果您现在重新加载浏览器扩展程序并单击按钮,您会发现……实际上什么也没有发生。也就是说,您会收到第一个和第二个 alert 调用,但段落的内容不会更改。这是因为您没有告诉 Chrome 可以从 lerner.co.il 或任何其他 URL 检索数据。由于从外部 URL 检索数据是潜在的危险事件,会将您暴露给外部世界的事物,因此您需要显式允许其使用。这是通过返回 manifest.json 并添加“permissions”键来完成的


{
    "name": "ATF sample extension",
    "version": "1.1",
    "manifest_version": 2,
    "description": "Description of my ATF sample extension",
    "browser_action": {
        "default_icon": "icon.png",
        "default_popup": "popup.html"
    },
    "permissions": [
        "http://lerner.co.il/"
    ]
}

“permissions”键可以包含各种各样的项目,从 URL(在本例中)到通配符匹配,再到 Google 定义的关键字。例如,如果您希望您的扩展程序使用 HTML5 功能,例如地理定位或本地存储,则需要在此处指示。

现在,如果您想修改 popup.html,即您使用浏览器扩展程序获得的弹出窗口,那么这一切都很好。如果您实际上想与页面本身进行交互,无论是从中读取还是写入,该怎么办?答案是,您可以通过编写不是 Chrome 世界中已知的“浏览器操作”,而是“内容脚本”来做到这一点。

现在,内容脚本需要不同的 manifest.json,但它也引发了关于您如何与页面交互的问题,该页面本身可能正在执行一些 JavaScript。答案是 Chrome 提供了一种称为“隔离世界”的有趣工具,其中两个独立的 JavaScript 环境——一个在页面上,另一个在浏览器中——可以独立运行,每个环境都有自己的 JavaScript 库(以及 jQuery 版本,如果需要),但可以同时与 DOM 和页面内容进行交互。这种隔离不仅意味着您的内容脚本可以以多种方式处理页面内容,而无需担心干扰现有的 JavaScript,而且还意味着页面无法“突破”其沙箱,感染或以其他方式影响浏览器本身。

我应该注意到,尽管我没有在本文的示例中使用它们,但 Chrome 通过“chrome”对象提供了各种各样的 JavaScript 方法和功能,您可以通过 manifest.json 中的 permissions 键访问这些方法和功能。这些方法使您可以访问(例如)当前选项卡和窗口,真正允许您将浏览器用作应用程序平台,而不仅仅是显示内容的机制。

结论

Chrome 的设计目的是使用 Web 技术,而扩展程序机制最能体现这一点,它使用 HTML、CSS 和 JavaScript 的组合来产生新的用户体验。现在,浏览器扩展程序并非万能药;它们打破了 Web 与浏览器无关,并且所有内容都可以按需从服务器下载的想法。但是,如果您的整个组织都将使用 Chrome,或者您正在寻找与现有页面交互的东西,或者您想向浏览器添加功能,那么 Chrome 的扩展程序机制使您可以轻松地进行实验和尝试新想法。

资源

Google Chrome 的主页是 http://google.com/chrome。Chromium(其开源对应物)的主页位于 http://chromium.org

有关编写 Chrome 扩展程序的广泛信息,包括视频教程,可从 Google 获得,网址为 http://developer.chrome.com/extensions

具体来说,您可以阅读有关 manifest.json 标准及其可以包含的内容的更多信息,网址为 http://developer.chrome.com/extensions/manifest.html

jQuery 是 JavaScript 库中的巨头,网址为 http://jquery.org

Reuven M. Lerner 是一位长期从事 Web 开发的开发人员,提供 Python、Git、PostgreSQL 和数据科学方面的培训和咨询服务。他撰写了两本编程电子书(《Practice Makes Python》和《Practice Makes Regexp》),并为程序员发布免费的每周新闻通讯,网址为 http://lerner.co.il/newsletter。Reuven 在 Twitter 上的账号是 @reuvenmlerner,与妻子和三个孩子住在以色列的莫迪因。

加载 Disqus 评论