创建您自己的浏览器扩展,第 1 部分: 将您的触角延伸至 Chrome

为 Chorme 编写一个基础浏览器扩展

每个浏览器都有其自己的粉丝、批评者、优势和劣势。它们的共同点是人们将越来越多的时间花费于其中。本系列文章将介绍如何为 Chrome、Firefox、Internet Explorer 和 Safari 构建一个浏览器扩展。您可以为每个浏览器构建相同的基础扩展,感受扩展每个浏览器是什么样子,执行这些常见任务是困难还是简单,以及发布您的扩展会涉及到什么。在本文中,您将构建一个 Chrome 扩展。

Duane O'Brien, 软件开发人员, 自由职业

Duane O'Brien 的照片Duane O'Brien 是一名硕果累累的计算机科学家。他撰写了许多关于开发 Web 应用程序和各种 PHP 框架的文章。要想更多地了解 Duane,请访问 他的博客 或关注他的 微博



2012 年 9 月 14 日

开始之前

对于本文,您需要下载和安装 Google Chrome V19 或更高版本(本文示例是基于 V19 的)。您也需要一些可以编辑 HTML、CSS 和 JavaScript 的工具。如果您有使用 Chrome 或者 Chrome 扩展的经验将会很有帮助的。花一点时间浏览 Chrome Web Store(请参阅 参考资料)。看看提供的扩展并先行试用一番,这将为本文提供一些相关背景。


为什么构建浏览器扩展?

您想要构建一个浏览器扩展有几个原因,浏览器扩展的常见应用是在一个浏览器和另一应用程序或服务之间创建一个交互。Evernote、1Password 和 Adobe Shadow 都是这样做的,就像许多其他扩展一样。或者,您想要将一些新功能添加到缺乏该功能的浏览器,通过添加开发人员工具或者屏幕截图实用程序即可。有些开发人员编写具有专门用途的扩展,如体育得分追踪器、特定网站增强和天气预报等。人们可以使用扩展做很多不同事情。您要做什么呢?


您要构建什么扩展?

要在 Chrome 中演示构建扩展流程,需要编写一个名为 Gawkblocker 的扩展。Gawkblocker 将允许您锁定某些您出于各种原因试图不去访问的域。Gawkblocker 包含以下组件:

  • 一个弹出窗口(显示您将要锁定的域)
  • 一个可见浏览器图标(扩展入口点)
  • 一个选项页面(配置您想要锁定以及您想要访问的域)

一般来说,Gawkblocker 将为每个选项卡或窗口附加一个监听器,当选项卡 URL 发生变化时,扩展将比较 URL 和锁定域列表。如果 URL 与一个锁定域匹配,请求将被重定向到一个扩展页面(参见 图 1)。

图 1. Gawkblocker 扩展
屏幕截图显示将被 Gawkblocker 锁定的站点

Gawkblocker 以特定的方式扩展至浏览器,来进行一些将在其他扩展中进行的特定工作。您需要回答这些问题:

  • 在浏览器 UI 中拥有一席之地有多难?
  • 在浏览器会话之间持久化数据涉及到什么?
  • 不同扩展部分彼此如何通信?
  • 您对用户数据的研究有多深入?

构建 Gawkblocker 的流程应该可以回答这些问题。


您的参考文档是什么?

您的参考文档是 Chrome Extension docs(参阅 参考资料)。编写 Chrome 扩展的大部分文档是非常全面的,一个关于如何构建 “Hello World” 的简单单页教程将为您提供有关如何组合扩展的超级简单概述,但是不太适用于深入研究该主题的人员。


Chrome 扩展剖析

一个典型 Chorme 扩展由一个清单文件和一些背景页面、UI 页面和内容脚本组合而组成。

每一个均是以一个清单文件 manifest.json 开始。该文件包含 Chrome 需要了解如何加载您扩展的信息,如标题、描述、所需权限和图标引用。

背景页面是一个在其自己上下文中启动并运行的单一页面,无论打开多少个选项卡或窗口。一个扩展只能有一个背景页面。对于想要一个进程以在 Chorme 所有页面中运行持久化的扩展来说,它们是很有用的。

UI 页面指的是扩展呈现给用户的任何页面。UI 页面可能是一个弹出窗口、一个选项页面、一个部分扩展页面、或者一个覆盖默认 Chrome 页面(如新选项卡 页面)。

内容脚本是您添加到 Web 页面进行交互的 JavaScript 文件。内容脚本在其自己独立的上下文中执行,但是它们可以访问页面的 DOM。内容脚本也可使用一个特定消息传递 API 与扩展中的其他页面进行通信。

