在前面的章节,我们已经接触到了Chrome扩展中常用的大多数API,本章将挑选部分较为常用的高级API进行讲解,以便有更高要求的读者阅读。
5.1下载
Chrome提供了downloads API,扩展可以通过此API管理浏览器的下载功能,包括暂停、搜索和取消等。
相对于管理下载,更令人关注的是创建下载的功能。Chrome应用市场中之前包括很多下载页面所有图片等类似功能的扩展,大多数是将图片包含在一个网页中让用户另存为,或者是列出所有URL让用户自行下载。这样做明显不友好,Chrome处于早期版本时,开发者对开放下载功能的呼声也越来越高。所以本节将重点讲解如何让扩展通过downloads接口创建下载,有关进一步管理下载行为的内容请感兴趣的读者自行阅读。完整有关downloads接口的官方文档可以通过http://developer.chrome.com/extensions/downloads阅读。
扩展使用downloads接口需要在Manifest文件中声明downloads权限:
"permissions": [ "downloads" ]
创建下载可以通过downloads中的download方法实现。download方法包含两个参数,第一个是有关下载的属性对象,包括URL、保存位置、文件名等信息,第二个是创建成功后的回调函数。
chrome.downloads.download(options, callback);
其中options的完整结构如下:
{ url: 下载文件的url, filename: 保存的文件名, conflictAction: 重名文件的处理方式, saveAs: 是否弹出另存为窗口, method: 请求方式(POST或GET), headers: 自定义header数组, body: POST的数据 }
其中conflictAction的取值只能是uniquify(在文件名后添加带括号的序号保证文件名唯一)、overwrite(覆盖)和prompt(给出提示让用户自行决定重命名或者覆盖)。
filename可以是单纯的文件名,如'foo.txt';也可以带有相对路径,如'mypath/foo.txt'。但不可以是绝对路径,或是一个目录,也不可以在路径中包含上级路径'..'。如这三种情况都是非法的:'/mypath/foo.txt'、'mypath/'、'../mypath/foo.txt'。
如果给定了filename,同时saveAs属性为true,则弹出的另存为对话框中,文件名一栏的默认值会被设为filename指定的值。
下面让我们来一起编写一个下载当前页面所有图片的扩展。
这个扩展我准备设计成用户在页面点击右键时,菜单中包含一个下载所有图片的选项。这个过程首先要在右键菜单中创建一个选项,我们需要一个background脚本。因为要获取当前页面的图片元素,所以我们要向当前标签页注入脚本。这几点分析清楚之后,我们就可以开始了。
首先创建manifest.json。
{ "manifest_version": 2, "name": "Save all images", "version": "1.0", "description": "Save all images in current tab", "background": { "scripts": ["background.js"], "persistent": false }, "permissions": [ "activeTab", "contextMenus", "downloads" ] }
下面编写background.js文件,这个文件用来创建右键菜单,并在用户点击菜单后向当前标签页注入脚本,最后还要完成下载的行为。
chrome.runtime.onInstalled.addListener(function(){ chrome.contextMenus.create({ 'id':'saveall', 'type':'normal', 'title':'保存所有图片', }); }); chrome.contextMenus.onClicked.addListener(function(info, tab){ if(info.menuItemId == 'saveall'){ chrome.tabs.executeScript(tab.id, {file: 'main.js'}, function(results){ if (results && results[0] && results[0].length){ results[0].forEach(function(url) { chrome.downloads.download({ url: url, conflictAction: 'uniquify', saveAs: false }); }); } }); } });
最后来编写注入脚本,main.js。
[].map.call(document.getElementsByTagName('img'), function(img){ return img.src; });
至此,通过右键菜单下载所有图片的扩展就编写完成了。
本例中没有指定扩展的图标,但在成熟的产品中,自定义右键菜单时,应当指定一个16像素的图标。
本节所涉及到的代码可以通过https://github.com/sneezry/chrome_extensions_and_apps_programming/tree/master/save_all_images 下载得到。
5.2网络请求
Chrome提供了较为完整的方法供扩展程序分析、阻断及更改网络请求,同时也提供了一系列较为全面的监听事件以监听整个网络请求生命周期的各个阶段。网络请求的整个生命周期所触发事件的时间顺序如下图所示。
网络请求的生命周期,图片来自developer.chrome.com
要对网络请求进行操作,需要在Manifest中声明webRequest权限以及相关被操作的URL。如需要阻止网络请求,需要声明webRequestBlocking权限。
"permissions": [ "webRequest", "webRequestBlocking", "*://*.google.com/" ]
上面的权限声明表示此扩展可以对浏览器向Google发起的网络请求进行更改。webRequest接口无法在Event Page中使用。
目前对于网络请求,比较实用的功能包括阻断连接、更改header和重定向。
下面的代码阻断了所有向bad.example.com的连接:
chrome.webRequest.onBeforeRequest.addListener( function(details){ return {cancel: true}; }, { urls: [ "*://bad.example.com/*" ] }, [ "blocking" ] );
而下面的代码则将所有连接中的User-Agent信息都删除了:
chrome.webRequest.onBeforeSendHeaders.addListener( function(details){ for(var i=0, headerLen=details.requestHeaders.length; i<headerLen; ++i){ if(details.requestHeaders[i].name == 'User-Agent'){ details.requestHeaders.splice(i, 1); break; } } return {requestHeaders: details.requestHeaders}; }, { urls: [ "<all_urls>" ] }, [ "blocking", "requestHeaders" ] );
需要注意的是,header中的如下属性是不支持更改的:Authorization、Cache-Control、Connection、Content-Length、Host、If-Modified-Since、If-None-Match、If-Range、Partial-Data、Pragma、Proxy-Authorization、Proxy-Connection和Transfer-Encoding。
下面的代码将所有访问www.google.com.hk的请求重定向到了www.google.com:
chrome.webRequest.onBeforeRequest.addListener( function(details){ return {redirectUrl: details.url.replace( "www.google.com.hk", "www.google.com")}; }, { urls: [ "*://www.google.com.hk/*" ] }, [ "blocking" ] );
想想我们是不是可以做一个本地的JS Library CDN呢?
所有事件中,回调函数所接收到的信息对象均包括如下属性:requestId、url、method、frameId、parentFrameId、tabId、type和timeStamp。其中type可能的值包括"main_frame"、"sub_frame"、"stylesheet"、"script"、"image"、"object"、"xmlhttprequest"和"other"。
除了onBeforeRequest和onErrorOccurred事件外,其他所有事件返回的信息对象均包含HttpHeaders属性;onHeadersReceived、onAuthRequired、onResponseStarted、onBeforeRedirect和onCompleted事件均包括statusLine属性以显示请求状态,如'HTTP/0.9 200 OK'。其他的属性还包括scheme、realm、challenger、isProxy、ip、fromCache、statusCode、redirectUrl和error等,由于使用范围较小,在此不详细介绍,读者可自行到http://developer.chrome.com/extensions/webRequest 阅读完整内容。
5.3代理
代理可以让用户通过代理服务器浏览网络资源以达到匿名访问等目的。代理的类型有多种,常用的包括http代理和socks代理等。有时我们不希望所有的网络资源都通过代理浏览,这种情况下通常会使用pac脚本来告诉浏览器使用代理访问的规则。
Chrome浏览器提供了代理设置管理接口,这样可以让扩展来做到更加智能的代理设置。要让扩展使用代理接口,需要声明proxy权限:
"permissions": [ "proxy" ]
通过chrome.proxy.settings.set方法可以设置代理服务器,该方法需要两个参数,一个是代理设置对象,另一个是回调函数。
代理设置对象包括mode属性、rules属性和pacScript属性。其中mode属性为代理模式,可选的值有'direct'(直接连接,即不通过代理)、'auto_detect'(通过WPAD协议自动获取pac脚本)、'pac_script'(使用指定的pac脚本)、'fixed_servers'(固定的代理服务器)和'system'(使用系统的设置)。
rules属性和pacScript属性都是可选的,rules指定了不同的协议通过不同的代理,比如:
var config = { mode: "fixed_servers", rules: { proxyForHttp: { scheme: "socks5", host: "1.2.3.4", port: 1080 }, proxyForHttps: { scheme: "socks5", host: "1.2.3.5", port: 1080 }, proxyForFtp: { scheme: "http", host: "1.2.3.6", port: 80 } bypassList: ["foobar.com"] } }; chrome.proxy.settings.set( {value: config}, function() { });
上面的代码定义了所有http协议的流量都使用1.2.3.4:1080这个socks5代理服务器代理浏览,所有https协议的流量都使用1.2.3.5:1080这个socks5代理服务器浏览,所有ftp协议的流量都使用1.2.3.6:80这个http代理服务器浏览,而foobar.com的流量不使用任何代理服务器,直接进行访问。rules还提供了singleProxy属性(任何协议都使用此代理)和fallbackProxy属性(未匹配到的协议使用此代理)。
pacScript指定了使用的pac脚本,可以通过url属性指定脚本位置,也可以直接通过data属性指定脚本内容。pacScript还提供了mandatory属性以让浏览器决定当pac无效时是否阻止自动切换成直接访问,此属性默认为false,即当pac无效时浏览器直接访问。
通过chrome.proxy.settings.get方法可以获取到浏览器当前的代理设置:
chrome.proxy.settings.get( {}, function(config) { console.log(config.value); } );
本节将不为大家提供demo,而是直接带大家分析目前比较流行的Chrome代理管理扩展,SwitchySharp有关代理设置的核心代码。
SwitchySharp的完整代码可以通过https://code.google.com/p/switchysharp获取到,其中代理设置核心的代码为assets/scripts/plugin.js,可以通过https://code.google.com/p/switchysharp/source/browse/assets/scripts/plugin.js在线查看此文件。
var ProxyPlugin = {}; ProxyPlugin.memoryPath = memoryPath; ProxyPlugin.proxyMode = Settings.getValue('proxyMode', 'direct'); ProxyPlugin.proxyServer = Settings.getValue('proxyServer', ''); ProxyPlugin.proxyExceptions = Settings.getValue('proxyExceptions', ''); ProxyPlugin.proxyConfigUrl = Settings.getValue('proxyConfigUrl', ''); ProxyPlugin.autoPacScriptPath = Settings.getValue('autoPacScriptPath', ''); ProxyPlugin.mute = false;
SwitchySharp首先声明了一个ProxyPlugin对象,此对象用来储存代理设置和代理设置方法。其中proxyMode属性为代理模式,和上文中讲到的代理模式相对应,但fixed_server模式在proxyMode中对应的值为manual;proxyServer属性为代理服务器地址;proxyExceptions属性为不使用代理设置的例外,与上文提到的bypassList相对应;proxyConfigUrl属性为pac脚本的URL;autoPacScriptPath为SwitchySharp中自动切换模式下使用的pac脚本路径。
mute属性用来记录代理是否正在设置当中,如果不是,则此属性值为false,如果代理设置正在被更改,则此值为ture,用来避免设置冲突。最后_proxy属性用来获取Chrome中代理设置的方法,为了做到最大限度兼容,SwitchySharp对代理接口依然处于实验性阶段版本的Chrome进行了优化:
if (chrome.experimental !== undefined && chrome.experimental.proxy !== undefined) ProxyPlugin._proxy = chrome.experimental.proxy; else if (chrome.proxy !== undefined) ProxyPlugin._proxy = chrome.proxy; else alert('Need proxy api support, please update your Chrome');
ProxyPlugin的updateProxy方法用来更新代理设置选项,这个方法在开始就先判断mute的值是否为真,也就是判断此时代理设置是否正在被更改,如果是则退出避免设置冲突。
ProxyPlugin._parseProxy = function (str) { if (str) { var proxy = {scheme:'http', host:'', port:80}; var t1 = null; var t = str.indexOf(']') + 1; if (t > 0) { t1 = new Array(); t1.push(proxy.host = str.substr(0, t)); if (t < str.length - 1) t1.push(str.substr(t + 1)); } else { t1 = str.split(':'); proxy.host = t1[0]; } var t2 = proxy.host.split('='); if (t2.length > 1) { proxy.scheme = t2[0] == 'socks' ? 'socks4' : t2[0]; proxy.host = t2[1]; } if (t1.length > 1) proxy.port = parseInt(t1[1]); return proxy; } else return {} };
_parseProxy方法用来解析声明多种代理的规则字符串,此方法将字符串转化为用于fixed_servers模式下的rules对象。
ProxyPlugin.setProxy = function (proxyMode, proxyString, proxyExceptions, proxyConfigUrl) { ... switch (proxyMode) { case 'system': config = {mode:"system"}; break; ... } ProxyPlugin.mute = true; ProxyPlugin._proxy.settings.set({'value':config}, function () { ProxyPlugin.mute = false; if (ProxyPlugin.setProxyCallback != undefined) { ProxyPlugin.setProxyCallback(); ProxyPlugin.setProxyCallback = undefined; } }); profile = null; config = null; return 0; };
最后setProxy方法将ProxyPlugin中与设置相关的属性重新整合成一个适用于chrome.proxy.settings.set方法的config对象,并调用ProxyPlugin._proxy.settings.set方法使之生效。
5.4系统信息
Chrome提供了获取系统CPU、内存和存储设备的信息,要获取这些信息,需要在Manifest中分别声明如下权限:
"permissions": [ "system.cpu", "system.memory", "system.storage" ]
三个接口都提供了getInfo方法以获取信息:
chrome.system.cpu.getInfo(function(info){ console.log(info); }); chrome.system.memory.getInfo(function(info){ console.log(info); }); chrome.system.storage.getInfo(function(info){ console.log(info); });
CPU的信息包括numOfProcessors、archName、modelName、features和processors,其中processors为一个记录所有逻辑处理器信息的数组。
内存信息包括capacity和availableCapacity,即总容量和可用容量。
存储空间信息为一个包含多个存储设备信息的数组,每个存储设备的信息包括id、name、type和capacity,其中type的可能值包括fixed(本地磁盘)、removable(可移动磁盘)和unknown(未知设备)。
system.storage还提供了获取指定设备剩余空间和移除移动磁盘的方法1:
chrome.system.storage.getAvailableCapacity(deviceId, function(info){ console.log(info.availableCapacity); }); chrome.system.storage.ejectDevice(deviceId, function(result){ console.log(result); });
1 目前getAvailableCapacity在稳定版Chrome中不可用。
chome.system.storage.onAttached和chome.system.storage.onDetached事件分别用于监听可移动设备的插入和移除。
chrome.system.storage.onAttached.addListener(function(info){ console.log(info); }); chrome.system.storage.onDetached.addListener(function(deviceId){ console.log(deviceId); });
以上三个接口目前来说还比较新,这意味着Google可能会添加新的方法或者更改现有的方法,也可能移除这些方法,建议开发者在使用这些接口时谨慎选择。
-----------------------------------------------------
转载请注明来源此处
原地址:#
发表