├── README.md ├── manifest-incognito.md ├── options.md ├── getstarted.md ├── manifest.md ├── contextMenus.md ├── desktop_notifications.md ├── background_pages.md ├── storage.md ├── xhr.md ├── declare_permissions.md ├── autoupdate.md ├── browserAction.md ├── overview.md ├── content_scripts.md └── messaging.md /README.md: -------------------------------------------------------------------------------- 1 | ## Chrome扩展程序开发部分中文文档 2 | 3 | 本系列文章翻译自 [Google Chrome Extensions](https://developer.chrome.com/) 4 | 5 | 只翻译了一部分常用功能的文档,已经足够让我们开发一个 chrome 扩展工具了 6 | 7 | ## 目录 8 | ### 入门 9 | * [入门:建立chrome扩展程序](getstarted.md) 10 | 11 | ### 概述 12 | * [概述:准备好了](overview.md) 13 | 14 | ### 常用核心文档 15 | 16 | * [浏览器按钮 browserAction](browserAction.md) 17 | * [鼠标右键菜单 contextMenus](contextMenus.md) 18 | * [内容脚本 content_scripts](content_scripts.md) 19 | * [声明权限](declare_permission.md) 20 | * [丰富的通知](desktop_notifications.md) 21 | * [清单文件格式 manifest](manifest.md) 22 | * [清单文件-隐身模式 incognito.md](manifest-incognito.md) 23 | * [消息传递](messaging.md) 24 | * [后台网页 background_pages](background_pages.md) 25 | * [storage](storage.md) 26 | * [选项](options.md) 27 | * [自动更新](autoupdate.md) 28 | * [跨站 XMLHttpRequest](xhr.md) 29 | -------------------------------------------------------------------------------- /manifest-incognito.md: -------------------------------------------------------------------------------- 1 | # 清单文件——隐身模式 2 | 3 | 在清单文件的 "incognito" 字段使用 "spanning" 或 "split" 指定扩展程序如果在隐身模式下允许运行的话应该有怎样的行为。 4 | 5 | 只有扩展程序可以选择,应用总是使用默认值:Chrome 应用为 "spanning",可安装的网上应用与旧版打包应用为 "split"。 6 | 7 | ## 跨越模式 8 | 9 | 对于扩展程序和 Chrome 应用来说默认值为 "spanning",意味着它在单个共享进程中运行,任何来自隐身标签页的事件或消息都将发送至共享的进程,以 incognito 标志表示它的来源。由于隐身标签页无法使用这一共享进程,使用 "spanning" 隐身模式不能将扩展程序包中的网页加载到隐身标签页的主框架。 10 | 11 | ## 分离模式 12 | 13 | 对于可安装的网上应用与旧版打包应用来说默认值为 "split",意味着隐身窗口中的所有应用页面在它们自己的隐身进程中运行。如果应用或扩展程序包含后台网页,它们也会在隐身进程中运行。隐身进程与普通进程同时运行,但是使用单独的仅保留在内存中的 Cookie 存储区。每一个进程只能接收到来自它自己上下文的事件与消息(例如,隐身进程只会看到隐身标签页的更新)。两个进程之间无法互相通信。 14 | 15 | ## 如何选择 16 | 17 | 选择的准则是,如果您的扩展程序或者应用需要在隐身模式下加载标签页,请使用 split 隐身行为。如果您的扩展程序或应用需要登录远程服务器或者在本地保留设置,请使用 spanning 隐身行为。 18 | 19 | chrome.storage.sync 和 chrome.storage.local 始终 在普通与隐身进程中共享,建议使用它们保存您的扩展程序设置。 -------------------------------------------------------------------------------- /options.md: -------------------------------------------------------------------------------- 1 | # 选项 2 | 3 | 为了让用户自定义您的扩展程序的行为,您可能会提供一个选项页面。如果这样做的话,选项页面的链接将会在扩展程序管理页面(chrome://extensions)中显示,单击“选项”链接打开一个新标签页,指向您的选项页面。 4 | 5 | ## 第一步:在清单文件中声明您的选项页面 6 | 7 | manifest.json 8 | 9 | ``` 10 | { 11 | "name": "我的扩展程序", 12 | ... 13 | "options_page": "options.html", 14 | ... 15 | } 16 | ``` 17 | 18 | ## 第二步:编写您的选项页面 19 | 20 | 如下是一个选项页面的例子: 21 | 22 | options.html 23 | 24 | ``` html 25 | 26 | 我的测试扩展程序选项 27 | 28 | 29 | 30 | 我最喜欢的颜色: 31 | 37 | 38 |
39 |
40 | 41 | 42 | 43 | 44 | 45 | 46 | ``` 47 | 48 | options.js 49 | 50 | ``` 51 | // 将选项保存在 localStorage 中。 52 | function save_options() { 53 | var select = document.getElementById("color"); 54 | var color = select.children[select.selectedIndex].value; 55 | localStorage["favorite_color"] = color; 56 | 57 | // 更新状态,告诉用户选项已保存。 58 | var status = document.getElementById("status"); 59 | status.innerHTML = "Options Saved."; 60 | setTimeout(function() { 61 | status.innerHTML = ""; 62 | }, 750); 63 | } 64 | 65 | // 从保存在 localStorage 中的值恢复选定的内容。 66 | function restore_options() { 67 | var favorite = localStorage["favorite_color"]; 68 | if (!favorite) { 69 | return; 70 | } 71 | var select = document.getElementById("color"); 72 | for (var i = 0; i < select.children.length; i++) { 73 | var child = select.children[i]; 74 | if (child.value == favorite) { 75 | child.selected = "true"; 76 | break; 77 | } 78 | } 79 | } 80 | document.addEventListener('DOMContentLoaded', restore_options); 81 | document.querySelector('#save').addEventListener('click', save_options); 82 | ``` 83 | 84 | -------------------------------------------------------------------------------- /getstarted.md: -------------------------------------------------------------------------------- 1 | # 入门:创建chrome的扩展程序 2 | 3 | 扩展程序允许你为 Chrome 浏览器增加功能,但不需要深入研究本机代码。可以使用在网页开发中已经很熟悉的前端技术来开发新的扩展程序。 4 | 5 | 下面将实现一个称为浏览器按钮的用户界面元素,将一个可单击的按钮放在浏览器地址栏的右边,单击按钮弹出弹出窗口,显示很多猫。 6 | 7 | 不翻译废话了,直接开编辑器一步一步的写吧: 8 | 9 | ## 要声明的内容 10 | 我们要创建一个清单文件 manifest.json 。这是一个 JSON 格式的目录,包含了扩展程序的名称、描述、版本号等属性。从更高层次来看,我们使用它来向 Chrome 浏览器生命扩展程序将会做什么以及所需要的权限。 11 | 12 | 为了显示猫,应该告诉 Chrome 要创建一个按钮,还希望自由访问某个特定来源的猫。 13 | 14 | 创建manifest.json: 15 | 16 | ``` 17 | { 18 | "manifest_version": 2, 19 | 20 | "name": "One-click Kittens", 21 | "description": "This extension demonstrates a browser action with kittens.", 22 | "version": "1.0", 23 | 24 | "permissions": [ 25 | "https://secure.flickr.com/" 26 | ], 27 | "browser_action": { 28 | "default_icon": "icon.png", 29 | "default_popup": "popup.html" 30 | } 31 | } 32 | ``` 33 | ### manifest.json 文件的含义 34 | 35 | 第一行生命我们使用的manifest版本为 2 (1已经过时了,废弃) 36 | 37 | 接下来的部分定义扩展程序的名称、描述、版本。这些会现用户显示已安装的扩展程序,同时在Chrome的商店中向潜在的新用户推广 38 | 39 | 最后一部分是权限请求,用于访问https://secure.flickr.com/上的数据,同时声明了该扩展实现了一个浏览器按钮,并为它指定一个默认图标和弹出窗口 40 | 41 | ### 资源 42 | manifest文件指向了两个资源文件:icon.png 和 popup.html。 这两个资源必须在扩展程序包中存在,所以必须要创建。 43 | 44 | icon.png 会在地址栏右侧出现,等待用户交互。 45 | popup.html 文件还需要额外的js,直接从google代码库[下载]() 46 | 47 | 现在工作目录中应该有四个文件: icon.png、manifest.json、popup.html、popup.js 。下一步就是加载。 48 | 49 | ### 加载扩展程序 50 | 从Chrome商店上下载的扩展程序打包为 .crx 文件,便于发布但不便于开发,Chrome提供了可以加载工作目录用于测试的方式: 51 | 52 | 1. 在Chrome中访问 chrome://extensions 53 | 2. 选中右上角开发者模式 54 | 3. 单击加载正在开发的扩展程序,弹出对话框 55 | 4. 选择自己的工作目录 56 | 57 | 如果加载无效,顶部将会显示错误消息。 58 | 59 | ### 修改代码 60 | 现在已经准备好了第一个扩展程序,可以尝试做些修改,修改popup.js:将 var QUERY = 'kittens'; 改为 var QUERY = 'puppies'; 61 | 62 | 保存后在单击扩展程序的浏览器按钮,是没有更新到新的效果的。需要让Chrome知道发生了更改,回到 chrome://extensions 中单击重新加载或者刷新页面,即完成更新。 63 | 64 | ### 接下来 65 | * 概述会对Chrome扩展做进一步的介绍 66 | * 调试教程非常适合帮助代码Debug 67 | * Chrome 扩展程序能够访问强大的 API,远远超过了普通网页可用的部分:浏览器按钮就是冰山一角。chrome.* API 文档将让你了解每一个 API。 68 | * 开发者指南包含了很多额外的链接,指向你感兴趣的文档。 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /manifest.md: -------------------------------------------------------------------------------- 1 | #清单文件格式 2 | 3 | 每一个扩展程序、可安装的网络应用以及主题背景都有一个 [JSON](http://www.json.org/) 格式的清单文件,名为 manifest.json,提供重要信息。 4 | 5 | ## 字段概述 6 | 7 | 如下代码展示了支持的清单文件字段,以及讨论每一个字段的链接。只有 name 和 version 字段是必需的。 8 | 9 | ``` 10 | { 11 | // 必选 12 | "manifest_version": 2, 13 | "name": "我的扩展程序", 14 | "version": "版本字符串", 15 | 16 | // 推荐 17 | "default_locale": "en", 18 | "description": "纯文本描述", 19 | "icons": {...}, 20 | 21 | // 选择某一个(或者无) 22 | "browser_action": {...}, 23 | "page_action": {...}, 24 | 25 | // 可选 26 | "author": ..., 27 | "background": { 28 | // 推荐 29 | "persistent": false 30 | }, 31 | "background_page": ..., 32 | "chrome_settings_overrides": ..., 33 | "chrome_url_overrides": {...}, 34 | "commands": { 35 | "global": ... 36 | }, 37 | "content_pack": ..., 38 | "content_scripts": [{...}], 39 | "content_security_policy": "策略字符串", 40 | "converted_from_user_script": ..., 41 | "current_locale": ..., 42 | "devtools_page": ..., 43 | "externally_connectable": { 44 | "matches": ["*://*.example.com/*"] 45 | }, 46 | "file_browser_handlers": [...], 47 | "homepage_url": "http://path/to/homepage", 48 | "import": ..., 49 | "incognito": "spanning 或 split", 50 | "input_components": ..., 51 | "key": "公钥", 52 | "minimum_chrome_version": "版本字符串", 53 | "nacl_modules": [...], 54 | "oauth2": ..., 55 | "offline_enabled": true, 56 | "omnibox": { 57 | "keyword": "aString" 58 | }, 59 | "optional_permissions": ..., 60 | "options_page": "aFile.html", 61 | "page_actions": ..., 62 | "permissions": [...], 63 | "platforms": ..., 64 | "plugins": [...], 65 | "requirements": {...}, 66 | "sandbox": [...], 67 | "script_badge": ..., 68 | "short_name": "短名称", 69 | "signature": ..., 70 | "spellcheck": ..., 71 | "storage": { 72 | "managed_schema": "schema.json" 73 | }, 74 | "system_indicator": ..., 75 | "tts_engine": ..., 76 | "update_url": "http://path/to/updateInfo.xml", 77 | "web_accessible_resources": [...] 78 | } 79 | ``` -------------------------------------------------------------------------------- /contextMenus.md: -------------------------------------------------------------------------------- 1 | # chrome.contextMenus 2 | 3 | * 描述: 使用 chrome.contextMenus API 向 Google Chrome 浏览器的右键菜单添加项目。您可以选择您在右键菜单中添加的项目应用于哪些类型的对象,例如图片、超链接和页面。 4 | * 从 Chrome 6 开始稳定支持。 5 | * 权限:"contextMenus" 6 | 7 | ## 用法 8 | 9 | 右键菜单项可以在任何文档(或文档中的框架)中出现,即使它们使用 file:// 或 chrome:// 协议的 URL。要控制您的菜单项在哪些文档中出现,在您调用 create() 或 update() 方法时请指定 documentUrlPatterns 属性。 10 | 11 | 您可以根据自己的需要创建任意数目的右键菜单项,但是如果您的扩展程序一次显示超过一个菜单项,Google Chrome 浏览器会自动将它们折叠至一个父菜单中。 12 | 13 | 14 | ## 清单文件 15 | 16 | 您必须在扩展程序的清单文件中声明 "contextMenus" 权限才能使用有关 API,您还应该指定一个 16×16 像素的图标,显示在您的菜单项旁边。例如: 17 | 18 | manifest.json 19 | 20 | ``` 21 | { 22 | "name": "我的扩展程序", 23 | ... 24 | "permissions": [ 25 | "contextMenus" 26 | ], 27 | "icons": { 28 | "16": "icon-bitty.png", 29 | "48": "icon-small.png", 30 | "128": "icon-large.png" 31 | }, 32 | ... 33 | } 34 | ``` 35 | 36 | 如果您只提供 19px 或 38px 图标大小中的一个,扩展程序系统将会缩放您提供的图标,以适应用户显示器的像素密度,这有可能会丢失细节或使它看上去很模糊。注册默认图标的旧语法仍然支持 37 | 38 | manifest.json 39 | 40 | ``` 41 | { 42 | "name": "我的扩展程序", 43 | ... 44 | "browser_action": { 45 | ... 46 | "default_icon": "images/icon19.png" // 可选 47 | // 等价于 "default_icon": { "19": "images/icon19.png" } 48 | }, 49 | ... 50 | } 51 | ``` 52 | 53 | ## 例子 54 | 55 | 您可以在 [extensions/samples](https://developer.chrome.com/extensions/samples#search:contextMenus) 目录中找到简单例子。 56 | 57 | 58 | ## 方法 59 | 60 | 每个方法的具体参数属性直接看[API](https://developer.chrome.com/extensions/contextMenus) 61 | 62 | ### create 63 | `rome.browserAction.setTitle(object details)` 64 | 创建一个新的右键菜单项。注意,如果创建过程中发生错误,您可能要等到回调函数调用时才能得知。 65 | 66 | ### update 67 | `chrome.contextMenus.update(integer or string id, object updateProperties, function callback)` 68 | 69 | 70 | 更新以前创建的菜单项。 71 | 72 | ### remove 73 | 74 | `chrome.contextMenus.remove(integer or string menuItemId, function callback)` 75 | 76 | 移除右键菜单项。 77 | 78 | ### removeAll 79 | 80 | `chrome.contextMenus.removeAll(function callback)` 81 | 82 | 移除该扩展程序添加的所有右键菜单项。 83 | 84 | ## 事件 85 | 86 | 87 | ### onClicked 88 | 89 | `chrome.contextMenus.onClicked.addListener(function callback)` 90 | 91 | 当右键菜单项单击时产生。 92 | 93 | -------------------------------------------------------------------------------- /desktop_notifications.md: -------------------------------------------------------------------------------- 1 | # 丰富的通知 2 | 3 | > 警告:网络通知 API 中的 webKitNotifications.createHTMLNotification() 已弃用,新的 网络通知 API 只允许文本通知。Chrome 浏览器的通知 API 很快就会在稳定版中支持,网络通知将更新为新的丰富通知格式。 4 | 5 | 6 | 使用丰富的通知告诉用户发生了一些重要的事。通知出现在浏览器窗口的外面,如下面的截图所示,通知的显示方式与位置的具体细节取决于具体平台。 7 | 8 | ![](https://developer.chrome.com/static/images/notification-windows.png) 9 | 10 | ![](https://developer.chrome.com/static/images/notification-mac.png) 11 | 12 | ![](https://developer.chrome.com/static/images/notification-linux.png) 13 | 14 | 您只要用一点点 JavaScript 代码以及可选的一个与您的扩展程序一起打包的 HTML 页面就能创建通知窗口。 15 | 16 | ##例子 17 | 18 | 首先在您的清单文件中声明 notifications 权限: 19 | 20 | manifest.json 21 | 22 | ``` 23 | { 24 | "name": "我的扩展程序", 25 | "manifest_version": 2, 26 | ... 27 | "permissions": [ 28 | "notifications" 29 | ], 30 | ... 31 | // 注意:由于 bug 134315,您必须将您希望与 createNotification() 32 | // 一起使用的所有图片声明为可以在网页中访问的资源。 33 | "web_accessible_resources": [ 34 | "48.png" 35 | ], 36 | } 37 | ``` 38 | 然后,使用 webkitNotifications 对象创建通知: 39 | 40 | ``` 41 | // 注意:没有必要调用 webkitNotifications.checkPermission()。 42 | // 声明了 notifications 权限的扩展程序总是允许创建通知。 43 | 44 | // 创建一个简单的文本通知: 45 | var notification = webkitNotifications.createNotification( 46 | '48.png', // 图标 URL,可以是相对路径 47 | '您好!', // 通知标题 48 | '内容(Lorem ipsum...)' // 通知正文文本 49 | ); 50 | 51 | // 或者创建 HTML 通知: 52 | var notification = webkitNotifications.createHTMLNotification( 53 | 'notification.html' // HTML 的 URL,可以是相对路径 54 | ); 55 | 56 | // 然后显示通知。 57 | notification.show(); 58 | ``` 59 | 60 | ## API 参考 61 | 参见[桌面通知规范草案](http://dev.chromium.org/developers/design-documents/desktop-notifications/api-specification)(英文)。 62 | 63 | ## 与其他视图通信 64 | 65 | 您可以使用 extension.getBackgroundPage 和 extension.getViews 在您的扩展程序中实现通知与其他视图间的通信。例如: 66 | 67 | notification.js 68 | 69 | ``` 70 | chrome.extension.getBackgroundPage().doThing(); 71 | ``` 72 | 73 | background.js 74 | 75 | ``` 76 | chrome.extension.getViews({type:"notification"}).forEach(function(win) { 77 | win.doOtherThing(); 78 | }); 79 | ``` 80 | 81 | ## 更多例子 82 | 83 | 您可以在 [examples/api/notifications](http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/notifications/) 目录中找到一个使用通知的简单例子。有关其他例子以及查看源代码的帮助,请参见[示例](http://developer.chrome.com/extensions/samples.html)。 84 | 85 | 另外,您也可以参见 html5rocks.com(英文)上的[通知教程](http://www.html5rocks.com/tutorials/notifications/quick/)。请忽略与权限有关的代码,如果您声明了 "notifications" 权限它们就没有必要了。 -------------------------------------------------------------------------------- /background_pages.md: -------------------------------------------------------------------------------- 1 | # 后台网页 background_pages 2 | 3 | 扩展程序通常需要有一个长时间运行的脚本来管理一些任务或状态,而后台网页就是为这一目的而设立。 4 | 5 | 如架构概述所述,后台网页是一个 HTML 页面,在扩展程序的进程中运行。它在您的扩展程序整个生命周期中都存在,并且一次只有一个活动的实例。(例外:如果您的扩展程序使用[“分离式”隐身模式](developer.chrome.com/extensions/manifest/incognito.html),则会为隐身窗口创建另一个实例。) 6 | 7 | 在一个具有后台页面的典型扩展程序中,用户界面(例如浏览器按钮或页面按钮以及选项页面)通过哑视图(dumb view)实现,当视图需要某些状态时,则从后台页面请求状态;当后台页面得知状态更改时,后台页面通知相应的视图更新。 8 | 9 | 10 | 11 | ## 清单文件 12 | 请在扩展程序的清单文件(manifest)中注册您的后台页面。通常情况下,后台页面不需要任何 HTML 标记,这种情况下后台页面可以单独使用 JavaScript文件实现,如下列代码所示: 13 | 14 | manifest.json 15 | 16 | ```js 17 | { 18 | "name": "我的扩展程序", 19 | ... 20 | "background": { 21 | "scripts": ["background.js"] 22 | }, 23 | ... 24 | } 25 | 26 | ``` 27 | 后台页面将由扩展程序系统生成,包含 scripts 属性中列出的每一个文件。 28 | 29 | 如果您需要在您的后台页面中指定 HTML,您可以改用 page 属性: 30 | 31 | ```js 32 | { 33 | "name": "我的扩展程序", 34 | ... 35 | "background": { 36 | "page": "background.html" 37 | }, 38 | ... 39 | } 40 | ``` 41 | 如果您需要浏览器早些启动,例如显示通知,您可能还需要指定 "background" 权限。 42 | 43 | ## 详情 44 | 45 | 您可以通过直接的脚本调用在不同页面间通信,类似于在框架间通信。extension.getViews 方法返回属于您的扩展程序的所有活动页面的 window 对象,而 extension.getBackgroundPage 方法返回后台页面。 46 | 47 | ## 例子 48 | 以下代码片段演示了后台页面是如何与扩展程序中的其他页面通信的,同时也演示了您应该如何使用后台页面处理诸如用户单击之类的事件。 49 | 50 | 这一例子中的扩展程序有一个后台页面和多个创建自 image.html 的页面(使用 tabs.create)。 51 | 52 | background.js 53 | 54 | ```js 55 | // 单击浏览器按钮时作出反应。 56 | chrome.browserAction.onClicked.addListener(function(tab) { 57 | var viewTabUrl = chrome.extension.getURL('image.html'); 58 | var imageUrl = /* 某个图片的 URL */; 59 | 60 | // 查找扩展程序中的所有页面,找到我们可以使用的一个。 61 | var views = chrome.extension.getViews(); 62 | for (var i = 0; i < views.length; i++) { 63 | var view = views[i]; 64 | 65 | // 如果这一视图有正确的 URL 并且还未使用…… 66 | if (view.location.href == viewTabUrl && !view.imageAlreadySet) { 67 | 68 | // ……调用其中一个函数并设置属性。 69 | view.setImageUrl(imageUrl); 70 | view.imageAlreadySet = true; 71 | break; // 完成 72 | } 73 | } 74 | }); 75 | ``` 76 | image.html 77 | 78 | ```html 79 | 80 | 85 | 86 | 87 |