对于 Gawkblocker,您将使用一个清单文件、一个包含某些核心功能的 JavaScript 文件(便于携带的一些东西)、一个背景页面、一个选项页面、一个弹出窗口、一个重定向登录页面和一个或两个图标。如果您想看看活动的扩展是否如上所述,可以从 Chrome Web Store 下载一个运行中的 Gawkblocker 扩展。


Gawkblocker 清单文件

清单 1 是 Gawkblocker 的 manifest.json 文件的一个副本

清单 1. Gawkblocker 的 manifest.json 文件
{
  "name": "GawkBlocker",
  "version": "1.7",
  "description": "Tired of taking the Nerd Bait? Use GawkBlocker!",
  "background_page" : "background.html",
  "options_page": "options.html",
  "icons": {
    "16" : "images/GB-19.png",
    "48" : "images/GB-48.png",
    "128" : "images/GB-128.png"
  },
  "browser_action": {
    "default_icon": "images/GB-19.png",
    "default_title": "GawkBlocker",
    "default_popup": "popup.html"
  },
  "permissions": ["tabs"]
}

详细看看这些条目。

version
当您上传一个新版本扩展到 Chrome Web Store 时,需要增加版本号,否则会上传失败。
icon
图标属性包含一个可用图标列表,按大小排列。Chrome 寻找特定大小的图标以在不同的上下文中使用。清单 1 指定与扩展相关的图标文件路径。
browser_action
当您指定一个 browser_action 时,将告诉 Chrome 您想要将一个图标放在 URL 栏右边(Chrome 将该 URL 称之为 Omnibar),单击图标将执行某一操作。在本例中,单击浏览器活动按钮将打开一个弹出窗口。
当您的扩展应用于任何 Web 页面时,只需要指定一个 browser_action。如果您想要一个备选方案,仅适用于特定页面或特定类型的页面,那么指定一个 page_action。不能同时指定 page_action 和 browser_action,只能选择其中一个。
permissions
在此用例中,您需要请求的惟一权限是 ‘选项卡’,该权限将为您提供一些关于单个选项卡的信息(您真正需要的是 URL)。这里提供了很多权限,但是您只能请求您需要的权限。Chrome 和 Android 处理权限的方式非常类似,在用户安装您的扩展时,Chrome 会明确显示扩展所请求的权限,而在安装 Android 应用程序时几乎以相同的方式向您提示一列权限。

现在您已经了解了该清单,接下来我们看看为 Gawkblocker 提供功能的文件。


Gawkblocker 核心类

大多数 Gawkblocker 所做的一切都是由单个核心 JavaScript 文件控制的。在该文件中,您可以定义一个 Storage Manager 对象 (SM) 来处理会话间的持久化数据(现在对于 localStorage 来说这只是一个包装器),和一个处理常见功能(管理锁定站点列表和选项)的 Gawkblocker 对象 (GB)。请参见 清单 2

清单 2. 定义一个 Storage Manager 对象和 Gawkblocker 对象
var SM = (function () {

    var my = {};

    my.get = function (key) {
        return localStorage.getItem(key);
    }

    ...

    return my;

}());

var GB = (function (SM) {
    var my = {};

    my.blockTheseSites = {
        "gawker.com"        : "Gawker Media",
        "io9.com"           : "SciFi Blog",
        "gizmodo.com"       : "Gadget Blog",
        ...
    }

    if (!SM.get("blocklist")) {
        SM.put("blocklist", JSON.stringify(my.blockTheseSites));
    }

    my.getBlockedSites = function () {
        return JSON.parse(SM.get("blocklist"));
    }

    my.setWatchThisInstead = function (value) {
        ...
    }

    my.getWatchThisInstead = function () {
        return SM.get("instead");        
    }

    my.addBlockedSite = function (site) {
        my.blockedSites = JSON.parse(SM.get("blocklist"));
        my.blockedSites[site] = "Custom Add";
        SM.put("blocklist", JSON.stringify(my.blockedSites));
    }

    my.removeBlockedSite = function (site) {
        my.blockedSites = JSON.parse(SM.get("blocklist"));
        delete my.blockedSites[site];
        SM.put("blocklist", JSON.stringify(my.blockedSites));
    }

    return my;
}(SM));

这里,我使用 Module Patten 创建一对相当便携的对象。现在看看在背景页面中怎样使用它们。


背景页面

Gawkblocker 将使用背景页面监听 URL 并将它们与锁定站点列表进行比较。记住,您只能有一个背景页面实例,它是供所有打开的选项卡和窗口共享(不包括 incognito 窗口、除非您请求该权限或者用户明确允许)。因为背景页面本身没有可见组件,其页面上也没有显示相关的 HTML。

清单 3 显示监听更新选项卡的代码。

