周日在家无聊,研究了下chrome的扩展程序(extensions),所谓的扩展程序其实没那么难,基本会html、js、css的很容易上手
简单的例子:获取特定网页上的信息,生成一维条码图片和预览页面,并打印预览页面
基本概念
一个应用(扩展)其实是压缩在一起的一组文件,包括HTML,CSS,Javascript脚本,图片文件,还有其它任何需要的文件。 应用(扩展)本质上来说就是web页面,它们可以使用所有的浏览器提供的API,从XMLHttpRequest到JSON到HTML5全都有。
应用(扩展)可以与Web页面交互,或者通过content script或cross-origin XMLHttpRequests与服务器交互。应用(扩展)还可以访问浏览器提供的内部功能,例如标签或书签等。
应用(扩展)的界面
很多应用(不包括WebApp)会以browser action或page action的形式在浏览器界面上展现出来。每个应用(扩展)最多可以有一个browser action或page action。当应用(扩展)的图标是否显示出来是取决于单个的页面时,应当选择page action;当其它情况时可以选择browser action。
应用也可以通过其它方式提供界面,比如加入到上下文菜单,提供一个选项页面或者用一个content script改变页面的显示等。可以在"开发指南"中找到应用(扩展)特性的完整列表以及实现的细节。
文件
每个应用(扩展)都应该包含下面的文件:
- 一个manifest文件
- 一个或多个html文件(除非这个应用是一个皮肤)
- 可选的一个或多个javascript文件
- 可选的任何需要的其他文件,例如图片
在开发应用(扩展)时,需要把这些文件都放到同一个目录下。发布应用(扩展)时,这个目录全部打包到一个应用(扩展)名是.crx的压缩文件中。如果使用Chrome Developer Dashboard,上传应用(扩展),可以自动生成.crx文件。
引用文件
任何需要的文件都可以放到应用(扩展)中,但是怎么使用它们呢?一般的说,可以像在普通的HTML文件中那样使用相对地址来引用一个文件。下面的例子演示了如何引用images子目录下的文件myimage.png
![](images/myimage.png)
如果使用chrome开发者工具,可以看到每一个应用(扩展)中的文件也可以用一个绝对路径来表示:
chrome-extension://<extensionID>/<pathToFile>
在这个URL中,是为每一个应用(扩展)生成的唯一ID。从chrome://extensions页面中可以看到已经安装的所有应用(扩展)的唯一ID。是文件在应用(扩展)目录下的路径,也就是它的相对路径。
在这个URL中,名为manifest.json的文件包含了应用(扩展)的基本信息,例如最重要的文件列表,应用(扩展)所需要的权限等。
基本架构
绝大多数应用(扩展)都包含一个背景页面(background page),用来执行应用(扩展)的主要功能。
上图显示了安装了两个应用(扩展)的浏览器。两个应用(扩展)分别是黄色图标代表的browser action和蓝色图标代表的page action。在background.html文件里定义了browser action和javascript代码。在两个窗口里browser action都可以工作。
页面
背景页面并不是应用(扩展)中唯一的页面。例如,一个browser action可以包含一个弹窗(popup),而弹窗就是用html页面实现的。应用(扩展)还可以使用chrome.tabs.create()或者window.open()来显示内部的HTML文件。
应用(扩展)里面的HTML页面可以互相访问各自DOM树中的全部元素,或者互相调用其中的函数。
下图显示了一个browser action的弹窗的架构。弹窗的内容是由HTML文件(popup.html)定义的web页面。它不必复制背景页面(background.html)里的代码,因为它可以直接调用背景页面中的函数。
Content scripts
如果一个应用(扩展)需要与web页面交互,那么就需要使用一个content script。Content script脚本是指能够在浏览器已经加载的页面内部运行的javascript脚本。可以将content script看作是网页的一部分,而不是它所在的应用(扩展)的一部分。
Content script可以获得浏览器所访问的web页面的详细信息,并可以对页面做出修改。下图显示了一个content script可以读取并修改当前页面的DOM树。但是它并不能修改它所在应用(扩展)的背景页面的DOM树。
Content script与它所在的应用(扩展)并不是完全没有联系。一个content script脚本可以与所在的应用(扩展)交换消息,如下图所示。例如,当一个content script从页面中发现一个RSS种子时,它可以发送一条消息。或者由背景页面发送一条消息,要求content script修改一个网页的内容。
页面间的通信
一个应用(扩展)中的HTML页面间经常需要互相通信。由于一个应用(扩展)的所有页面是在同一个进程的同一个线程中运行的,因此它们之间可以直接互相调用各自的函数。
可以使用chrome.extension中的方法来获取应用(扩展)中的页面,例如getViews()和getBackgroundPage()。一旦一个页面得到了对应用(扩展)中其它页面的引用,它就可以调用被引用页面中的函数,并操作被引用页面的DOM树。
保存数据和隐身模式
应用(扩展)可以使用HTML5的 Web Storage API(例如localStorage)来保存数据,或者向服务器发出请求来保存数据。当需要保存数据的时候,首先需要确定是否从隐身模式窗口中发出的请求。缺省情况下,应用(扩展)是不会运行在隐身模式下的,而webapp是会的。需要明确用户在隐身模式下究竟需要应用(扩展)或webapp做什么。
隐身模式保证在该窗口下浏览不会留下痕迹。当处理隐身窗口的数据时,一定要遵循这个前提。例如,如果一个的应用(扩展)的功能是将浏览历史保存在云端(服务器),那么不要保存隐身模式下的浏览历史。另一方面,任何窗口下都可以保存应用(扩展)的数据,不论是否隐身。
重要规则:如果一条数据可能表明用户在网上看了什么或做了什么,不要在隐身模式下保存它。
要检查窗口是否在隐身模式下,检查Tab或Window对象的incognito属性。例如:
var bgPage = chrome.extension.getBackgroundPage();
function saveTabData(tab, data) {
if (tab.incognito) {
bgPage[tab.url] = data; // Persist data ONLY in memory
} else {
localStorage[tab.url] = data; // OK to store data
}
}
具体实现思路
//manifest.json
{
"manifest_version": 2,
"name": "条码打印插件",
"version": "0.0.1",
"background": {
//"page":"background.html"
"scripts": ["jquery2.1.1.min.js", "background.js"]
},
"page_action": {
"default_icon": {
"19": "cnblogs_19.png",
"38": "cnblogs_38.png"
},
"default_title": "条码预览", // shown in tooltip
"default_popup": "popup.html"
},
"permissions": ["tabs", "http://localhost/"],
"content_scripts": [{
"matches": ["http://localhost/*"],
"js": ["jquery2.1.1.min.js", "content_script.js"]
}]
}
content获取页面的数据,传给background,popup从background获取信息并生成条码和预览页面,打印popup
但实际做的过程中发现,popup页面无法调用打印函数print();网上查阅资料未果。
于是换种思路,数据可以传给服务器,由服务器生成打印页面。
翻阅文档,发现还可以覆写特定页面,这样连服务端也省了。
Override替代页
使用替代页,可以将Chrome默认的一些特定页面替换掉,改为使用扩展提供的页面。这让扩展开发者可以开发更多有趣或者实用的基本功能页面。替代页通常会有大量的CSS和JavaScript代码。
扩展可以替代如下页面:
书签管理器:从工具菜单上点击书签管理器时访问的页面,或者从地址栏直接输入 chrome://bookmarks。
历史记录:从工具菜单上点击历史记录时访问的页面,或者从地址栏直接输入 chrome://history。
新标签页:当创建新标签的时候访问的页面,或者从地址栏直接输入 chrome://newtab。
注意:一个扩展只能替代一个页面。
注意:如果你替代隐身窗口的页面,请注意要在manifest中将 incognito 属性指定为 "spanning"。
注意:你不能替代隐身窗口的新标签页。
Manifest
下面是在extension manifest中注册替代页的写法。
{
"name": "My extension",
...
"chrome_url_overrides" : {
"pageToOverride": "myPage.html"
},
...
}
对于示例中的pageToOverride,可替换成如下关键字
- bookmarks
- history
- newtab