文件可以告诉Chrome关于这个扩展的相关信息,它是整个扩展的入口,也是Chrome扩展必不可少的部分
注:
Google的官方文档中对于扩展和应用给出了两个不同的Manifest介绍界面,这是因为有些属性只能由扩展使用,而有些属性只能由应用使用。
如果这两者同时出现在同一个Manifest文件中,就会使Chrome困惑,不知是按照扩展对待这个程序还是按照应用来对待这个程序。
但无论是扩展还是应用,它们的Manifest又有很多共有的属性。
Chrome扩展的Manifest必须包含name
、version
和manifest_version
属性,目前来说manifest_version
属性值只能为数字2,对于应用来说,还必须包含app
属性。
browser_action
、page_action
、background
、permissions
、options_page
、content_scripts
,
所以我们可以保留一份manifest.json模板,当编写新的扩展时直接填入相应的属性值。
可以应对大部分的扩展的模板:
{ "app": { "background": { "scripts": ["background.js"] } }, "manifest_version": 2, "name": "My Extension", "version": "versionString", "default_locale": "en", "description": "A plain text description", "icons": { "16": "images/icon16.png", "48": "images/icon48.png", "128": "images/icon128.png" }, "browser_action": { "default_icon": { "19": "images/icon19.png", "38": "images/icon38.png" }, "default_title": "Extension Title", "default_popup": "popup.html" }, "page_action": { "default_icon": { "19": "images/icon19.png", "38": "images/icon38.png" }, "default_title": "Extension Title", "default_popup": "popup.html" }, "background": { "scripts": ["background.js"] }, "content_scripts": [ { "matches": ["http://www.google.com/*"], "css": ["mystyles.css"], "js": ["jquery.js", "myscript.js"] } ], "options_page": "options.html", "permissions": [ "*://www.google.com/*" ], "web_accessible_resources": [ "images/*.png" ] }在官方文档中可以找到完整的Manifest属性列表,扩展在 https://developer.chrome.com/extensions/manifest
实际上就是对用户当前浏览页面的DOM进行操作。通过Manifest中的content_scripts
属性可以指定将哪些脚本何时注入到哪些页面中,
当用户访问这些页面后,相应脚本即可自动运行,从而对页面DOM进行操作。
Manifest的content_scripts
属性值为数组类型,数组的每个元素可以包含:
matches
//定义了哪些页面会被注入脚本
exclude_matches//定义了哪些页面不会被注入脚本
css /js //对应要注入的样式表和JavaScript
run_at//何时进行注入
all_frames
//脚本是否会注入到嵌入式框架中
include_globs//
exclude_globs
//全局URL匹配,最终脚本是否会被注入由matches
、exclude_matches
、include_globs
和exclude_globs
的值共同决定。
简单的说,如果URL匹配mathces
值的同时也匹配include_globs
的值,会被注入;如果URL匹配exclude_matches
的值或者匹配exclude_globs
的值,则不会被注入。
content_scripts
中的脚本只是共享页面的DOM1,而并不共享页面内嵌JavaScript的命名空间。也就是说,如果当前页面中的JavaScript有一个全局变量a
,content_scripts
中注入的脚本也可以有一个全局变量a
,两者不会相互干扰。
当然你也无法通过content_scripts
访问到页面本身内嵌JavaScript的变量和函数。
跨域指的是JavaScript通过XMLHttpRequest
请求数据时,调用JavaScript的页面所在的域和被请求页面的域不一致。
对于网站来说,浏览器出于安全考虑是不允许跨域。另外,对于域相同,但端口或协议不同时,浏览器也是禁止的。下表给出了进一步的说明:
URL | 说明 | 是否允许请求 |
---|---|---|
http://a.example.com/ http://a.example.com/a.txt |
同域下 | 允许 |
http://a.example.com/ http://a.example.com/b/a.txt |
同域下不同目录 | 允许 |
http://a.example.com/ http://a.example.com:8080/a.txt |
同域下不同端口 | 不允许 |
http://a.example.com/ https://a.example.com/a.txt |
同域下不同协议 | 不允许 |
http://a.example.com/ http://b.example.com/a.txt |
不同域下 | 不允许 |
http://a.example.com/ http://a.foo.com/a.txt |
不同域下 | 不允许 |
解决:
但这个规则如果同样限制Chrome扩展应用,就会使其能力大打折扣,所以Google允许Chrome扩展应用不必受限于跨域限制。
但出于安全考虑,需要在Manifest的permissions
属性中声明需要跨域的权限。
比如,如果我们想设计一款获取维基百科数据并显示在其他网页中的扩展,就要在Manifest中进行如下声明:
{ ... "permissions": [ "*://*.wikipedia.org/*" ] }
可以利用如下的代码发起异步请求:
function httpRequest(url, callback){ var xhr = new XMLHttpRequest(); xhr.open("GET", url, true); xhr.onreadystatechange = function() { if (xhr.readyState == 4) { callback(xhr.responseText); } } xhr.send(); }
这样每次发起请求时,只要调用httpRequest
函数,并传入要请求的URL和接收返回结果的函数就可以了。为什么要使用callback
函数接收请求结果,而不直接用return
将结果作为函数值返回呢?因为XMLHttpRequest
不会阻塞下面代码的运行。
为了更加明确地说清上述问题,让我们来举两个例子。
function count(n){ var sum = 0; for(var i=1; i<=n; i++){ sum += i; } return sum; } var c = count(5)+1; console.log(c); 这个例子会在控制台显示16, 因为:<code>count</code>函数是一个阻塞函数,在它没有运行完之前它会阻塞下面的代码运行, 所以直到<code>count</code>计算结束后才将结果返回后再加<code>1</code>赋给变量<code>c</code>,最后将变量<code>c</code>的值打印出来
unction httpRequest(url){ var xhr = new XMLHttpRequest(); xhr.open("GET", url, true); xhr.onreadystatechange = function() { if (xhr.readyState == 4) { return xhr.responseText; } } xhr.send(); } var html = httpRequest('test.txt'); console.log(html); 虽然请求的资源内容为<code>Hello World!</code>,但却并没有正确地显示出来 原因: <code>httpRequest</code>函数不是一个阻塞函数,在它没运行完之前后面的代码就已经开始运行, 这样<code>html</code>变量在<code>httpRequest</code>函数没返回值之前就被赋值,所以最终的结果必然就是<code>undefined</code>了。
既然这样,如何将非阻塞函数的最终结果传递下去呢?方法就是使用回调函数。在Chrome扩展应用的API中,大部分函数都是非阻塞函数,
所以使用回调函数传递结果的方法以后会经常用到。
让我们来用回调函数的形式重写第二个例子:
function httpRequest(url, callback){ var xhr = new XMLHttpRequest(); xhr.open("GET", url, true); xhr.onreadystatechange = function() { if (xhr.readyState == 4) { callback(xhr.responseText); } } xhr.send(); } var html; httpRequest('test.txt', function(result){ html = result; console.log(html); <code>httpRequest</code>函数运行的结果已经被正确地打印出来了。 });
{ "manifest_version": 2, "name": "查看我的IP", "version": "1.0", "description": "查看我的电脑当前的公网IP", "icons": { "16": "images/icon16.png", "48": "images/icon48.png", "128": "images/icon128.png" }, "browser_action": { "default_icon": { "19": "images/icon19.png", "38": "images/icon38.png" }, "default_title": "查看我的IP", "default_popup": "popup.html" }, "permissions": [ "http://sneezryworks.sinaapp.com/ip.php" //的Manifest定义了这个扩展允许对http://sneezryworks.sinaapp.com/ip.php发起跨域请求 ] }
<html> <head> <style> * { margin: 0; padding: 0; } body { width: 400px; height: 100px; } div { line-height: 100px; font-size: 42px; text-align: center; } </style> </head> <body> <div id="ip_div">正在查询……</div> <script src="js/my_ip.js"></script>//js文件夹下有my_ip.js文件 </body> </html>
function httpRequest(url, callback){ var xhr = new XMLHttpRequest(); xhr.open("GET", url, true); xhr.onreadystatechange = function() { if (xhr.readyState == 4) { callback(xhr.responseText); } } xhr.send(); } httpRequest('http://sneezryworks.sinaapp.com/ip.php', function(ip){ document.getElementById('ip_div').innerText = ip; });
结果:
作为一个开发者,安全问题永远都不应被轻视。在你从外域获取到数据后,不要轻易作为当前页面元素的innerHTML
直接插入,更不要用eval
函数去执行它,否则很可能将用户置于危险的境地。如果要将请求到的数据写入页面,可以使用innerText
,就像我们这个查看IP的扩展那样。如果是JSON格式是数据就使用JSON.parse
函数去解析。为了避免请求数据返回的格式错误,结合try-catch
一起使用也是不错的选择。