清单 3. 监听更新选项卡
chrome.tabs.onUpdated.addListener(function(tabId, changedInfo, tab) {
    for (site in GB.getBlockedSites()) {
        if (tab.url.match(site)) {
            chrome.tabs.update(tabId, {"url" : GB.getWatchThisInstead()}, 
function () {});
        }
    }
});

这里有更高效的方法来遍历锁定站点列表,但是这并不是您应该关注的。相反,看看如何使用 chrome.tabs.onUpdated.addListener 附加到 Chrome 上,以及在一个回调函数中传递。

每个 chrome.* API 调用都不同,但一般来说它们均遵守此模式,即调用一个方法以及在回调函数中传递。多数 API 调用是异步的。这可能会导致计时问题,如果您不希望出现这种行为,阅读关于该主题的参考文档。


弹出窗口

Gawkblocker 并不真的需要一个弹出窗口,但是包括一个弹出窗口的话,可以为用户提供一个方便的地方来查看当前处于锁定状态的站点。

清单 4. 弹出页面
$(document).ready(function(){
    $.each(chrome.extension.getBackgroundPage().GB.getBlockedSites(), 
function (index, value) {
        $("#blockedlist").append("<div class='siterow' title='"+value+"'>
<div class='sitename'>"+index+"</div><span class='sitedesc'> : 
"+value+"</span></div>");
    });
});

chrome.extension.getBackgroundPage().GB.getBlockedSites() 的调用是实现从背景页面获取信息并在弹出页面显示的方法。这是处理扩展中页面间通信的一种方法,尽管在弹出窗口中没必要这样做。您可能想要包括 gawkblocker.js 文件,并直接调用 GB 对象。但是如果有很多异步活动,使用不同组件从相同地方提问是很有用的。

弹出窗口询问背景页面要求提供一个锁定站点列表。然后它会遍历该列表,并且将锁定站点细节添加到弹出窗口的显示 div 中(参见 图 2)。

图 2. 弹出窗口
awkblocker 中显示锁定站点的弹出窗口的屏幕截图

选项页面

您需要一个地方来控制扩展行为。如果您使用清单指定一个选项页面(正如您所做的),用户就可以轻易地从扩展管理页面或者右键单击浏览器活动按钮进行访问。选项页面本身也是可选的(参见 图 3)。

图 3. 选项页面
屏幕截图显示 Gawkblocker 的可配置选项

Gawkblocker 使用选项页面支持用户在访问锁定站点时指定行为、添加新站点到锁定列表、或者从锁定列表彻底删除站点(参见 清单 5)。

清单 5. 选项页面
$("#blockthistoo").click(function () {
    GB.addBlockedSite($("#dontgothere").val());
    ...
});

您可以仔细检查背景页面,就像检查弹出窗口那样(参见 清单 6)。

清单 6. 背景页面
$("#blockthistoo").click(function () {
    chrome.extension.getBackgroundPage().GB.addBlockedSite($("#dontgothere").val());
    ...
});

您可能偏爱其中一个方法,具体取决于您的扩展。


重定向登录页面

最后,当重定向一个请求时,需要转到一个本地页面。该页面是所有页面中最简单的。正如所写的那样,它只是一个嵌入 YouTube 视频的页面 (“Hey You! Don't Watch That! Watch This!”)。它并不与其余扩展以任何有意义的方式进行交互,它只是该流程的终点(参见 图 4)。

图 4. 重定向登录页面
标题为 “Wouldn't you rather watch madness??” 的浏览器以及一个运行中的视频的屏幕截图

整合起来

现在,您已经有了一个强大的扩展,但是该如何对其进行测试呢?首先,加载未包装它们的扩展文件。这被称之为加载一个未打包扩展,而且您可以从 Extension Managemen 页面对其进行控制(参阅 参考资料)(您也可以右键单击任何已安装的扩展并选择 Manage Extensions)。

要加载未包装的扩展,选择 Developer Mode 以启动 Load unpacked extension 按钮。要加载您的扩展,单击 Load unpacked extension 按钮并导航到包含该扩展的文件夹。如果您的清单文件有一个错误,您的扩展可能被禁用或者无法加载。如果是这样的话,Chrome 将会告诉您。

一旦确定您的扩展已准备就绪,将有 2 个不同选择供您发布扩展。您可能会发布原始文件,但用户将无法安装此扩展,除非他们也进入扩展管理页面并启用开发人员模式。另一个选择是发布一个已包装扩展,或者将您的扩展放入 Chrome Web Store。


发布一个已包装扩展

在扩展管理页面,挨着 Load unpacked extension 按钮的是一个 Pack extension 按钮(参见 图 5)。单击该按钮启动包装发布扩展流程。Chrome 将向您询问未包装扩展的目录,也可以选择一个私有密钥文件。随后将对该私有密钥文件进行讨论。

图 5. 扩展管理页面
Chromes 扩展管理页面的屏幕截图

第一次包装扩展时,Chrome 将生成两个文件:一个 .crx 文件和一个 .pem 文件。.crx 文件是您的包装扩展,包含所有准备发布的文件。.pem 文件是上面提到的私有密钥。Chrome 要求您保证此文件的安全,这意味着,如果您想要将一个更新打包到扩展中,就需要该私有密钥文件。没有它,Chrome 将认为该更新是一个全新扩展。

一旦您的包装扩展准备就绪,您就可以以任何方式发布,以电子邮件形式发送、放在您的网站上,将其包装在您的安装程序中,放在闪存驱动器中,或任何适合您计划的方法均可。发布它的主要缺点是您也要自己处理更新过程。

由于您的 Chrome 扩展要检查更新,添加一个行到清单文件是来说明在哪查找更新(参见 清单 7)。

清单 7. 检查更新
{
  "name": "GawkBlocker",
  "version": "1.7",
  "update_url": "http://yourawesomedomain.com/ext/updates.xml",
  ...
}

然后,您需要托管一个遵守参考文档自动更新部分指定的格式的 XML 文件。这并不复杂,但是在您的那一端需要更多工作,这也意味着在 Chrome Web Store 中您不能获得任何可见度。


将您的扩展放入 Chrome Web Store

与您自己发布扩展相比,将扩展放入 Chrome Web Store 的过程并不涉及多少技术,但是您需要做一些事情。

首先您需要在 Web 商店中以开发人员的身份完成设置。您需要一个 Google 帐户。登录成功后,转到 Developer Dashboard 以开发人员身份注册。这需要 5 美元,您需要使用 Google Wallet 支付。如果您的 Google 帐户已得到确认,可以立刻在 Developer Dashboard 中激活,如果您的帐户是新的或者不常使用,可能会有一个延迟,直至帐户审查通过。

注册为一个开发人员之后,返回未包装扩展目录,并压缩所有扩展文件(只是 HTML、CSS、JavaScript 和一些图片文件)。不包括 .crx 或 .pem 文件。Chrome Web Store 将从您的压缩包中创建一个 .crx 文件,然后使用一个私有 .pem 文件进行签署。

您的帐户和压缩文件准备好之后,就可以单击该控制面板上大的蓝色大按钮 Add new item,上传您的扩展(参见 图 6)。

图 6. Chrome Web Store
Chrome Web Store 开发人员页面的屏幕截图

如果您压缩文件有一些错误,控制面板将输出一些错误消息。如果您的压缩文件是格式良好的,并且您的清单文件格式也正确,您将看到出现一个页面,询问关于扩展的元数据(屏幕截图、国家和描述等)。输入您扩展的相应信息,就可以将该扩展轻松地放入商店。


了解更新如何工作

扩展放入商店后,进行一些小更改并将更新上传到商店来感受该流程。常见 gotcha 是忘记更新清单文件中的版本号,每次当您上传一个更新时,可以单击 Extensions Management 页面上的 Update extensions now 按钮强制执行一个更新。对于这些更新来说 Chrome Extensions 每几个小时 ping 一次,所以更新您的用户不需要花费太长时间。


寻找答案

我们来看看如何证实问题的答案:

在浏览器 UI 中拥有一席之地有多难?结果证明非常简单,您只需要在清单文件中指定一个 browser_action 并提供一个图标。

在浏览器会话间持久化数据涉及到什么?在 Chrome 中,您有权访问 localStorage 一直持续到应用程序发布。这使得持久化浏览器会话之间数据变得非常简单。

不同扩展部分彼此如何通信?所有页面可以使用 chrome.extension.getBackgroundPage() 与背景页面通信,并且消息传递 API 允许不同扩展组件彼此通信,如果需要的话。

您对用户数据的研究有多深入?与用户允许程度一样。每个权限都有特定警告,尽管用户不完全清楚那些权限隐含了什么。


结束语

您已经编写了一个 Chrome 基础扩展,然后您就可以开始深入了解它了。您可以在 chrome.* API 中获取更多信息,现在您只是了解了一点皮毛。在掌握基础知识之后,您可以从头开始,看看能够将您的 Chrome 扩展到多远。


下载

描述名字大小
文章源代码GawkBlocker.zip60KB

参考资料

学习

获得产品和技术

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Open source, Web development, XML
ArticleID=835224
ArticleTitle=创建您自己的浏览器扩展,第 1 部分: 将您的触角延伸至 Chrome
publish-date=09142012