88 | 图片在此: 89 |

90 | 91 | 92 | 93 | 94 | 95 | ``` 96 | 97 | ## 方法 98 | 99 | 100 | -------------------------------------------------------------------------------- /storage.md: -------------------------------------------------------------------------------- 1 | # chrome.storage 2 | 3 | * 描述: 使用 chrome.storage API 存储、获取用户数据,追踪用户数据的更改。 4 | * 可用版本: 从 Chrome 20 开始稳定支持。 5 | * 权限: "storage" 6 | * 了解更多: [Chrome 应用办公时间:Chrome 存储 API ](https://developers.google.com/live/shows/7320022/) 7 | [Chrome 应用办公时间:存储 API 深入探索](https://developers.google.com/live/shows/7320022-1/) 8 | 9 | 10 | ## 概述 11 | 12 | 这一 API 为扩展程序的存储需要而特别优化,它提供了与 [localStorage API](https://developer.mozilla.org/en/DOM/Storage#localStorage) 相同的能力,但是具有如下几个重要的区别: 13 | 14 | * 用户数据可以通过 Chrome 浏览器的同步功能自动同步(使用 `storage.sync`)。 15 | * 您的扩展程序的内容脚本可以直接访问用户数据,而不需要后台页面。 16 | * 即使使用[分离式隐身行为](http://developer.chrome.com/extensions/manifest/incognito.html),用户的扩展程序设置也会保留。 17 | * 它是异步的,并且能够进行大量的读写操作,因此比阻塞和串行化的 `localStorage API` 更快。 18 | * 用户数据可以存储为对象(`localStorage API` 以字符串方式存储数据)。 19 | * 可以读取管理员为扩展程序配置的企业策略(使用 `storage.managed` 和 [架构](http://developer.chrome.com/extensions/manifest/storage.html) )。 20 | 21 | ## 清单文件 22 | 23 | 您必须在扩展程序的清单文件中声明 "storage" 权限才能使用存储 API。例如: 24 | 25 | manifest.json 26 | 27 | ```js 28 | { 29 | "name": "我的扩展程序", 30 | ... 31 | "permissions": [ 32 | "storage" 33 | ], 34 | ... 35 | } 36 | ``` 37 | 38 | ## 用法 39 | 40 | 如果要为您的扩展程序储存用户数据,您可以使用 storage.sync 或 storage.local。使用 storage.sync 时,储存的数据将会自动在用户启用同步功能并已经登录的所有 Chrome 浏览器间同步。 41 | 42 | 当 Chrome 浏览器处于离线状态时,Chrome 浏览器将数据储存在本地。下一次浏览器在线时,Chrome 浏览器将会同步数据。即使用户关闭了同步,storage.sync 仍然有效,只是此时它与 storage.local 的行为相同。 43 | 44 | **不应该储存机密的用户信息!存储区没有加密。** 45 | 46 | storage.managed 存储区是只读的。 47 | 48 | ## 存储空间与调用频率限制 49 | 50 | chrome.storage 并不像一辆大卡车那样,而是像一系列的管道,如果您不理解这一点的话,这样的管道很容易被填满。如果当您存入消息时它们填满了,它将会变成细线,任何人向其中存入大量数据都有可能使操作产生延迟。 51 | 52 | 有关目前对存储 API 的限制以及超出限制的结果,请参见 [sync](http://developer.chrome.com/extensions/storage.html#sync-properties) 和 [local](http://developer.chrome.com/extensions/storage.html#local-properties) 的配额信息。 53 | 54 | ## 例子 55 | 56 | 以下例子检查用户在表单中保存的 CSS 代码,如果找到的话则存储下来。 57 | 58 | ```js 59 | function saveChanges() { 60 | // 获取表单中保存的值。 61 | var theValue = textarea.value; 62 | // 确保包含代码。 63 | if (!theValue) { 64 | message('错误:没有指定值'); 65 | return; 66 | } 67 | // 使用 Chrome 扩展程序的存储 API 保存它。 68 | chrome.storage.sync.set({'value': theValue}, function() { 69 | // 通知保存完成。 70 | message('设置已保存'); 71 | }); 72 | } 73 | ``` 74 | 75 | 如果您希望追踪数据对象的更改,您可以向 onChanged 事件添加监听器,每当存储有任何更改时将会产生该事件。如下是监听对已保存内容的更改的示例代码: 76 | 77 | ```js 78 | chrome.storage.onChanged.addListener(function(changes, namespace) { 79 | for (key in changes) { 80 | var storageChange = changes[key]; 81 | console.log('存储键“%s”(位于“%s”命名空间中)已更改。' + 82 | '原来的值为“%s”,新的值为“%s”。', 83 | key, 84 | namespace, 85 | storageChange.oldValue, 86 | storageChange.newValue); 87 | } 88 | }); 89 | ``` 90 | 91 | #### 具体属性和事件函数 92 | 参考 [https://developer.chrome.com/extensions/storage.html](https://developer.chrome.com/extensions/storage.html) 93 | -------------------------------------------------------------------------------- /xhr.md: -------------------------------------------------------------------------------- 1 | # 跨站 XMLHttpRequest 2 | 3 | 普通网页可以通过 [XMLHttpRequest](http://www.w3.org/TR/XMLHttpRequest/) 对象向远程服务器发送和接收数据,但是它们受到[同源策略](http://en.wikipedia.org/wiki/Same_origin_policy)的限制。扩展程序不受这一限制,只要首先请求跨站权限,扩展程序就可以与来源范围外的远程服务器通信。 4 | 5 | ## 扩展程序的来源 6 | 每一个运行中的扩展程序在它自己的安全来源中存在,如果不请求额外的权限,扩展程序只能使用 XMLHttpRequest 获取自己的资源。例如,如果扩展程序包含一个名为 config.json 的 JSON 配置文件,位于 config_resources 文件夹中,扩展程序可以像这样获取文件内容: 7 | 8 | ``` 9 | var xhr = new XMLHttpRequest(); 10 | xhr.onreadystatechange = handleStateChange; // 在其他地方实现。 11 | xhr.open("GET", chrome.extension.getURL('/config_resources/config.json'), true); 12 | xhr.send(); 13 | ``` 14 | 如果扩展程序尝试使用除了它自己的安全来源,例如 http://www.google.com,除非扩展程序已经请求了相应的跨站权限,浏览器会禁止这一请求。 15 | 16 | ## 请求跨站权限 17 | 通过向[清单文件](manifest.md)的 [declare_permissions.md](permissions) 部分添加主机或主机匹配表达式,扩展程序可以请求访问位于自己的来源外的远程服务器。 18 | 19 | ``` 20 | { 21 | "name": "我的扩展程序", 22 | ... 23 | "permissions": [ 24 | "http://www.google.com/" 25 | ], 26 | ... 27 | } 28 | ``` 29 | 30 | 跨站权限值可以是完整的主机名,例如: 31 | 32 | * "http://www.google.com/" 33 | * "http://www.gmail.com/" 34 | 35 | 或者也可以是匹配表达式,例如: 36 | 37 | * "http://*.google.com/" 38 | * "http://*/" 39 | 40 | "http://*/" 这一匹配表达式允许所有可及域名的访问。注意,这里的匹配表达式和[内容脚本匹配表达式](match_patterns.md)类似,但是主机后的任何路径信息都将被忽略。 41 | 42 | 同时注意访问权限是通过主机和协议同时授予的,如果扩展程序需要指定某个主机安全和非安全的 HTTP 访问,必须分开声明权限: 43 | 44 | ``` 45 | "permissions": [ 46 | "http://www.google.com/", 47 | "https://www.google.com/" 48 | ] 49 | ``` 50 | 51 | ## 安全性考虑 52 | 当使用通过 XMLHttpRequest 获取的资源时,您的后台网页应该小心,以免跨站脚本攻击。特别地,避免使用如下一些危险的 API: 53 | 54 | ``` 55 | var xhr = new XMLHttpRequest(); 56 | xhr.open("GET", "http://api.example.com/data.json", true); 57 | xhr.onreadystatechange = function() { 58 | if (xhr.readyState == 4) { 59 | // 警告!可能会执行恶意脚本! 60 | var resp = eval("(" + xhr.responseText + ")"); 61 | ... 62 | } 63 | } 64 | xhr.send(); 65 | ``` 66 | ``` 67 | var xhr = new XMLHttpRequest(); 68 | xhr.open("GET", "http://api.example.com/data.json", true); 69 | xhr.onreadystatechange = function() { 70 | if (xhr.readyState == 4) { 71 | // 警告!可能会插入恶意脚本 72 | document.getElementById("resp").innerHTML = xhr.responseText; 73 | ... 74 | } 75 | } 76 | xhr.send(); 77 | ``` 78 | 因此,您应该首选更安全的不运行脚本的 API: 79 | 80 | ``` 81 | var xhr = new XMLHttpRequest(); 82 | xhr.open("GET", "http://api.example.com/data.json", true); 83 | xhr.onreadystatechange = function() { 84 | if (xhr.readyState == 4) { 85 | // JSON.parse 不执行攻击者的脚本。 86 | var resp = JSON.parse(xhr.responseText); 87 | } 88 | } 89 | xhr.send(); 90 | ``` 91 | 92 | ``` 93 | var xhr = new XMLHttpRequest(); 94 | xhr.open("GET", "http://api.example.com/data.json", true); 95 | xhr.onreadystatechange = function() { 96 | if (xhr.readyState == 4) { 97 | // innerText 不会让攻击者插入 HTML 元素。 98 | document.getElementById("resp").innerText = xhr.responseText; 99 | } 100 | } 101 | xhr.send(); 102 | ``` 103 | 104 | 另外,通过 HTTP 接收资源时尤其要小心。如果你的扩展程序在不安全的网络环境中使用,网络攻击者(即中间人攻击)可以修改响应,甚至可能攻击您的扩展程序。请尽可能地在可能的情况下首选 HTTPS。 105 | 106 | ### 与内容安全策略的关系 107 | 如果您在你的清单文件中添加 content_security_policy 属性修改了应用或扩展程序默认的[内容安全策略](contentSecurityPolicy.md),就需要确保您需要连接的所有主机都受到允许。尽管默认策略不限制连接到哪些主机,当你显式添加 connect-src 或 default-src 指示符时要特别注意。 -------------------------------------------------------------------------------- /declare_permissions.md: -------------------------------------------------------------------------------- 1 | # 声明权限 2 | 3 | 要使用大多数 chrome.* API,你的扩展程序或应用程序必须在清单文件的 "permissions" 字段中声明它的意图。每一个权限既可以是已知字符串列表中的某一个(例如 "geolocation"),也可以是授予访问一个或多个主机权限的匹配表达式。权限可以帮助你在扩展程序或应用程序受到攻击时尽可能减小损失,某些权限也会在安装前向用户显示,这些将在权限警告中详细描述。 4 | 5 | 如果某个 API 需要你在清单文件中声明某个权限,它的文档会告诉你如何去做,例如存储 API 网页会向你演示如何声明 "storage" 权限。 6 | 7 | 如下是一个清单文件中权限部分的例子 8 | 9 | ``` 10 | "permissions": [ 11 | "tabs", 12 | "bookmarks", 13 | "http://www.blogger.com/", 14 | "http://*.google.com/", 15 | "unlimitedStorage" 16 | ], 17 | ``` 18 | 19 | 下表列出了当前可用的权限。 20 | 21 | * "[scheme]:[host]/*" 指定主机权限。如果扩展程序或应用需要与页面上运行的代码交互则必须指定该权限。许多扩展程序的能力,例如跨站 XMLHttpRequest、以编程方式插入内容脚本以及扩展程序的 Cookie API 都需要主机权限。有关语法上的详情,请参见匹配表达式。允许指定路行,但始终视为 /*。 22 | * "activeTab" 根据 activeTab 规范请求授予扩展程序权限。 23 | * "alarms" 使你的扩展程序能够访问 chrome.alarms API。 24 | * "background" 25 | * 让 Chrome 很早就启动很晚才退出,以便应用和扩展程序可以有更长的生命周期。 26 | 27 | 如果已安装的任何托管应用、打包应用或扩展程序拥有 "background" 权限,Chrome 浏览器在用户登录计算机时就会(不可见地)运行,那时用户还没有亲自运行 Chrome 浏览器。"background" 权限也会使 Chrome 浏览器继续运行(即使在最后一个窗口已经关闭),直到用户主动退出 Chrome 浏览器。 28 | 29 | 注意:已禁用的应用和扩展程序以未安装对待。 30 | 通常你使用 "background" 权限时,同时也会使用后台网页、事件页面或(对于托管应用)后台窗口。 31 | 32 | * "bookmarks" 使你的扩展程序能够访问 chrome.bookmarks API。 33 | * "browsingData" 使你的扩展程序能够访问 chrome.browsingData API。 34 | * "clipboardRead" 如果扩展程序或应用使用 document.execCommand('paste') 则必须指定。 35 | * "clipboardWrite" 表示扩展程序或应用可以使用 document.execCommand('copy') 或 document.execCommand('cut')。托管应用必须指定该权限,建议扩展程序和打包应用也指定该权限。 36 | * "contentSettings" 使你的扩展程序能够访问 chrome.contentSettings API。 37 | * "contextMenus" 使你的扩展程序能够访问 chrome.contextMenus API。 38 | * "cookies" 使你的扩展程序能够访问 chrome.cookies API。 39 | * "debugger" 使你的扩展程序能够访问 chrome.debugger API。 40 | * "declarativeContent" 使你的扩展程序能够访问 chrome.declarativeContent API。 41 | * "declarativeWebRequest" 使你的扩展程序能够访问 chrome.declarativeWebRequest API。 42 | * "desktopCapture" 使你的扩展程序能够访问 chrome.desktopCapture API。 43 | * "dns" 使你的扩展程序能够访问 chrome.dns API。 44 | * "downloads" 使你的扩展程序能够访问 chrome.downloads API。 45 | * "experimental" 如果扩展程序或应用使用 chrome.experimental.* API 则必须指定。 46 | * "fileBrowserHandler" 使你的扩展程序能够访问 chrome.fileBrowserHandler API。 47 | * "fontSettings" 使你的扩展程序能够访问 chrome.fontSettings API。 48 | * "gcm" 使你的扩展程序能够访问 chrome.gcm API。 49 | * "geolocation" 允许扩展程序或应用使用提议的 HTML5 地理定位 API 而不需要提示用户权限。 50 | * "history" 使你的扩展程序能够访问 chrome.history API。 51 | * "identity" 使你的扩展程序能够访问 chrome.identity API。 52 | * "idle" 使你的扩展程序能够访问 chrome.idle API。 53 | * "idltest" 使你的扩展程序能够访问 chrome.idltest API。 54 | * "infobars" 使你的扩展程序能够访问 chrome.infobars API。 55 | * "location" 使你的扩展程序能够访问 chrome.location API。 56 | * "management" 使你的扩展程序能够访问 chrome.management API。 57 | * "notifications" 允许扩展程序使用提议的 HTML5 通知 API 而不用调用权限方法(例如 checkPermission())。有关更多信息,请参见桌面通知。 58 | * "pageCapture" 使你的扩展程序能够访问 chrome.pageCapture API。 59 | * "power" 使你的扩展程序能够访问 chrome.power API。 60 | * "privacy" 使你的扩展程序能够访问 chrome.privacy API。 61 | * "processes" 使你的扩展程序能够访问 chrome.processes API。 62 | * "proxy" 使你的扩展程序能够访问 chrome.proxy API。 63 | * "pushMessaging" 使你的扩展程序能够访问 chrome.pushMessaging API。 64 | * "sessions" 使你的扩展程序能够访问 chrome.sessions API。 65 | * "signedInDevices" 使你的扩展程序能够访问 chrome.signedInDevices API。 66 | * "storage" 使你的扩展程序能够访问 chrome.storage API。 67 | * "system.cpu" 使你的扩展程序能够访问 chrome.system.cpu API。 68 | * "system.display" 使你的扩展程序能够访问 chrome.system.display API。 69 | * "system.memory" 使你的扩展程序能够访问 chrome.system.memory API。 70 | * "system.storage" 使你的扩展程序能够访问 chrome.system.storage API。 71 | * "tabCapture" 使你的扩展程序能够访问 chrome.tabCapture API。 72 | * "tabs" 使你的扩展程序能够访问 $ref:[tabs.Tab Tab] 对象的特权字段,包括 $ref:[tabs chrome.tabs] 和 $ref:[windows chrome.windows] 在内的多种 API 都使用 Tab 对象。在很多情况下,你的扩展程序不需要声明 "tabs" 权限就能使用这些 API。 73 | * "topSites" 使你的扩展程序能够访问 chrome.topSites API。 74 | * "tts" 使你的扩展程序能够访问 chrome.tts API。 75 | * "ttsEngine" 使你的扩展程序能够访问 chrome.ttsEngine API。 76 | * "unlimitedStorage" 提供无限的存储空间,保存 HTML5 客户端数据,例如数据库以及本地存储文件。如果没有这一权限,扩展程序或应用的本地存储将限制在 5 MB 以内。 77 | 注意:该权限仅应用于网络 SQL 数据库以及应用程序缓存(参见问题 58985)。另外,当前还不能和包含通配符的子域名一起使用,例如 http://*.example.com。 78 | * "webNavigation" 使你的扩展程序能够访问 chrome.webNavigation API。 79 | * "webRequest" 使你的扩展程序能够访问 chrome.webRequest API。 80 | * "webRequestBlocking" 如果扩展程序以阻塞方式使用 chrome.webRequest API 则必须指定。 -------------------------------------------------------------------------------- /autoupdate.md: -------------------------------------------------------------------------------- 1 | # 自动更新 autoupdate 2 | 3 | 我们希望扩展程序能够像Google Chrome浏览器一样自动更新,以便加入漏洞及安全补丁,增加新特性,增强性能,改善用户界面。 4 | 5 | 如果您使用 [Chrome 开发者信息中心](https://chrome.google.com/webstore/developer/dashboard)发布您的扩展程序,您可以忽略这一页面。您可以利用信息中心向用户以及 Chrome 网上应用店发布您的扩展程序的更新版本。 6 | 7 | 如果您想在网上应用店以外的地方托管您的扩展程序,请继续读下去。您还应该阅读[托管](http://developer.chrome.com/extensions/hosting.html)以及[打包](http://developer.chrome.com/extensions/packaging.html)。 8 | 9 | ## 概述 10 | 11 | * 扩展程序的清单文件可以包含 "update_url" 属性,指向检查更新的位置。 12 | * 检查更新返回的内容为一个更新清单 XML 文档,列出扩展程序的最新版本。 13 | 14 | 每隔几小时,浏览器都会检查已安装的扩展程序是否有更新 URL。浏览器会对每一个扩展程序的更新 URL 发出请求,查询更新清单 XML 文件。如果更新清单提到了比已安装的扩展程序更新的版本,浏览器下载并安装新版本。与手动更新相同,新的 .crx 文件必须与已安装的扩展程序使用同一私有密钥签名。 15 | 16 | ## 更新 URL 17 | 18 | 如果您要托管自己的扩展程序,您需要在您的 manifest.json 文件中添加 "update_url" 属性,如下所示: 19 | 20 | manifest.json 21 | 22 | ```js 23 | { 24 | "name": "我的扩展程序", 25 | ... 26 | "update_url": "http://myhost.com/mytestextension/updates.xml", 27 | ... 28 | } 29 | ``` 30 | 31 | 32 | ## 更新清单 33 | 34 | 服务器返回的更新清单应该为一个如下形式的 XML 文档(高亮部分表示您需要修改的部分): 35 | 36 | updates.xml 37 | 38 | ```xml 39 | 40 | 41 | 42 | 43 | 44 | 45 | ``` 46 | 47 | 这一 XML 格式借用了 Omaha(Google 用于更新的基础架构)的格式,有关细节请参见 https://code.google.com/p/omaha/(英文)。扩展程序系统使用更新清单中 元素的以下属性: 48 | 49 | * appid 50 | 扩展程序或 Chrome 应用标识符,基于公共密钥的散列值生成,如打包部分所述。您可以进入扩展程序页面(chrome://extensions)找到您的扩展程序标识符。 51 | 52 | * codebase 53 | 指向扩展程序 .crx 文件的 URL。 54 | 55 | * version 56 | 用于客户端,确定是否应该下载 codebase指定的 .crx 文件。这一属性应该与 .crx 文件中 manifest.json 文件中的 "version" 属性一致。 57 | 58 | 更新清单 XML 文件可以包含多个 元素,描述不同扩展程序的信息。 59 | 60 | ## 测试 61 | 62 | 默认更新检查频率为几个小时,但是您可以使用扩展程序页面中的i立即更新扩展程序按钮强制更新。 63 | 64 | 另一个选择是使用 --extensions-update-frequency 命令行参数,以秒为单位,设置更频繁的更新间隔。例如,要每隔 45 秒就检查更新,像这样运行 Google Chrome 浏览器: 65 | 66 | ``` 67 | chrome.exe --extensions-update-frequency=45 68 | ``` 69 | 70 | 注意,这会影响到所有已安装的扩展程序,所以您应该考虑到这会带来的带宽以及服务器负载。您可能需要临时卸载除了您正在测试的扩展程序以外的所有其他扩展程序,并且在正常的浏览器使用过程中不应该以这一选项运行浏览器。 71 | 72 | ## 高级用法:请求参数 73 | 74 | 基本的自动更新机制使得服务器端的工作十分简单,只要在任何普通的 Web 服务器(例如 Apache)上加入一个静态 XML 文件,并在发布您的扩展程序的新版本时更新此 XML 文件即可。 75 | 76 | 更高级的开发者可能希望利用我们请求更新清单时附加的参数,指示扩展程序的标识符和版本。这样,就可以对所有扩展程序都使用相同的更新 URL,指向运行动态服务器端代码而不是静态 XML 文件的 URL。 77 | 78 | 请求参数的格式为: 79 | 80 | ``` 81 | ?x= 82 | ``` 83 | 84 | 其中 为如下格式的内容经过 URL 编码后所得的字符串: 85 | 86 | ``` 87 | id=&v= 88 | ``` 89 | 90 | 例如,假设您有两个扩展程序,都指向相同的更新 URL(http://test.com/extension_updates.php): 91 | 92 | * 扩展程序 1 93 | 标识符:"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" 94 | 版本:"1.1" 95 | * 扩展程序 2 96 | 标识符:"bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb" 97 | 版本:"0.4" 98 | 99 | 则向每一个单独的扩展程序发出的请求分别为: 100 | 101 | * http://test.com/extension_updates.php?x=id%3Daaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%26v%3D1.1 102 | * http://test.com/extension_updates.php?x=id%3Dbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb%26v%3D0.4 103 | 104 | 105 | 对于每一个唯一的更新 URL,多个扩展程序可以在一个请求中列出。对于上述例子,如果用户安装了这两个扩展程序,则两个请求合并为一个请求: 106 | 107 | ``` 108 | http://test.com/extension_updates.php?x=id%3Daaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%26v%3D1.1&x=id%3Dbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb%26v%3D0.4 109 | ``` 110 | 111 | 如果已安装的扩展程序数目太多,使用相同的更新 URL 使得 GET 请求 URL 太长(大约 2000 个字符以上),更新检查程序会发送必要的附加 GET 请求。 112 | 113 | **注意: 将来可能会发出单个 POST 请求,包含请求参数,而不是发出多个 GET 请求。** 114 | 115 | 116 | ## 高级用法:最小浏览器版本 117 | 118 | 我们不断地在向扩展程序系统中添加更多的 API,可能您发布的更新版本的扩展程序只能在更新版本的浏览器中运行。尽管 Google Chrome 浏览器会自动更新,大部分用户更新至给定的新版本仍然需要几天时间。要确保给定的扩展程序更新仅应用于您指定的版本或更高版本的 Google Chrome 浏览器,您可以在您的更新清单中向 元素添加 "prodversionmin" 属性。例如: 119 | 120 | updates.xml 121 | 122 | ```xml 123 | 124 | 125 | 126 | 127 | 128 | 129 | ``` 130 | -------------------------------------------------------------------------------- /browserAction.md: -------------------------------------------------------------------------------- 1 | # chrome.browserAction 2 | 3 | * 描述: 使用浏览器按钮可以在 Google Chrome 浏览器主窗口中地址栏右侧的工具栏中添加图标。除了图标,浏览器按钮还可以有工具提示、徽章和弹出内容。 4 | * 从 Chrome 5 开始稳定支持 5 | * 清单文件(manifest): "browser_action": {...} 6 | 7 | 在下图中,地址栏右侧多种颜色的正方形是一个浏览器按钮的图标,图标下有一弹出内容。 8 | 9 | ![](http://gtms04.alicdn.com/tps/i4/TB1s3sbHXXXXXc3aFXX8qhPLFXX-363-226.png) 10 | 11 | 如果您想创建一个不是永远可见的图标,请使用[页面按钮](extensions/pageAction.md),而不是浏览器按钮。 12 | 13 | ## 清单文件 14 | 15 | 如下所示在扩展程序的[清单文件](extensions/manifest.md)中注册您的浏览器按钮 16 | 17 | manifest.json 18 | 19 | ``` 20 | { 21 | "name": "我的扩展程序", 22 | ... 23 | "browser_action": { 24 | "default_icon": { // 可选 25 | "19": "images/icon19.png", // 可选 26 | "38": "images/icon38.png" // 可选 27 | }, 28 | "default_title": "Google Mail", // 可选,在工具提示中显示 29 | "default_popup": "popup.html" // 可选 30 | }, 31 | ... 32 | } 33 | ``` 34 | 35 | 如果您只提供 19px 或 38px 图标大小中的一个,扩展程序系统将会缩放您提供的图标,以适应用户显示器的像素密度,这有可能会丢失细节或使它看上去很模糊。注册默认图标的旧语法仍然支持 36 | 37 | manifest.json 38 | 39 | ``` 40 | { 41 | "name": "我的扩展程序", 42 | ... 43 | "browser_action": { 44 | ... 45 | "default_icon": "images/icon19.png" // 可选 46 | // 等价于 "default_icon": { "19": "images/icon19.png" } 47 | }, 48 | ... 49 | } 50 | ``` 51 | 52 | ## 用户界面的几个部分 53 | 浏览器按钮可以包含图标、工具提示、徽章和弹出内容。 54 | 55 | ### 图标 56 | 57 | 浏览器按钮图标的宽度和高度最大可以为 19 dip(设备无关像素),更大的图标会被缩小。为了最佳结果,请使用 19 dip 大小的正方形图标。 58 | 59 | 您可以通过两种方式设置图标:使用静态图像或者使用 HTML5 canvas 元素。对于简单的应用来说,使用静态图像更方便,但是您可以使用 canvas 元素创建动态的用户界面,例如平滑的动画。 60 | 61 | 静态图像可以是 WebKit 能够显示的任何格式,包括 BMP、GIF、ICO、JPEG 或 PNG。对于未打包的扩展程序来说,图片必须是 PNG 格式。 62 | 63 | 要设置图标,请使用清单文件中 browser_action 部分的 default_icon 属性,或者调用 setIcon 方法。 64 | 65 | 要在屏幕像素密度(比值 size_in_pixel / size_in_dip)不为 1 的时候正确显示图标,图标可以定义为一组不同大小的图片。实际显示的图片将从这一组图标中选取,以便最佳匹配 19 dip 图标的像素大小。目前一组图标可以包含像素大小为 19 和 38 的图片。 66 | 67 | ### 工具提示 68 | 69 | 要设置工具提示,使用清单文件中 browser_action 部分的 default_title 属性,或者调用 setTitle 方法。对于 default_title 属性,您可以指定区域相关的字符串,有关更多细节请参考[国际化支持](extensions/i18n.md)。 70 | 71 | ### 徽章 72 | 73 | 浏览器按钮可以选择在图标上显示徽章,即一小段文字。徽章使得更新浏览器按钮更容易,来显示有关扩展程序状态的一点点信息。 74 | 75 | 因为徽章所拥有的空间有限,它不应超过四个字符。 76 | 77 | 分别调用 setBadgeText 和 setBadgeBackgroundColor 设置徽章的内容和颜色。 78 | 79 | ### 弹出内容 80 | 81 | 如果浏览器按钮有弹出内容,当用户单击图标时会出现弹出内容。弹出内容可以包含任何您需要的 HTML 内容,并且会自动调整大小以适应内容。 82 | 83 | 要向您的浏览器按钮添加弹出内容,请创建一个 HTML 文件存放弹出内容,并在清单文件中 browser_action 部分的 default_popup 属性指定该 HTML 文件或者调用 setPopup 方法。 84 | 85 | ## 提示 86 | 为了达到最好的视觉效果,请遵循下面的指导: 87 | 88 | * 对于适用于大多数页面的功能请使用浏览器按钮。 89 | * 对于仅适用于少数页面的内容请不要使用浏览器按钮,而应该使用页面按钮。 90 | * 请使用较大的、颜色丰富的图标,充分利用 19×19 像素的空间。浏览器按钮的图标应该看上去比页面按钮的图标更大一些、更明显一些。 91 | * 请不要试着模仿 Google Chrome 浏览器的单色 Chrome 菜单图标。这样与主题背景不协调,并且无论如何,扩展程序应该更加突出一点。 92 | * 请使用 Alpha 透明,使您的图标边框更加平滑。因为很多人使用主题背景,您的图标应该在各种背景颜色下看着舒服。 93 | * 请不要一直使用动画图标,那样只会显得很烦人。 94 | 95 | ## 例子 96 | 97 | 您可以在 [examples/api/browserAction](http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/browserAction/) 目录中找到使用浏览器按钮的简单例子。有关其他示例和查看源代码的帮助,请参见[示例](https://developer.chrome.com/extensions/samples)。 98 | 99 | # chrome.browserAction 参考 100 | 101 | [详细最新的API地址](https://developer.chrome.com/extensions/browserAction) 102 | 103 | ## 类型 104 | ### ColorArray 105 | array of integer 106 | ### ImageDataType 107 | 图片的像素数据,必须为 ImageData 对象(例如来自 canvas 元素)。 108 | 109 | ## 方法 110 | 111 | ### setTitle 112 | `rome.browserAction.setTitle(object details)` 113 | 114 | 设置浏览器按钮的标题,显示在工具提示中。 115 | 116 | 参数 117 | 118 | * details ( object ) 119 | 120 | 属性 121 | * title ( string ) 122 | 当鼠标移到浏览器按钮上时应显示的字符串。 123 | * tabId ( optional integer ) 124 | 125 | 将更改限制在当某一特定标签页选中时应用,当该标签页关闭时,更改的内容自动恢复。 126 | 127 | ### getTitle 128 | `chrome.browserAction.getTitle(object details, function callback)` 129 | 130 | 获取浏览器按钮的标题。 131 | 132 | ### setIcon 133 | 134 | `chrome.browserAction.setIcon(object details, function callback)` 135 | 136 | 设置浏览器按钮的图标。图标既可以指定为图片文件的路径,也可以指定来自 canvas 元素的像素数据,或者这两者中任意一个的词典。path 或 imageData 属性中有且只有一个必须指定。 137 | 138 | ### setPopup 139 | 140 | `chrome.browserAction.setPopup(object details)` 141 | 142 | 设置当用户单击浏览器按钮时显示为弹出内容的 HTML 文档。 143 | 144 | ### getPopup 145 | 146 | `chrome.browserAction.getPopup(object details, function callback)` 147 | 148 | 获取设置为浏览器按钮弹出内容的 HTML 文档。 149 | 150 | ### setBadgeText 151 | 152 | `chrome.browserAction.setBadgeText(object details)` 153 | 154 | 设置浏览器按钮上的徽章,显示在图标上。 155 | 156 | ### getBadgeText 157 | 158 | `chrome.browserAction.getBadgeText(object details, function callback)` 159 | 160 | 获取浏览器按钮上的徽章,如果没有指定标签页,则返回用于所有标签页的徽章。 161 | 162 | ### setBadgeBackgroundColor 163 | 164 | `chrome.browserAction.setBadgeBackgroundColor(object details)` 165 | 设置徽章的背景颜色。 166 | 167 | ### getBadgeBackgroundColor 168 | 169 | `chrome.browserAction.getBadgeBackgroundColor(object details, function callback)` 170 | 171 | 获取浏览器按钮上的徽章,如果没有指定标签页,则返回用于所有标签页的徽章。 172 | 173 | ### enable 174 | 175 | `chrome.browserAction.enable(integer tabId)` 176 | 177 | 为某一标签页启用浏览器按钮。默认情况下,浏览器按钮是启用的。 178 | 179 | ### disable 180 | 181 | `chrome.browserAction.disable(integer tabId)` 182 | 183 | 为某一标签页禁用浏览器按钮。 184 | 185 | ## 事件 186 | 187 | ### onClicked 188 | 189 | 浏览器按钮的图标单击时产生,如果浏览器按钮有弹出内容则不会触发该事件。 190 | 191 | #### addListener 192 | 193 | `chrome.browserAction.onClicked.addListener(function callback)` 194 | 195 | 参数 196 | 197 | * callback ( function ) 198 | callback 参数应该指定一个如下形式的函数: 199 | 200 | `function(tabs.Tab tab) {...};` 201 | 202 | * tab ( tabs.Tab ) 203 | 204 | -------------------------------------------------------------------------------- /overview.md: -------------------------------------------------------------------------------- 1 | # 概述 2 | 3 | 当你看完该页面以及[入门](getstarted.md)教程后,你已经准备好开始编写扩展程序了。 4 | 5 | ## 基础 6 | 扩展程序由一些文件(HTML,js,css,图片等其他文件)经过zip打包得到,其本质还是网页,可以使用浏览器网页提供的所有API,如 XMLHttpRequest、JSON、HTML 5 等。 7 | 8 | 扩展程序可以通过 [内容脚本]() 或 [XMLHttpRequest]() 与网页或者服务器交互,扩展程序也可以以编程方式与浏览器功能(例如 [书签]() 和 [标签页]() 交互)。 9 | 10 | ### 扩展程序的用户界面 11 | 许多扩展程序(但不包括 Chrome 应用)以浏览器按钮或页面按钮的形式向 Google Chrome 浏览器增加用户界面,每个扩展程序最多能有一个浏览器按钮或页面按钮。当扩展程序与大部分网页相关时选择使用浏览器按钮,当扩展程序的图标显示还是消失取决于具体网页时选择使用页面按钮。 12 | 13 | 扩展程序(以及 Chrome 应用)也可以以其他形式呈现用户界面,例如在 Chrome 浏览器的右键菜单中添加内容,提供选项页面,或者利用内容脚本更改页面的显示方式。有关完整的扩展程序功能以及每一种功能的实现细节,请看 [开发者指南]() 。 14 | 15 | ## 文件 16 | 每一个扩展程序包含以下文件: 17 | 18 | * 一个清单文件 19 | * 一个或多个 HTML 文件(除非扩展程序是一个主题背景) 20 | * 可选:一个或多个 JavaScript 文件 21 | * 可选:扩展程序需要的任何其他文件,例如图片 22 | 23 | ### 引用文件 24 | 可以在扩展程序中放置任何文件,使用的话可以通过相对 URL 引用文件,如: 25 | ` ` 26 | 27 | 使用Chrome的调试器可发现,扩展程序中的每一个文件也可以通过绝对 URL 访问,如: 28 | `chrome-extension://<扩展程序标识符>/<文件路径>` 29 | 30 | 在这 URL 中,<扩展程序标识符> 是扩展程序系统为每一个扩展程序生成的唯一标识符,进入 chrome://extensions 就能看到所有扩展程序的标识符。<文件路径> 是扩展程序的主目录下的文件位置,与相对 URL 相同。 31 | 32 | ### 清单文件 33 | manifest.json 提供有关扩展程序的各种信息,例如最重要的文件和扩展程序可能具有的能力。以下是一个典型的清单文件,用于一个浏览器按钮,它将会访问来自 google.com 的信息: 34 | 35 | ``` 36 | { 37 | "name": "我的扩展程序", 38 | "version": "2.1", 39 | "description": "从 Google 获取信息。", 40 | "icons": { "128": "icon_128.png" }, 41 | "background": { 42 | "persistent": false, 43 | "scripts": ["bg.js"] 44 | }, 45 | "permissions": ["http://*.google.com/", "https://*.google.com/"], 46 | "browser_action": { 47 | "default_title": "", 48 | "default_icon": "icon_19.png", 49 | "default_popup": "popup.html" 50 | } 51 | } 52 | ``` 53 | 54 | 关于manifest文件的更多细节: [manifest.json](manifest.md) 55 | 56 | 57 | ## 架构 58 | 许多扩展程序有一个后台网页,它是一个包含扩展程序主要逻辑的不可见页面。扩展程序也可以包含其他页面,展现扩展程序的用户界面。如果扩展程序需要与用户加载的网页交互(相对于包含在扩展程序中的页面),扩展程序必须使用内容脚本(content-script)。 59 | 60 | ### 后台网页 61 | 下图所示的浏览器至少安装了两个扩展程序:一个浏览器按钮(黄色图标)和一个页面按钮(蓝色图标)。浏览器按钮和页面按钮都有后台页面。下图显示了浏览器按钮的后台页面,由 background.html 定义,并且包含在这两个窗口中控制浏览器按钮的 JavaScript 代码。 62 | 63 | ![]() 64 | 65 | 后台网页分两种:持久运行的后台网页与事件页面。顾名思义,持续运行的后台网页保持打开状态,事件页面根据需要打开与关闭。除非绝对需要你的后台网页一直运行,最好首选事件页面。 66 | 67 | 有关更多细节,参考 [事件页面]()与 [后台网页]()。 68 | 69 | ### 用户界面网页 70 | 扩展程序可以包含普通的 HTML 网页,用来显示扩展程序的用户界面。例如,浏览器按钮可以包含弹出菜单,通过 HTML 文件实现。任何一个扩展程序都可以有选项页面,让用户自定义扩展程序的工作方式。另外一种特殊页面是替代页面。最后,可以使用 tabs.create 或 window.open() 来显示扩展程序中的任何其他 HTML 文件。 71 | 72 | 扩展程序中的 HTML 网页可以互相访问其他页面的全部 DOM,并且可以互相调用函数。 73 | 74 | 下图显示了浏览器按钮弹出菜单的架构。弹出菜单是由一个 HTML 文件(popup.html)定义的网页,该扩展程序也正好有一个后台网页(background.html)。弹出窗口不用重复后台网页中的代码,因为弹出窗口可以调用后台网页上的函数。 75 | 76 | 77 | 有关更多细节,参考 [浏览器按钮]()、[选项]()、[替代页面]() 和 [页面间通信]() 这些部分。 78 | 79 | ### 内容脚本 content_script 80 | 如果你的扩展程序需要与网页交互,就需要使用内容脚本。内容脚本是一些 js 代码,它们在浏览器中已加载页面的上下文中执行。应该将内容脚本视为已加载页面的一部分,而不是打包在一起的扩展程序(它所属的扩展程序)的一部分。 81 | 82 | 内容脚本可以读取浏览器访问的网页的细节,并且可以修改页面。在下图中,内容脚本可以读取并且修改显示的网页的 DOM。然而,它不能修改所属扩展程序后台网页的 DOM。 83 | ![]() 84 | 85 | 内容脚本并不是完全与所属扩展程序隔离的。内容脚本可以与所属扩展程序交换消息,如下图箭头所示。例如,每当在浏览器页面中发现 RSS 供稿时,内容脚本可以发送消息,反过来后台页面也可以发送消息要求内容脚本更改浏览器页面的外观。 86 | ![]() 87 | 88 | 更多细节,参考 [内容脚本]() 89 | 90 | ## 使用 chrome.* API 91 | 扩展程序除了能够使用网页和应用可以使用的所有 API 外,还能使用仅用于 Chrome 浏览器的 API(通常称为 chrome.* API)来更好地与浏览器集成。例如,任何扩展程序或网上应用可以使用标准的 window.open() 方法来打开一个网页,但是如果您想指定网页应该显示在哪个窗口中,您的扩展程序就可以使用仅用于 Chrome 浏览器的 tabs.create 方法。 92 | 93 | ### 异步方法与同步方法的区别 94 | 大部分 chrome.* API 的方法都是异步的,它们不等待操作完成就立即返回。如果您需要知道操作结果,您可以向方法传递一个回调函数,回调函数将稍后在方法返回后的某个时刻执行(可能很久之后)。下面是一个异步方法签名的例子: 95 | 96 | chrome.tabs.create(object createProperties, function callback) 97 | 98 | 也有一些 chrome.* 方法是同步的。同步的方法没有回调参数,因为它们只有当所有操作完成后才返回。通常,同步方法有返回值类型。考虑 runtime.getURL 方法: 99 | 100 | string chrome.runtime.getURL() 101 | 102 | 该方法没有回调参数,但是有返回值类型 string,因为它同步地返回 URL,不进行任何其他异步操作。 103 | 104 | ### 例子:使用回调函数 105 | 假设您想在用户当前选定的标签页中打开新的页面。要想这么做,您首先需要获得当前标签页的标识符(使用 tabs.query),然后使该标签转到指定的新的 URL(使用 tabs.update)。 106 | 107 | 假如 query() 是同步的,您可能会写这样的代码: 108 | 109 | ``` 110 | //以下代码不能正常工作! 111 | var tab = chrome.tabs.query({'active': true}); //错误! 112 | chrome.tabs.update(tab.id, {url:newUrl}); 113 | someOtherFunction(); 114 | ``` 115 | 116 | 这样的方法不行,因为 query() 是异步的,它不等待操作完成就返回了,并且事实上它都不返回任何值(尽管有些异步方法会返回信息)。您可以通过它签名中的 callback参数看出 query() 是异步的: 117 | 118 | `chrome.tabs.query(object queryInfo, function callback)` 119 | 120 | 要改正上面的代码,您必须使用那个回调参数。以下代码显示如何定义一个回调函数,从 query() 获得结果(通过名为 tab 的参数)并调用 update()。 121 | 122 | ``` 123 | //以下代码可以正常工作 124 | chrome.tabs.query({'active': true}, function(tabs) { 125 | chrome.tabs.update(tabs[0].id, {url: newUrl}); 126 | }); 127 | someOtherFunction(); 128 | ``` 129 | 在这一例子中,以上几行是按照这样的顺序执行的:1、4、2。只有在有关当前选定标签的信息可用后,即 query() 返回后的某一时刻,才调用在 query() 中指定的回调函数(并且执行第二行)。尽管 update() 是异步的,这一例子没有使用回调参数,因为我们对于调用的结果并不感兴趣。 130 | 131 | ### 更多 132 | 更多信息,参考 chrome.* API 文档和下面的视频(英文): 133 | 134 | 135 | ## 页面间的通信 136 | 扩展程序中的 HTML 网页通常需要通信。因为一个扩展程序的所有网页在同一个进程中的同一个线程上执行,网页之间可以直接调用函数。 137 | 138 | 要获得扩展程序中的网页,请使用 chrome.extension 方法,例如 extension.getViews 和 extension.getBackgroundPage。一旦一个网页引用了扩展程序中的其他网页,第一个网页可以执行其他网页上的函数,并且可以操纵它们的 DOM。 139 | 140 | ## 保存数据和隐身模式 141 | 扩展程序可以使用 HTML5 网页存储 API(例如 localStorage)或者向服务器发出请求保存数据。每当您要保存任何数据前,首先要考虑它是否来自隐身窗口。默认情况下,扩展程序不在隐身窗口中运行。当浏览器处于隐身模式时,您需要考虑用户对您的扩展程序的需求。 142 | 143 | 隐身模式确保不会留下任何痕迹。当处理来自隐身窗口的数据时,尽可能地遵守这一约定。例如,如果您的扩展程序通常将浏览器历史记录保存至云端,不要保存来自隐身窗口的历史记录。另一方面,您可以在任何窗口中保存您的扩展程序设置,无论隐身与否。 144 | 145 | 准则: 如果某些数据可能显示用户在网上的访问记录或者用户所做的事情,千万不要保存这些来自隐身窗口的数据。 146 | 要确定窗口是否处于隐身模式,检查相关的 tabs.Tab 或 windows.Window 对象的 icognito 属性。例如: 147 | 148 | ``` 149 | function saveTabData(tab, data) { 150 | if (tab.incognito) { 151 | chrome.runtime.getBackgroundPage(function(bgPage) { 152 | bgPage[tab.url] = data; // 仅在内存中保留数据 153 | }); 154 | } else { 155 | localStorage[tab.url] = data; // 可以保存数据 156 | } 157 | } 158 | ``` 159 | 160 | ## 下一步 161 | 162 | * 教程:入门 163 | * 教程:调试 164 | * 开发者指南 165 | * 示例 166 | * 视频(英文),例如扩展程序的消息传递 167 | -------------------------------------------------------------------------------- /content_scripts.md: -------------------------------------------------------------------------------- 1 | # 内容脚本(content_scripts) 2 | 3 | 内容脚本是在网页的上下文中运行的 JavaScript 文件,它们可以通过标准的[文档对象模型(DOM)](http://www.w3.org/TR/DOM-Level-2-HTML/)来获取浏览器访问的网页详情,或者作出更改。 4 | 5 | 如下是内容脚本可以实现的一些功能的例子: 6 | 7 | * 在网页中找出未链接的URL,并将它们转换为超链接 8 | * 增加字体大小,使文本更具有可读性 9 | * 发现并处理 DOM 中的[微格式](http://microformats.org/)数据 10 | 11 | 然而,内容脚本也有一些限制,它们**不能** : 12 | 13 | * 调用 chrome.* API(除了 chrome.extension 中的一部分) 14 | * 使用所属扩展程序页面中定义的变量或函数 15 | * 使用网页或其他内容脚本中定义的变量或函数 16 | 17 | 这些限制并不如看上去那么糟糕,内容脚本可以间接地通过与所属扩展程序交换[消息](messaging.md)的方式,来使用 chrome.* API、访问扩展程序数据并请求扩展程序完成操作。内容脚本也可以像所属扩展程序一样向拥有主机权限的站点发出[跨站 XMLHttpRequest](xhr.md),另外也可以使用共享的 DOM [与网页通信](#host-page-communication)。有关内容脚本能做什么与不能做什么的更多内部细节,请参见[执行环境](#execution-environment)。 18 | 19 | ## 清单文件 20 | 如果你的内容脚本代码每一次都需要插入到网页中,在扩展程序的清单文件中使用 content_scripts 字段注册它,如以下例子所示: 21 | 22 | ``` 23 | { 24 | "name": "我的扩展程序", 25 | ... 26 | "content_scripts": [ 27 | { 28 | "matches": ["http://www.google.com/*"], 29 | "css": ["mystyles.css"], 30 | "js": ["jquery.js", "myscript.js"] 31 | } 32 | ], 33 | ... 34 | } 35 | ``` 36 | 如果只是有时需要插入代码,应该使用 [permissions](declare_permissions.md) 字段,如[以编程方式插入](#pi)部分所述。 37 | 38 | 使用 content_scripts 字段,扩展程序可以向一个页面中插入多个内容脚本,每个内容脚本可以有多个 JavaScript 和 CSS 文件,content_scripts 数组中的每一个项目可以包含如下属性: 39 | 40 | | 名称 | 类型 | 描述 | 41 | | :------------- |:-------------| :-----| 42 | | matches | array of strings | 必选。 指定内容脚本要插入到哪些页面中去。有关这些字符串语法的更多细节请参见匹配表达式,有关如何排除 URL 的信息请参见[匹配表达式和范围](#match-patterns-globs) | 43 | | exclude_matches | array of strings | 可选。 排除不需要插入内容脚本的页面。有关这些字符串语法的更多细节请参见匹配表达式,有关如何排除 URL 的信息请参见[匹配表达式和范围](#match-patterns-globs) | 44 | | css | array of strings | 可选。 要插入匹配页面的 CSS 文件列表,它们将在页面的所有 DOM 构造或显示之前,按照数组中指定的顺序插入。 | 45 | | js | array of strings | 可选。 要插入匹配页面的 JavaScript 文件列表,它们将按照数组中指定的顺序插入。 | 46 | | run_at | strings | 可选。 控制 js 中的 JavaScript 文件何时插入,可以为 "document_start"、"document_end" 或 "document_idle",默认为 "document_idle"。 如果是 "document_start",这些文件将在 css 中指定的文件之后,但是在所有其他 DOM 构造或脚本运行之前插入。

如果是 "document_end",文件将在 DOM 完成之后立即插入,但是在加载子资源(如图像与框架)之前插入。

如果是 "document_idle",浏览器将在 "document_end" 和刚发生 window.onload 事件这两个时刻之间选择合适的时候插入,具体的插入时间取决于文档的复杂程度以及加载文档所花的时间,并且浏览器会尽可能地为加快页面加载速度而优化。

注意:如果使用 "document_idle",内容脚本不一定会收到 window.onload 事件,因为它们可能在这一事件已经发生后再执行。在大多数情况下,在 "document_idle" 时运行的内容脚本没有必要监听 onload 事件,因为它们保证在 DOM 完成后运行。如果您的脚本确实需要在 window.onload 之后运行,您可以检查 document.readyState 属性确定 onload 事件是否已经发生。| 47 | | all_frames | boolean | 可选。 控制内容脚本运行在匹配页面的所有框架中还是仅在顶层框架中。 | 48 | | include_globs | array of string | 可选。 在应用 matches 之后仅包含同时匹配这一范围的 URL。该属性是为了模拟 Greasemonkey 中的 @include 关键字,有关更多详情请参见下面的[匹配表达式和范围](#match-patterns-globs) | 49 | | include_globs | array of string | 可选。 在应用 matches 之后排除匹配这一范围的 URL。该属性是为了模拟 Greasemonkey 中的 @exclude 关键字,有关更多详情请参见下面的[匹配表达式和范围](#match-patterns-globs) | 50 | 51 | ### 匹配表达式和范围 52 | 53 | 如果页面的 URL 匹配任何 matches 指定的表达式以及任何 include_globs 指定的表达式,并且不匹配 exclude_matches 和 exclude_globs 指定的表达式,则会在页面中插入内容脚本。因为 matches 属性是必需的,exclude_matches、include_globs 和 exclude_globs 只能用来限制受到影响的页面。 54 | 例如,假设 matches 为 ["http://\*.nytimes.com/\*"]: 55 | 56 | * 如果 exclude_matches 为 ["\*://\*/\*business*"],那么内容脚本会插入“http://www.nytimes.com/health”,但不会插入“http://www.nytimes.com/business”。 57 | * 如果 include_globs 为 ["\*nytimes.com/???s/\*"],那么内容脚本会插入“http://www.nytimes.com/arts/index.html”和“http://www.nytimes.com/jobs/index.html”,但不会插入“http://www.nytimes.com/sports/index.html”。 58 | * 如果 exclude_globs 为 ["\*science\*"],那么内容脚本会插入“http://www.nytimes.com”,但不会插入“http://science.nytimes.com”或“http://www.nytimes.com/science”。 59 | 60 | 范围(glob)属性与匹配表达式相比遵循不同并且更灵活的语法。所有含有“通配符”星号和问号的 URL 都是可接受的范围字符串,星号(*)匹配任意长度的字符串(包括空字符串),问号(?)匹配任意单个字符。 61 | 62 | 例如,范围 "http://???.example.com/foo/*" 匹配以下任一 URL: 63 | 64 | * "http://www.example.com/foo/bar" 65 | * "http://the.example.com/foo/" 66 | 67 | 但是不匹配如下 URL: 68 | 69 | * "http://my.example.com/foo/bar" 70 | * "http://example.com/foo/" 71 | * "http://www.example.com/foo" 72 | 73 | ## 以编程方式插入 74 | 75 | 如果您的 JavaScript 或 CSS 代码不需要插入匹配的每一个页面时,例如,如果您希望当用户单击浏览器按钮的图标时才运行脚本,以编程方式插入代码就十分有用。 76 | 77 | 要向页面中插入代码,您的扩展程序必须拥有该页面的[主机权限](xhr.md#requesting-permission),并且还需要用到 chrome.tabs 模块。您可以使用清单文件中的 [permissions](declare_permissions.md) 字段获得这些权限。 78 | 79 | 一旦您拥有了相应的权限,您可以通过调用 [tabs.executeScript](tabs.md#method-executeScript) 向页面插入 JavaScript 代码。要插入 CSS 代码,请使用 [tabs.insertCSS](tabs.md#method-insertCSS)。 80 | 81 | 下列代码(来自 [make_page_red](http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/browserAction/make_page_red/) 例子)当用户单击浏览器按钮时向当前标签页插入并执行 JavaScript 代码。 82 | 83 | ``` 84 | chrome.browserAction.onClicked.addListener(function(tab) { 85 | chrome.tabs.executeScript({ 86 | code: 'document.body.style.backgroundColor="red"' 87 | }); 88 | }); 89 | ``` 90 | ``` 91 | "permissions": [ 92 | "activeTab" 93 | ], 94 | ``` 95 | 96 | 当浏览器正在显示一个 HTTP 页面,并且用户单击该扩展程序的浏览器按钮时,扩展程序将页面的 bgcolor 属性设为'red'。结果,除非页面通过 CSS 设置了背景颜色,页面将变成红色。 97 | 98 | 通常,您不会直接插入代码(如前面的例子所示),而是将代码放在一个文件中。您可以像这样插入文件的内容: 99 | 100 | ``` 101 | chrome.tabs.executeScript(null, {file: "content_script.js"}); 102 | ``` 103 | 104 | ## 执行环境 105 | 106 | 内容脚本在一个称为隔离环境的特殊环境中执行。它们可以访问所在页面的 DOM,但是不能访问当前页面创建的任何 JavaScript 变量或函数。对于每个内容脚本来说,就像没有其他 JavaScript 在当前页面上执行。反过来也是如此:在当前页面运行的 JavaScript 不能调用或访问任何内容脚本定义的函数或变量。 107 | 108 | 例如,考虑如下简单页面: 109 | 110 | ``` 111 | 112 | 113 | 121 | 122 | ``` 123 | 假设如下内容脚本插入到了 hello.html 中: 124 | 125 | ``` 126 | var greeting = "您好啊,"; 127 | var button = document.getElementById("mybutton"); 128 | button.person_name = "Roberto"; 129 | button.addEventListener("click", function() { 130 | alert(greeting + button.person_name + "。"); 131 | }, false); 132 | ``` 133 | 134 | 现在,如果按钮按下,您将会看到两条问候。 135 | 136 | 隔离环境允许每一个内容脚本更改自己的 JavaScript 环境,而不用担心是否会与页面或其他内容脚本发生冲突。例如,一个内容脚本可以包含 JQuery v1 而页面可以包含 JQuery v2,并且它们互不影响。 137 | 138 | 隔离环境的另一个重要好处是将页面上的 JavaScript 和扩展程序中的 JavaScript 完全区分开来,这样我们就可以为内容脚本提供额外功能,而这些额外功能不应该从网页中访问,我们也不用担心访问它们的网页。 139 | 140 | 网页与扩展程序之间共享的 JavaScript 对象值得注意,例如 `window.onload` 事件。每一个隔离环境拥有该对象自己的副本,对该对象赋值只影响这一独立的副本。例如,网页和扩展程序都可以给 `window.onload` 赋值,但是都不能读取另外一方的事件处理器。事件处理器将按照它们赋值的顺序调用。 141 | 142 | ## 与嵌入的页面通信 143 | 尽管内容脚本的执行环境和所在页面是互相隔离的,但是它们都可以访问页面的 DOM。如果页面想要和内容脚本通信(或者通过内容脚本与扩展程序通信),必须通过共享的 DOM 进行。 144 | 145 | 可以使用 window.postMessage(或者 window.webkitPostMessage 用于可传输(Transferable)的对象): 146 | 147 | ``` 148 | var port = chrome.runtime.connect(); 149 | 150 | window.addEventListener("message", function(event) { 151 | // 我们只接受来自我们自己的消息 152 | if (event.source != window) 153 | return; 154 | 155 | if (event.data.type && (event.data.type == "FROM_PAGE")) { 156 | console.log("内容脚本接收到:" + event.data.text); 157 | port.postMessage(event.data.text); 158 | } 159 | }, false); 160 | ``` 161 | 162 | ``` 163 | document.getElementById("theButton").addEventListener("click", 164 | function() { 165 | window.postMessage({ type: "FROM_PAGE", text: "来自网页的问候!" }, "*"); 166 | }, false); 167 | ``` 168 | 在上面的例子中,example.html(不是扩展程序的一部分)向自己发送消息,由内容脚本截获并检查,然后发送至扩展程序进程。通过这种方式,页面建立了与扩展程序之间的通信,通过类似的方式反过来也是可能的。 169 | 170 | 171 | ## 安全性考虑 172 | 当您编写内容脚本时,您应该注意两个安全问题。首先,注意不要向您插入内容脚本的网站引入安全隐患。例如,如果您的内容脚本从另一个网站接收内容(例如通过发出 [XMLHttpRequest](xhr.md)),一定要注意把内容插入当前页面前过滤可能的[跨站脚本攻击](https://en.wikipedia.org/wiki/Cross-site_scripting)。例如,首选 innerText 而不是 innerHTML 插入内容。当您在 HTTPS 页面上获取来自 HTTP 的内容时要特别小心,因为如果用户在不安全的网络环境中,HTTP 内容可能会因为网络中的[中间人攻击](http://en.wikipedia.org/wiki/Man-in-the-middle_attack)而遭到破坏。 173 | 174 | 第二,尽管在隔离环境中运行您的内容脚本提供了某些保护,但是如果您不加区分地使用来自网页的内容,恶意网页仍然可能攻击您的内容脚本。例如,以下形式是危险的: 175 | 176 | ``` 177 | var data = document.getElementById("json-data") 178 | // 警告!可能会执行恶意脚本! 179 | var parsed = eval("(" + data + ")") 180 | ``` 181 | ``` 182 | var elmt_id = ... 183 | // 警告!elmt_id可能为"); …恶意脚本… //"! 184 | window.setTimeout("animate(" + elmt_id + ")", 200); 185 | ``` 186 | 因此,改用更安全的不运行脚本的 API: 187 | 188 | ``` 189 | var data = document.getElementById("json-data") 190 | // JSON.parse 不会运行攻击者的脚本。 191 | var parsed = JSON.parse(data); 192 | ``` 193 | ``` 194 | var elmt_id = ... 195 | // 闭包形式的 setTimeout 不会运行脚本。 196 | window.setTimeout(function() { 197 | animate(elmt_id); 198 | }, 200); 199 | ``` 200 | ## 引用扩展程序的文件 201 | 使用 [extension.getURL](extension.md#method-getURL) 获得扩展程序的文件 URL,您可以像任何其他 URL 一样使用获得的结果,如以下代码所示。 202 | 203 | ``` 204 | //用来显示 <扩展程序目录>/images/myimage.png 的代码: 205 | var imgURL = chrome.extension.getURL("images/myimage.png"); 206 | document.getElementById("someImage").src = imgURL; 207 | ``` 208 | ## 例子 209 | 一个简单的通过消息通信的例子在[Message Timer](https://developer.chrome.com/extensions/samples#message-timer)中,有关以编程方式插入的例子请参见[Page Redder](https://developer.chrome.com/extensions/samples#page-redder)和[Email this page (by Google)](https://developer.chrome.com/extensions/samples#email-this-page-(by-google\))。 210 | 211 | ## 视频 212 | 以下视频(英文)讨论了内容脚本的重要概念 213 | [第一个视频描述了内容脚本和隔离环境。](https://www.youtube.com/watch?v=laLudeUmXHM) 214 | 215 | 接下来的视频描述了消息传递,突出演示了[内容脚本如何向所属扩展发送请求](https://www.youtube.com/watch?v=B4M_a7xejYI)。 -------------------------------------------------------------------------------- /messaging.md: -------------------------------------------------------------------------------- 1 | # 消息传递 2 | 3 | 由于内容脚本在网页而不是扩展程序的环境中运行,它们通常需要某种方式与扩展程序的其余部分通信。例如,RSS 阅读器扩展程序可能使用内容脚本检测页面上是否存在 RSS 供稿,然后通知后台页面,为当前页面显示页面按钮图标。 4 | 5 | 扩展程序和内容脚本间的通信使用消息传递的方式。两边均可以监听另一边发来的消息,并通过同样的通道回应。消息可以包含任何有效的 JSON 对象(null、boolean、number、string、array 或 object)。对于一次性的请求有一个简单的 API,同时也有更复杂的 API,允许你通过长时间的连接与共享的上下文交换多个消息。另外你也可以向另一个扩展程序发送消息,只要你知道它的标识符,这将在跨扩展程序消息传递部分介绍。 6 | 7 | ## 简单的一次性请求 8 | 9 | 如果你只需要向你的扩展程序的另一部分发送一个简单消息(以及可选地获得回应),你应该使用比较简单的 runtime.sendMessage 或 tabs.sendMessage 方法。这些方法分别让你从内容脚本向扩展程序或者反过来发送一个可以通过 JSON 序列化的消息,可选的 callback 参数允许你在需要的时候从另一边处理回应。 10 | 11 | 如下列代码所示从内容脚本中发送请求: 12 | 13 | contentscript.js: 14 | 15 | ``` 16 | chrome.runtime.sendMessage({greeting: "你好"}, function(response) { 17 | console.log(response.farewell); 18 | }); 19 | ``` 20 | 21 | 从扩展程序向内容脚本发送请求与上面类似,唯一的区别是你需要指定发送至哪一个标签页。这一例子演示如何向选定标签页中的内容脚本发送消息。 22 | 23 | background.html 24 | 25 | ``` 26 | chrome.tabs.query({active: true, currentWindow: true}, function(tabs) { 27 | chrome.tabs.sendMessage(tabs[0].id, {greeting: "你好"}, function(response) { 28 | console.log(response.farewell); 29 | }); 30 | }); 31 | ``` 32 | 33 | 在接收端,你需要设置一个 runtime.onMessage 事件监听器来处理消息。 34 | 35 | ``` 36 | chrome.runtime.onMessage.addListener( 37 | function(request, sender, sendResponse) { 38 | console.log(sender.tab ? 39 | "来自内容脚本:" + sender.tab.url : 40 | "来自扩展程序"); 41 | if (request.greeting == "你好") 42 | sendResponse({farewell: "再见"}); 43 | }); 44 | ``` 45 | 46 | 注意: 如果多个页面都监听 onMessage 事件,对于某一次事件只有第一次调用 sendResponse() 能成功发出回应,所有其他回应将被忽略。 47 | 48 | ## 长时间的连接 49 | 50 | 有时候需要长时间的对话,而不是一次请求和回应。在这种情况下,你可以分别使用 runtime.connect 或 tabs.connect 从你的内容脚本建立到扩展程序(或者反过来)的长时间连接。建立的通道可以有一个可选的名称,让你区分不同类型的连接。 51 | 52 | 使用长时间连接的一种可能的情形为自动填充表单的扩展程序。对于一次登录操作,内容脚本可以连接到扩展程序页面,每次页面上的输入元素需要填写表单数据时向扩展程序发送消息。共享的连接允许扩展程序保留来自内容脚本的不同消息之间的状态联系。 53 | 54 | 建立连接时,两端都将获得一个 runtime.Port 对象,用来通过建立的连接发送和接收消息。 55 | 56 | 如下代码演示如何从内容脚本中建立连接,发送并监听消息: 57 | 58 | contentscript.js 59 | 60 | ``` 61 | var port = chrome.runtime.connect({name: "敲门"}); 62 | port.postMessage({joke: "敲门"}); 63 | port.onMessage.addListener(function(msg) { 64 | if (msg.question == "是谁?") 65 | port.postMessage({answer: "女士"}); 66 | else if (msg.question == "哪位女士?") 67 | port.postMessage({answer: "Bovary 女士"}); 68 | }); 69 | ``` 70 | 71 | 从扩展程序向内容脚本发送请求与之类似,唯一的区别是你需要指定连接到哪一个标签页。你只需要将以上例子中的连接调用替换为 tabs.connect。 72 | 73 | 为了处理传入连接,你需要设置一个 runtime.onConnect 事件监听器。这一步无论在内容脚本还是扩展程序页面中都是一样的。当你的扩展程序的另一部分调用 connect() 时,会产生这一事件,同时传递你可以通过建立的连接发送和接受消息的 runtime.Port 对象。如下代码演示如何回应传入连接: 74 | 75 | ``` 76 | chrome.runtime.onConnect.addListener(function(port) { 77 | console.assert(port.name == "敲门"); 78 | port.onMessage.addListener(function(msg) { 79 | if (msg.joke == "敲门") 80 | port.postMessage({question: "是谁?"}); 81 | else if (msg.answer == "女士") 82 | port.postMessage({question: "哪位女士?"}); 83 | else if (msg.answer == "Bovary 女士") 84 | port.postMessage({question: "我没听清楚。"}); 85 | }); 86 | }); 87 | ``` 88 | 89 | 你可能想知道连接何时关闭,例如你需要为每一个打开的端口单独保留状态。这种情况下你可以监听 runtime.Port.onDisconnect 事件,当连接的另一端调用 runtime.Port.disconnect 或包含该端口的页面已结束(例如标签页转到了另一个页面)时,对于每一个端口确保都会发生一次该事件。 90 | 91 | ## 跨扩展程序消息传递 92 | 93 | 除了在你的扩展程序的不同组成部分间发送消息以外,你也可以使用消息传递 API 与其他扩展程序通信。这样你可以提供一个公共的 API,让其他扩展程序使用。 94 | 95 | 监听传入的请求和连接与处理内部的消息类似,唯一的区别是你分别使用 runtime.onMessageExternal 和 runtime.onConnectExternal 事件。如下是分别处理这两个事件的例子: 96 | 97 | ``` 98 | // 用于简单的请求: 99 | chrome.runtime.onMessageExternal.addListener( 100 | function(request, sender, sendResponse) { 101 | if (sender.id == blacklistedExtension) 102 | return; // 不允许这一扩展程序访问 103 | else if (request.getTargetData) 104 | sendResponse({targetData: targetData}); 105 | else if (request.activateLasers) { 106 | var success = activateLasers(); 107 | sendResponse({activateLasers: success}); 108 | } 109 | }); 110 | 111 | // 用于长时间的连接: 112 | chrome.runtime.onConnectExternal.addListener(function(port) { 113 | port.onMessage.addListener(function(msg) { 114 | // 有关处理 onMessage 事件的示例请参见其他例子 115 | }); 116 | }); 117 | ``` 118 | 同样,向另一个扩展程序发送消息与在你的扩展程序中发送消息类似,唯一的区别是你必须传递你需要与之通信的扩展程序的标识符。例如: 119 | 120 | ``` 121 | // 我们需要与之通信的扩展程序的标识符。 122 | var laserExtensionId = "abcdefghijklmnoabcdefhijklmnoabc"; 123 | 124 | // 发出一个简单请求: 125 | chrome.runtime.sendMessage(laserExtensionId, {getTargetData: true}, 126 | function(response) { 127 | if (targetInRange(response.targetData)) 128 | chrome.runtime.sendMessage(laserExtensionId, {activateLasers: true}); 129 | }); 130 | 131 | // 建立一个长时间的连接: 132 | var port = chrome.runtime.connect(laserExtensionId); 133 | port.postMessage(...); 134 | ``` 135 | 136 | ## 从网页发送消息 137 | 与跨扩展程序消息传递类似,你的应用或扩展程序可以接受并响应来自普通网页的消息。要使用该特性,你必须首先在你的 manifest.json 中指定你希望与之通信的网站,例如: 138 | 139 | manifest.json 140 | 141 | ``` 142 | "externally_connectable": { 143 | "matches": ["*://*.example.com/*"] 144 | } 145 | ``` 146 | 这样会将消息传递 API 提供给所有匹配你指定的 URL 表达式的网页。URL 表达式必须至少包含一个二级域名,也就是说禁止使用类似于“*”、“*.com”、“*.co.uk”和“*.appspot.com”之类的主机名。在网页中,使用 runtime.sendMessage 或 runtime.connect API 向指定应用或扩展程序发送消息。例如: 147 | 148 | ``` 149 | // 我们希望与之通信的扩展程序标识符。 150 | var editorExtensionId = "abcdefghijklmnoabcdefhijklmnoabc"; 151 | 152 | // 发送一个简单的请求: 153 | chrome.runtime.sendMessage(editorExtensionId, {openUrlInEditor: url}, 154 | function(response) { 155 | if (!response.success) 156 | handleError(url); 157 | }); 158 | ``` 159 | 160 | 在你的应用或扩展程序中,你可以通过 runtime.onMessageExternal 或 runtime.onConnectExternal API 监听来自网页的消息,与跨扩展程序消息传递类似。只有网页才能发起连接。如下是一个例子: 161 | 162 | ``` 163 | chrome.runtime.onMessageExternal.addListener( 164 | function(request, sender, sendResponse) { 165 | if (sender.url == blacklistedWebsite) 166 | return; // 不允许该网页访问 167 | if (request.openUrlInEditor) 168 | openUrl(request.openUrlInEditor); 169 | }); 170 | ``` 171 | 172 | ## 原生消息通信 173 | 174 | 扩展程序可以与原生应用程序交换消息。支持该特性的原生应用程序必须注册一个了解如何与扩展程序通信的原生消息宿主,Chrome 浏览器将在单独的进程中启动宿主,并通过标准输入和标准输出流与之通信。 175 | 176 | ### 原生消息通信宿主 177 | 178 | 为了注册一个原生消息通信宿主,应用程序必须安装一个清单文件,定义原生消息通信宿主的配置。如下是这一清单文件的例子: 179 | 180 | manifest.json 181 | 182 | ``` 183 | { 184 | "name": "com.my_company.my_application", 185 | "description": "我的应用程序", 186 | "path": "C:\\Program Files\\My Application\\chrome_native_messaging_host.exe", 187 | "type": "stdio", 188 | "allowed_origins": [ 189 | "chrome-extension://knldjmfmopnpolahpmmgbagdohdnhkik/" 190 | ] 191 | } 192 | ``` 193 | 194 | * 名称: 描述 195 | * name: 原生消息通信宿主的名称,客户端需要将该字符串传递给 runtime.connectNative 或 runtime.sendNativeMessage。 196 | * description: 应用程序的简短描述。 197 | * path: 原生消息通信宿主的二进制文件路径。在 Linux 和 OSX 上必须使用绝对路径,在 Windows 上可以使用相对于清单文件所在目录的路径。 198 | * type: 与原生消息通信宿主交流时所使用的接口类型。目前该参数只有一种可能的值:stdio,它表示 Chrome 浏览器应该使用 stdin(标准输入)和 stdout(标准输出)与宿主通信。 199 | * allowed_origins: 允许访问原生消息通信宿主的扩展程序列表。 200 | 201 | 清单文件的类型取决与平台: 202 | 203 | * Windows: 204 | > 清单文件可以在文件系统中的任意位置,应用程序的安装程序必须创建如下注册表键 HKEY_LOCAL_MACHINE\SOFTWARE\Google\Chrome\NativeMessagingHosts\com.my_company.my_application,并将键的默认值设置为清单文件的完整路径。 205 | * OSX: 206 | > 清单文件必须位于 /Library/Google/Chrome/NativeMessagingHosts/com.my_company.my_application.json 207 | * Linux: 208 | > 清单文件必须位于 /etc/opt/chrome/native-messaging-hosts/com.my_company.my_application.json 209 | 210 | Chrome 浏览器在单独的进程中启动每一个原生消息通信宿主,并使用标准输入(stdin)与标准输出(stdout)与之通信。向两个方向发送消息时使用相同的格式:每一条消息使用 JSON 序列化,以 UTF-8 编码,并在前面附加 32 位的消息长度(使用本机字节顺序)。 211 | 212 | 使用 runtime.connectNative 创建消息传递端口时,Chrome 浏览器会启动原生消息传递宿主进程,并让它一直运行,直到端口释放。如果消息是使用 runtime.sendNativeMessage 发送,没有创建消息传递端口,Chrome 浏览器会为每一条消息创建一个新的原生消息传递宿主进程。在这种情况下,宿主进程产生的第一条消息作为原始请求的响应处理,也就是说,Chrome 浏览器会将它传递给调用 runtime.sendNativeMessage 时指定的回调函数,而原生消息传递宿主产生的所有其他消息则会忽略。 213 | 214 | ### 连接到原生应用程序 215 | 216 | 向原生应用程序发送和接收消息类似与跨扩展程序消息传递,主要的区别是用 runtime.connectNative 代替 runtime.connect,用 runtime.sendNativeMessage 代替 runtime.sendMessage。 217 | 218 | 以下例子创建一个 runtime.Port 对象,连接到原生消息通信宿主 com.my_company.my_application,开始监听来自该端口的消息,并发送一条消息: 219 | 220 | ``` 221 | var port = chrome.runtime.connectNative('com.my_company.my_application'); 222 | port.onMessage.addListener(function(msg) { 223 | console.log("收到 " + msg); 224 | }); 225 | port.onDisconnect.addListener(function() { 226 | console.log("已断开"); 227 | }); 228 | port.postMessage({ text: "我的应用程序,你好!" }); 229 | ``` 230 | 231 | runtime.sendNativeMessage 可以用来向原生应用程序发送消息,而不用创建端口。例如: 232 | 233 | ``` 234 | chrome.runtime.sendNativeMessage('com.my_company.my_application', 235 | { text: "你好" }, 236 | function(response) { 237 | console.log("收到 " + response); 238 | }); 239 | ``` 240 | 241 | ## 安全性考虑 242 | 当你从内容脚本或另一个扩展程序接收消息时,你的后台网页应该小心,以免遭到跨站脚本攻击。特别地,避免使用下面这些危险的 API: 243 | 244 | background.js 245 | 246 | ``` 247 | chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) { 248 | // 警告!可能会执行恶意脚本! 249 | var resp = eval("(" + response.farewell + ")"); 250 | }); 251 | ``` 252 | ``` 253 | chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) { 254 | // 警告!可能会插入恶意脚本! 255 | document.getElementById("resp").innerHTML = response.farewell; 256 | }); 257 | ``` 258 | 259 | 你应该首选不运行脚本的更安全的API: 260 | 261 | background.js 262 | 263 | ``` 264 | chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) { 265 | // JSON.parse 不会执行攻击者的脚本。 266 | var resp = JSON.parse(response.farewell); 267 | }); 268 | ``` 269 | ``` 270 | chrome.tabs.sendMessage(tab.id, {greeting: "hello"}, function(response) { 271 | // innerText 不会让攻击者插入 HTML 元素。 272 | document.getElementById("resp").innerText = response.farewell; 273 | }); 274 | ``` 275 | 276 | ## 例子 277 | 278 | 可以在 [examples/api/messaging](http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/api/messaging/) -->目录中找到使用消息通信的简单例子,examples/api/nativeMessaging 包含使用原生消息通信的示例应用程序,另外请参见 [contentscript_xhr](http://src.chromium.org/viewvc/chrome/trunk/src/chrome/common/extensions/docs/examples/howto/contentscript_xhr) 例子,在这个例子中内容脚本与所属扩展程序交换消息,以便扩展程序可以代表内容脚本发出跨站请求。有关更多例子以及查看源代码的帮助,请参见[示例](http://developer.chrome.com/extensions/samples.html)。 --------------------------------------------------------------------------------