From 33058f2b292b3a581333bdfb21b8f671898c5060 Mon Sep 17 00:00:00 2001 From: Peter Bengtsson Date: Tue, 8 Dec 2020 14:40:17 -0500 Subject: initial commit --- files/zh-cn/mozilla/add-ons/sdk/builder/index.html | 13 + .../add-ons/sdk/guides/content_scripts/index.html | 486 +++++++++++ files/zh-cn/mozilla/add-ons/sdk/guides/index.html | 115 +++ .../multiprocess_firefox_and_the_sdk/index.html | 212 +++++ .../sdk/guides/working_with_events/index.html | 122 +++ .../add-ons/sdk/high-level_apis/base64/index.html | 65 ++ .../sdk/high-level_apis/clipboard/index.html | 101 +++ .../mozilla/add-ons/sdk/high-level_apis/index.html | 12 + .../sdk/high-level_apis/notifications/index.html | 129 +++ .../add-ons/sdk/high-level_apis/panel/index.html | 899 +++++++++++++++++++++ .../add-ons/sdk/high-level_apis/tabs/index.html | 669 +++++++++++++++ .../add-ons/sdk/high-level_apis/url/index.html | 191 +++++ .../add-ons/sdk/high-level_apis/widget/index.html | 839 +++++++++++++++++++ files/zh-cn/mozilla/add-ons/sdk/index.html | 104 +++ .../mozilla/add-ons/sdk/low-level_apis/index.html | 26 + .../sdk/low-level_apis/test_assert/index.html | 283 +++++++ .../sdk/low-level_apis/ui_button_action/index.html | 526 ++++++++++++ files/zh-cn/mozilla/add-ons/sdk/tools/index.html | 14 + .../zh-cn/mozilla/add-ons/sdk/tools/jpm/index.html | 652 +++++++++++++++ .../add-ons/sdk/tools/package_json/index.html | 312 +++++++ .../add_a_menu_item_to_firefox/index.html | 92 +++ .../adding_a_button_to_the_toolbar/index.html | 83 ++ .../annotator/implementing_the_widget/index.html | 92 +++ .../add-ons/sdk/tutorials/annotator/index.html | 36 + .../sdk/tutorials/annotator/overview/index.html | 54 ++ .../sdk/tutorials/display_a_popup/index.html | 135 ++++ .../sdk/tutorials/getting_started/index.html | 172 ++++ .../sdk/tutorials/getting_started_(jpm)/index.html | 162 ++++ .../zh-cn/mozilla/add-ons/sdk/tutorials/index.html | 144 ++++ .../add-ons/sdk/tutorials/installation/index.html | 135 ++++ .../mozilla/add-ons/sdk/tutorials/l10n/index.html | 381 +++++++++ .../sdk/tutorials/list_open_tabs/index.html | 72 ++ .../sdk/tutorials/listen_for_page_load/index.html | 42 + .../modifying_the_page_hosted_by_a_tab/index.html | 109 +++ .../modifying_web_pages_based_on_url/index.html | 210 +++++ .../sdk/tutorials/open_a_web_page/index.html | 52 ++ .../sdk/tutorials/troubleshooting/index.html | 39 + .../add-ons/sdk/tutorials/unit_testing/index.html | 102 +++ .../tutorials/\346\227\245\345\277\227/index.html" | 62 ++ .../index.html" | 49 ++ 40 files changed, 7993 insertions(+) create mode 100644 files/zh-cn/mozilla/add-ons/sdk/builder/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/guides/content_scripts/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/guides/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/guides/multiprocess_firefox_and_the_sdk/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/guides/working_with_events/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/high-level_apis/base64/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/high-level_apis/clipboard/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/high-level_apis/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/high-level_apis/notifications/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/high-level_apis/panel/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/high-level_apis/tabs/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/high-level_apis/url/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/high-level_apis/widget/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/low-level_apis/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/low-level_apis/test_assert/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/low-level_apis/ui_button_action/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/tools/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/tools/jpm/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/tools/package_json/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/tutorials/add_a_menu_item_to_firefox/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/tutorials/adding_a_button_to_the_toolbar/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/tutorials/annotator/implementing_the_widget/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/tutorials/annotator/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/tutorials/annotator/overview/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/tutorials/display_a_popup/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/tutorials/getting_started/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/tutorials/getting_started_(jpm)/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/tutorials/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/tutorials/installation/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/tutorials/l10n/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/tutorials/list_open_tabs/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/tutorials/listen_for_page_load/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/tutorials/modifying_the_page_hosted_by_a_tab/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/tutorials/modifying_web_pages_based_on_url/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/tutorials/open_a_web_page/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/tutorials/troubleshooting/index.html create mode 100644 files/zh-cn/mozilla/add-ons/sdk/tutorials/unit_testing/index.html create mode 100644 "files/zh-cn/mozilla/add-ons/sdk/tutorials/\346\227\245\345\277\227/index.html" create mode 100644 "files/zh-cn/mozilla/add-ons/sdk/tutorials/\346\267\273\345\212\240\344\270\200\344\270\252\350\217\234\345\215\225\351\241\271/index.html" (limited to 'files/zh-cn/mozilla/add-ons/sdk') diff --git a/files/zh-cn/mozilla/add-ons/sdk/builder/index.html b/files/zh-cn/mozilla/add-ons/sdk/builder/index.html new file mode 100644 index 0000000000..1baa282d43 --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/builder/index.html @@ -0,0 +1,13 @@ +--- +title: Builder +slug: Mozilla/Add-ons/SDK/Builder +translation_of: Archive/Add-ons/Add-on_SDK/Builder +--- +

The Add-on Builder was a web-based development environment that allowed developers to create add-ons using the SDK APIs, but without needing to use the cfx command line tool. It was retired on April 1, 2014, and the "builder.addons.mozilla.org" domain now redirects to this page.
+
+ If you have only used the SDK through the Builder, you already know most of what you need to know to develop using just the SDK. The high-level and low-level APIs used for Builder add-ons are exactly the same for Builder and SDK. To switch to the SDK:

+ diff --git a/files/zh-cn/mozilla/add-ons/sdk/guides/content_scripts/index.html b/files/zh-cn/mozilla/add-ons/sdk/guides/content_scripts/index.html new file mode 100644 index 0000000000..fa95b15db3 --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/guides/content_scripts/index.html @@ -0,0 +1,486 @@ +--- +title: Content Scripts(内容脚本) +slug: Mozilla/Add-ons/SDK/Guides/Content_Scripts +translation_of: Archive/Add-ons/Add-on_SDK/Guides/Content_Scripts +--- +
{{AddonSidebar}} +

很多 add-ons 需要访问和修改 web 页面的内容。但是 add-on 的主代码不能直接访问 web 内容。替代方案是, SDK add-ons 需要使用一些分散的脚本代理访问 web 内容,这些脚本被称作内容脚本(content scripts)。本页面描述如何开发和部署内容脚本。

+ +

内容脚本是在使用SDK时很令人疑惑的点,但你很有可能不得不使用它们。下面有五个基本原则:

+ + + +

这个完整的 add-on 表现出所有的这些原则。它的"main.js"使用 tabs 模块附加了一个内容脚本到当前标签页。本例中内容脚本作为字符串传递,内容脚本简单地替换了页面的内容:

+ +
// main.js
+var tabs = require("sdk/tabs");
+var contentScriptString = 'document.body.innerHTML = "<h1>this page has been eaten</h1>";'
+
+tabs.activeTab.attach({
+  contentScript: contentScriptString
+});
+ +

下面的高层次 SDK 模块能使用内容脚本来修改 web 页面:

+ + + +

另外,还能使用 HTML 定义了一些 SDK 用户接口组件,并且使用分类的脚本来和这些内容交互。从很多方面来讲,这些脚本就像内容脚本一样,但它们并不是本文的关注点。要学习如何和用户接口模块的内容交互,请参看模块定义文档:panelsidebarframe

+ +

这篇指南中列出的几乎所有的示例都是完整并且且最小的,可以在 Github 的 addon-sdk-content-scripts repository 页面上获得。

+ +

加载用户脚本

+ +
+

你可以声明一个字符串或者指定 contentScriptcontentScriptFile 选项加载一个单独的脚本。contentScript 选项接受一个作为脚本的字符串:

+ +
// main.js
+
+var pageMod = require("sdk/page-mod");
+var contentScriptValue = 'document.body.innerHTML = ' +
+                         ' "<h1>Page matches ruleset</h1>";';
+
+pageMod.PageMod({
+  include: "*.mozilla.org",
+  contentScript: contentScriptValue
+});
+ +

contentScriptFile 选项接受一个作为 resource:// URL 的字符串,指向一个存储在你的 add-on 的 data 目录中的脚本文件。jpm不会默认创建"data"目录,所以你必须创建该目录并将你的用户脚本放进去。

+ +

本 add-on 提供一个 URL ,指向"content-script.js"文件,存储在 add-on 根目录下的 data 子目录:

+ +
// main.js
+
+var data = require("sdk/self").data;
+var pageMod = require("sdk/page-mod");
+
+pageMod.PageMod({
+  include: "*.mozilla.org",
+  contentScriptFile: data.url("content-script.js")
+});
+ +
// content-script.js
+
+document.body.innerHTML = "<h1>Page matches ruleset</h1>";
+ +
+

从 Firefox 34 开始,你可以使用"./content-script.js"替代 self.data.url("content-script.js")。所以你可以像这样重写:

+ +
var pageMod = require("sdk/page-mod");
+
+pageMod.PageMod({
+  include: "*.mozilla.org",
+  contentScriptFile: "./content-script.js"
+});
+
+
+ +
+

除非你的内容脚本非常简单并且固定是一个静态的字符串,请不要使用 contentScript:否则,你会在从 AMO 获取你的add-on上遇到问题。

+ +

相反,把脚本放到一个单独的文件并用 contentScriptFile 加载它。这回事你的代码更易维护、安全、调试和审核。

+
+ +

你可以给 contentScriptcontentScriptFile 传递字符串数组来加载多个脚本:

+ +
// main.js
+
+var tabs = require("sdk/tabs");
+
+tabs.on('ready', function(tab) {
+  tab.attach({
+      contentScript: ['document.body.style.border = "5px solid red";', 'window.alert("hi");']
+  });
+});
+
+ +
// main.js
+
+var data = require("sdk/self").data;
+var pageMod = require("sdk/page-mod");
+
+pageMod.PageMod({
+  include: "*.mozilla.org",
+  contentScriptFile: [data.url("jquery.min.js"), data.url("my-content-script.js")]
+});
+ +

如果你这么做,这些脚本之间可以直接交互,就像他们被同一个 web 页面加载一样。

+ +

你也可以把 contentScriptcontentScriptFile 一起用。如果你这么做,使用 contentScriptFile 定义的脚本会在使用 contentScript 定义的脚本之前加载。这使你能够用 URL 加载比如 jQuery 这样的 JavaScript 库,然后传递一个简单的能够使用jQuery脚本:

+ +
// main.js
+
+var data = require("sdk/self").data;
+var pageMod = require("sdk/page-mod");
+
+var contentScriptString = '$("body").html("<h1>Page matches ruleset</h1>");';
+
+pageMod.PageMod({
+  include: "*.mozilla.org",
+  contentScript: contentScriptString,
+  contentScriptFile: data.url("jquery.js")
+});
+ +
+

除非你的内容脚本非常简单并且固定是一个静态的字符串,请不要使用 contentScript:否则,在从 AMO 获取你的 add-on 上,你会遇到问题。

+ +

相反,把脚本放到一个单独的文件并用 contentScriptFile 加载它。这回事你的代码更易维护、安全、调试和审核。

+
+ +

控制附加脚本的时间

+ +

contentScriptWhen 选项指定了什么时候加载内容脚本。从这里选一个:

+ +
    +
  • "start":页面 document 元素插入 DOM 之后,立即加载脚本。这时 DOM 的内容仍未加载,所以脚本不能与其交互。
  • +
  • "ready":页面 DOM 加载完后加载脚本:也就是说,在那个时间点 DOMContentLoaded 事件触发。这时,内容脚本可以和DOM内容交互,但外部引用的样式表和图片可能还没有完成加载。
  • +
  • "end":页面上所有内容(DOM、JS、CSS、images)加载完后,加载脚本,就是在 window.onload 事件触发的时候
  • +
+ +

默认值为 "end"

+ +

注意 tab.attach() 不支持 contentScriptWhen,因为它原来就是在页面加载页面的时候被调用的。

+ +

传递配置选项

+ +

contentScriptOptions 是一个作为只读对象暴露给内容脚本的JSON对象,在 self.options 的属性里:

+ +
// main.js
+
+var tabs = require("sdk/tabs");
+
+tabs.on('ready', function(tab) {
+  tab.attach({
+      contentScript: 'window.alert(self.options.message);',
+      contentScriptOptions: {"message" : "hello world"}
+  });
+});
+ +

这里可以使用任何可以转成json的值(object、array、string等等)。

+ +

访问 DOM

+ +

内容脚本可以访问页面的 DOM,就像任何页面中加载的脚本(页面脚本)一样。但是内容脚本和页面脚本之间是隔离的:

+ +
    +
  • 内容脚本不能看到任何由页面脚本添加到页面的 JavaScript 对象
  • +
  • 如果页面脚本重定义了某个 DOM 对象的行为,但内容脚本只会看到原来的那个行为。
  • +
+ +

相反也是如此:页面脚本不能看到内容脚本添加的 JavaScript 对象。

+ +

例如,假想一个页面用页面脚本添加变量 foowindow 对象:

+ +
<!DOCTYPE html">
+<html>
+  <head>
+    <script>
+    window.foo = "hello from page script"
+    </script>
+  </head>
+</html>
+ +

在这个脚本后面加载到页面的其他脚本也可以访问 foo。但是内容脚本不能:

+ +
// main.js
+
+var tabs = require("sdk/tabs");
+var mod = require("sdk/page-mod");
+var self = require("sdk/self");
+
+var pageUrl = self.data.url("page.html")
+
+var pageMod = mod.PageMod({
+  include: pageUrl,
+  contentScript: "console.log(window.foo);"
+})
+
+tabs.open(pageUrl);
+ +
console.log: my-addon: null
+
+ +

这种隔离策略有着很合理的理由。首先,这意味着内容脚本不会泄露对象给 web 页面,这样可能会打开安全漏洞。第二,这意味着,在内容脚本创建对象的时候,可以不用担心是否会和页面脚本添加的对象相冲突。

+ +

这种隔离意味着,例如,如果一个 web 页面加载了 jQuery 库,那么内容脚本不能够看到由该库添加的 jQuery 对象——但是可以看到内容脚本添加的自己的 jQuery 对象,并且它不会和页面脚本的 jQuery 版本冲突。

+ +

和页面脚本交互

+ +

一般来说,这种内容脚本和页面脚本的隔离正是你所希望的。但是有时候你也许会希望和页面脚本交互:你想在内容脚本和页面脚本之间共享对象来,来在它们之间发送消息。如果你需要这么做,请阅读和页面脚本交互

+ +

事件监听器

+ +

你可以监听 DOM 的事件,就像在页面脚本中一样,但是有两个重要的区别:

+ +

第一,如果你向 setAttribute() 传递字符串,来定义了事件监听器,那么此监听器被当做是在页面上下文中的,所以它不能访问任何内容脚本中的变量。

+ +

如下,内容脚本会失败报错"theMessage is not defined":

+ +
var theMessage = "Hello from content script!";
+anElement.setAttribute("onclick", "alert(theMessage);");
+ +

Second, if you define an event listener by direct assignment to a global event handler like onclick, then the assignment might be overridden by the page. For example, here's an add-on that tries to add a click handler by assignment to window.onclick:

+ +
var myScript = "window.onclick = function() {" +
+               "  console.log('unsafewindow.onclick: ' + window.document.title);" +
+               "}";
+
+require("sdk/page-mod").PageMod({
+  include: "*",
+  contentScript: myScript,
+  contentScriptWhen: "start"
+});
+ +

这个示例会在大多数页面上正常工作,但是会在定义 onclick 的页面上失败:

+ +
<html>
+  <head>
+  </head>
+  <body>
+    <script>
+    window.onclick = function() {
+      window.alert("it's my click now!");
+    }
+    </script>
+  </body>
+</html>
+ +

由于这些原因,最好还是用 addEventListener() 添加一个事件监听器,定义监听器为一个函数:

+ +
var theMessage = "Hello from content script!";
+
+anElement.onclick = function() {
+  alert(theMessage);
+};
+
+anotherElement.addEventListener("click", function() {
+  alert(theMessage);
+});
+ +

和 add-on 通信

+ +

为了使 add-on 脚本和内容脚本相互通信,任何一通信端都要访问 port 对象。

+ +
    +
  • 要从一头发送消息到另一头,使用 port.emit()
  • +
  • 要从另一头接收消息,使用 port.on()
  • +
+ +

消息是异步的:也就是说,发送方不会等待接收方的回应,而仅仅是发送消息完后继续处理别的事情。

+ +

这里有一个简单的 add-on 使用 port 发送一个消息到内容脚本:

+ +
// main.js
+
+var tabs = require("sdk/tabs");
+var self = require("sdk/self");
+
+tabs.on("ready", function(tab) {
+  var worker = tab.attach({
+    contentScriptFile: self.data.url("content-script.js")
+  });
+  worker.port.emit("alert", "Message from the add-on");
+});
+
+tabs.open("http://www.mozilla.org");
+ +
// content-script.js
+
+self.port.on("alert", function(message) {
+  window.alert(message);
+});
+ +
+

context-menu 模块没有使用这里描述的通信模型。了解更多关于使用 context-menu 和内容脚本通信的事情,参看 context-menu documentation

+
+ +

在内容脚本中访问 port

+ +

内容脚本中,port 对象是作为global下 self 对象的属性。所以要从内容脚本中发送消息的话:

+ +
self.port.emit("myContentScriptMessage", myContentScriptMessagePayload);
+ +

要从 add-on 代码接收消息

+ +
self.port.on("myAddonMessage", function(myAddonMessagePayload) {
+  // Handle the message
+});
+ +
+

注意 global下 self 对象和 self 模块完全不一样,后者提供一个API给 add-on,用来访问它的数据文件和ID。

+
+ +

在内容脚本中访问 port

+ +

在 add-on 代码中,联通 add-on 和某一特定内容脚本上下文的通道被封装入 worker 对象。所以和内容脚本通信的 port 对象其实是其相对应的 worker 对象的一个属性。

+ +

但是,这个 worker 没有暴露给 add-on 代码,以及同样所有的模块。

+ +

page-worker

+ +

page-worker 对象直接整合了 work API。所以要从一个由 page-worker 关联的内容脚本接收消息的话,你可以使用 pageWorker.port.on()

+ +
// main.js
+
+var self = require("sdk/self");
+
+var pageWorker = require("sdk/page-worker").Page({
+  contentScriptFile: self.data.url("content-script.js"),
+  contentURL: "http://en.wikipedia.org/wiki/Internet"
+});
+
+pageWorker.port.on("first-para", function(firstPara) {
+  console.log(firstPara);
+});
+ +

要从你的 add-on 发送用户定义的消息,你可以只调用 pageWorker.port.emit()

+ +
// main.js
+
+var self = require("sdk/self");
+
+var pageWorker = require("sdk/page-worker").Page({
+  contentScriptFile: self.data.url("content-script.js"),
+  contentURL: "http://en.wikipedia.org/wiki/Internet"
+});
+
+pageWorker.port.on("first-para", function(firstPara) {
+  console.log(firstPara);
+});
+
+pageWorker.port.emit("get-first-para");
+ +
// content-script.js
+
+self.port.on("get-first-para", getFirstPara);
+
+function getFirstPara() {
+  var paras = document.getElementsByTagName("p");
+  if (paras.length > 0) {
+    var firstPara = paras[0].textContent;
+    self.port.emit("first-para", firstPara);
+  }
+}
+ +

page-mod

+ +

单个 page-mod 对象可以附加它的脚本到多个页面,每个页面有它自己的上下文来运行内容脚本,所以每个页面都需要相互隔离的通道(worker)。

+ +

所以 page-mod 没有直接整合 worker 的 API。而是在每次内容脚本被附加到页面时,page-mod 发送一个 attach 事件,它的监听器会给对应的上下文传递一个 worker。通过为 attach 提供一个监听器,你可以访问被一个 page-mod 附加到页面上的内容脚本的 port 对象:

+ +
// main.js
+
+var pageMods = require("sdk/page-mod");
+var self = require("sdk/self");
+
+var pageMod = pageMods.PageMod({
+  include: ['*'],
+  contentScriptFile: self.data.url("content-script.js"),
+  onAttach: startListening
+});
+
+function startListening(worker) {
+  worker.port.on('click', function(html) {
+    worker.port.emit('warning', 'Do not click this again');
+  });
+}
+ +
// content-script.js
+
+window.addEventListener('click', function(event) {
+  self.port.emit('click', event.target.toString());
+  event.stopPropagation();
+  event.preventDefault();
+}, false);
+
+self.port.on('warning', function(message) {
+  window.alert(message);
+});
+
+ +

上面的 add-on 里有两条消息:

+ +
    +
  • 当用户点击页面元素时,click 从 page-mod 被发送到当前 add-on。
  • +
  • warning 发送一条傻气的字符串回给page-mod
  • +
+ +

Tab.attach()

+ +

Tab.attach() 方法返回一个 worker,你可以用来和附加的内容脚本通信。

+ +

这个 add-on 添加了一个按钮到Firefox:等用户点击按钮是,这个 add-on 附加一个内容脚本到当前的标签页,发送给内容脚本一条名为 "my-addon-message"的消息,并且监听名为"my-script-response"的响应:

+ +
//main.js
+
+var tabs = require("sdk/tabs");
+var buttons = require("sdk/ui/button/action");
+var self = require("sdk/self");
+
+buttons.ActionButton({
+  id: "attach-script",
+  label: "Attach the script",
+  icon: "./icon-16.png",
+  onClick: attachScript
+});
+
+function attachScript() {
+  var worker = tabs.activeTab.attach({
+    contentScriptFile: self.data.url("content-script.js")
+  });
+  worker.port.on("my-script-response", function(response) {
+    console.log(response);
+  });
+  worker.port.emit("my-addon-message", "Message from the add-on");
+}
+
+ +
// content-script.js
+
+self.port.on("my-addon-message", handleMessage);
+
+function handleMessage(message) {
+  alert(message);
+  self.port.emit("my-script-response", "Response from content script");
+}
+ +

port的API

+ +

参看 port 对象的参考文档.

+
+ +

postMessage的API

+ +

port 对象加载之前,add-on 代码和内容脚本可以使用另一个 API 通信:

+ + + +

这个API依然可用,并且还有文档,但是没有理由替代前文描述的 port API。 例外是 context-menu 模块,它还是使用 postMessage。

+ +

内容脚本的内容脚本

+ +

内容脚本可用直接和其他同一个上下文中的内容脚本通信。举个例子,如果一次 Tab.attach() 的调用附加了两个脚本,那么他们可用直接相互查看,就像加载在同一页面内的页面脚本一样。但是如果你调用 Tab.attach() 两次,每次附加一个内容脚本,那么这些内容脚本之间不能通信。你必须使用port API 通过 add-on 的主代码来传递消息。

+ +

跨域的内容脚本

+ +

默认情况下,内容脚本没有跨域的权限。特别是,它们不能访问在不同 iframe 中的在另外的域名上的内容,也不能发起跨域的 XMLHttpRequests。

+ +

但是,你可以把需要的域名添加到 package.json"permissions"键下的 "cross-domain-content"键下,为这些域名打开这些特性。参阅文章跨域内容脚本

+
+ +
 
+ +
 
diff --git a/files/zh-cn/mozilla/add-ons/sdk/guides/index.html b/files/zh-cn/mozilla/add-ons/sdk/guides/index.html new file mode 100644 index 0000000000..51fbdef445 --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/guides/index.html @@ -0,0 +1,115 @@ +--- +title: 教程 +slug: Mozilla/Add-ons/SDK/Guides +tags: + - NeedsTranslation + - TopicStub +translation_of: Archive/Add-ons/Add-on_SDK/Guides +--- +

下面列出了一些文章可以加深你对SDK的理解

+
+

投稿者的教程

+
+
+
+
+ 起步
+
+ 学会如何使用SDK : 编写代码, 调试bug, 提交补丁, 审核项目, 获得帮助.
+
+ 模块
+
+ 通过SDK学会模块的使用 (以CommonJS为规范), 懂得如何使用 sandboxes 和compartments 提高安全性, 并且了解内置的 SDK module loader (被称为Cuddlefish).
+
+ 类 和 继承
+
+ 学会继承在JavaScript中的运行机制, 使用构造(constructors)原型(prototypes), 并知道如何使用SDK提供的函数帮助器简化它.
+
+
+
+
+
+ 私有成员
+
+ 通过 前缀, 闭包, 和WeakMaps 学会私有成员如何在JavaScript中的实现, 使用 命名空间(通常指WeakMaps) 学会SDK如何支持私有成员.
+
+ 脚本运行流程
+
+ SDK的设计目的是为了使控制网页内容的扩展脚本可以在不同进程的环境中运行. 这篇文章强调了这一设计的特点.
+
+
+
+
+

SDK的基础结构

+
+
+
+
+ SDK 模块结构
+
+ SDK是可重复使用的 JavaScript 模块. 这里解释了什么是模块, 怎样加载模块, 和SDK模块树的构造.
+
+ SDK API 生存周期
+
+ 为SDK的API定义生命周期,  包括API稳定性的排名
+
+
+
+
+
+ 程序 ID
+
+ 程序ID 是扩展独一无二的标识符. 教程解释了如何定义你自己的程序 ID.
+
+ Firefox 兼容
+
+ 解决不同版本SDK生成的扩展与不同版本Firefox的兼容问题
+
+
+
+
+

SDK 常用技巧

+
+
+
+
+ 善用 事件触发
+
+ 通过SDK的事件触发框架 写出以事件驱动为基础的代码
+
+
+
+
+
+ 脚本的两种类型
+
+ 这篇文章可以帮助你理解扩展中的API和普通脚本的区别
+
+
+
+

 

+
+

XUL 迁移

+
+
+
+
+ XUL 迁移教程
+
+ 把XUL扩展迁移到SDK的技巧
+
+ XUL 与 SDK 不同
+
+ 比较 传统的以XUL为基础的扩展 和 SDK 两者优点和缺点
+
+
+
+
+
+ 移植例子
+
+ 一个简单地教你如何让 基于XUL的扩展 迁移到 SDK中的实例
+
+
+
+

 

diff --git a/files/zh-cn/mozilla/add-ons/sdk/guides/multiprocess_firefox_and_the_sdk/index.html b/files/zh-cn/mozilla/add-ons/sdk/guides/multiprocess_firefox_and_the_sdk/index.html new file mode 100644 index 0000000000..c22dd0181e --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/guides/multiprocess_firefox_and_the_sdk/index.html @@ -0,0 +1,212 @@ +--- +title: 多进程 Firefox 与 SDK +slug: Mozilla/Add-ons/SDK/Guides/Multiprocess_Firefox_and_the_SDK +translation_of: Archive/Add-ons/Add-on_SDK/Guides/Multiprocess_Firefox_and_the_SDK +--- +

我们目前正在使 Firefox 变为多进程,它为浏览器界面使用一个操作系统进程,为运行的网页使用另一个进程来执行代码,这个项目被称为 "electrolysis" 或者 "e10s"。更多信息请参考多进程 Firefox 相关页面

+ +

本文章介绍了开发者如何测试基于 SDK 的附加组件是否与多进程的 Firefox 兼容,以及如何解决出现的问题。

+ +

SDK 的合约

+ +

SDK 为附加组件的开发者承诺了:

+ + + +

在实践中,大多数底层 API 将正常工作,但可以直接访问网页内容的底层 API 无法正常工作。

+ +

兼容性垫片

+ +

举例来说,你可能认为这是行不通的:

+ +
var contentDocument = require("sdk/window/utils")
+  .getMostRecentBrowserWindow().content.document;
+ +

但是,Firefox 为附加组件提供了许多 API 的兼容性垫片。这意味着许多 API,包括上述例子的那个,仍然工作。虽然有两条警示需注意:

+ + + +

We don't yet have a complete list of low-level APIs that are not multiprocess compatible: that is, will not work without the shims. Where we know that a low-level API is not multiprocess compatible, we've indicated that in the documentation page for it.

+ +

测试

+ +

To test whether an add-on works without the shims, use the "multiprocess" permission.

+ +
+

Note that you can only do this if you are using jpm. You can't use the "multiprocess" permission if you are using cfx.

+
+ +

Setting this permission will disable the shims for your add-on, so you can test to see if it's really multiprocess compatible or not.

+ +

However, there's a catch: some of the SDK's APIs themselves still depend on the shims. So by setting the "multiprocess" permission, your add-on might not work, even if you are only using high-level APIs. For example:

+ +
var selection = require("sdk/selection");
+
+function myListener() {
+  console.log(selection.text);
+}
+
+selection.on('select', myListener);
+
+ +

This add-on will not work if you've set the "multiprocess" permission, because sdk/selection depends on the shims. We're working on fixing these problems: see bug 1004745 and its dependencies.

+ +

使用框架脚本

+ +

If the shims don't enable your add-on to work properly, or you're trying to remove your dependency on the shims, then the solution is the same as it is for all add-ons:

+ + + +

In the rest of this section we'll outline some SDK-specific details of using the message manager. However, you'll also need to read the main message manager documentation for the details on working with frame scripts.

+ +
+

If you're used to working with content scripts in the SDK, then frame scripts might feel similar, but they're actually very different. Frame scripts are more like a part of the main add-on code that just happens to be running in the content process.

+ +

Differences between frame scripts and content scripts include:

+ + +
+ +

访问消息管理器

+ +

全局消息管理器

+ +

To access the global message manager, you load the nsIMessageListenerManager service. Before you can do this in an SDK add-on, you have to require("chrome") to get access to the Components object:

+ +
const {Cc, Ci} = require("chrome");
+
+var globalMM = Cc["@mozilla.org/globalmessagemanager;1"]
+  .getService(Ci.nsIMessageListenerManager);
+ +

窗口消息管理器

+ +

The window message manager is available as the messageManager property of a chrome window. To understand what a chrome window is, you need to distinguish three sorts of windows:

+ + + +

So to get a window message manager from the SDK, you have a couple of options:

+ +

(1) Use the window/utils module:

+ +
// Note that although this code uses window/utils,
+// it's safe to run in the chrome process because
+// it only accesses chrome objects.
+
+// get the active chrome window
+var browserWindow = require("sdk/window/utils").getMostRecentBrowserWindow();
+
+var windowMM = browserWindow.messageManager;
+ +

(2) Use viewFor to convert an SDK window to a chrome window:

+ +
// get the active SDK window
+var windows = require("sdk/windows").browserWindows;
+var activeWindow = windows.activeWindow;
+
+// convert it to a chrome window
+var browserWindow = require("sdk/view/core").viewFor(activeWindow);
+
+var windowMM = browserWindow.messageManager;
+ +

浏览器消息管理器

+ +

The browser message manager is available as the messageManager property of an XUL browser.

+ +

In the SDK, to get a browser for a given tab, you can use the tabs/utils module's getBrowserForTab function. getBrowserForTab expects an XUL tab as an argument, so if you have an SDK tab object, you'll need to convert it to an XUL tab using viewFor:

+ +
// get the active SDK tab
+var tab = require("sdk/tabs").activeTab;
+// convert it to an XUL tab
+var xulTab = require("sdk/view/core").viewFor(tab);
+// get the XUL browser for this tab
+var xulBrowser = require("sdk/tabs/utils").getBrowserForTab(xulTab);
+
+var browserMM = xulBrowser.messageManager;
+ +

Again, although this code uses tabs/utils, it's safe to run in the chrome process because it only accesses chrome objects.

+ +

载入框架脚本

+ +

You can load frame scripts using the message manager, passing in a chrome:// or resource:// URL pointing to the script. With the SDK, the simplest approach is to keep frame scripts under your add-on's data directory, and pass in a resource:// URL created using self.data.url:

+ +
const self = require("sdk/self");
+
+messageManager.loadFrameScript(self.data.url("frame-script.js"), false);
+ +

Note that unlike the APIs to load content scripts, you can only load a single frame script here.

+ +

例子

+ +

For example, this add-on trivially accesses content directly using a low-level API:

+ +
function logContent() {
+  var contentDocument = require("sdk/window/utils")
+    .getMostRecentBrowserWindow().content.document;
+  console.log(contentDocument.body.innerHTML);
+}
+
+require("sdk/ui/button/action").ActionButton({
+  id: "log-content",
+  label: "Log Content",
+  icon: "./icon-16.png",
+  onClick: logContent
+});
+ +

This add-on will work by default due to the shims, but will break if you set multiprocessCompatible. So you could rewrite the add-on to use frame scripts:

+ +
/*
+frame-script.js is in the "data" directory, and has this content:
+
+sendAsyncMessage("sdk-low-level-apis-e10s@jetpack:got-content",
+                 content.document.body.innerHTML);
+
+*/
+
+const self = require("sdk/self");
+
+function logContentAsync() {
+  var tab = require("sdk/tabs").activeTab;
+  var xulTab = require("sdk/view/core").viewFor(tab);
+  var xulBrowser = require("sdk/tabs/utils").getBrowserForTab(xulTab);
+
+  var browserMM = xulBrowser.messageManager;
+  browserMM.loadFrameScript(self.data.url("frame-script.js"), false);
+  browserMM.addMessageListener("sdk-low-level-apis-e10s@jetpack:got-content",
+                               logContent);
+}
+
+function logContent(message) {
+  console.log(message.data);
+}
+
+require("sdk/ui/button/action").ActionButton({
+  id: "log-content",
+  label: "Log Content",
+  icon: "./icon-16.png",
+  onClick: logContentAsync
+});
+ +

现在附加组件能正常工作了,即使你设置了 multiprocessCompatible。

diff --git a/files/zh-cn/mozilla/add-ons/sdk/guides/working_with_events/index.html b/files/zh-cn/mozilla/add-ons/sdk/guides/working_with_events/index.html new file mode 100644 index 0000000000..4c24b84e13 --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/guides/working_with_events/index.html @@ -0,0 +1,122 @@ +--- +title: Working with Events +slug: Mozilla/Add-ons/SDK/Guides/Working_with_Events +translation_of: Archive/Add-ons/Add-on_SDK/Guides/Working_with_Events +--- +

The Add-on SDK supports event-driven programming.

+

Objects emit events on state changes that might be of interest to add-on code, such as browser windows opening, pages loading, network requests completing, and mouse clicks. By registering a listener function to an event emitter an add-on can receive notifications of these events.

+

We talk about content scripts in more detail in the Working with Content Scripts guide.

+

Additionally, if you're using content scripts to interact with web content, you can define your own events and use them to communicate between the main add-on code and the content scripts. In this case one end of the conversation emits the events, and the other end listens to them.

+

So there are two main ways you will interact with the EventEmitter framework:

+ +

This guide only covers the first of these; the second is explained in the Working with Content Scripts guide.

+

Adding Listeners

+

You can add a listener to an event emitter by calling its on(type, listener) method.

+

It takes two parameters:

+ +

For example, the following add-on registers a listener with the tabs module to listen for the ready event, and logs a string to the console reporting the event:

+
var tabs = require("sdk/tabs");
+
+tabs.on("ready", function () {
+  console.log("tab loaded");
+});
+
+

It is not possible to enumerate the set of listeners for a given event.

+

The value of this in the listener function is the object that emitted the event.

+

Listening to all events

+
+

This example uses the action button API, which is only available from Firefox 29 onwards.

+
+

From Firefox 28 onwards, you can pass the wildcard "*" as the type argument. If you do this, the listener will be called for any event emitted by that object, and its argument will be the name of the event:

+
var ui = require('sdk/ui');
+var panels = require("sdk/panel");
+var self = require("sdk/self");
+
+var panel = panels.Panel({
+  contentURL: self.data.url("panel.html")
+});
+
+panel.on("*", function(e) {
+  console.log("event " + e + " was emitted");
+});
+
+var button = ui.ActionButton({
+  id: "my-button",
+  label: "my button",
+  icon: "./icon-16.png",
+  onClick: handleClick
+});
+
+function handleClick(state) {
+  panel.show({
+    position: button
+  });
+}
+

This wildcard feature does not yet work for the tabs or windows modules.

+

Adding Listeners in Constructors

+

Event emitters may be modules, as is the case for the ready event above, or they may be objects returned by constructors.

+

In the latter case the options object passed to the constructor typically defines properties whose names are the names of supported event types prefixed with "on": for example, "onOpen", "onReady" and so on. Then in the constructor you can assign a listener function to this property as an alternative to calling the object's on() method.

+

For example: the ActionButton object emits an event when the button is clicked.

+

The following add-on creates a button and assigns a listener to the onClick property of the options object supplied to the button's constructor. The listener loads https://developer.mozilla.org/:

+
require("sdk/ui/button/action").ActionButton({
+  id: "visit-mozilla",
+  label: "Visit Mozilla",
+  icon: "./icon-16.png",
+  onClick: function() {
+    require("sdk/tabs").open("https://developer.mozilla.org/");
+  }
+});
+
+

This is exactly equivalent to constructing the button and then calling the button's on() method:

+
var button = require("sdk/ui/button/action").ActionButton({
+  id: "visit-mozilla",
+  label: "Visit Mozilla",
+  icon: "./icon-16.png"
+});
+
+button.on("click", function() {
+  require("sdk/tabs").open("https://developer.mozilla.org/");
+});
+
+

Removing Event Listeners

+

Event listeners can be removed by calling removeListener(type, listener), supplying the type of event and the listener to remove.

+

The listener must have been previously been added using one of the methods described above.

+

In the following add-on, we add two listeners to the tabs module's ready event. One of the handler functions removes the listener again.

+

Then we open two tabs.

+
var tabs = require("sdk/tabs");
+
+function listener1() {
+  console.log("Listener 1");
+  tabs.removeListener("ready", listener1);
+}
+
+function listener2() {
+  console.log("Listener 2");
+}
+
+tabs.on("ready", listener1);
+tabs.on("ready", listener2);
+
+tabs.open("https://www.mozilla.org");
+tabs.open("https://www.mozilla.org");
+
+

We should see output like this:

+
info: tabevents: Listener 1
+info: tabevents: Listener 2
+info: tabevents: Listener 2
+
+

Listeners will be removed automatically when the add-on is unloaded.

diff --git a/files/zh-cn/mozilla/add-ons/sdk/high-level_apis/base64/index.html b/files/zh-cn/mozilla/add-ons/sdk/high-level_apis/base64/index.html new file mode 100644 index 0000000000..bbde3e418f --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/high-level_apis/base64/index.html @@ -0,0 +1,65 @@ +--- +title: base64 +slug: Mozilla/Add-ons/SDK/High-Level_APIs/base64 +translation_of: Archive/Add-ons/Add-on_SDK/High-Level_APIs/base64 +--- +

{{AddonSidebar}}

+ +
+

不稳定

+
+ +

使用Base64算法编码和解码数据

+ +
var base64 = require("sdk/base64");
+
+var encodedData = base64.encode("Hello, World");//"SGVsbG8sIFdvcmxk"
+var decodedData = base64.decode(encodedData);//"Hello, World"
+ +

Globals

+ +

函数

+ +

encode(data, charset)

+ +

将数据编码成ASCII的Base64字符串。

+ +
参数
+ +

data : string
+ 需要被编码的字符串

+ +

charset : string
+ 字符串的编码字符集(可选)。唯一能接受的值“UTF-8”。为了进行编码和解码Unicode字符串,需要设置字符集参数:

+ +
var base64 = require("sdk/base64");
+
+var encodedData = base64.encode(unicodeString, "utf-8");
+
+ +
返回
+ +

string : 编码后的Base64字符串。

+ +

decode(data, charset)

+ +

解码一个已使用base-64编码的数据字符串

+ +
参数
+ +

data : string
+ 需要被解码的字符串

+ +

charset : string
+ 字符串的编码字符集(可选)。唯一能接受的值“UTF-8”。为了进行编码和解码Unicode字符串,需要设置字符集参数:

+ +
var base64 = require("sdk/base64");
+
+var decodedData = base64.decode(encodedData, "utf-8");
+
+ +
返回
+ +

string : 解码后的字符串

+ +
 
diff --git a/files/zh-cn/mozilla/add-ons/sdk/high-level_apis/clipboard/index.html b/files/zh-cn/mozilla/add-ons/sdk/high-level_apis/clipboard/index.html new file mode 100644 index 0000000000..e56a9223cd --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/high-level_apis/clipboard/index.html @@ -0,0 +1,101 @@ +--- +title: clipboard(剪贴板) +slug: Mozilla/Add-ons/SDK/High-Level_APIs/clipboard +translation_of: Archive/Add-ons/Add-on_SDK/High-Level_APIs/clipboard +--- +

{{AddonSidebar}}

+ +
+

稳定版

+
+ +

操作系统剪贴板,设置或获取其内容

+ +

用法

+ +

你可以选择性地设置将要获取或设置的内容的格式. 支持一下格式:

+ + + +

如果没有提供格式参数的话,剪贴板模块会自动检测。

+ +

现在在Windows操作系统下,"image"格式并不支持透明度。

+ +

示例

+ +

设置和获取剪贴板内容。

+ +
var clipboard = require("sdk/clipboard");
+clipboard.set("Lorem ipsum dolor sit amet");
+var contents = clipboard.get();
+ +

将剪贴板内容设置为某些html。

+ +
var clipboard = require("sdk/clipboard");
+clipboard.set("<blink>Lorem ipsum dolor sit amet</blink>", "html");
+ +

如果剪贴板内容中包含有html,则将其在新标签页中打开。

+ +
var clipboard = require("sdk/clipboard");
+if (clipboard.currentFlavors.indexOf("html") != -1)
+  require("sdk/tabs").open("data:text/html;charset=utf-8," + clipboard.get("html"));
+ +

将剪贴板内容设置为一幅图片。

+ +
var clipboard = require("sdk/clipboard");
+clipboard.set("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYA" +
+              "AABzenr0AAAASUlEQVRYhe3O0QkAIAwD0eyqe3Q993AQ3cBSUKpygfsNTy" +
+              "N5ugbQpK0BAADgP0BRDWXWlwEAAAAAgPsA3rzDaAAAAHgPcGrpgAnzQ2FG" +
+              "bWRR9AAAAABJRU5ErkJggg%3D%3D");
+ +

如果剪贴板内容中包含图片,则将其在新标签页中打开。

+ +
var clipboard = require("sdk/clipboard");
+if (clipboard.currentFlavors.indexOf("image") != -1)
+  require("sdk/tabs").open(clipboard.get());
+ +

如前所述,图片的参数类型很容易被忽略,例如在网页中选中复制一张图片,得到的格式会是html而不是所预期的image。如果你是想将剪贴板的内容设置成像data URL这样的文本字符串而不是图片的话,可以通过将格式设置为text实现。

+ +
var clipboard = require("sdk/clipboard");
+
+clipboard.set("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYA" +
+              "AABzenr0AAAASUlEQVRYhe3O0QkAIAwD0eyqe3Q993AQ3cBSUKpygfsNTy" +
+              "N5ugbQpK0BAADgP0BRDWXWlwEAAAAAgPsA3rzDaAAAAHgPcGrpgAnzQ2FG" +
+              "bWRR9AAAAABJRU5ErkJggg%3D%3D", "text");
+ +

Globals

+ +

函数

+ +

set(data, datatype)

+ +

将用户剪贴板上的内容用data替换。

+ +
参数
+ +

data : string
+ 要放入剪贴板的内容。

+ +

datatype : string
+ 内容的格式 ,为"text"或"html" 或 "image"。可选参数.

+ +

get(datatype)

+ +

获取用户当前剪贴板的内容。

+ +
参数
+ +

datatype : string
+ 只有当剪贴板中的内容格式为datatype指定的格式时才获取 (可选).。当剪贴板中的内容非所提供的datatype指定的格式时,函数回返回null。

+ +

属性

+ +

currentFlavors

+ +

剪贴板上的内容有时会是多种格式。例如,HTML内容即能匹配HTML格式(html),又能匹配纯文本格式(text)。这个属性为一个数组,数组中的元素是剪贴板内容所匹配的所有格式,如["text","html"]。

+ +
 
diff --git a/files/zh-cn/mozilla/add-ons/sdk/high-level_apis/index.html b/files/zh-cn/mozilla/add-ons/sdk/high-level_apis/index.html new file mode 100644 index 0000000000..d0d7bd569a --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/high-level_apis/index.html @@ -0,0 +1,12 @@ +--- +title: High-Level APIs +slug: Mozilla/Add-ons/SDK/High-Level_APIs +translation_of: Archive/Add-ons/Add-on_SDK/High-Level_APIs +--- +

{{AddonSidebar}}

+ +

此页面列出的模块使用的是高级的API:创建用户界面,与网络进行交互并与浏览器交互。

+ +

除非文件明确说明,否则这些API是稳定的,我们避免做出不兼容的改变。 + +{{ LandingPageListSubpages ("/en-US/Add-ons/SDK/High-Level_APIs", 5) }}

diff --git a/files/zh-cn/mozilla/add-ons/sdk/high-level_apis/notifications/index.html b/files/zh-cn/mozilla/add-ons/sdk/high-level_apis/notifications/index.html new file mode 100644 index 0000000000..415450dca7 --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/high-level_apis/notifications/index.html @@ -0,0 +1,129 @@ +--- +title: notifications(通知) +slug: Mozilla/Add-ons/SDK/High-Level_APIs/notifications +tags: + - Add-on SDK + - 通知 +translation_of: Archive/Add-ons/Add-on_SDK/High-Level_APIs/notifications +--- +

{{AddonSidebar}}

+ +
+

Stable

+
+ +

向用户展示短暂的 toaster 风格的桌面消息。

+ +

用法

+ +

本 API 支持Windows、使用Growl(或者像OS X 10.9 Mavericks那样的通知中心)的 OS X 的桌面通知,以及使用 libnotify 的Linux系统

+ +

这儿有个典型的例子。当消息被点击,控制台上回记录一个字符串。

+ +
var notifications = require("sdk/notifications");
+notifications.notify({
+  title: "Jabberwocky",
+  text: "'Twas brillig, and the slithy toves",
+  data: "did gyre and gimble in the wabe",
+  onClick: function (data) {
+    console.log(data);
+    // console.log(this.data) would produce the same result.
+  }
+});
+
+ +

下面这个示例用来展示一个保存在 add-on 的 data 目录下的图标。参看 self 模块文档以获取更多信息。

+ +
var notifications = require("sdk/notifications");
+var self = require("sdk/self");
+var myIconURL = self.data.url("myIcon.png");
+
+notifications.notify({
+  text: "I have an icon!",
+  iconURL: myIconURL
+});
+ +
+

从 Firefox 34 起,你能使用 "./myIcon.png" 作为 self.data.url("myIcon.png") 的别名。所以你也可以把上面的代码重写成这样:

+ +
var notifications = require("sdk/notifications");
+var myIconURL = "./myIcon.png";
+
+notifications.notify({
+  text: "I have an icon!",
+  iconURL: myIconURL
+});
+
+ +

本模块依赖于底层系统的通知服务。如果用户的系统不支持桌面通知或者通知服务没有运行:

+ + + +

Globals

+ +

函数

+ +

notify(options)

+ +

向用户展示一个短暂的通知

+ +
参数
+ +

options : object
+ 可选项:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameType 
titlestring +

作为消息标题的字符串。

+
text作为消息体的字符串。 +

 

+
iconURLstring +

消息里的图标的 URL 。可以是个远程的、本地的或者使用 self 模块的 URL。

+
onClickfunction +

用户点击消息是调用的函数。它会传递一个 data 值。

+
datastring +

传递给 onClick 的字符串。

+
+ +
 
+ +
 
+ +
 
diff --git a/files/zh-cn/mozilla/add-ons/sdk/high-level_apis/panel/index.html b/files/zh-cn/mozilla/add-ons/sdk/high-level_apis/panel/index.html new file mode 100644 index 0000000000..3a86fb0c61 --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/high-level_apis/panel/index.html @@ -0,0 +1,899 @@ +--- +title: panel +slug: Mozilla/Add-ons/SDK/High-Level_APIs/panel +translation_of: Archive/Add-ons/Add-on_SDK/High-Level_APIs/panel +--- +
+

稳定版本

+
+ +

创建临时的 add-on's 用户界面对话框.

+ +

用法

+ +

该模块导出了一个单独的构造函数函数 Panel() 构建一个Panel对话框 .

+ +

面板是一个对话框。其内容是指定的HTML,你可以在它执行脚本,所以面板的外观和行为是有限的只有你可以使用HTML,CSS,JavaScript。

+ +

下面的截图显示了一个面板,其内容是从当前打开的选项卡的列表中构建的:

+ +

+ +

面板是呈现临时用户界面中容易忽视和解聘的用户比模态对话框的方式,是有用的,因为面板隐藏瞬间用户交互部分的应用程序接口外。

+ +


+ 面板的内容加载快作为它是创建,面板显示之前,和内容仍然加载时面板是隐藏的,所以它是可能保持面板周围的背景,更新其内容适当地准备下一次显示。

+ +


+ 您的插件可以接收通知,当面板显示或隐藏通过听其显示和隐藏事件。
+ 打开面板将关闭已打开的面板。

+ +

Panel 内容

+ +

该面板的内容指定为HTML,它是在contenturl选项面板的构造函数提供的URL加载。

+ +


+ 你可以加载远程HTML到面板:

+ +
var panel = require("sdk/panel").Panel({
+  width: 180,
+  height: 180,
+  contentURL: "https://en.wikipedia.org/w/index.php?title=Jetpack&useformat=mobile"
+});
+
+panel.show();
+ +

+ +

你也可以加载HTML已经打包你的 add-on,这可能是你将如何创建对话框。要做到这一点,在你的 add-on 的 data 目录中保存的HTML 和加载使用 data.url()方法,由 self 模块导出, 像下面:

+ +
var panel = require("sdk/panel").Panel({
+  contentURL: require("sdk/self").data.url("myFile.html")
+});
+
+panel.show();
+ +
+

从Firefox 34以后, 你可以使用 "./myFile.html" 作为self.data.url("myFile.html")别名. 所以你可以重写成下面的示例:

+ +
var panel = require("sdk/panel").Panel({
+  contentURL: "./myFile.html"
+});
+
+panel.show();
+
+
+ +

Panel positioning

+ +

By default the panel appears in the center of the currently active browser window. You can position the panel by passing a position to the panel's constructor or to its show() method.

+ +

Attaching panels to buttons

+ +

You can attach a panel to a toggle button by passing the button itself as the position option to the panel's show() method or to its constructor:

+ +
var { ToggleButton } = require('sdk/ui/button/toggle');
+var panels = require("sdk/panel");
+var self = require("sdk/self");
+
+var button = ToggleButton({
+  id: "my-button",
+  label: "my button",
+  icon: {
+    "16": "./icon-16.png",
+    "32": "./icon-32.png",
+    "64": "./icon-64.png"
+  },
+  onChange: handleChange
+});
+
+var panel = panels.Panel({
+  contentURL: self.data.url("panel.html"),
+  onHide: handleHide
+});
+
+function handleChange(state) {
+  if (state.checked) {
+    panel.show({
+      position: button
+    });
+  }
+}
+
+function handleHide() {
+  button.state('window', {checked: false});
+}
+ +

+ +

Updating panel content

+ +

You can update the panel's content by:

+ + + +

Scripting panel content

+ +

You can't directly access your panel's content from your main add-on code. To access the panel's content, you need to load a script into the panel. In the SDK these scripts are called "content scripts" because they're explicitly used for interacting with web content.

+ +

While content scripts can access the content they're attached to, they can't use the SDK's APIs. So implementing a complete solution usually means you have to send messages between the content script and the main add-on code.

+ + + +

For example, here's an add-on whose content script intercepts mouse clicks on links inside the panel, and sends the target URL to the main add-on code. The content script sends messages using self.port.emit() and the add-on script receives them using panel.port.on().

+ +
var myScript = "window.addEventListener('click', function(event) {" +
+               "  var t = event.target;" +
+               "  if (t.nodeName == 'A')" +
+               "    self.port.emit('click-link', t.toString());" +
+               "}, false);"
+
+var panel = require("sdk/panel").Panel({
+  contentURL: "http://www.bbc.co.uk/mobile/index.html",
+  contentScript: myScript
+});
+
+panel.port.on("click-link", function(url) {
+  console.log(url);
+});
+
+panel.show();
+ +

This example uses contentScript to supply the script as a string. It's usually better practice to use contentScriptFile, which is a URL pointing to a script file saved under your add-on's data directory.

+ +
+

Warning: Unless your content script is extremely simple and consists only of a static string, don't use contentScript: if you do, you may have problems getting your add-on approved on AMO.

+ +

Instead, keep the script in a separate file and load it using contentScriptFile. This makes your code easier to maintain, secure, debug and review.

+
+ +

Getting user input

+ +
+

Note: This example uses the action button API, which is only available from Firefox 29 onwards.

+
+ +

The following add-on adds a button which displays a panel when clicked. The panel just contains a {{HTMLElement("textarea")}} element: when the user presses the return key, the contents of the <textarea> is sent to the main add-on code.

+ +

+ +

The add-on consists of six files:

+ + + +

"main.js" is saved in your add-on's lib directory, and the other files go in your add-on's data directory:

+ +
my-addon/
+         data/
+              get-text.js
+              icon-16.png
+              icon-32.png
+              icon-64.png
+              text-entry.html
+         lib/
+             main.js
+
+ +

The "main.js" looks like this:

+ +
var data = require("sdk/self").data;
+// Construct a panel, loading its content from the "text-entry.html"
+// file in the "data" directory, and loading the "get-text.js" script
+// into it.
+var text_entry = require("sdk/panel").Panel({
+  contentURL: data.url("text-entry.html"),
+  contentScriptFile: data.url("get-text.js")
+});
+
+// Create a button
+require("sdk/ui/button/action").ActionButton({
+  id: "show-panel",
+  label: "Show Panel",
+  icon: {
+    "16": "./icon-16.png",
+    "32": "./icon-32.png",
+    "64": "./icon-64.png"
+  },
+  onClick: handleClick
+});
+
+// Show the panel when the user clicks the button.
+function handleClick(state) {
+  text_entry.show();
+}
+
+// When the panel is displayed it generated an event called
+// "show": we will listen for that event and when it happens,
+// send our own "show" event to the panel's script, so the
+// script can prepare the panel for display.
+text_entry.on("show", function() {
+  text_entry.port.emit("show");
+});
+
+// Listen for messages called "text-entered" coming from
+// the content script. The message payload is the text the user
+// entered.
+// In this implementation we'll just log the text to the console.
+text_entry.port.on("text-entered", function (text) {
+  console.log(text);
+  text_entry.hide();
+});
+ +

The content script "get-text.js" looks like this:

+ +
// When the user hits return, send the "text-entered"
+// message to main.js.
+// The message payload is the contents of the edit box.
+var textArea = document.getElementById("edit-box");
+textArea.addEventListener('keyup', function onkeyup(event) {
+  if (event.keyCode == 13) {
+    // Remove the newline.
+    text = textArea.value.replace(/(\r\n|\n|\r)/gm,"");
+    self.port.emit("text-entered", text);
+    textArea.value = '';
+  }
+}, false);
+// Listen for the "show" event being sent from the
+// main add-on code. It means that the panel's about
+// to be shown.
+//
+// Set the focus to the text area so the user can
+// just start typing.
+self.port.on("show", function onShow() {
+  textArea.focus();
+});
+ +

Finally, the "text-entry.html" file defines the <textarea> element:

+ +
<html>
+<head>
+    <style type="text/css" media="all">
+      textarea {
+        margin: 10px;
+      }
+      body {
+        background-color: gray;
+      }
+    </style>
+  </head>
+<body>
+    <textarea rows="13" cols="33" id="edit-box"></textarea>
+  </body>
+</html>
+ +

Finally, save these three icon files to the "data" directory:

+ + + + + + + + + + + + + + + + +
icon-16.png
icon-32.png
icon-64.png
+ +

To learn much more about content scripts, see the Working with Content Scripts guide.

+ +

Scripting trusted panel content

+ +
+

Note: This example uses the action button API, which is only available from Firefox 29 onwards.

+
+ +

We've already seen that you can package HTML files in your add-on's data directory and use them to define the panel's content. We can call this "trusted" content, because unlike content loaded from a source outside the add-on, the add-on author knows exactly what it's doing. To interact with trusted content you don't need to use content scripts: you can just include a script from the HTML file in the normal way, using script tags.

+ +

Like a content script, these scripts can communicate with the add-on code using the postMessage() API or the port API. The crucial difference is that these scripts access the postMessage and port objects through the addon object, whereas content scripts access them through the self object.

+ +

To show the difference, we can easily convert the text-entry add-on above to use normal page scripts instead of content scripts.

+ +

The main add-on code is exactly the same as the main add-on code in the previous example, except that we don't attach a content script:

+ +
var data = require("sdk/self").data;
+// Construct a panel, loading its content from the "text-entry.html"
+// file in the "data" directory, and loading the "get-text.js" script
+// into it.
+var text_entry = require("sdk/panel").Panel({
+  contentURL: data.url("text-entry.html")
+});
+
+// Create a button
+require("sdk/ui/button/action").ActionButton({
+  id: "show-panel",
+  label: "Show Panel",
+  icon: {
+    "16": "./icon-16.png",
+    "32": "./icon-32.png",
+    "64": "./icon-64.png"
+  },
+  onClick: handleClick
+});
+
+// Show the panel when the user clicks the button.
+function handleClick(state) {
+  text_entry.show();
+}
+
+// When the panel is displayed it generated an event called
+// "show": we will listen for that event and when it happens,
+// send our own "show" event to the panel's script, so the
+// script can prepare the panel for display.
+text_entry.on("show", function() {
+  text_entry.port.emit("show");
+});
+
+// Listen for messages called "text-entered" coming from
+// the content script. The message payload is the text the user
+// entered.
+// In this implementation we'll just log the text to the console.
+text_entry.port.on("text-entered", function (text) {
+  console.log(text);
+  text_entry.hide();
+});
+ +

The page script is exactly the same as the content script above, except that instead of self, we use addon to access the messaging APIs:

+ +
// When the user hits return, send the "text-entered"
+// message to main.js.
+// The message payload is the contents of the edit box.
+var textArea = document.getElementById("edit-box");
+textArea.addEventListener('keyup', function onkeyup(event) {
+  if (event.keyCode == 13) {
+    // Remove the newline.
+    text = textArea.value.replace(/(\r\n|\n|\r)/gm,"");
+    addon.port.emit("text-entered", text);
+    textArea.value = '';
+  }
+}, false);
+// Listen for the "show" event being sent from the
+// main add-on code. It means that the panel's about
+// to be shown.
+//
+// Set the focus to the text area so the user can
+// just start typing.
+addon.port.on("show", function onShow() {
+  textArea.focus();
+});
+ +

Finally, the HTML file now references "get-text.js" inside a {{HTMLElement("script")}} tag:

+ +
<html>
+<head>
+    <style type="text/css" media="all">
+      textarea {
+        margin: 10px;
+      }
+      body {
+        background-color: gray;
+      }
+    </style>
+  </head>
+  <body>
+    <script src="get-text.js"></script>
+    <textarea rows="13" cols="33" id="edit-box"></textarea>
+  </body>
+</html>
+
+ +

Styling panel content

+ +

The panel's default style is different for each operating system:

+ +

This helps to ensure that the panel's style is consistent with the dialogs displayed by Firefox and other applications, but means you need to take care when applying your own styles.

+ +

If the panel's content is packaged along with your add-on and specified using an HTML file in your data directory, you can style it by embedding CSS directly in the HTML file or by referencing a CSS file stored under data:

+ +
<!DOCTYPE HTML>
+<html>
+  <head>
+    <link href="panel-style.css" type="text/css" rel="stylesheet">
+  </head>
+  <body>
+    My panel content
+  </body>
+</html>
+ +

From Firefox 31 onwards, you can style panel content using the contentStyle or contentStyleFile options. You can use these options even if the panel content is not packaged along with the add-on:

+ +
var panel = require("sdk/panel").Panel({
+  contentURL: "https://en.wikipedia.org/w/index.php?title=Jetpack&useformat=mobile",
+  contentStyle: "body { border: 3px solid blue; }"
+});
+
+panel.show();
+ +
var self = require("sdk/self");
+
+var panel = require("sdk/panel").Panel({
+  contentURL: "https://en.wikipedia.org/w/index.php?title=Jetpack&useformat=mobile",
+  contentStyleFile: self.data.url("panel-style.css")
+});
+
+panel.show();
+ +

Private browsing

+ +

If your add-on has not opted into private browsing, and it calls panel.show() when the currently active window is a private window, then the panel will not be shown.

+ +

Panel limitations

+ +

Although panels can host HTML documents, they are not implemented as browser tabs, so many things that work in normal web pages do not work inside panels:

+ + + +

Globals

+ +

Constructors

+ +

Panel(options)

+ +

Creates a panel.

+ +
Parameters
+ +

options : object
+ Optional options:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameType 
widthnumber +

The width of the panel in pixels. Optional.

+
heightnumber +

The height of the panel in pixels. Optional.

+
position +

object, button, widget

+
+

The position of the panel. Ignored if the panel is opened by a widget.

+ +

This may be one of three things:

+ +
    +
  • a toggle button. If this is supplied the panel will be shown attached to the button. See the section on attaching panels to buttons.
  • +
  • a widget object. If this is supplied the panel will be shown attached to the widget.
  • +
  • an object which specifies where in the window the panel should be shown. The rest of this section describes this object.
  • +
+ +

The position object has one or more of the following properties: top, right, bottom and left. Their values are expressed in pixels. Any other properties will be ignored.

+ +

The default alignment along each axis is centered: so to display a panel centred along the vertical or horizontal axis, just omit that axis:

+ +
+// Show the panel centered horizontally and
+// aligned to the bottom of the content area
+require("sdk/panel").Panel({
+  position: {
+   bottom: 0
+  }
+}).show();
+
+// Show the panel centered vertically and
+// aligned to the left of the content area
+require("sdk/panel").Panel({
+  position: {
+    left: 0
+  }
+}).show();
+
+// Centered panel, default behavior
+require("sdk/panel").Panel({}).show();
+ +

As with the CSS top, bottom, left, and right properties, setting both top and bottom or both left and right will implicitly set the panel's height or width relative to the content window:

+ +
+// Show the panel centered horizontally, with:
+// - the top edge 40px from the top
+// of the content window
+// - the bottom edge 100px from the bottom
+// of the content window
+require("sdk/panel").Panel({
+  position: {
+    top: 40,
+    bottom: 100
+  }
+}).show();
+ +

If you set both top and bottom, but also set the panel's height explicitly using the height property, then the panel will ignore bottom, just as CSS does for its properties with the same name:

+ +
+// Show the panel centered horizontally, with:
+// - the top edge 40px from the top
+// of the content window
+// - a height of 400px
+require("sdk/panel").Panel({
+  position: {
+    top: 40,
+    bottom: 100,
+  },
+  height: 400
+}).show();
+
+// This is equivalent to:
+
+require("panel").Panel({
+  position {
+    top: 40
+  },
+  height: 400
+}).show();
+ +

The same principle is applied in the horizontal axis with width, left and right.

+
focusboolean +

Set to false to prevent taking the focus away when the panel is shown. Only turn this off if necessary, to prevent accessibility issue. Optional, default to true.

+
contentURLstring,URL +

The URL of the content to load in the panel. That is, they can't refer to remote scripts. The URLs are usually constructed using self.data.url().

+ +
+

From Firefox 34, you can use "./my-file.html" as an alias for self.data.url("my-file.html").

+
+
allowobject +

An optional object describing permissions for the content. It should contain a single key named script whose value is a boolean that indicates whether or not to execute script in the content. script defaults to true.

+
contentScriptFilestring,array +

A URL or an array of URLs. The URLs point to scripts to load into the panel.

+ +

The scripts must be packaged with the add-on under the add-on's data directory. That is, they can't refer to remote scripts. The URLs are usually constructed using self.data.url().

+ +
+

From Firefox 34, you can use "./my-script.js" as an alias for self.data.url("my-script.js").

+
+ +

Content scripts specified by this property are loaded before those specified by the contentScript property.

+
contentScriptstring,array +

A string or an array of strings containing the texts of content scripts to load. Content scripts specified by this property are loaded after those specified by the contentScriptFile property.

+
contentStyleFilestring, array +

A URL or an array of URLs. The URLs point to CSS stylesheets to load into the panel.

+ +

The stylesheets must be packaged with the add-on under the add-on's data directory. That is, they can't refer to remote stylesheets. The URLs are usually constructed using self.data.url().

+ +

Stylesheets specified by this property are loaded before those specified by the contentStyle property.

+
contentStylestring, array +

A string or an array of strings containing the texts of stylesheets to load. Stylesheets specified by this property are loaded after those specified by the contentStyleFile property.

+
contentScriptWhenstring +

When to load the content scripts. This may take one of the following values:

+ +
    +
  • "start": load content scripts immediately after the document element for the panel is inserted into the DOM, but before the DOM content itself has been loaded
  • +
  • "ready": load content scripts once DOM content has been loaded, corresponding to the DOMContentLoaded event
  • +
  • "end": load content scripts once all the content (DOM, JS, CSS, images) for the panel has been loaded, at the time the window.onload event fires
  • +
+ +

This property is optional and defaults to "end".

+
contentScriptOptionsobject +

Read-only value exposed to content scripts under addon.options property.

+ +

Any kind of jsonable value (object, array, string, etc.) can be used here. Optional.

+
contextMenuboolean +
+

New in Firefox 33

+
+ +

Whether to show a context menu when the user context-clicks in the panel. The context menu will be the same one that's displayed in web pages. Optional, defaults to false.

+
onMessagefunction +

Include this to listen to the panel's message event.

+
onShowfunction +

Include this to listen to the panel's show event.

+
onHidefunction +

Include this to listen to the panel's hide event.

+
+ +

Panel

+ +

The Panel object represents a floating modal dialog that can by an add-on to present user interface content.

+ +

Once a panel object has been created it can be shown and hidden using its show() and hide() methods. Once a panel is no longer needed it can be deactivated using destroy().

+ +

The content of a panel is specified using the contentURL option. An add-on can interact with the content of a panel using content scripts which it supplies in the contentScript and/or contentScriptFile options. For example, a content script could create a menu and send the user's selection to the add-on.

+ +

Methods

+ +

destroy()

+ +

Destroys the panel, unloading any content that was loaded in it. Once destroyed, the panel can no longer be used. If you just want to hide the panel and might show it later, use hide instead.

+ +

postMessage(message)

+ +

Sends a message to the content scripts.

+ +
Parameters
+ +

message : value
+ The message to send. Must be stringifiable to JSON.

+ +

show(options)

+ +

Displays the panel.

+ +

If the options argument is given, it will be shallow merged with the options provided in the constructor: the options passed in the show method takes precedence.

+ +

Passing options here is useful for making temporary changes without touching the default values.

+ +
Parameters
+ +

options : object
+ Optional options:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameType 
widthnumber +

The width of the panel in pixels. Optional.

+
heightnumber +

The height of the panel in pixels. Optional.

+
positionobject +

The position of the panel. Optional. See Panel's options for further details.

+
focusboolean +

Set to false to prevent taking the focus away when the panel is shown.

+
+ +

hide()

+ +

Stops displaying the panel.

+ +

resize(width, height)

+ +

Resizes the panel.

+ +
Parameters
+ +

width : number
+ The new width of the panel in pixels.

+ +

height : number
+ The new height of the panel in pixels.

+ +

on(type, listener)

+ +

Registers an event listener with the panel.

+ +
Parameters
+ +

type : string
+ The type of event to listen for.

+ +

listener : function
+ The listener function that handles the event.

+ +

removeListener(type, listener)

+ +

Unregisters an event listener from the panel.

+ +
Parameters
+ +

type : string
+ The type of event for which listener was registered.

+ +

listener : function
+ The listener function that was registered.

+ +

Properties

+ +

port

+ +

EventEmitter object that allows you to:

+ + + +

See the guide to communicating using port for details.

+ +

isShowing

+ +

Tells if the panel is currently shown or not. This property is read-only.

+ +

height

+ +

The height of the panel in pixels.

+ +

width

+ +

The width of the panel in pixels.

+ +

focus

+ +

Whether or not focus will be taken away when the panel is shown. This property is read-only.

+ +

contentURL

+ +

The URL of content loaded into the panel. This can point to local content loaded from your add-on's "data" directory or remote content. Setting it updates the panel's content immediately.

+ +

allow

+ +

An object describing permissions for the content. It contains a single key named script whose value is a boolean that indicates whether or not to execute script in the content.

+ +

contentScriptFile

+ +

A local file URL or an array of local file URLs of content scripts to load. Content scripts specified by this property are loaded before those specified by the contentScript property.

+ +

contentScript

+ +

A string or an array of strings containing the texts of content scripts to load. Content scripts specified by this property are loaded after those specified by the contentScriptFile property.

+ +

contentScriptWhen

+ +

When to load the content scripts. This may have one of the following values:

+ + + +

contentScriptOptions

+ +

Read-only value exposed to content scripts under addon.options property.

+ +

Any kind of jsonable value (object, array, string, etc.) can be used here. Optional.

+ +

Events

+ +

show

+ +

This event is emitted when the panel is shown.

+ +

hide

+ +

This event is emitted when the panel is hidden.

+ +

message

+ +

If you listen to this event you can receive message events from content scripts associated with this panel. When a content script posts a message using self.postMessage(), the message is delivered to the add-on code in the panel's message event.

+ +
Arguments
+ +

value : Listeners are passed a single argument which is the message posted from the content script. The message can be any JSON-serializable value.

+ +

error

+ +

This event is emitted when an uncaught runtime error occurs in one of the panel's content scripts.

+ +
Arguments
+ +

Error : Listeners are passed a single argument, the Error object.

diff --git a/files/zh-cn/mozilla/add-ons/sdk/high-level_apis/tabs/index.html b/files/zh-cn/mozilla/add-ons/sdk/high-level_apis/tabs/index.html new file mode 100644 index 0000000000..b85bb94ab3 --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/high-level_apis/tabs/index.html @@ -0,0 +1,669 @@ +--- +title: tabs +slug: Mozilla/Add-ons/SDK/High-Level_APIs/tabs +translation_of: Archive/Add-ons/Add-on_SDK/High-Level_APIs/tabs +--- +

{{AddonSidebar}}

+ +
+

Stable

+
+ +

打开、操作和访问标签页,以及接收标签页事件

+ +

用法

+ +

打开标签页

+ +

你可以注册事件监听器,以便在标签打开、关闭、完成DOM内容加载、被激活或被闲置时接收通知:

+ +
var tabs = require("sdk/tabs");
+tabs.open("http://www.example.com");
+ +

跟踪标签页

+ +

You can register event listeners to be notified when tabs open, close, finish loading DOM content, or are made active or inactive:

+ +
var tabs = require("sdk/tabs");
+
+// Listen for tab openings.
+tabs.on('open', function onOpen(tab) {
+  myOpenTabs.push(tab);
+});
+
+// Listen for tab content loads.
+tabs.on('ready', function(tab) {
+  console.log('tab is loaded', tab.title, tab.url);
+});
+ +

访问标签页

+ +

The module itself can be used as a list of all opened tabs across all windows. In particular, you can enumerate it:

+ +
var tabs = require('sdk/tabs');
+for (let tab of tabs)
+  console.log(tab.title);
+ +

You can also access individual tabs by index:

+ +
var tabs = require('sdk/tabs');
+
+tabs.on('ready', function () {
+  console.log('first: ' + tabs[0].title);
+  console.log('last: ' + tabs[tabs.length-1].title);
+});
+ +

You can access the currently active tab:

+ +
var tabs = require('sdk/tabs');
+
+tabs.on('activate', function () {
+  console.log('active: ' + tabs.activeTab.url);
+});
+ +

跟踪单个标签页

+ +

Given a tab, you can register event listeners to be notified when the tab is closed, activated or deactivated, or when the page hosted by the tab is loaded or retrieved from the "back-forward cache":

+ +
var tabs = require("sdk/tabs");
+
+function onOpen(tab) {
+  console.log(tab.url + " is open");
+  tab.on("pageshow", logShow);
+  tab.on("activate", logActivate);
+  tab.on("deactivate", logDeactivate);
+  tab.on("close", logClose);
+}
+
+function logShow(tab) {
+  console.log(tab.url + " is loaded");
+}
+
+function logActivate(tab) {
+  console.log(tab.url + " is activated");
+}
+
+function logDeactivate(tab) {
+  console.log(tab.url + " is deactivated");
+}
+
+function logClose(tab) {
+  console.log(tab.url + " is closed");
+}
+
+tabs.on('open', onOpen);
+ +

操作标签页

+ +

You can get and set various properties of tabs (but note that properties relating to the tab's content, such as the URL, will not contain valid values until after the tab's ready event fires). By setting the url property you can load a new page in the tab:

+ +
var tabs = require("sdk/tabs");
+tabs.on('activate', function(tab) {
+  tab.url = "http://www.example.com";
+});
+ +

在标签页中运行脚本

+ +

You can attach a content script to the page hosted in a tab, and use that to access and manipulate the page's content (see the Modifying the Page Hosted by a Tab tutorial):

+ +
var tabs = require("sdk/tabs");
+
+tabs.on('activate', function(tab) {
+  var worker = tab.attach({
+    contentScript: 'self.port.emit("html", document.body.innerHTML);'
+  });
+  worker.port.on("html", function(message) {
+    console.log(message)
+  })
+});
+ +

Note that tab.attach is tab-centric: if the user navigates to a new page in the same tab, then the worker and content scripts will be reattached to the new page.

+ +

附加样式表

+ +
+

Firefox 34 新增。

+
+ +

You can't attach style sheets to a tab using tab.attach(), but from Firefox 34 onwards you can attach and detach them using the low-level stylesheet/style and content/mod APIs. Here's an add-on that uses a toggle button to attach a stylesheet to the active tab, and detach it again. The stylesheet is called "style.css" and is located in the add-on's "data" directory:

+ +
var tabs = require("sdk/tabs");
+var { attach, detach } = require('sdk/content/mod');
+var { Style } = require('sdk/stylesheet/style');
+var { ToggleButton } = require("sdk/ui/button/toggle");
+
+var style = Style({
+  uri: './style.css'
+});
+
+var button = ToggleButton({
+  id: "stylist",
+  label: "stylist",
+  icon: "./icon-16.png",
+  onChange: function(state) {
+    if (state.checked) {
+      attach(style, tabs.activeTab);
+    }
+    else {
+      detach(style, tabs.activeTab);
+    }
+  }
+});
+ +

隐私窗口

+ +

If your add-on has not opted into private browsing, then you won't see any tabs that are hosted by private browser windows.

+ +

Tabs hosted by private browser windows won't be seen if you enumerate the tabs module itself, and you won't receive any events for them.

+ +

To learn more about private windows, how to opt into private browsing, and how to support private browsing, refer to the documentation for the private-browsing module.

+ +

转为XUL标签页

+ +

To convert from the high-level Tab objects used in this API to the low-level XUL tab objects used in the tabs/utils API and by traditional add-ons, use the viewFor() function exported by the viewFor module.

+ +

To convert back the other way, from a XUL tab to a high-level Tab object, use the modelFor() function, exported by the modelFor module.

+ +

Here's an example converting from a high-level Tab to a XUL tab and then back the other way:

+ +
var { modelFor } = require("sdk/model/core");
+var { viewFor } = require("sdk/view/core");
+
+var tabs = require("sdk/tabs");
+var tab_utils = require("sdk/tabs/utils");
+
+function mapHighLevelToLowLevel(tab) {
+  // get the XUL tab that corresponds to this high-level tab
+  var lowLevelTab = viewFor(tab);
+  // now we can, for example, access the tab's content directly
+  var browser = tab_utils.getBrowserForTab(lowLevelTab);
+  console.log(browser.contentDocument.body.innerHTML);
+  // get the high-level tab back from the XUL tab
+  var highLevelTab = modelFor(lowLevelTab);
+  console.log(highLevelTab.url);
+}
+
+tabs.on("ready", mapHighLevelToLowLevel);
+
+ +

Note that directly accessing XUL objects and web content like this means you're no longer protected by the compatibility guarantees made by the SDK's high-level APIs. In particular, your code might not work with multiprocess Firefox.

+ +

全局变量

+ +

函数

+ +

open(options)

+ +

Opens a new tab. The new tab will open in the active window or in a new window, depending on the inNewWindow option.

+ +

示例

+ +
var tabs = require("sdk/tabs");
+
+// Open a new tab on active window and make tab active.
+tabs.open("http://www.mysite.com");
+
+// Open a new tab in a new window and make it active.
+tabs.open({
+  url: "http://www.mysite.com",
+  inNewWindow: true
+});
+
+// Open a new tab on active window in the background.
+tabs.open({
+  url: "http://www.mysite.com",
+  inBackground: true
+});
+
+// Open a new tab as an app tab and do something once it's open.
+tabs.open({
+  url: "http://www.mysite.com",
+  isPinned: true,
+  onOpen: function onOpen(tab) {
+    // do stuff like listen for content
+    // loading.
+  }
+});
+ +
参数
+ +

options : object
+ 必选项:

+ + + + + + + + + + + + + + + + +
NameType 
urlstring +

String URL to be opened in the new tab. This is a required property.

+
+ +

可选项:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameType 
isPrivateboolean +

Boolean which will determine whether the new tab should be private or not. If your add-on does not support private browsing this will have no effect. See the private-browsing documentation for more information. Defaults to false.

+
inNewWindowboolean +

If present and true, a new browser window will be opened and the URL will be opened in the first tab in that window. This is an optional property.

+
inBackgroundboolean +

If present and true, the new tab will be opened to the right of the active tab and will not be active. This is an optional property.

+
isPinnedboolean +

If present and true, then the new tab will be pinned as an app tab.

+
onOpenfunction +

A callback function that will be registered for the 'open' event. This is an optional property.

+
onClosefunction +

A callback function that will be registered for the 'close' event. This is an optional property.

+
onReadyfunction +

A callback function that will be registered for the 'ready' event. This is an optional property.

+
onLoadfunction +

A callback function that will be registered for the 'load' event. This is an optional property.

+
onPageShowfunction +

A callback function that will be registered for the 'pageshow' event. This is an optional property.

+
onActivatefunction +

A callback function that will be registered for the 'activate' event. This is an optional property.

+
onDeactivatefunction +

A callback function that will be registered for the 'deactivate' event. This is an optional property.

+
+ +

属性

+ +

activeTab

+ +

The currently active tab in the active window. This property is read-only. To activate a Tab object, call its activate method.

+ +

示例

+ +
// Get the active tab's title.
+var tabs = require("sdk/tabs");
+console.log("title of active tab is " + tabs.activeTab.title);
+ +

length

+ +

The number of open tabs across all windows.

+ +

事件

+ +

open

+ +

This event is emitted when a new tab is opened. This does not mean that the content has loaded, only that the browser tab itself is fully visible to the user.

+ +

Properties relating to the tab's content (for example: title, favicon, and url) will not be correct at this point. If you need to access these properties, listen for the ready event:

+ +
var tabs = require("sdk/tabs");
+tabs.on('open', function(tab){
+  tab.on('ready', function(tab){
+    console.log(tab.url);
+  });
+});
+ +
参数
+ +

Tab : Listeners are passed the tab object that just opened.

+ +

close

+ +

This event is emitted when a tab is closed. When a window is closed this event will be emitted for each of the open tabs in that window.

+ +
参数
+ +

Tab : Listeners are passed the tab object that has closed.

+ +

ready

+ +

This event is emitted when the DOM for a tab's content is ready. It is equivalent to the DOMContentLoaded event for the given content page.

+ +

A single tab will emit this event every time the DOM is loaded: so it will be emitted again if the tab's location changes or the content is reloaded.

+ +

After this event has been emitted, all properties relating to the tab's content can be used.

+ +
参数
+ +

Tab : Listeners are passed the tab object that has loaded.

+ +

activate

+ +

This event is emitted when an inactive tab is made active.

+ +
参数
+ +

Tab : Listeners are passed the tab object that has become active.

+ +

deactivate

+ +

This event is emitted when the active tab is made inactive.

+ +
参数
+ +

Tab : Listeners are passed the tab object that has become inactive.

+ +

Tab

+ +

A Tab instance represents a single open tab. It contains various tab properties, several methods for manipulation, as well as per-tab event registration.

+ +

Tabs emit all the events described in the Events section. Listeners are passed the Tab object that triggered the event.

+ +

方法

+ +

pin()

+ +

Pins this tab as an app tab.

+ +

unpin()

+ +

Unpins this tab.

+ +

close(callback)

+ +

Closes this tab.

+ +
参数
+ +

callback : function
+ A function to be called when the tab finishes its closing process. This is an optional argument.

+ +

reload()

+ +

Reloads this tab.

+ +

activate()

+ +

Makes this tab active, which will bring this tab to the foreground.

+ +

getThumbnail()

+ +

Returns thumbnail data URI of the page currently loaded in this tab.

+ +

attach(options)

+ +

Attach one or more scripts to the document loaded in the tab. Note that by attaching inside ready event, this becomes tab-centric: if the user navigates to a new page in the same tab, then the content scripts will be reattached to the new page.

+ +

示例

+ +
var tabs = require("sdk/tabs");
+
+tabs.on('ready', function(tab) {
+  var worker = tab.attach({
+      contentScript:
+        'document.body.style.border = "5px solid red";'
+  });
+});
+ +
参数
+ +

options : object
+ 可选项:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameType 
contentScriptFilestring,array +

The local file URLs of content scripts to load. Content scripts specified by this option are loaded before those specified by the contentScript option. Optional.

+
contentScriptstring,array +

A string or an array of strings of code to be evaluated in the context. Content scripts specified by this option are loaded after those specified by the contentScriptFile option. Optional.

+
contentScriptOptionsobject +

You can use this option to define read-only values for your content scripts.

+ +

The option consists of an object literal listing name:value pairs for the values you want to provide to the content script. For example:

+ +
+// main.js
+
+const tabs = require("sdk/tabs");
+
+tabs.open({
+  url: "./page.html",
+  onReady: function(tab) {
+    tab.attach({
+      contentScriptFile: "./content-script.js",
+      contentScriptOptions: {
+        a: "blah"
+      }
+    });
+  }
+});
+ +

The values are accessible to content scripts via the self.options property:

+ +
+// content-script.js
+
+alert(self.options.a);
+
onMessagefunction +

A function called when the content worker receives a message from content scripts. Listeners are passed a single argument, the message posted from the content script. Optional.

+
onErrorfunctionA function called when the content worker receives an error from content scripts. Listeners are passed a single argument, error, which is the error posted from the content script and an object of type Error. Optional
+ +
返回
+ +

Worker : Worker 对象能够用来和内容脚本通信。查看 Content Scripts guide 了解详细信息。

+ +

属性

+ +

id

+ +

The unique id for the tab. This property is read-only.

+ +

title

+ +

The title of the tab (usually the title of the page currently loaded in the tab) This property can be set to change the tab title.

+ +

url

+ +

The URL of the page currently loaded in the tab. This property can be set to load a different URL in the tab.

+ +

favicon

+ +

The URL of the favicon for the page currently loaded in the tab. This property is read-only.

+ +
This property is deprecated. From version 1.15, use the favicon module's getFavicon() function instead.
+ +

contentType

+ +
+

This is currently an experimental API, so we might change it in future releases.

+ +

Returns the MIME type that the document currently loaded in the tab is being rendered as. This may come from HTTP headers or other sources of MIME information, and might be affected by automatic type conversions performed by either the browser or extensions. This property is read-only.

+
+ +

index

+ +

The index of the tab relative to other tabs in the application window. This property can be set to change its relative position.

+ +

isPinned

+ +

Whether or not this tab is pinned as an app tab. This property is read-only.

+ +

window

+ +

标签页的window对象.

+ +

readyState

+ +
+

Firefox 33 新增。

+
+ +

A string telling you the load state of the document hosted by this tab. This corresponds directly to Document.readyState. It has one of four possible values:

+ + + +

Once a tab's readyState has entered "interactive", you can retrieve properties such as the document's URL.

+ +

事件

+ +

close

+ +

This event is emitted when the tab is closed. It's also emitted when the tab's window is closed.

+ +
参数
+ +

Tab : Listeners are passed the tab object.

+ +

ready

+ +

This event is emitted when the DOM for the tab's content is ready. It is equivalent to the DOMContentLoaded event for the given content page. At this point the document itself is fully loaded and parsed, but resources such as stylesheets and images may still be loading.

+ +

A single tab will emit this event every time the DOM is loaded: so it will be emitted again if the tab's location changes or the content is reloaded. After this event has been emitted, all properties relating to the tab's content can be used.

+ +
参数
+ +

Tab : Listeners are passed the tab object.

+ +

load

+ +

This event is emitted when the page for the tab's content is loaded. It is equivalent to the load event for the given content page. At this point the document and its resources, such as images and stylesheets, have finished loading.

+ +

This event can be used for pages that do not have a DOMContentLoaded event, like images. For pages that have a DOMContentLoaded event, load is fired after ready.

+ +

A single tab will emit this event every time the page is loaded: so it will be emitted again if the tab's location changes or the content is reloaded. After this event has been emitted, all properties relating to the tab's content can be used.

+ +
参数
+ +

Tab : Listeners are passed the tab object.

+ +

pageshow

+ +

The pageshow event is emitted when the page for a tab's content is loaded. It is equivalent to the pageshow event for the given content page.

+ +

This event is similar to the load and ready events, except unlike load and ready, pageshow is triggered if the page was retrieved from the bfcache. This means that if the user loads a page, loads a new page, then moves back to the previous page using the "Back" button, the pageshow event is emitted when the user moves back to the previous page, while the load and ready events are not.

+ +

This event is not emitted when the tab is made the active tab: to get notified about that, you need to listen to the activate event.

+ +

After this event has been emitted, all properties relating to the tab's content can be used. It is emitted after load and ready.

+ +
参数
+ +

Tab : Listeners are passed the tab object.

+ +

persisted : Listeners are passed a boolean value indicating whether or not the page was loaded from the bfcache.

+ +

activate

+ +

This event is emitted when the tab is made active.

+ +

Note that you cannot guarantee that a tab's content, or even its url, are initialized at the time activate is emitted. This is because when a new tab is opened, its activate event may be emitted before the content is loaded.

+ +

You can use the tab's readyState property to determine whether the tab's content and url will be available: if readyState is uninitialized or loading, then you can't access the tab's properties and must wait for the tab's ready event.

+ +
参数
+ +

Tab : Listeners are passed the tab object.

+ +

deactivate

+ +

This event is emitted when the tab is made inactive.

+ +
参数
+ +

Tab : Listeners are passed the tab object.

diff --git a/files/zh-cn/mozilla/add-ons/sdk/high-level_apis/url/index.html b/files/zh-cn/mozilla/add-ons/sdk/high-level_apis/url/index.html new file mode 100644 index 0000000000..f98da9baf9 --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/high-level_apis/url/index.html @@ -0,0 +1,191 @@ +--- +title: url +slug: Mozilla/Add-ons/SDK/High-Level_APIs/url +translation_of: Archive/Add-ons/Add-on_SDK/High-Level_APIs/url +--- +
+

实验性的

+
+ +

构建,验证,解析URL

+ +

Globals

+ +

构造函数

+ +

URL(source, base)

+ +

URL构造函数可以创建一个URL对象,并验证提供的字符串是否是有效的URL。SDK中,任何接受URL参数的API,除非特殊说明,均接受的是此对象而不是字符串。

+ +
参数
+ +

source : string
+ 一个表示URL的字符串,如果接受的参数不是有效的URL字符串,此构造函数会抛出一个异常。

+ +

base : string
+ 一个设置的字符串,用来表示一个相对路径的源。

+ +

DataURL(uri)

+ +

DataURL构造函数创建一个data: URL对象, 并验证提供的字符串是否是一个有效的data: URL。

+ +
参数
+ +

uri : string
+ 一个表示Data URL的字符串。如果它不是一个合法的URL字符串,此构造函数会抛出一个错误。

+ +

函数

+ +

toFilename(url)

+ +

尝试将给定的URL转换成本地文件路径。这个方法会自动尝试解决非文件协议, such as the resource: protocol, to their place on the file system. 除非URL无法转换,否则,本方法会将本地路径作为字符串返回。

+ +
参数
+ +

url : string
+ 字符串格式的URL。

+ +
返回
+ +

string : 转换后的本地路径字符串

+ +

fromFilename(path)

+ +

讲一个给定的路径转换成 file: URL.

+ +
参数
+ +

path : string
+ 需要被转换的本地文件路径字符串。

+ +
Returns
+ +

string : 转换后的字符串。

+ +

isValidURI(uri)

+ +

检查一个URL字符串是合法。 isValidURI("http://mozilla.org") 将返回 true, 而 isValidURI("mozilla.org") 将返回 false

+ +
参数
+ +

uri : string
+ 需要被测试的URL。

+ +
Returns
+ +

boolean : 代表字符串是否有效的布尔值。

+ +

getTLD(url)

+ +

返回给定域名的顶级域名: 内部使用的是 getPublicSuffix()

+ +
var urls = require("sdk/url");
+console.log(urls.getTLD("http://www.bbc.co.uk/"));          // "co.uk"
+console.log(urls.getTLD("https://developer.mozilla.org/")); // "org"
+ +
参数
+ +

url : string
+ 给定的URL字符串

+ +
返回
+ +

string : 对应的顶级域名。

+ +

URL

+ +

方法

+ +

toString()

+ +

返回URL的字符表示形式。

+ +
返回
+ +

string : 字符串格式的URL。

+ +

属性

+ +

scheme

+ +

URL使用的协议

+ +

userPass

+ +

URL中的username:password 部分,null 表示不存在。

+ +

host

+ +

URL的主机部分。 null 表示无此部分。例子:

+ +
var url = require("sdk/url").URL("https://developer.mozilla.org/en-US/Add-ons");
+console.log(url.host);  // developer.mozilla.org
+ +

port

+ +

URL使用的端口。 null 表示不存在。

+ +

path

+ +

URL的路径部分,例子:

+ +
var url = require("sdk/url").URL("https://developer.mozilla.org/en-US/Add-ons");
+console.log(url.path);  // /en-US/Add-ons
+ +

hostname

+ +

表示URL的域的字符串。参见 window.location.hostname

+ +

pathname

+ +

'/'开始的路径字符串。参见 window.location.pathname

+ +

hash

+ +

'#' 开始的hash字符串,参见 window.location.hash

+ +

href

+ +

整个URL字符串,参见 window.location.href

+ +

origin

+ +

该URL的源的字符串,参见 window.location.origin

+ +

protocol

+ +

该URL使用的协议字符串,包括最后的':',参见 window.location.protocol

+ + + +

以'?'开始的URL的参数段,包括最开始的'?'参见window.location.search.

+ +

DataURL

+ +

方法

+ +

toString()

+ +

返回数据的URL字符串形式。如果是 base64 的URL,数据会以base64编码方式编码。

+ +
返回
+ +

string : URL字符串

+ +

属性

+ +

mimeType

+ +

数据的MIME类型,默认为空字符串。

+ +

parameters

+ +

一个HashMap的数据包含URL参数。默认情况下是一个空对象。

+ +

base64

+ +

定义了数据属性的编码。

+ +

data

+ +

包含数据的URL字符串。如果URI给构造函数包含Base64参数,这个字符串会被解码。

diff --git a/files/zh-cn/mozilla/add-ons/sdk/high-level_apis/widget/index.html b/files/zh-cn/mozilla/add-ons/sdk/high-level_apis/widget/index.html new file mode 100644 index 0000000000..3d374a0752 --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/high-level_apis/widget/index.html @@ -0,0 +1,839 @@ +--- +title: widget +slug: Mozilla/Add-ons/SDK/High-Level_APIs/widget +tags: + - ZH +translation_of: Archive/Add-ons/Add-on_SDK/High-Level_APIs/widget +--- +

{{LegacyAddonsNotice}}{{AddonSidebar}}

+ +
+

Deprecated in Firefox 29 and removed in Firefox 38.

+ +

The widget API is deprecated from Firefox 29 onwards. Please see the ui module for replacements. In particular, for a simple button, try the action button or toggle button APIs, and for a more complex widget try the toolbar or sidebar APIs.

+
+ +

Create a simple user interface for an add-on in Firefox's add-on bar.

+ +

Usage

+ +

"Widgets" are small pieces of content that live in the Firefox 4 add-on bar. They can be simple icons or complex web pages. You can attach panels to them that open when they're clicked, or you can define a custom click handler to perform some other action, like opening a web page in a tab.

+ +

There are a few advantages to using widgets over an ad hoc user interface. First, your users will be accustomed to interacting with add-ons via widgets and the add-on bar. Second, it allows Firefox to treat your interface as a first-class citizen. For example, in the future Firefox may allow the user to drag widgets from the add-on bar to other toolbars. By exposing your interface as a widget, your add-on would automatically inherit such functionality.

+ +

Creation and content

+ +

Widgets can contain images or arbitrary web content. You can include this content inline as a string by using the content property, or point to content using a URL with the contentURL property.

+ +

Upon creation, the widget is automatically added to the add-on bar. You can set the width of a widget, but the height is fixed so as to fit in the add-on bar. If the content is an image, it is automatically scaled to be 16x16 pixels.

+ +

For example, this widget contains an image, so it looks like a simple icon:

+ +
require("sdk/widget").Widget({
+  id: "mozilla-icon",
+  label: "My Mozilla Widget",
+  contentURL: "http://www.mozilla.org/favicon.ico"
+});
+ +

You can make contentURL point to an HTML or icon file which you have packaged inside your add-on. Just save the file in your add-on's data directory, and reference it using the data.url() method of the self module:

+ +
var data = require("sdk/self").data;
+
+require("sdk/widget").Widget({
+  id: "my-widget",
+  label: "My Widget",
+  contentURL: data.url("my-content.html")
+});
+ +

This widget contains an entire web page:

+ +
require("sdk/widget").Widget({
+  id: "hello-display",
+  label: "My Hello Widget",
+  content: "Hello!",
+  width: 50
+});
+ +

Widgets are quite small by default, so this example used the width property to grow it in order to show all the text.

+ +

Scripting widget content

+ +

To interact with the widget's content you need to load a separate script into the panel. In the SDK these scripts are called "content scripts" because they're explicitly used for interacting with web content.

+ +

While content scripts can access the content they're attached to, they can't use the SDK's APIs. So implementing a complete solution usually means you have to send messages between the content script and the main add-on code.

+ + + +
+

Unless your content script is extremely simple and consists only of a static string, don't use contentScript: if you do, you may have problems getting your add-on approved on AMO.

+ +

Instead, keep the script in a separate file and load it using contentScriptFile. This makes your code easier to maintain, secure, debug and review.

+
+ + +

For example, suppose we want to implement a media player as an add-on. We could implement the main user interface as a widget hosting an array of buttons to control play/pause/stop functions.

+ +

We can then use a content script to listen for clicks on those buttons. But because content scripts can't use the SDK's APIs, we'll want the content script to send messages to the main add-on code, which can then implement the media player functions using the SDK.

+ +

The widget's content is specified using HTML like this:

+ +
<html>
+  <body>
+    <img src="play.png" id="play-button">
+    <img src="pause.png" id="pause-button">
+    <img src="stop.png" id="stop-button">
+  </body>
+</html>
+ +

We just include three icons, and assign an ID to each one. This HTML file, and the icon files it references, are saved in the add-on's data directory.

+ +

Next, we write a content script that listens for click events on each icon and sends the corresponding message to the main add-on code:

+ +
var play_button = document.getElementById("play-button");
+play_button.onclick = function() {
+  self.port.emit("play");
+}
+
+var pause_button = document.getElementById("pause-button");
+pause_button.onclick = function() {
+  self.port.emit("pause");
+}
+
+var stop_button = document.getElementById("stop-button");
+stop_button.onclick = function() {
+  self.port.emit("stop");
+}
+ +

We save this file in the add-on's data directory as "button-script.js". Finally. in the add-on's "main.js" file, we create the widget, assign it the HTML file and the content script, and listen for events from the content script:

+ +
const widgets = require("sdk/widget");
+const data = require("sdk/self").data;
+
+var player = widgets.Widget({
+  id: "player",
+  width: 72,
+  label: "Player",
+  contentURL: data.url("buttons.html"),
+  contentScriptFile: data.url("button-script.js")
+});
+
+player.port.on("play", function() {
+  console.log("playing");
+});
+
+player.port.on("pause", function() {
+  console.log("pausing");
+});
+
+player.port.on("stop", function() {
+  console.log("stopping");
+});
+ +

To learn much more about content scripts, see the Working with Content Scripts guide.

+ +

Scripting trusted widget content

+ +

We've already seen that you can package HTML files in your add-on's data directory and use them to define the widget's content. We can call this "trusted" content, because unlike content loaded from a source outside the add-on, the add-on author knows exactly what it's doing. To interact with trusted content you don't need to use content scripts: you can just include a script from the HTML file in the normal way, using script tags.

+ +

Like a content script, these scripts can communicate with the add-on code using the postMessage() API or the port API. The crucial difference is that these scripts access the postMessage and port objects through the addon object, whereas content scripts access them through the self object.

+ +

To show the difference, convert the player add-on above to use normal page scripts instead of content scripts.

+ +

First, in the content script, change self to addon, and wrap it in a function:

+ +
function init() {
+  var play_button = document.getElementById("play-button");
+  play_button.onclick = function() {
+    addon.port.emit("play");
+  }
+
+  var pause_button = document.getElementById("pause-button");
+  pause_button.onclick = function() {
+    addon.port.emit("pause");
+  }
+
+  var stop_button = document.getElementById("stop-button");
+  stop_button.onclick = function() {
+    addon.port.emit("stop");
+  }
+}
+ +

Next, add a script tag to reference "button-script.js", and call its init() function on load:

+ +
<html>
+  <head>
+    <script src="button-script.js"></script>
+  </head>
+  <body onLoad="init()">
+    <img src="play.png" id="play-button">
+    <img src="pause.png" id="pause-button">
+    <img src="stop.png" id="stop-button">
+  </body>
+</html>
+
+ +

Finally, remove the line attaching the content script from "main.js":

+ +
const widgets = require("sdk/widget");
+const data = require("sdk/self").data;
+
+var player = widgets.Widget({
+  id: "player",
+  width: 72,
+  label: "Player",
+  contentURL: data.url("buttons.html")
+});
+
+player.port.emit("init");
+
+player.port.on("play", function() {
+  console.log("playing");
+});
+
+player.port.on("pause", function() {
+  console.log("pausing");
+});
+
+player.port.on("stop", function() {
+  console.log("stopping");
+});
+ +

Attaching panels to widgets

+ +

You can supply a panel to the widget's constructor: if you do this, the panel is automatically displayed when the user clicks the widget.

+ + +

+ +
data = require("sdk/self").data
+
+var clockPanel = require("sdk/panel").Panel({
+  width:215,
+  height:160,
+  contentURL: data.url("clock.html")
+});
+
+require("sdk/widget").Widget({
+  id: "open-clock-btn",
+  label: "Clock",
+  contentURL: data.url("History.png"),
+  panel: clockPanel
+});
+ +

Note that this is, at the moment, the only way you can attach a panel to a widget.

+ +

You must supply the panel in the widget's constructor for it to work. If you assign the panel to the widget after construction, the panel can still be shown but will not be anchored to the widget:

+ +
data = require("sdk/self").data
+
+var clockPanel = require("sdk/panel").Panel({
+  width:215,
+  height:160,
+  contentURL: data.url("clock.html")
+});
+
+widget = require("sdk/widget").Widget({
+  id: "open-clock-btn",
+  label: "Clock",
+  contentURL: data.url("History.png")
+});
+
+widget.panel = clockPanel;
+
+// Will not be anchored
+widget.panel.show();
+ +

Also, if you try to call panel.show() inside your widget's click event listener, the panel will not be anchored:

+ +
data = require("sdk/self").data
+
+var clockPanel = require("sdk/panel").Panel({
+  width:215,
+  height:160,
+  contentURL: data.url("clock.html")
+});
+
+require("sdk/widget").Widget({
+  id: "open-clock-btn",
+  label: "Clock",
+  contentURL: data.url("History.png"),
+  panel: clockPanel,
+  onClick: function() {
+    // Will not be anchored
+    this.panel.show();
+  }
+});
+ +

See bug 638142.

+ +

Private windows

+ +

If your add-on has not opted into private browsing, then your widget will not appear in any private browser windows.

+ +

To learn more about private windows, how to opt into private browsing, and how to support private browsing, refer to the documentation for the private-browsing module.

+ +

Examples

+ +

For conciseness, these examples create their content scripts as strings and use the contentScript property. In your own add-ons, you will probably want to create your content scripts in separate files and pass their URLs using the contentScriptFile property. See Working with Content Scripts for more information.

+ +
var widgets = require("sdk/widget");
+
+// A basic click-able image widget.
+widgets.Widget({
+  id: "google-link",
+  label: "Widget with an image and a click handler",
+  contentURL: "http://www.google.com/favicon.ico",
+  onClick: function() {
+    require("sdk/tabs").activeTab.url = "http://www.google.com/";
+  }
+});
+ +
// A widget that changes display on mouseover.
+widgets.Widget({
+  id: "mouseover-effect",
+  label: "Widget with changing image on mouseover",
+  contentURL: "http://www.yahoo.com/favicon.ico",
+  onMouseover: function() {
+    this.contentURL = "http://www.bing.com/favicon.ico";
+  },
+  onMouseout: function() {
+    this.contentURL = "http://www.yahoo.com/favicon.ico";
+  }
+});
+ +
// A widget that updates content on a timer.
+widgets.Widget({
+  id: "auto-update-widget",
+  label: "Widget that updates content on a timer",
+  content: "0",
+  contentScript: 'setTimeout(function() {' +
+                 '  document.body.innerHTML++;' +
+                 '}, 2000)',
+  contentScriptWhen: "ready"
+});
+ +
// A widget created with a specified width, that grows.
+var myWidget = widgets.Widget({
+  id: "widget-effect",
+  label: "Wide widget that grows wider on a timer",
+  content: "I'm getting longer.",
+  width: 50,
+});
+require("sdk/timers").setInterval(function() {
+  myWidget.width += 10;
+}, 1000);
+ +
// A widget communicating bi-directionally with a content script.
+var widget = widgets.Widget({
+  id: "message-test",
+  label: "Bi-directional communication!",
+  content: "<foo>bar</foo>",
+  contentScriptWhen: "ready",
+  contentScript: 'self.on("message", function(message) {' +
+                 '  alert("Got message: " + message);' +
+                 '});' +
+                 'self.postMessage("ready");',
+  onMessage: function(message) {
+    if (message == "ready")
+      widget.postMessage("me too");
+  }
+});
+ +

Globals

+ +

Constructors

+ +

Widget(options)

+ +

Creates a new widget. The widget is immediately added to the add-on bar.

+ +
Parameters
+ +

options : object
+ Required options:

+ + + + + + + + + + + + + + + + + + + + + +
NameType 
labelstring +

A string description of the widget used for accessibility, title bars, and error reporting.

+
idstring +

A string used to identify your widget in order to save its location when the user moves it in the browser. This string has to be unique and must not be changed over time.

+
+ +

Optional options:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameType 
contentstring +

An optional string value containing the displayed content of the widget. It may contain HTML. Widgets must have either the content property or the contentURL property set.

+ +

If the content is an image, it is automatically scaled to be 16x16 pixels.

+
contentURLstring +

An optional string URL to content to load into the widget. This can be local content or remote content, an image or web content. Widgets must have either the content property or the contentURL property set.

+ +

If the content is an image, it is automatically scaled to be 16x16 pixels.

+
panelPanel +

An optional panel to open when the user clicks on the widget. Note: If you also register a "click" listener, it will be called instead of the panel being opened. However, you can show the panel from the listener by calling this.panel.show().

+
widthinteger +

Optional width in pixels of the widget. If not given, a default width is used.

+
onClickfunction +

Include this to listen to the widget's click event.

+
onMessagefunction +

Include this to listen to the widget's message event.

+
onMouseoverfunction +

Include this to listen to the widget's mouseover event.

+
onMouseoutfunction +

Include this to listen to the widget's mouseout event.

+
onAttachfunction +

Include this to listen to the widget's attach event.

+
tooltipstring +

Optional text to show when the user's mouse hovers over the widget. If not given, the label is used.

+
allowobject +

An optional object describing permissions for the content. It should contain a single key named script whose value is a boolean that indicates whether or not to execute script in the content. script defaults to true.

+
contentScriptFilestring,array +

A local file URL or an array of local file URLs of content scripts to load. Content scripts specified by this property are loaded before those specified by the contentScript property.

+
contentScriptstring,array +

A string or an array of strings containing the texts of content scripts to load. Content scripts specified by this property are loaded after those specified by the contentScriptFile property.

+
contentScriptWhenstring +

When to load the content scripts. This may take one of the following values:

+ +
    +
  • "start": load content scripts immediately after the document element for the widget is inserted into the DOM, but before the DOM content itself has been loaded
  • +
  • "ready": load content scripts once DOM content has been loaded, corresponding to the DOMContentLoaded event
  • +
  • "end": load content scripts once all the content (DOM, JS, CSS, images) for the widget has been loaded, at the time the window.onload event fires
  • +
+ +

This property is optional and defaults to "end".

+
contentScriptOptionsobject +

Read-only value exposed to content scripts under self.options property.

+ +

Any kind of jsonable value (object, array, string, etc.) can be used here. Optional.

+
+ +

Widget

+ +

Represents a widget object.

+ +

Methods

+ +

destroy()

+ +

Removes the widget from the add-on bar.

+ +

postMessage(data)

+ +

Sends a message to the widget's content scripts.

+ +
Parameters
+ +

data : value
+ The message to send. The message can be any JSON-serializable value.

+ +

on(type, listener)

+ +

Registers an event listener with the widget.

+ +
Parameters
+ +

type : string
+ The type of event to listen for.

+ +

listener : function
+ The listener function that handles the event.

+ +

removeListener(type, listener)

+ +

Unregisters an event listener from the widget.

+ +
Parameters
+ +

type : string
+ The type of event for which listener was registered.

+ +

listener : function
+ The listener function that was registered.

+ +

getView(window)

+ +

Retrieve a WidgetView instance of this widget relative to a browser window.

+ +
Parameters
+ +

window : BrowserWindow
+ The BrowserWindow instance to match.

+ +
Returns
+ +

WidgetView : A WidgetView instance associated with the browser window. Any changes subsequently applied to this object will only be applied to the widget attached to that window.

+ +

Properties

+ +

label

+ +

The widget's label. Read-only.

+ +

content

+ +

A string containing the widget's content. It can contain HTML. Setting it updates the widget's appearance immediately. However, if the widget was created using contentURL, then this property is meaningless, and setting it has no effect.

+ +

contentURL

+ +

The URL of content to load into the widget. This can point to local content loaded from your add-on's "data" directory or remote content, an image or web content. Setting it updates the widget's appearance immediately. However, if the widget was created using content, then this property is meaningless, and setting it has no effect.

+ +

Setting the contentURL property will break the channel of communication between this widget and any content scripts it contains. Messages sent from the content script will no longer be received by the main add-on code, and vice versa. This issue is currently tracked as bug 825434.

+ +

panel

+ +

A panel to open when the user clicks on the widget.

+ +

width

+ +

The widget's width in pixels. Setting it updates the widget's appearance immediately.

+ +

tooltip

+ +

The text of the tooltip that appears when the user hovers over the widget.

+ +

allow

+ +

A object describing permissions for the content. It contains a single key named script whose value is a boolean that indicates whether or not to execute script in the content.

+ +

contentScriptFile

+ +

A local file URL or an array of local file URLs of content scripts to load.

+ +

contentScript

+ +

A string or an array of strings containing the texts of content scripts to load.

+ +

contentScriptWhen

+ +

When to load the content scripts. This may have one of the following values:

+ + + +

contentScriptOptions

+ +

Read-only value exposed to content scripts under self.options property.

+ +

Any kind of jsonable value (object, array, string, etc.) can be used here. Optional.

+ +

port

+ +

Object that allows you to:

+ + + +

See the guide to communicating using port for details.

+ +

Events

+ +

attach

+ +

This event is emitted when a browser window is opened and a new WidgetView object is created. If the widget has a content script, this event is fired only when the content script is applied according to the contentScriptWhen attribute.

+ +
Arguments
+ +

WidgetView : The related WidgetView object.

+ +

click

+ +

This event is emitted when the widget is clicked.

+ +
Arguments
+ +

WidgetView : Listeners are passed a single argument which is the WidgetView that triggered the click event.

+ +

message

+ +

If you listen to this event you can receive message events from content scripts associated with this widget. When a content script posts a message using self.postMessage(), the message is delivered to the add-on code in the widget's message event.

+ +
Arguments
+ +

value : Listeners are passed a single argument which is the message posted from the content script. The message can be any JSON-serializable value.

+ +

mouseover

+ +

This event is emitted when the user moves the mouse over the widget.

+ +

mouseout

+ +

This event is emitted when the user moves the mouse away from the widget.

+ +

WidgetView

+ +

Represents a widget instance specific to one browser window.

+ +

Anything you do to an instance of this object will only be applied to the instance attached to its browser window: widget instances attached to other browser windows will be unaffected.

+ +

By contrast, any changes you make to an instance of the normal Widget class will be applied across all browser windows.

+ +

This class has all the same methods, attributes and events as the Widget class except for the getView method and the attach event.

+ +

In this example WidgetView is used to display different content for http and https schemes:

+ +
// A widget that update its content specifically to each window.
+var tabs = require("sdk/tabs");
+var windows = require("sdk/windows").browserWindows;
+var widget = require("sdk/widget").Widget({
+  id: "window-specific-test",
+  label: "Widget with content specific to each window",
+  content: " ",
+  width: 50
+});
+// Observe tab switch or document changes in each existing tab:
+function updateWidgetState(tab) {
+  var view = widget.getView(tab.window);
+  if (!view) return;
+  // Update widget displayed text:
+  view.content = tab.url.match(/^https/) ? "Secured" : "Unsafe";
+}
+tabs.on('ready', updateWidgetState);
+tabs.on('activate', updateWidgetState);
+ +

Methods

+ +

destroy()

+ +

Removes the widget view from the add-on bar.

+ +

postMessage(data)

+ +

Sends a message to the widget view's content scripts.

+ +
Parameters
+ +

data : value
+ The message to send. The message can be any JSON-serializable value.

+ +

on(type, listener)

+ +

Registers an event listener with the widget view.

+ +
Parameters
+ +

type : string
+ The type of event to listen for.

+ +

listener : function
+ The listener function that handles the event.

+ +

removeListener(type, listener)

+ +

Unregisters an event listener from the widget view.

+ +
Parameters
+ +

type : string
+ The type of event for which listener was registered.

+ +

listener : function
+ The listener function that was registered.

+ +

Properties

+ +

label

+ +

The widget view's label. Read-only.

+ +

content

+ +

A string containing the widget view's content. It can contain HTML. Setting it updates the widget view's appearance immediately. However, if the widget view was created using contentURL, then this property is meaningless, and setting it has no effect.

+ +

contentURL

+ +

The URL of content to load into the widget. This can point to local content loaded from your add-on's "data" directory or remote content, an image or web content. Setting it updates the widget's appearance immediately. However, if the widget was created using content, then this property is meaningless, and setting it has no effect.

+ +

Setting the contentURL property will break the channel of communication between this widget and any content scripts it contains. Messages sent from the content script will no longer be received by the main add-on code, and vice versa. This issue is currently tracked as bug 825434.

+ +

panel

+ +

A panel to open when the user clicks on the widget view.

+ +

width

+ +

The widget view's width in pixels. Setting it updates the widget view's appearance immediately.

+ +

tooltip

+ +

The text of the tooltip that appears when the user hovers over the widget view.

+ +

allow

+ +

A object describing permissions for the content. It contains a single key named script whose value is a boolean that indicates whether or not to execute script in the content.

+ +

contentScriptFile

+ +

A local file URL or an array of local file URLs of content scripts to load.

+ +

contentScript

+ +

A string or an array of strings containing the texts of content scripts to load.

+ +

contentScriptWhen

+ +

When to load the content scripts. This may have one of the following values:

+ + + +

contentScriptOptions

+ +

Read-only value exposed to content scripts under self.options property.

+ +

Any kind of jsonable value (object, array, string, etc.) can be used here. Optional.

+ +

port

+ +

Object that allows you to:

+ + + +

See the guide to communicating using port for details.

+ +

Events

+ +

detach

+ +

The detach event is fired when the widget view is removed from its related window. This can occur if the window is closed, Firefox exits, or the add-on is disabled.

+ +

click

+ +

This event is emitted when the widget view is clicked.

+ +

message

+ +

If you listen to this event you can receive message events from content scripts associated with this widget view. When a content script posts a message using self.postMessage(), the message is delivered to the add-on code in the widget view's message event.

+ +
Arguments
+ +

value : Listeners are passed a single argument which is the message posted from the content script. The message can be any JSON-serializable value.

+ +

mouseover

+ +

This event is emitted when the user moves the mouse over the widget view.

+ +

mouseout

+ +

This event is emitted when the user moves the mouse away from the widget view.

diff --git a/files/zh-cn/mozilla/add-ons/sdk/index.html b/files/zh-cn/mozilla/add-ons/sdk/index.html new file mode 100644 index 0000000000..3c6398ed48 --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/index.html @@ -0,0 +1,104 @@ +--- +title: 附加组件 SDK +slug: Mozilla/Add-ons/SDK +tags: + - More Example + - Need Tanslate + - TopicStub +translation_of: Archive/Add-ons/Add-on_SDK +--- +
+

Support for extensions using XUL/XPCOM or the Add-on SDK was removed in Firefox 57, released November 2017. As there is no supported version of Firefox enabling these technologies, this page will be removed by December 2020.

+ +

Add-ons using the techniques described in this document are considered a legacy technology in Firefox. Don't use these techniques to develop new add-ons. Use WebExtensions instead. If you maintain an add-on which uses the techniques described here, consider migrating it to use WebExtensions.

+ +

Starting from Firefox 53, no new legacy add-ons will be accepted on addons.mozilla.org (AMO) for desktop Firefox and Firefox for Android.

+ +

Starting from Firefox 57, only extensions developed using WebExtensions APIs will be supported on Desktop Firefox and Firefox for Android.

+ +

Even before Firefox 57, changes coming up in the Firefox platform will break many legacy extensions. These changes include multiprocess Firefox (e10s), sandboxing, and multiple content processes. Legacy extensions that are affected by these changes should migrate to use WebExtensions APIs if they can. See the "Compatibility Milestones" document for more information.

+ +

A wiki page containing resources, migration paths, office hours, and more, is available to help developers transition to the new technologies.

+
+ +

你可以使用Add-on SDK来开发Firefox的附加组件。你可以使用各种各样的标准Web技术:JavaScript, HTML和CSS。该SDK不仅包括一些用来创建附加组件的 JavaScript API,还提供了开发、运行、测试、打包附加组件的一些工具。

+ +
+

教程

+ +
+
+
+
快速入门
+
如何安装SDK使用jpm工具开发、测试、打包附加组件
+
与浏览器交互
+
打开网页监听页面加载列出打开的标签页
+
开发技术
+
学习常用开发技术,如单元测试日志创建可复用模块本地化移动开发
+
+
+ +
+
+
创建用户界面组件
+
创建用户界面组件如工具条按钮, 上下文菜单菜单项对话框
+
修改网页
+
+

通过URL匹配修改页面或者动态修改某个tab选项卡

+
+
将其组合在一起
+
关于注释器的附加组件的例子演示.
+
+
+
+ +
+

指南

+ +
+
+
+
贡献者指南
+
学习如何开始构建 SDK和关于SDK最重要的术语, 如 模块 类和继承, 私有属性, 内容处理.
+
SDK的基础构建
+
SDK的底层技术方面: 模块,程序ID, Firefox 兼容的规则定义.
+
内容脚本
+
+
+

一个详细的指南内容脚本

+
+
+
+
+ +
+
+
SDK语法
+
SDK的事件框架和插件的脚本和内容脚本之间的区别。
+
SDK的 事件框架add-on scripts 和 content scripts 的区别.
+
XUL 迁移
+
指导  移植 XUL add-ons 到 SDK. 本指南包括 两工具 和 一个 工作实例 比较 XUL 和 add-on.
+
+
+
+ +
+

参考

+ +
+
+
+
高级API
+
高级SDK API的参考文档.
+
工具参考
+
用于 jpm工具 开发的参考文档, 测试, 打包add-ons, 全局 控制台记录, 和package.json 文件.
+
+
+ +
+
+
低级API
+
低级SDK API的参考文档.
+
+
+
diff --git a/files/zh-cn/mozilla/add-ons/sdk/low-level_apis/index.html b/files/zh-cn/mozilla/add-ons/sdk/low-level_apis/index.html new file mode 100644 index 0000000000..673c369430 --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/low-level_apis/index.html @@ -0,0 +1,26 @@ +--- +title: 低层 API +slug: Mozilla/Add-ons/SDK/Low-Level_APIs +translation_of: Archive/Add-ons/Add-on_SDK/Low-Level_APIs +--- +

{{AddonSidebar}}

+ +

本节的模块实现了低层的 API,这些模块大至分为三类:

+ + + +

这些模块仍在开发中,我们预期在将来发布的版本中会做出不兼容的变更。

+ +

{{ LandingPageListSubpages ("/zh-CN/Add-ons/SDK/Low-Level_APIs", 5) }}

+ +

 

diff --git a/files/zh-cn/mozilla/add-ons/sdk/low-level_apis/test_assert/index.html b/files/zh-cn/mozilla/add-ons/sdk/low-level_apis/test_assert/index.html new file mode 100644 index 0000000000..5f7537ec42 --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/low-level_apis/test_assert/index.html @@ -0,0 +1,283 @@ +--- +title: test/assert +slug: Mozilla/Add-ons/SDK/Low-Level_APIs/test_assert +translation_of: Archive/Add-ons/Add-on_SDK/Low-Level_APIs/test_assert +--- +

{{LegacyAddonsNotice}}{{AddonSidebar}}

+ +
+

Unstable

+
+ +

实现在 CommonJS Unit Testing specification version 1.1 其中定义的断言接口。

+ +

Usage

+ +

To use the assert module, write a set of unit tests following the instructions in the unit testing tutorial. Each test will be passed an Assert object when you run the tests using jpm test. You can use this object to make assertions about your program's state.

+ +

For example:

+ +
var a = 1;
+
+exports["test value of a"] = function(assert) {
+  assert.ok(a == 1, "test that a is 1");
+}
+
+require("sdk/test").run(exports);
+ +

Globals

+ +

Constructors

+ +

Assert(logger)

+ +

Create a new Assert object. This function is only called by the unit test framework, and not by unit tests themselves.

+ +
Parameters
+ +

logger : object
+ Object used to log the results of assertions.

+ +

Assert

+ +

An object used to make assertions about a program's state in order to implement unit tests.

+ +

The Assert object's interface is defined by the CommonJS Unit Testing specification, version 1.1.

+ +

Methods

+ +

ok(guard, message)

+ +

Tests whether an expression evaluates to true.

+ +
assert.ok(a == 1, "test that a is equal to one");
+ +

This is equivalent to:

+ +
assert.equal(a == 1, true, "test that a is equal to one");
+ +
Parameters
+ +

guard : expression
+ The expression to evaluate.

+ +

message : string
+ Optional message to log, providing extra information about the test.

+ +

equal(actual, expected, message)

+ +

Tests shallow, coercive equality with ==:

+ +
assert.equal(1, 1, "test that one is one");
+ +
Parameters
+ +

actual : object
+ The actual result.

+ +

expected : object
+ The expected result.

+ +

message : string
+ Optional message to log, providing extra information about the test.

+ +

notEqual(actual, expected, message)

+ +

Tests that two objects are not equal, using !=:

+ +
assert.notEqual(1, 2, "test that one is not two");
+ +
Parameters
+ +

actual : object
+ The actual result.

+ +

expected : object
+ The expected result.

+ +

message : string
+ Optional message to log, providing extra information about the test.

+ +

deepEqual(actual, expected, message)

+ +

Tests that two objects have a deep equality relation. Deep equality is defined in the CommonJS specification for Assert, item 7, which is quoted here:

+ +
+
    +
  1. All identical values are equivalent, as determined by ===.
  2. +
  3. If the expected value is a Date object, the actual value is equivalent if it is also a Date object that refers to the same time.
  4. +
  5. Other pairs that do not both pass typeof value == "object", equivalence is determined by ==.
  6. +
  7. For all other Object pairs, including Array objects, equivalence is determined by having the same number of owned properties (as verified with Object.prototype.hasOwnProperty.call), the same set of keys (although not necessarily the same order), equivalent values for every corresponding key, and an identical "prototype" property. Note: this accounts for both named and indexed properties on Arrays.
  8. +
+
+ +
assert.deepEqual({ a: "foo" }, { a: "foo" }, "equivalent objects");
+ +
Parameters
+ +

actual : object
+ The actual result.

+ +

expected : object
+ The expected result.

+ +

message : string
+ Optional message to log, providing extra information about the test.

+ +

notDeepEqual(actual, expected, message)

+ +

Tests that two objects do not have a deep equality relation, using the negation of the test for deep equality:

+ +
assert.notDeepEqual({ a: "foo" }, Object.create({ a: "foo" }),
+                    "object's inherit from different prototypes");
+ +
Parameters
+ +

actual : object
+ The actual result.

+ +

expected : object
+ The expected result.

+ +

message : string
+ Optional message to log, providing extra information about the test.

+ +

strictEqual(actual, expected, message)

+ +

Tests that two objects are equal, using the strict equality operator ===:

+ +
// This test will pass, because "==" will perform type conversion
+exports["test coercive equality"] = function(assert) {
+  assert.equal(1, "1", "test coercive equality between 1 and '1'");
+}
+
+// This test will fail, because the types are different
+exports["test strict equality"] = function(assert) {
+  assert.strictEqual(1, "1", "test strict equality between 1 and '1'");
+}
+ +
Parameters
+ +

actual : object
+ The actual result.

+ +

expected : object
+ The expected result.

+ +

message : string
+ Optional message to log, providing extra information about the test.

+ +

notStrictEqual(actual, expected, message)

+ +

Tests that two objects are not equal, using the negation of the strict equality operator ===:

+ +
// This test will fail, because "==" will perform type conversion
+exports["test coercive equality"] = function(assert) {
+  assert.notEqual(1, "1", "test coercive equality between 1 and '1'");
+}
+
+// This test will pass, because the types are different
+exports["test strict equality"] = function(assert) {
+  assert.notStrictEqual(1, "1", "test strict equality between 1 and '1'");
+}
+ +
Parameters
+ +

actual : object
+ The actual result.

+ +

expected : object
+ The expected result.

+ +

message : string
+ Optional message to log, providing extra information about the test.

+ +

throws(block, error, message)

+ +

Assert that a block of code throws the expected exception.

+ +

This method takes an optional Error argument:

+ + + + +

For example, suppose we define two different custom exceptions:

+ +
function MyError(message) {
+  this.name = "MyError";
+  this.message = message || "Default Message";
+}
+
+MyError.prototype = new Error();
+MyError.prototype.constructor = MyError;
+
+function AnotherError(message) {
+  this.name = "AnotherError";
+  this.message = message || "Default Message";
+    console.log(this.message);
+}
+
+AnotherError.prototype = new Error();
+AnotherError.prototype.constructor = AnotherError;
+ +

We can check the type of exception by passing a function as the Error argument:

+ +
exports["test exception type 1 expected to pass"] = function(assert) {
+  assert.throws(function() {
+    throw new MyError("custom message");
+  },
+  MyError,
+  "test throwing a specific exception");
+}
+
+exports["test exception type 2 expected to fail"] = function(assert) {
+  assert.throws(function() {
+    throw new MyError("custom message");
+  },
+  AnotherError,
+  "test throwing a specific exception");
+}
+ +

We can check the message by passing a regular expression:

+ +
exports["test exception message 1 expected to pass"] = function(assert) {
+  assert.throws(function() {
+    throw new MyError("custom message");
+  },
+  /custom message/,
+  "test throwing a specific message");
+}
+
+exports["test exception message 2 expected to pass"] = function(assert) {
+  assert.throws(function() {
+    throw new AnotherError("custom message");
+  },
+  /custom message/,
+  "test throwing a specific exception");
+}
+
+exports["test exception message 3 expected to fail"] = function(assert) {
+  assert.throws(function() {
+    throw new MyError("another message");
+  },
+  /custom message/,
+  "test throwing a specific message");
+}
+ +
Parameters
+ +

block : block
+ The block of code to test.

+ +

error : function|RegExp
+ Either a constructor function returning the type of exception expected, or a regular expression expected to match the exception's message property.

+ +

message : string
+ Optional message to log, providing extra information about the test.

diff --git a/files/zh-cn/mozilla/add-ons/sdk/low-level_apis/ui_button_action/index.html b/files/zh-cn/mozilla/add-ons/sdk/low-level_apis/ui_button_action/index.html new file mode 100644 index 0000000000..d826b15d7f --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/low-level_apis/ui_button_action/index.html @@ -0,0 +1,526 @@ +--- +title: ui/button/action +slug: Mozilla/Add-ons/SDK/Low-Level_APIs/ui_button_action +translation_of: Archive/Add-ons/Add-on_SDK/Low-Level_APIs/ui_button_action +--- +
+

实验性的(译者备注:暂不翻译,可能会废弃)

+
+

Add a button to the Firefox user interface. With this module you can create buttons that display icons and can respond to click events.

+

Usage

+


+ Creating buttons

+

To create a button you must give it an ID, an icon, and a label:

+
var { ActionButton } = require("sdk/ui/button/action");
+
+var button = ActionButton({
+    id: "my-button",
+    label: "my button",
+    icon: {
+      "16": "./firefox-16.png",
+      "32": "./firefox-32.png"
+    },
+    onClick: function(state) {
+        console.log("button '" + state.label + "' was clicked");
+    }
+  });
+

By default, the button appears in the Firefox toolbar:

+

However, users can move it to the Firefox menu panel using the toolbar customization feature:

+

+

Badged buttons

+
+

New in Firefox 36.

+
+

You can add a "badge" to a button using its badge property. This can be a number or a string, and you can update it at any time. By default the badge's color is red, but you can set your own color using the badgeColor property, specified as a CSS <color> value:

+
var { ToggleButton } = require("sdk/ui/button/toggle");
+
+var button = ToggleButton({
+    id: "my-button1",
+    label: "my button1",
+    icon: "./icon-16.png",
+    onChange: changed,
+    badge: 0,
+    badgeColor: "#00AAAA"
+  });
+
+function changed(state) {
+  button.badge = state.badge + 1;
+  if (state.checked) {
+    button.badgeColor = "#AA00AA";
+  }
+  else {
+    button.badgeColor = "#00AAAA";
+  }
+}
+

+

Specifying multiple icons

+

You can specify just one icon, or multiple icons in different sizes.

+

If you specify multiple icons, Firefox will select the best-fitting icon based on the device screen resolution and the place the icon appears. For example, in the screenshots above, Firefox uses the small icon when the button is in the toolbar and the large icon when the button is in the menu panel. Read more about specifying icons in the reference documentation for the ActionButton constructor.

+

Responding to click events

+

You can respond to click events by assigning a listener to the button's click event. You can do this in the button's constructor, by assigning the listener to the onClick option. You can also add, or change, the listener afterwards:

+
var { ActionButton } = require("sdk/ui/button/action");
+
+var button = ActionButton({
+    id: "my-button",
+    label: "my button",
+    icon: {
+      "16": "./firefox-16.png",
+      "32": "./firefox-32.png"
+    },
+    onClick: firstClick
+  });
+
+function firstClick(state) {
+  console.log("You clicked '" + state.label + "'");
+  button.removeListener("click", firstClick);
+  button.on("click", subsequentClicks);
+}
+
+function subsequentClicks(state) {
+  console.log("You clicked '" +  state.label + "' again");
+}
+

The listener is passed a state object that contains all the button's properties.

+

You can generate click events programmatically with the button's click() method.

+

Disabling buttons

+

You can disable a button by setting its disabled property to true. A disabled button will not generate click events and its icon will appear disabled:

+

+

Updating state

+

You can update all the button's properties except for its id.

+

By default, the button has global state: that is, its properties are the same across all open windows and tabs, and updating them updates the button's state across all open windows and tabs.

+

You can set state to be specific to a window or tab using the button's state() method. To set state like this, call state() with 2 parameters:

+ +

Here's an add-on with a button that disables itself when you click it, but only for the currently active window:

+
var { ActionButton } = require("sdk/ui/button/action");
+
+var button = ActionButton({
+    id: "my-button",
+    label: "my button",
+    icon: {
+      "16": "./firefox-16.png",
+      "32": "./firefox-32.png"
+    },
+    onClick: disableForThisWindow
+  });
+
+function disableForThisWindow(state) {
+  button.state("window", {
+    disabled: true
+  });
+}
+

To fetch the state for a specific window or tab, call state(), passing in the window or tab you are interested in, and it will return the state:

+
var labelForActiveTab = button.state("tab").label;
+

To learn more about this, see the API documentation for state().

+

Destroying buttons

+

When you've finished with a button, destroy it by calling its destroy() method. After that, any attempts to access any of its properties or to call any of its methods will throw exceptions.

+

Globals

+

Constructors

+

ActionButton(options)

+

Creates an action button.

+
Parameters
+

options : object
+ Required options:

+ + + + + + + + + + + + + + + + + + + + + + + + + +
NameType 
idstring +

The button's ID. This is used internally to keep track of this button. The ID must be unique within your add-on.

+
labelstring +

The button's human-readable label. When the button is in the toolbar, this appears in a tooltip, and when the button is in the menu, it appears underneath the button as a legend.

+
iconurl, string, object +

One or more icons for the button. You can specify this in one of three ways: 

+
    +
  • as a resource:// URL pointing at an icon file in your add-on's "data" directory, typically constructed using self.data.url(iconfile)
  • +
  • as a relative path: a string in the form "./iconfile", where "iconfile" is a relative path to the icon file beginning in your add-on's "data" directory
  • +
  • as an object, or dictionary of key-value pairs. Here you can specify a range of sizes for your button's icon. Each key-value pair specifies an icon: +
      +
    • each value specifies an image file as a resource:// URL or relative path.
    • +
    • each key must be a numeric string such as "16", or "32", which represents the size in pixels of the corresponding image.
    • +
    +
  • +
+
+var { ActionButton } = require('sdk/ui/button/action');
+var self = require("sdk/self");
+
+var button1 = ActionButton({
+    id: "my-button1",
+    label: "my button1",
+    icon: self.data.url("firefox-16.png")
+  });
+
+var button2 = ActionButton({
+    id: "my-button2",
+    label: "my button2",
+    icon: "./firefox-16.png"
+  });
+
+var button3 = ActionButton({
+    id: "my-button3",
+    label: "my button3",
+    icon: {
+      "16" : "./firefox-16.png",
+      "32" : "./firefox-32.png",
+      "64" : "./firefox-64.png"
+    }
+  });
+

If you use the final form, Firefox will automatically choose the best-fit icon for your button, depending on the device screen resolution and where the button is in the UI. On a device with a "normal" screen resolution, the toolbar has space for 18 x 18 pixels and the menu panel has space for 32 x 32 pixels. On a high resolution screen (such as a HiDPI display), these are doubled to 36 x 36 and 64 x 64 pixels, respectively. So you can supply three icon files:

+
+icon: {
+  "16": "./addon16.png",
+  "32": "./addon32.png",
+  "64": "./addon64.png"
+}
+

This will look fine in both toolbar and menu panel, and for both screen resolutions. However, the icons in the toolbar will not quite fill the space available, so you can instead supply four icons:

+
+icon: {
+  "18": "./addon18.png", // toolbar icon non HiDPI
+  "32": "./addon32.png", // menu panel icon non HiDPI
+  "36": "./addon36.png", // toolbar icon HiDPI
+  "64": "./addon64.png"  // menu panel icon HiDPI
+}
+
+
+

Optional options:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameType 
disabledboolean +

Determines whether the button is disabled. Disabled buttons appear disabled in the UI, and do not respond to clicks. Defaults to false.

+
onClickfunction +

Click handler for the button.

+
badgeNumber or String +
+

New in Firefox 36.

+
+

Badge to attach to the button.

+
badgeColorCSS <color> value +
+

New in Firefox 36.

+
+

Color for the badge. If badgeColor is omitted and badge is specified, then the badge is red.

+
+

ActionButton

+

Methods

+

click()

+

Click the button. This will cause the button to generate the click event:

+
var { ActionButton } = require('sdk/ui/button/action');
+
+var button = ActionButton({
+  id: "my-button",
+  label: "my button",
+  icon: {
+    "16": "./firefox-16.png",
+    "32": "./firefox-32.png"
+  },
+  onClick: function(state) {
+      console.log("You clicked '" + state.label + "'");
+  }
+});
+
+button.click();
+
+

state()

+

Get or set the button's state for a specific window or tab.

+

By default, a button's properties are global, meaning that they are the same across all open windows and tabs, and that if you update these properties, then they are updated across all windows and tabs. But sometimes you want a button attached to one window (or tab) to have a different state to a button attached to a different window (or tab). That's what state() is for.

+

To set a button's properties for a specific window or tab, call state(), passing it the window or tab you want the property to apply to, and the property value to set. A special shortcut allows you to pass the string "window" or "tab" to select the currently active window or tab.

+

For example, if you have a button like this:

+
var { ActionButton } = require('sdk/ui/button/action');
+
+var button = ActionButton({
+  id: "my-button",
+  label: "default",
+  icon: "./firefox-16.png"
+});
+

You can change its label for only the currently active window like this:

+
button.state("window", {
+  "label" : "window-specific label"
+});
+

You can change its label for only the currently active tab like this:

+
button.state("tab", {
+  "label" : "tab-specific label"
+});
+
+

To fetch the button state for a specific window or tab, call state(), passing it the window or tab you're interested in, and it will return a state object containing all the properties for the button associated with that window or tab. Again. you can use the strings "window" or "tab" as shortcuts. For example, this add-on:

+ +
var { ActionButton } = require('sdk/ui/button/action');
+var tabs = require("sdk/tabs");
+
+var button = ActionButton({
+  id: "my-button",
+  label: "default label",
+  icon: "./firefox-16.png"
+});
+
+tabs.open({
+  url: "https://mozilla.org/",
+  onOpen: onNewTab
+});
+
+function onNewTab(tab) {
+  // Modify the label only for the new tab
+  button.state(tab, {
+    "label" : "tab-specific label"
+  });
+
+  // access the global label -> "default label"
+  console.log(button.label);
+
+  // access the window's label -> "default label"
+  console.log(button.state("window").label);
+
+  // access the first tab's label -> "default label"
+  console.log(button.state(tabs[0]).label);
+
+  // access the second tab's label -> "tab-specific label"
+  console.log(button.state(tabs[1]).label);
+}
+

Setting a property won't affect a more-specific property setting. For example, if you have a window with two tabs, and you set a tab-specific label, then set the window-specific label, this will not overwrite the tab-specific label:

+
var { ActionButton } = require('sdk/ui/button/action');
+var tabs = require("sdk/tabs");
+
+var button = ActionButton({
+  id: "my-button",
+  label: "default label",
+  icon: "./firefox-16.png"
+});
+
+tabs.open({
+  url: "https://mozilla.org/",
+  onOpen: onNewTab
+});
+
+function onNewTab(tab) {
+  // Modify the label only for the new tab
+  button.state(tab, {
+    "label" : "tab-specific label"
+  });
+
+  // Modify the label for the window
+  button.state("window", {
+    "label" : "window-specific label"
+  });
+
+  // access the global label -> "default label"
+  console.log(button.label);
+
+  // access the window's label -> "window-specific label"
+  console.log(button.state("window").label);
+
+  // access the first tab's label -> "window-specific label"
+  console.log(button.state(tabs[0]).label);
+
+  // access the second tab's label -> "tab-specific label"
+  console.log(button.state(tabs[1]).label);
+}
+

The best way to think of this is as a tree: the global state is the root, followed by the state for each window, followed by the state for each tab in a window. If a property value for a node in the tree has not been set explicitly using state(), then it inherits its value from the next level up. So if you have one window containing two tabs, and have set the button's label only for tab A, then tab B will inherit label's value from the window, and changing the value for the window will implicitly change the value for tab B.

+

To delete a tab- or window-specific state, assign null to the property. After that, the property will inherit its value from the less-specific state as before:

+
var { ActionButton } = require('sdk/ui/button/action');
+var tabs = require("sdk/tabs");
+
+var button = ActionButton({
+  id: "my-button",
+  label: "default label",
+  icon: "./firefox-16.png"
+});
+
+tabs.open({
+  url: "https://mozilla.org/",
+  onOpen: onNewTab
+});
+
+function onNewTab(tab) {
+  // Modify the label only for the new tab
+  button.state(tab, {
+    "label" : "tab-specific label"
+  });
+
+  // Modify the label for the window
+  button.state("window", {
+    "label" : "window-specific label"
+  });
+
+  // access the global label -> "default label"
+  console.log(button.label);
+
+  // access the window's label -> "window-specific label"
+  console.log(button.state("window").label);
+
+  // access the first tab's label -> "window-specific label"
+  console.log(button.state(tabs[0]).label);
+
+  // access the second tab's label -> "tab-specific label"
+  console.log(button.state(tabs[1]).label);
+
+  // Reset the tab-specific state
+  button.state(tab, null);
+
+  // access the second tab's label -> "window-specific label"
+  console.log(button.state(tabs[1]).label);
+}
+

Finally, you can pass the button itself into state(). This is an alternative way to set or get the global state. The reason for using this, rather than setting properties individually, is that you can define an object with the properties to set in one place, then apply it to the global state with a single line:

+
const defaultState = {
+  "label": "default label",
+  "icon": "./default.png",
+}
+
+const differentState = {
+  "label": "different label",
+  "icon": "./different.png",
+}
+
+var { ActionButton } = require("sdk/ui/button/action");
+
+var button = ActionButton({
+    id: "default-label",
+    label: "default label",
+    icon: "./default.png",
+    onClick: function(state) {
+      if (button.label == "default label") {
+        button.state(button, differentState);
+      }
+      else {
+        button.state(button, defaultState);
+      }
+      console.log(button.state(button).label);
+      console.log(button.state(button).icon);
+    }
+  });
+
+
Parameters
+

target : button, tab, window, string

+ +

state : object, null
+ Include this parameter only if you are setting state. It is an object containing all the properties you wish to set. For example:

+
button.state("tab", {
+  "label" : "tab-specific label",
+  "icon": "./tab-specific-icon.ico"
+});
+

To reset state, pass null:

+
button.state("tab", null);
+
Returns
+

state : if you have passed the second state argument to make this function a setter, it returns undefined. Otherwise, it functions as a getter and returns the button's state for the specified object. This logs the state for the button associated with the currently active tab:

+
console.log(button.state("tab"));
+

This object represents a snapshot of the state at the time state() is called. It is not kept up to date with changes made to the button:

+
button.label = "foo";
+var state = button.state(button);
+button.label = "bar";
+console.log(state.label) // foo
+

on()

+

Add a listener to an event emitted by the button. The button only emits one type of event, click:

+
button.on("click", handleClick)
+
+function handleClick(state) {
+  console.log("button '" + state.label + "' was clicked");
+}
+
Parameters
+

event : string
+ The event to listen for. Action buttons only emit one type of event, "click".

+

listener : function
+ Function that will be called on click.

+

once()

+

Assign a listener to the first occurrence only of an event emitted by the button. The button only emits one type of event, click. The listener is automatically removed after the first time the event is emitted.

+
Parameters
+

event : string
+ The event to listen for. Action buttons only emit one type of event, "click".

+

listener : function
+ Function that will be called on click.

+

removeListener()

+

Removes an event listener. For example, this code is equivalent to once():

+
button.on("click", handleClick)
+
+function handleClick(state) {
+  console.log("button '" + state.label + "' was clicked");
+  button.removeListener("click", handleClick);
+} 
+
Parameters
+

event : string
+ The event to listener is listening for. Action buttons only emit one type of event, "click".

+

listener : function
+ The listener to remove.

+

destroy()

+

Destroy the button. After calling this function, the button will no longer appear in the UI, and accessing any of its properties or methods will throw an error.

+

Properties

+

id

+

The button's unique ID. This is read-only.

+

label

+

The button's label.

+

icon

+

The button's icon or icons, as a URL, relative path, or object containing a set of key-value pairs.

+

disabled

+

Boolean property indicating whether or not the button is disabled.

+

badge

+
+

New in Firefox 36.

+
+

Value to attach to the button as a badge. May be a number or a string.

+

badgeColor

+
+

New in Firefox 36.

+
+

Color for the badge, specified as a CSS <color> value.

+

Events

+

click

+

This event is emitted when a user clicks the button or your add-on calls the button's click() method.

+
Arguments
+

state : The button's state. This contains all the button's properties.

diff --git a/files/zh-cn/mozilla/add-ons/sdk/tools/index.html b/files/zh-cn/mozilla/add-ons/sdk/tools/index.html new file mode 100644 index 0000000000..8c67b4644e --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/tools/index.html @@ -0,0 +1,14 @@ +--- +title: Tools +slug: Mozilla/Add-ons/SDK/Tools +tags: + - Add-on SDK + - CFX + - JPM + - NeedsTranslation + - TopicStub +translation_of: Archive/Add-ons/Add-on_SDK/Tools +--- +

Articles listed here provide a reference for the SDK's tools:

+ +

{{ LandingPageListSubpages ("/en-US/Add-ons/SDK/Tools", 7) }}

diff --git a/files/zh-cn/mozilla/add-ons/sdk/tools/jpm/index.html b/files/zh-cn/mozilla/add-ons/sdk/tools/jpm/index.html new file mode 100644 index 0000000000..db429ef078 --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/tools/jpm/index.html @@ -0,0 +1,652 @@ +--- +title: jpm +slug: Mozilla/Add-ons/SDK/Tools/jpm +tags: + - Add-on SDK + - JPM +translation_of: Archive/Add-ons/Add-on_SDK/Tools/jpm +--- +

{{AddonSidebar}}

+ +
+

你能在Firefox 38或更高的版本中使用jpm。

+ +

本文为jpm参考。

+
+ +

cfx 的Node版本允许你测试、运行以及打包扩展。

+ +

你也可以阅读 jpm教程 开始学习。

+ +

jpm 用法:

+ +
jpm [command] [options]
+
+ +

jpm支持以下全局参数:

+ +
-h, --help        - 显示帮助信息并退出
+-V, --version     - 打印出jpm版本号
+--addon-dir       - 源代码目录,默认为当前目录
+
+ +

安装

+ +

jpm发布在node包管理器 npm 上。

+ +

安装npm

+ +

有两种方法来获取npm。

+ + + +

要测试安装是否成功,运行:

+ +
​/usr/bin/env node -v
+ +

如果出现错误提示  /usr/bin/env: node: No such file or directory 并且你的 nodejs 是通过包管理的方式安装的,那你的 nodejs 很有可能安装为其它的名字。为了保证jpm的兼容,PATH之中必须是以node为可执行文件名的。在Debian和Ubuntu系统上,你可以通过安装兼容包nodejs-legacy来解决这个问题:

+ +
sudo apt-get install nodejs-legacy
+ +

在其它的发行版中,你或许必须手动创建一共本地符号链接:​

+ +
sudo ln -s "$(which nodejs)" /usr/local/bin/node
+ +

安装jpm

+ +

在你安装好npm并且将其加入你的PATH中后,你可以像安装其他npm包一样来安装jpm。

+ +

全局安装jpm

+ +
npm install jpm --global
+ +

取决于你的安装,你可能需要管理员权限执行:

+ +
sudo npm install jpm --global
+ +

局部安装jpm

+ +

如果你不想,或者不能够全局安装jpm,你或许可以只为你安装它:

+ +
npm install jpm
+ +

在局部安装的情况下,为了在终端中运行jpm,你必须首先将目录"$HOME/node_modules/.bin/"添加到你的终端PATH中。将下行中的命令添加到$HOME/.profile的末尾来实现永久添加(.profile将在每次运行一个新终端时被执行):

+ +
export PATH="$HOME/node_modules/.bin/:$PATH"
+ +

通过git来安装jpm

+ +

另外,你也可以通过git安装jpm的最新版本

+ +
git clone https://github.com/mozilla-jetpack/jpm.git
+cd jpm
+npm install
+npm link
+
+ +

jpm安装完毕后

+ +

在全部搞定后,在命令行窗口中输入:

+ +
jpm
+ +

屏幕上显示了一系列可用的 jpm 命令。不同于 cfx,当在安装时使用了 --global 参数,你能在任何路径下启动的命令提示符中使用 jpm 命令。

+ +

还有问题?

+ +

如果你看不懂本文,要寻求帮助。SDK 用户和项目团队成员在项目邮件列表中讨论问题和建议。其他人也许会和你有一样的问题,所以试着搜索一下列表。也欢迎你发表一个新问题。你也可以在 Mozilla 的 IRC 网络#jetpack 房间里和其他SDK的用户聊天。

+ +

命令参考

+ +

有六个命令:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
jpm init创建一个基本的 add-on 作为你 add-on 的开端。
jpm run运行一个带有你的 add-on 的 Firefox 实例。
jpm test运行你插件的单元测试
jpm xpi将你的插件打包为 XPI 文件(火狐的插件扩展文件名)
jpm post把你的 add-on 打包成 XPI 文件,然后发送到某一个URL。
jpm watchpost无论是否有文件变更,把你的 add-on 打包成 XPI 文件发送到某一个URL。
jpm sign将你的插件打包为 XPI 文件并且取回一个由Mozilla签名的新XPI文件。
+ +

jpm init

+ +

这个命令从头开始初始化一个新的 add-on。

+ +

新建一个目录,转到该目录下,然后运行 jpm init

+ +
mkdir my-addon
+cd my-addon
+jpm init
+ +

然后你会被要求提供关于你的 add-on 的一些信息:这会用来创建 package.json 文件。

+ + + +

大部分字段都有一个默认值,显示在那些问题的后面的括号里。如果你按了回车,那么你的 add-on 就用那个默认值。

+ +

一旦你提供了一个值或者接受了默认值,你会看到"package.json"的完整内容,并被询问是否接受。

+ +

然后 jpm 创建一个基本的 add-on,作为你开发的起点,文件结构如下:

+ + + +

jpm run

+ +

此命令运行一个新的装有你的 add-on 的 Firefox实例:

+ +
jpm run
+ +

jpm run 有以下选项:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
-b --binary BINARY +

指定二进制文件,使用该版本的火狐。可以用全路径,也可以用相对路径。

+ +
+jpm run -b /path/to/Firefox/Nightly
+ 参看选择浏览器版本
--binary-args CMDARGS +

传递附件参数到 Firefox。

+ +

例如,为了传递 -jsconsole参数给 Firefox 并打开浏览器,试试下面命令:

+ +
+jpm run --binary-args -jsconsole
+ +

要传递多个参数,或者参数中间有空格,就把他们用引号括起来:

+ +
+jpm run --binary-args '-url mzl.la -jsconsole'
+
--debug运行这个 add-on 的Add-on 调试器
-o --overload PATH +

不再使用 Firefox 内建的 SDK 模块,使用指定 PATH 路径下的模块。如果加了 -o 但 PATH 没有指定,jpm 会寻找 JETPACK_ROOT 环境变量用作路径

+ +

参看重载内建模块以获取更多信息

+
-p --profile= PROFILE +

在你每次调用jpm run时,jpm 默认使用一个干净的临时的 Firefox 配置文件(profile)。使用--profile 选项以使得 jpm 使用已有的配置文件来打开 Firefox。

+ +

PROFILE的值可以是一个profile的名字或路径。

+ +

参看 使用 profile 以获取更多信息。

+
-v --verbose(查看)详细操作
--no-copy +
小心使用,因为 jpm run|test 会改变很多配置,不要和你的主配置文件一起使用。
+ +
只有使用 --profile 的时候回生效
+ 禁止配置文件的拷贝,允许重用配置。
 
+ +

jpm test

+ +

这个命令用来运行 add-on 的单元测试。命令:

+ + + +
jpm test
+ +

查看 单元测试教程 assert 模块参考文档获取更多具体内容。

+ +

jpm test 接受一下选项:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
-b --binary BINARY +

指定所用 Firefox 的版本。BINARY 可被指定为绝对路径或者基于当前路径的相对路径。

+ +
+jpm test -b /path/to/Firefox/Nightly
+ +

请查看选择一个浏览器版本

+
--binary-args CMDARGS +

传递附件参数给 Firefox。

+ +

例如,传递 -jsconsole 参数,它会打开浏览器控制台,试试下面的命令:

+ +
+jpm test --binary-args -jsconsole
+ +

传递多个参数,或者参数间有空格,请将它们用引号括起来:

+ +
+jpm test --binary-args '-url mzl.la -jsconsole'
+
--debug运行 add-on debugger 附件到当前 add-on 上。
-f --filter FILE[:TEST] +

只运行名字和 FILE 匹配的测试文件,且名字匹配 TEST(可选)的测试函数。

+ +
+jpm test --filter base64:btoa
+ +

上面的命令值会运行名字中含有"base64"的文件,在这些文件中只会运行名字中含有"btoa"的测试函数。

+
-o --overload PATH +

不使用 Firefox 内建的模块,而使用 PATH 路径下的模块。如果 -o 指定而 PATH 被忽略,jpm会查找 JETPACK_ROOT 环境变量并将其用作路径。

+ +

查看重载内建模块以获取更多信息。

+
-p --profile PROFILE +

你每次运行jpm call时,默认地,jpm使用一个干净的临时 Firefox 配置文件。使用--profile 选项来让 jpm 使用一个现有配置文件来打开火狐。

+ +

PROFILE 的值可以是配置文件的名称或者指向配置文件的路径。

+ +

查看使用配置文件来获取更多信息。

+
--stop-on-error +

即使测试失败,jpm test 默认还会继续运行测试。指定 --stop-on-error 的话,会在第一次失败后停止运行测试

+ +
+jpm test --stop-on-error
+
--tbplTreeherder 格式打印 test 的输出
--times NUMBER +

运行几遍 test:

+ +
+jpm test --times 2
+
-v --verbose详细操作。
--no-copy +

小心使用,因为 jpm run|test 会修改许多首选项,这些首选项在你的主配置文件中从未用过。

+ +

这仅仅在使用 --profile 时生效。

+ 禁用配置未经的拷贝,允许用户重用配置文件。
+ +

jpm xpi

+ +

这个命令将 add-on 打包为一个 XPI 文件,这是 Mozilla 附加组件的安装文件的格式。

+ +
jpm xpi
+ +

它会在当前目录(或者 --addon-dir 指定的目录)查找 package.json 文件,并创建相关的 XPI 文件。它忽略任何 ZIP 或 XPI文件(译者注:OSX 上测试下来不会默认忽略这些文件,若要忽略请编辑.jpmignore文件),以及任何测试文件。它会包含其他所有文件。如果你想排除其他文件,查看 .jpmignore 文件

+ +

一旦你构建了一个 XPI 文件,你可以通过提交到 addons.mozilla.org 来分发你的附加组件。

+ +

jpm xpi 接受下列选项:

+ + + + + + + + +
-v --verbose +

打印详细操作:

+ +
+jpm xpi -v
+
+ +

jpm post

+ +

这个命令把附加组件打包为 XPI 后 post 到某个URL。

+ +
jpm post
+ +

查找当前目录(或 --addon-dir)下的package.json 文件,创建 XPI 文件,post 到 --post-url

+ +

jpm post 接受以下选项:

+ + + + + + + + + + + + +
--post-url URL +

创建 XPI 文件后,将扩展 post 到这个URL。

+ +
+jpm post --post-url http://localhost:8888/
+ +

查看使用 Post 和 Watchpost 获取更多信息。

+
-v --verbose +

打印详细操作:

+ +
+jpm post --post-url http://localhost:8888/ -v
+
+ +

jpm watchpost

+ +

这个命令在打包附件组件为 XPI 文件后,无论文件是否改变都将其 post 到某个URL。

+ +
jpm watchpost
+ +

无论文件是否改变,在当前目录(或 --addon-dir)下创建一个 XPI并将其post到 --post-url

+ +

jpm watchpost 接受以下选项:

+ + + + + + + + + + + + +
--post-url URL +

创建 XPI 文件后,将扩展 post 到这个URL。

+ +
+jpm watchpost --post-url http://localhost:8888/
+ +

查看 Using Post 和 Watchpost 获取更多信息。

+
-v --verbose +

打印详细信息:

+ +
+jpm watchpost --post-url http://localhost:8888/ -v
+
+ +

jpm sign

+ +

此特性仅从 jpm 1.0.4 起被支持。

+ +

此命令为你的附加组件重新生成一个新的被Mozilla签名的 XPI 文件。这就允许你自己托管你的 add-on,这样用户可以在安装时避免 signed add-ons are required 错误。在用这个命令之前,你需要在 addons.mozilla.org 创建 API 证书

+ +

你可以为你已经生成的 XPI 文件签名,就像如下,通过传递 XPI 文件给 --xpi参数:

+ +
jpm sign --api-key ${AMO_API_KEY} --api-secret ${AMO_API_SECRET} --xpi <xpi file>
+ +

或者,你可以省略 --xpi 参数,这样 jpm sign 会从当前目录(或者 --addon-dir)生成一个XPI文件。

+ +
jpm sign --api-key ${AMO_API_KEY} --api-secret ${AMO_API_SECRET}
+ +

上面提交了一个 XPIaddons.mozilla.org 签名g API,如果通过验证,然后下载一个签名后的 XPI 文件到工作目录。

+ +

下面是一些运行 sign 命令的结果:

+ + + +

在底层,jpm signaddons.mozilla.org里创建了一个不会被列出的附加组件,这就意味着你必须自己把XPI文件分发给你的用户来安装。如果你需要创建一个在列表中的附加组件,只要直接将它提交到 addons.mozilla.org,在那里它自动会被签名。如果你在安装一个已签名的附加组件时遇到困难,参看调试一节。

+ +

jpm sign 接收以下参数:

+ + + + + + + + + + + + + + + + + + + + +
--api-key=API_KEY +

addons.mozilla.org key管理页面生成的API访问key(字符串)。

+
--api-secret=API_SECRET +

addons.mozilla.org key 管理页面生成的API访问密钥(字符串)。这个值应该被小心保密并且永远不要记录到版本控制中。如果你的密钥被破解,另一个开发者就能上传附加组件到你的账号上。你应该立即撤销泄露的API证书并且重新生成一份。

+
--api-url-prefix=http://.../api +

可选的API URL前缀,如果你想使用一个试用签名的API。

+ +

例如,你可以通过https://addons-dev.allizom.org/api/v3的认证来使用addons.mozilla.org的开发者版本。

+
--xpi=/path/to/file.xpi +

需要签名的XPI文件。如果没指定文件,一个新的XPI会在当前目录(或者--addon-dir)中生成。

+
+ +

一些技巧

+ +

选择浏览器版本

+ +

默认地,jpm runjpm test 运行 release 版本的 FireFox。你可以有一两种办法来告诉 jpm 使用一个不同的版本:

+ + + +

使用 .jpmignore 来忽略文件

+ +

使用 .jpmignore 就像使用 git.gitignore,Mercurial 的 .hgignore,或者 npm 的 .npmignore。通过使用这个文件,你可以在使用 jpm xpi 编译 .xpi 文件时,让 jpm 知道你想要忽略哪些文件。

+ +

这是个例子:

+ +
# Ignore .DS_Store files created by mac
+.DS_Store
+
+# Ignore any zip or xpi files
+*.zip
+*.xpi
+ +

有着以上内容的 .jpmignore 文件会忽略所有的 zip 文件和 .DS_Store 文件,这些文件不会包括在 jpm xpi 所生成的 xpi 文件中。

+ +

使用配置

+ +

默认地,jpm run 每次都会使用一个新的配置。这就意味着前一次运行 jpm 时的任何配置,默认都不会在下一次中使用。

+ +

这其中包括,比如你安装的任何附加组件,或者你的历史记录,或者任何使用 simple-storage API 存储的数据。

+ +

为了让 jpm 使用一个特定的配置,传递 --profile 选项,设定你想使用的配置文件的名字,或者是配置文件的路径。

+ +
jpm run --profile boogaloo
+ +
jpm run --profile path/to/boogaloo
+ +

如果你提供了 --profile 但是它的参数不是一个已有配置文件的名字或路径,jpm 会打开 配置文件管理器 让你选择一个已有的配置文件或者创建一个新的配置文件:

+ +
jpm run --profile i-dont-exist
+ +

开发时不用重启浏览器

+ +

因为每次调用 jpm run 时都会重启浏览,如果你十分频繁地修改附加组件,这就可能有点笨重了。一个替代的开发模型是使用 扩展自动安装器附加组件:它会在特定端口监听新 XPI 文件并且自动安装它们。这样,你就能测试新的改动,而不用重启浏览器:

+ + + +

你甚至可以用一个简单的脚本来自动化这一工作流程。例如:

+ +
jpm watchpost --post-url http://localhost:8888/
+ +

注意,比起使用 jpm run 运行附加组件时的日志级别,你在使用这个方法时控制台定义的日志级别是不一样的。这意味着,如果你想看到 console.log() 消息的输出,你必须微调一下配置。查看 logging levels 文档以获取这方面的详细信息。

+ +

重载内建模块

+ +

你用来实现你的附加组件的SDK模快是 Firefox 内建的。当你使用 jpm runjpm xpi 来运行或者打包附加组件时,附加组件使用其所在的 Firefox 版本中的模块版本。

+ +

作为附件组件开发者,这一般就是你想要的。但是如果你在开发 SDK 模块本身,这就不合适了。这时你需要:

+ + + +
jpm run -o
+ +

这会通知 jpm 去使用 SDK 模块的本地拷贝,而不是 Firefox 内部的模块。如果你不想设置 JETPACK_ROOT 环境变量,你可以使用 -o 传递SDK模块本地拷贝的位置:

+ +
jpm run -o "/path/to/addon-sdk/"
+ +

路径必须是一个绝对路径并且指向 SDK 的根目录(不是 addon-sdk/sdk 或 addon-sdk/sdk/lib)。

+ +

支持自托管附加组件的更新

+ +

此特性仅被 jpm 1.0.3 及之后版本支持,

+ +

当你给你的附加组件来添加特性或者修复 bug 时,你想让之前安装好的附加组件将自己从老版本更新到新版本。

+ +

如果你在 addons.mozilla.org 上列出你的附加组件,那么你要做的只是提交一个新的版本;附加组件默认会检查它们在 addons.mozilla.org 上的新版本。好了,你不用继续往下看这一节了。

+ +

如果你没有在 addons.mozilla.org 上列出你的附加组件,你需要生成一个 Mozilla 签名的 XPI 文件并告诉 Firefox 哪里可以找到这个附加组件的新版本。方法就是:

+ + + +

为了达到这个目的,package.json中需要包含两个额外的key :

+ + + +

+ +

如果你设置了 updateURLupdateLink(如果 updateKey 不是 HTTPS 的,那也要包括 updateKey),那么 jpm xpi 会:

+ + + +

然后你将更新清单托管到 updateURL,并且将新版 XPI 托管到 updateLink

+ +

获取更多这方面的详细信息,参看自动检查附件组件更新

+ +
 
+ +
 
diff --git a/files/zh-cn/mozilla/add-ons/sdk/tools/package_json/index.html b/files/zh-cn/mozilla/add-ons/sdk/tools/package_json/index.html new file mode 100644 index 0000000000..cd08ff2b64 --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/tools/package_json/index.html @@ -0,0 +1,312 @@ +--- +title: package.json +slug: Mozilla/Add-ons/SDK/Tools/package_json +translation_of: Archive/Add-ons/Add-on_SDK/Tools/package_json +--- +

{{AddonSidebar}}

+ +

The package.json file contains manifest data for your add-on, providing not only descriptive information about the add-on for presentation in the Add-ons Manager, but other metadata required of add-ons.

+ +

一些条目, 比如icon, namedescription, 有类似install manifest的格式, 并且package.json会写入install manifest, 当使用jpm xpi的时候.

+ +

Others, such as lib, permissions, and preferences, represent instructions to the jpm tool itself to generate and include particular code and data structures in your add-on.

+ +

其他如lib, permissionspreferences表示使用jpm工具的步骤, 生成你扩展包含的特殊代码和数据结构.

+ +

创建manifest

+ +

package.json起初是通过运行jpm init生成的,通常在扩展的根目录.比如下面(假设扩展目录是"my-addon")

+ +
{
+  "name": "my-addon",
+  "title": "my-addon",
+  "id": "jid1-1FERGV45e4f4f",
+  "description": "a basic add-on",
+  "author": "",
+  "license": "MPL-2.0",
+  "version": "0.1"
+}
+ +

如果你使用新的jpm tool话,可以方便的从package.json获取manifest数据, 通过require其他模块的方式:

+ +
var title = require("./package.json").title;
+ +

Key reference

+ +

package.json may contain the following keys:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
author +

The name of the package's original author; this could be the name of a person or a company. Defaults to an empty string. It may include a optional URL in parentheses and an email address in angle brackets.

+ +

This value will be used as the add-on's em:creator element in the install.rdf file generated by cfx.

+ +
Note: jpm supports NodeJS people fields.
+
contributors +

An array of additional author strings, identifying other contributors to the add-on.

+ +

These values will be used as the add-on's em:contributor elements in its install.rdf.

+ +
Note: This is deprecated along with cfx; it's not available when using jpm.
+
dependencies +

A string or an array of strings specifying the names of packages that this add-on requires in order to function properly.

+
description +

The add-on's description; this is a human-readable message describing what the add-on does. This defaults to the text "a basic add-on".

+ +

This value will be used as the add-on's em:description element in its install.rdf.

+
engines +

Object with supported applications (key) and required version numbers (value).

+ +
    +
  • firefox: Firefox Desktop
  • +
  • fennec: Firefox for Android
  • +
+ +

Example:

+ +
+  "engines": {
+    "firefox": ">=38.0a1",
+    "fennec": ">=38.0a1"
+  }
+
fullName {{deprecated_inline}} +
Note: This is deprecated along with cfx; it's not available when using jpm.
+ +

The full name of the package. It can contain spaces.

+ +

If this key is present its value will be used as the add-on's em:name element in its install.rdf.

+
harnessClassID {{deprecated_inline}} +
Note: This is deprecated along with cfx; it's not available when using jpm.
+ +

String in the GUID format.

+ +

This is used as a classID of the "harness service" XPCOM component. Defaults to a random GUID generated by cfx.

+
homepage +

The URL of the add-on's website.

+ +

This value will be used as the add-on's em:homepageURL element in its install.rdf.

+
icon +

The path to an image file containing the icon for the add-on. Defaults to icon.png. If no icon is specified, the standard add-on icon will be used by default.

+ +
+

When using jpm, relative path to the data directory (to make it re-usable for add-on HTML content) does not currently work. Instead you have to use following syntax:

+ +

resource://ID/data/icon-name.png

+ +

ID is the value from the id field. If it does not begin with the @-character, then @ has to be escaped as -at- and . as -dot-. If the ID is a GUID, the curly braces used in the id field are not included.

+
+ +

This value will be used as the add-on's em:iconURL element in its install.rdf.

+ +

The icon may be up to 48x48 pixels in size (although a bigger icon is tolerated here too)

+
icon64 {{deprecated_inline}} +
+

Note: This is deprecated along with cfx; it's not available when using jpm.

+
+ +

The path to an image containing the large icon for the add-on. Defaults to icon64.png. If you don't provide an icon here, the same icon as specified by icon will be used.

+ +

This value will be used as the add-on's em:icon64URL element in its install.rdf.

+ +

The icon may be up to 64x64 pixels in size.

+
id +

A globally unique identifier for the add-on.

+ +

This value will be used as the add-on's em:id element in its install.rdf.

+ +

See the Program ID documentation.

+
lib +

String representing the top-level module directory provided in this add-on. Defaults to "lib".

+ +
+

Note: This is deprecated along with cfx and is not available when using jpm.

+
+
license +

The name of the license under which the add-on is distributed, with an optional URL in parentheses. Defaults to "MPL-2.0".

+ +
+

Note: It is recommend that you use an SPDX license ID.

+
+
main +

A string representing the name of a program module that is located in one of the top-level module directories specified by lib. Defaults to "main".

+
name +

The add-on's name. This name cannot contain spaces or periods, and defaults to the name of the parent directory.

+ +

When the add-on is built as an XPI, if the fullName and title keys are not present, name is used as the add-on's em:name element in its install.rdf.

+
packages +
+

Note: This is deprecated along with cfx and is not available when using jpm.

+
+ +

A string pointing to a directory containing additional packages. Defaults to "packages".

+
permissions +

A set of permissions that the add-on needs.

+ +

private-browsing: a boolean indicating whether or not the add-on supports private browsing. If this value is not true or is omitted, then the add-on will not see any private windows or objects, such as tabs, that are associated with private windows. See the documentation for the private-browsing module.

+ +

cross-domain-content: a list of domains for which content scripts are given cross-domain privileges to access content in iframes or to make XMLHTTPRequests. See the documentation for enabling cross-domain content scripts.

+ +

multiprocess: a Boolean value declaring whether this add-on is, or is not, compatible with multiprocess Firefox.

+ +
+

Note the multiprocess permission is not supported by cfx.

+
+
preferences +

An array of JSON objects that use the following keys: name,type, value, title, and description. These JSON objects will be used to create a preferences interface for the add-on in the Add-ons Manager.

+ +

See the documentation for the simple-prefs module.

+
preferences-branchUse this to specify an alternative branch for your add-on's simple-prefs. See "Simple-prefs in the preferences system" for more details.
title +

The human-readable title of the package; this can contain spaces.

+ +

If this key is present its value will be used as the add-on's em:name element in its install.rdf.

+
translators +

An array of strings listing the people who contributed to the localization of this add-on.

+ +

These values will be used as the add-on's em:translator elements in its install.rdf.

+ +
Note: jpm supports NodeJS people fields.
+
unpack +

Same as the unpack in an install.rdf file.

+ +

Useful when the extension contains binaries.

+
updateKey +

Same as the updateKey in an install.rdf file.

+ +

See Supporting updates for self-hosted add-ons.

+ +
+

Note: This key is only available with jpm.

+
+
+

Same as the updateLink for an update.rdf file. Previously was --update-link in cfx.

+ +

See Supporting updates for self-hosted add-ons.

+ +
+

Note: This key is only available with jpm.

+
+
updateURL +

Same as the updateURL for an install.rdf file.

+ +

See Supporting updates for self-hosted add-ons.

+ +
+

Note: This key is only available with jpm.

+
+
version +

String representing the version of the add-on. Defaults to "0.0.1".

+ +

This value is used as the add-on's em:version element in its install.rdf.

+ +
+

Note: For jpm the version must be a valid semver.

+
+
+ +

 

diff --git a/files/zh-cn/mozilla/add-ons/sdk/tutorials/add_a_menu_item_to_firefox/index.html b/files/zh-cn/mozilla/add-ons/sdk/tutorials/add_a_menu_item_to_firefox/index.html new file mode 100644 index 0000000000..29348bbabb --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/tutorials/add_a_menu_item_to_firefox/index.html @@ -0,0 +1,92 @@ +--- +title: Add a Menu Item to Firefox +slug: Mozilla/Add-ons/SDK/Tutorials/Add_a_Menu_Item_to_Firefox +translation_of: Archive/Add-ons/Add-on_SDK/Tutorials/Add_a_Menu_Item_to_Firefox +--- +
+

学习本模块你需要 安装SDK 学习基础工具cfx.

+
+
+

如果你使用 jpm 替代cfx, 使用第三方模块的方式是不同的, 你应该阅读 jpm的相应版本替代本教程.

+

给予Python可能未来废弃,使用JPM基于nodejs(译者备注).

+
+

SDK还不能为火狐浏览器提供一个API添加新的菜单项。但它是可扩展的设计,所以任何人都可以建立和发布模块,使用插件开发者。幸运的是埃里克沃尔德写的MenuItems模块,能够使我们添加菜单项

+

本教程有双重的责任。它描述的一般方法,使用一个外部的在你的插件的第三方模块描述了如何使用特别的MenuItems模块添加一个菜单项

+

首先,创建一个新的扩展程序。创建一个目录名称为clickme(名称随意)找到它并运行cfx init初始化

+
mkdir clickme
+cd clickme
+cfx init
+
+

通常将创建目录结构:

+ +
+  
+

安装 menuitems

+

在 "clickme"下创建一个名称为"packages"的目录. 从 https://github.com/mykmelez/menuitems-jplib  下载 menuitems 并展开到刚才创建的"packages" 目录下:

+
mkdir packages
+cd packages
+tar -xf ../erikvold-menuitems-jplib-d80630c.zip
+
+

模块依赖

+

如果第三方模块取决于SDK模块可以马上使用它们但如果他们依赖于其他第三方模块你需要安装依赖以及

+

在软件包的主目录,你会发现一个叫做 "package.json"的文件.  打开它,看看一个名称"dependencies"的入口.  menuitems 包的入口:

+
"dependencies": ["vold-utils"]
+
+

这告诉我们需要安装 vold-utils 包, 我们可以从https://github.com/mykmelez/vold-utils-jplib 下载,并添加到 packages 目录下的旁边的menuitems.

+

使用menuitems

+

 Menuitems模块文档 告诉使用MenuItem()创建一个新的菜单项. MenuItem()可接受的附加选项, 我们将使用最迷你的配置:

+ +
+
+
var menuitem = require("menuitems").Menuitem({
+  id: "clickme",
+  menuid: "menu_ToolsPopup",
+  label: "Click Me!",
+  onCommand: function() {
+    console.log("clicked");
+  },
+  insertbefore: "menu_pageInfo"
+});
+
+  
+
+
+

接下来, 我们将声明menuitems的依赖包. 在你的add-on's package.json文件添加一行:

+
"dependencies": "menuitems"
+
+

Note that due to bug 663480, if you add a dependencies line to package.json, and you use any modules from the SDK, then you must also declare your dependency on that built-in package, like this:

+
"dependencies": ["menuitems", "addon-sdk"]
+
+

现在我们要做的。运行插件你会看到新的项目出现在工具菜单:选择它,你会看到info: clicked 出现在控制台

+

Caveats

+

第三方模块使用不直接支持的SDK功能的好方法,但由于第三方模块通常使用低级别的API它们可以通过Firefox的新版本

+

 

diff --git a/files/zh-cn/mozilla/add-ons/sdk/tutorials/adding_a_button_to_the_toolbar/index.html b/files/zh-cn/mozilla/add-ons/sdk/tutorials/adding_a_button_to_the_toolbar/index.html new file mode 100644 index 0000000000..8e4438f818 --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/tutorials/adding_a_button_to_the_toolbar/index.html @@ -0,0 +1,83 @@ +--- +title: 在工具栏上添加按钮 +slug: Mozilla/Add-ons/SDK/Tutorials/Adding_a_Button_to_the_Toolbar +translation_of: Archive/Add-ons/Add-on_SDK/Tutorials/Adding_a_Button_to_the_Toolbar +--- +
+

动手之前,必须先安装SDK并学习cfx基础.

+ +

这篇教程使用动作按钮API,需要Firefox 29或更新版本。

+
+ +

如果要给工具栏添加一个按钮,需要使用动作按钮切换按钮模块。

+ +

新建一个目录,打开它,执行cfx init

+ +

然后,把这三个图标保存到data文件夹:

+ + + + + + + + + + + + + + + + +
icon-16.png
icon-32.png
icon-64.png
+ +

随后打开lib目录下的main.js文件,添加如下代码:

+ +
var buttons = require('sdk/ui/button/action');
+var tabs = require("sdk/tabs");
+
+var button = buttons.ActionButton({
+  id: "mozilla-link",
+  label: "Visit Mozilla",
+  icon: {
+    "16": "./icon-16.png",
+    "32": "./icon-32.png",
+    "64": "./icon-64.png"
+  },
+  onClick: handleClick
+});
+
+function handleClick(state) {
+  tabs.open("https://www.mozilla.org/");
+}
+ +

现在输入cfx run来执行附加组件。按钮添加到了浏览器顶部的工具栏上:

+ +

你无法设置按钮的初始位置,但是用回可以使用浏览器的自定义功能移动它。其中的id属性是必需的,用来记录按钮的位置,因此,你最好不要在后续版本中修改它。

+ +

单击按钮,https://www.mozilla.org/将会在新的标签页中加载。

+ +

设定图标

+ +

icon属性可以设定单个或是一组不同大小图标。如果你用的是一组不同大小的图标,浏览器将会自动选择最适合当前屏幕分辨率的,然后显示在浏览器界面的按钮上。更多内容,参考设定多个图标

+ +

图标文件必须打包在附加组件内:它不可以指向一个远程文件。

+ +

你只要正确地设置了按钮的icon属性,它就会立刻生效。你可以改变全局的,在特定窗口的或者是特定标签页中显示的图标或者是其他状态属性。更多内容,参考更新状态

+ +

附加一个面板

+ +

如果你想把一个面板附加到按钮上,使用切换按钮API。这就和动作按钮API类似,只不过多了一个boolean值属性,表示按钮是否被checked。附加面板,要把按钮传递给面板的show()方法。更多细节,参考切换按钮文档

+ +

显示更多样的内容

+ +

添加按钮做不到的复杂界面功能,需要使用工具栏API。 使用工具栏API你可以得到一个用户界面内容的水平条。可以添加按钮或者是可以包含HTML,CSS,javaScript的frame到工具栏上。

+ +

进一步学习

+ + diff --git a/files/zh-cn/mozilla/add-ons/sdk/tutorials/annotator/implementing_the_widget/index.html b/files/zh-cn/mozilla/add-ons/sdk/tutorials/annotator/implementing_the_widget/index.html new file mode 100644 index 0000000000..04e4a75d23 --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/tutorials/annotator/implementing_the_widget/index.html @@ -0,0 +1,92 @@ +--- +title: 实现控件 +slug: Mozilla/Add-ons/SDK/Tutorials/Annotator/Implementing_the_widget +tags: + - 附加组件 +translation_of: Archive/Add-ons/Add-on_SDK/Tutorials/Annotator/Implementing_the_widget +--- +

我们想要控件做两件事:

+ + + +

因为控件无法区分鼠标左右键的点击,我们应当使用内容脚本来捕获单击事件并发送相应的消息到附加组件。

+ +

控件将有两个图标:一个在注释器启用时显示,一个在禁用时显示。

+ +

因此,我们需要创建三个文件:控件的内容脚本和两个图标。

+ +

在data子目录创建一个widget子目录,我们将把控件的文件保存在这里(注意,这个不是强制性的:你可以仅仅把它们放在data里,但分类放置会更加整洁)。

+ +

控件的内容脚本

+ +

控件的内容脚本仅仅监听鼠标的左右单击并发送相应的消息到附加组件代码:

+ +
this.addEventListener('click', function(event) {
+  if(event.button == 0 && event.shiftKey == false)
+    self.port.emit('left-click');
+
+  if(event.button == 2 || (event.button == 0 && event.shiftKey == true))
+    self.port.emit('right-click');
+    event.preventDefault();
+}, true);
+ +

把它保存在data/widget并命名为widget.js。

+ +

控件图标

+ +

你可以从这里复制图标:

+ +

+ +

或者自己动手做你觉得有创造性的图标。把它们保存在data/widget目录。

+ +

main.js

+ +

现在在lib目录创建main.js并添加下列内容:

+ +
var widgets = require('sdk/widget');
+var data = require('sdk/self').data;
+
+var annotatorIsOn = false;
+
+function toggleActivation() {
+  annotatorIsOn = !annotatorIsOn;
+  return annotatorIsOn;
+}
+
+exports.main = function() {
+
+  var widget = widgets.Widget({
+    id: 'toggle-switch',
+    label: 'Annotator',
+    contentURL: data.url('widget/pencil-off.png'),
+    contentScriptWhen: 'ready',
+    contentScriptFile: data.url('widget/widget.js')
+  });
+
+  widget.port.on('left-click', function() {
+    console.log('activate/deactivate');
+    widget.contentURL = toggleActivation() ?
+              data.url('widget/pencil-on.png') :
+              data.url('widget/pencil-off.png');
+  });
+
+  widget.port.on('right-click', function() {
+      console.log('show annotation list');
+  });
+}
+ +

注释器默认禁用。它创建控件并通过切换注释器的活动状态来回应来自控件内容脚本的消息。注意,由于bug 626326,附加组件状态栏的环境菜单会显示,尽管在控件的内容脚本中调用了event.preventDefault()。由于我们没有任何代码来显示注释,所以我们们仅仅记录右击事件到控制台。

+ +

现在在注释器目录输入cfx run,你应该看见在附加组件状态栏的控件:

+ +

+ +

左击和右击应当产生合适的调试输出,同时左击应当改变控件的图标为启用状态。

+ +

 

+ +

下一步,我们将添加代码来创建注释器

diff --git a/files/zh-cn/mozilla/add-ons/sdk/tutorials/annotator/index.html b/files/zh-cn/mozilla/add-ons/sdk/tutorials/annotator/index.html new file mode 100644 index 0000000000..4f76d67802 --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/tutorials/annotator/index.html @@ -0,0 +1,36 @@ +--- +title: 注释器(Annotator) +slug: Mozilla/Add-ons/SDK/Tutorials/Annotator +tags: + - 附加组件 +translation_of: Archive/Add-ons/Add-on_SDK/Tutorials/Annotator +--- +
+

请注意教程中Widget API适合的浏览器版本。(译者注)

+
+ +

在这个教程中,我们将构建一个使用许多SDK高级API的附加组件.

+ +

这个附加组件是一个注释器:它可以让用户选择网页的元素并输入有关的笔记(注释).注释器储存笔记.无论用户何时载入包含被注释元素的页面,这些元素都将会高亮显示,并且用户若把鼠标移动到被注释元素的上面,将会显示它的注释.

+ +

接着我们将给出这个注释器设计的快速简介,然后一步步的讨论如何实现.

+ +

如果你想参阅完整的附加组件,你可以在SDK例子目录找到它.

+ + diff --git a/files/zh-cn/mozilla/add-ons/sdk/tutorials/annotator/overview/index.html b/files/zh-cn/mozilla/add-ons/sdk/tutorials/annotator/overview/index.html new file mode 100644 index 0000000000..da94f8142a --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/tutorials/annotator/overview/index.html @@ -0,0 +1,54 @@ +--- +title: 概述 +slug: Mozilla/Add-ons/SDK/Tutorials/Annotator/Overview +tags: + - addon sdk example + - annotator example + - 附加组件 +translation_of: Archive/Add-ons/Add-on_SDK/Tutorials/Annotator/Overview +--- +

注释器使用内容脚本(content scripts)来构建用户界面,得到用户输入,并检查用户载入页面的DOM。

+ +

同时,主要模块包括程序逻辑和调节不同SDK对象的交互。

+ +

我们可以描述在主要模块和不同内容脚本的交互,像这样:

+ +

+ +

用户界面

+ +

注释器的主要用户界面由一个控件和三个面板组成。

+ + + +

除此之外,我们使用 notifications 模块来告知用户附加组件的储存限额已满。

+ +

用DOM工作

+ +

我们将使用 page-mods 来和用户打开的网页的DOM进行交互。

+ + + +

处理数据

+ +

我们将使用simple-storage模块来储存数据。

+ +

由于我们将记录潜在的敏感信息,我们想阻止用户在隐私浏览模式下创建注释,最简单的方式是删除附加组件中“package.json“文件的”private-browsing"键。这样,附加组件就不能看见任何隐私浏览窗口,同时注释器的控件也将不再出现。

+ +

开始行动

+ +

现在让我们来创建叫“注释器”的目录。Navigate to it and type cfx init.

+ +

下一步,我们将实现控件

diff --git a/files/zh-cn/mozilla/add-ons/sdk/tutorials/display_a_popup/index.html b/files/zh-cn/mozilla/add-ons/sdk/tutorials/display_a_popup/index.html new file mode 100644 index 0000000000..9bec4966d4 --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/tutorials/display_a_popup/index.html @@ -0,0 +1,135 @@ +--- +title: 显示弹出对话框 +slug: Mozilla/Add-ons/SDK/Tutorials/Display_a_Popup +translation_of: Archive/Add-ons/Add-on_SDK/Tutorials/Display_a_Popup +--- +
+

动手之前,必须先安装SDK并学习cfx基础知识.

+

T这篇教程使用动作按钮API需要Firefox 29或更新版本。

+
+

可以使用面板(panel)模块来显示弹出对话框。面板的内容通过HTML编写。你可以在面板上运行content script:尽管在面板里的脚本无法直接访问插件代码,但是你可以在面板脚本和插件代码间交换信息。

+

这里,我们做了一个会在单击时显示面板的动作按钮。面板上有一个<textarea>元素:用户按下return键时,<textarea>的内容会被发送给插件代码主程序。插件代码主程序会在控制台输出日志。.

+

The add-o插件由六个文件组成n consists of six files:

+ +

"main.js"像这样:

+
var data = require("sdk/self").data;
+// 构造面板,从"data"目录的"text-entry.html"加载
+// 内容,然后加载"get-text.js"脚本。
+var text_entry = require("sdk/panel").Panel({
+  contentURL: data.url("text-entry.html"),
+  contentScriptFile: data.url("get-text.js")
+});
+
+// 创建按钮
+require("sdk/ui/button/action").ActionButton({
+  id: "show-panel",
+  label: "Show Panel",
+  icon: {
+    "16": "./icon-16.png",
+    "32": "./icon-32.png",
+    "64": "./icon-64.png"
+  },
+  onClick: handleClick
+});
+
+// 在用户点击按钮时显示面板。
+function handleClick(state) {
+  text_entry.show();
+}
+
+// When the panel is displayed it generated an event called
+// "show": we will listen for that event and when it happens,
+// send our own "show" event to the panel's script, so the
+// script can prepare the panel for display.
+text_entry.on("show", function() {
+  text_entry.port.emit("show");
+});
+
+// 监听来自content脚本的text-entered消息。消息主体L是用户输入的文本。
+// 此实现,我们只在控制台显示日志。
+text_entry.port.on("text-entered", function (text) {
+  console.log(text);
+  text_entry.hide();
+});
+

content脚本"get-text.js"像这样:

+
+
// 用户按下回车,发送text-entered消息给main.js。
+// 消息主体是编辑框的内容。
+var textArea = document.getElementById("edit-box");
+textArea.addEventListener('keyup', function onkeyup(event) {
+  if (event.keyCode == 13) {
+    // Remove the newline.
+    text = textArea.value.replace(/(\r\n|\n|\r)/gm,"");
+    self.port.emit("text-entered", text);
+    textArea.value = '';
+  }
+}, false);
+// 监听由插件主程序发送的show事件。表示面板将要显示。
+//
+// 焦点放在textarea上,这样用户可以直接开始输入。
+self.port.on("show", function onShow() {
+  textArea.focus();
+});
+
+  
+
+

最后,"text-entry.html"文件定义了<textarea>元素:

+
+
+
<html>
+<head>
+    <style type="text/css" media="all">
+      textarea {
+        margin: 10px;
+      }
+      body {
+        background-color: gray;
+      }
+    </style>
+  </head>
+<body>
+    <textarea rows="13" cols="33" id="edit-box"></textarea>
+  </body>
+</html>
+
+  
+
+
+

最后,把这三个图标文件保存在"data"目录:

+ + + + + + + + + + + + + + + +
icon-16.png
icon-32.png
icon-64.png
+

试用以下:保存在lib目录,其他五个文件存放在插件的data目录:

+
my-addon/
+         data/
+              get-text.js
+              icon-16.png
+              icon-32.png
+              icon-64.png
+              text-entry.html
+         lib/
+             main.js
+
+

运行插件,点击按钮,你就会看见一个面板。输入几行文本,然后按下回车,你就会看见控制台里的输出。

+

自Firefox 30起,如果你使用切换按钮,就可以给它附加一个面板

+

进一步学习

+

学习panel模块的更多内容,见panel API参考

+

学习关于按钮的更多内容,见动作按钮切换按钮API参考。

diff --git a/files/zh-cn/mozilla/add-ons/sdk/tutorials/getting_started/index.html b/files/zh-cn/mozilla/add-ons/sdk/tutorials/getting_started/index.html new file mode 100644 index 0000000000..225739de76 --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/tutorials/getting_started/index.html @@ -0,0 +1,172 @@ +--- +title: 快速入门 +slug: Mozilla/Add-ons/SDK/Tutorials/Getting_started +tags: + - add-on sdk开发 + - cfx工具基础使用教程 + - 火狐扩展程序 +translation_of: Mozilla/Add-ons/SDK/Tutorials/Getting_Started_%28jpm%29 +--- +

{{AddonSidebar}}

+ +
+

Add-on SDK 里包含了一个命令行工具,你可以用此工具来初始化、运行、测试以及打包 add on。 这个工具称为 jpm,基于 Node.js 。它替代了以前的cfx工具。

+ +

你可以在 Firefox 38或之后的版本中使用。

+ +

本文阐述如何使用 jpm 开发。

+
+ +

本教程会过一遍使用 SDK 创建一个简单 add-on 的过程。

+ +

准备

+ +

要使用 SDK 创建 add-on,你需要:

+ + + +

初始化空的 add-on

+ +

在命令行提示符下,创建一个新目录。进入新文件夹,键入 jpm init,然后敲回车

+ +
mkdir my-addon
+cd my-addon
+jpm init
+
+ +

接着你会被要求提供一些关于你的 add-on 的信息:这会用来创建 package.json 文件。如果光按回车的话,就表示接受属性的默认值。关于 jpm init 的更多信息,参看 jpm command reference.

+ +

一旦你设置了这些属性的值或者接受默认值,你会看到 "package.json" 的完整内容并被询问是否接受这些设置。

+ +

实现 add-on

+ +

现在你可以写 add-on 的代码了。除非你修改了"entry point"的值(package.json 中的 "main"),一般情况将从你的 add-on 的根目录下的"index.js"文件开始。这个文件就是在之前步骤中创建的。打开这个文件并且添加以下代码:

+ +
var buttons = require('sdk/ui/button/action');
+var tabs = require("sdk/tabs");
+
+var button = buttons.ActionButton({
+  id: "mozilla-link",
+  label: "Visit Mozilla",
+  icon: {
+    "16": "./icon-16.png",
+    "32": "./icon-32.png",
+    "64": "./icon-64.png"
+  },
+  onClick: handleClick
+});
+
+function handleClick(state) {
+  tabs.open("http://www.mozilla.org/");
+}
+
+ +
+

注意在jpm中"entry point"的默认值为"index.js",意思是你的主文件是"index.js",且在你的 add-on的根目录下。

+ +

cfx中,入口默认是"main.js",并且在"lib"目录下

+
+ +

保存文件

+ +

接下来,在根目录下创建目录"data"

+ +
mkdir data
+
+ +

并保存这三个图标文件到"data"目录:

+ + + + + + + + + + + + + + + + +
icon-16.png
icon-32.png
icon-64.png
+ +

返回命令行,键入:

+ +
jpm run
+ +

这个 jpm 命令会运行一个带有你的 add-on 的 Firefox 的新实例。

+ +

如果找不到 Firefox,你可能要为它提供一个路径(如在Ubuntu中):

+ + +
jpm run -b /usr/bin/firefox
+ +

Firefox 启动以后,在浏览器右上角你能看到一个 Firefox logo 的图标。点击该图标,就会打开一个新标签页 http://www.mozilla.org/

+ +

这就是这个 add-on 的全部功能了。它用到了 SDK 的两个模块:action button 模块,使你能加个按钮到浏览器上,以及 tabs 模块,使你能完成对标签页的基本操作。本例中,我们以及创建了一个图标是Firefox图标的按钮,并添加一个点击处理程序,可以在新标签页中打开 Mozilla 页面。

+ +

试着编辑这个文件。例如,我们可以改变加载的页面:

+ +
var buttons = require('sdk/ui/button/action');
+var tabs = require("sdk/tabs");
+
+var button = buttons.ActionButton({
+  id: "mozilla-link",
+  label: "Visit Mozilla",
+  icon: {
+    "16": "./icon-16.png",
+    "32": "./icon-32.png",
+    "64": "./icon-64.png"
+  },
+  onClick: handleClick
+});
+
+function handleClick(state) {
+  tabs.open("https://developer.mozilla.org/");
+}
+ +

在命令行提示符下,再次执行 jpm run。这次点击按钮,它会带你到 https://developer.mozilla.org/ 页面。

+ +

打包 add-on

+ +

当你完成了 add-on 并准备发布它,你会需要把它打包为 XPI 文件。你能自己分发 XPI 文件,也可以把它们发布到 https://addons.mozilla.org,这样其他用户就能下载并按照它们。

+ +

要构建 XPI,只要在 add-on 的根目录下运行命令 jpm xpi

+ +
jpm xpi
+
+ +

你应该看到像这样的信息:

+ +
JPM info Successfully created xpi at /path/to/my-addon/@my-addon-0.0.1.xpi
+
+ +

要测试附加组件是否能正常运行,可以尝试在你自己的火狐中安装 XPI 文件。你可以在 Firefox 中按 Ctrl+O 组合键(Mac 中是 Cmd+O),或者选择"文件"菜单里的“打开”。这样会弹出一个文件选择对话框:转到"@my-addon.xpi"文件,打开并按照提示安装 add-on。

+ +

注意 Firefox 默认需要 add-on 有签名,即使是本地开发的 add-on。安装完后他们会在已安装的 add-on 列表中出现,只因没有签名而被禁用。开发的时候,又或者你不准备发布,你可以打开 about:config 设置 xpinstall.signatures.requiredfalse 来运行未经签名的组件。这个设置会应用到所有的 add-on,所以要十分小心,不要一不当心从别的地方安装了恶意组件。

+ +

要发布你的 add-on,请提交 XPI 文件到 addons.mozilla.org 或者如果你想在你自己的服务器上发布,运行 jpm sign

+ +

总结

+ +

本教程中我们使用了下面三个命令来构建和打包 add-on :

+ + + +

这是你在开发SDK add-on会用到的三个主要命令。完全的参考文档包含了所以你能使用的命令,以及这些命令的可选项。

+ +

这次开发的add-on 的代码用到了两个 SDK 模块,action buttontabs。这儿有参考文档,描述 SDK 中所有高层次低层次的 API。

+ +

下一步?

+ +

要了解你能用 SDK API 做些什么,试着继续阅读教程

diff --git a/files/zh-cn/mozilla/add-ons/sdk/tutorials/getting_started_(jpm)/index.html b/files/zh-cn/mozilla/add-ons/sdk/tutorials/getting_started_(jpm)/index.html new file mode 100644 index 0000000000..e09d6d4e55 --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/tutorials/getting_started_(jpm)/index.html @@ -0,0 +1,162 @@ +--- +title: JPM 入门! +slug: Mozilla/Add-ons/SDK/Tutorials/Getting_Started_(jpm) +tags: + - JPM + - add-on +translation_of: Archive/Add-ons/Add-on_SDK/Tutorials/Getting_Started_(jpm) +--- +
+

该附加SDK包含一个用于初始化,运行,测试和打包加载项的命令行工具。当前工具称为jpm,并且基于  Node.js .它替代了旧的cfx工具。

+ +

Firefox 38 以上的版本才可以使用jpm。

+ +

本文介绍如何使用jpm开发。

+
+ +

本教程将介绍使用SDK创建一个简单的附加组件。

+ +

先决条件

+ +

要使用SDK为Firefox创建附加组件,您需要:

+ + + +

初始化一个空的附加组件

+ +

在命令提示符下,创建一个新目录。导航到它,键入 jpm init, 然后按Enter键

+ +
mkdir my-addon
+cd my-addon
+jpm init
+
+ +

You'll then be asked to supply some information about your add-on: this will be used to create your add-on's package.json file. For now, just press Enter to accept the default for each property. For more information on jpm init, see the jpm command reference.

+ +

Once you've supplied a value or accepted the default for these properties, you'll be shown the complete contents of "package.json" and asked to accept it.

+ +

实现附加组件

+ +

Now you can write the add-on's code. Unless you've changed the value of "entry point" ("main" in package.json), this goes in "index.js" file in the root of your add-on. This file was created for you in the previous step. Open it and add the following code:

+ +
var buttons = require('sdk/ui/button/action');
+var tabs = require("sdk/tabs");
+
+var button = buttons.ActionButton({
+  id: "mozilla-link",
+  label: "Visit Mozilla",
+  icon: {
+    "16": "./icon-16.png",
+    "32": "./icon-32.png",
+    "64": "./icon-64.png"
+  },
+  onClick: handleClick
+});
+
+function handleClick(state) {
+  tabs.open("http://www.mozilla.org/");
+}
+
+ +
+

Note that "entry point" defaults to "index.js" in jpm, meaning that your main file is "index.js", and it is found directly in your add-on's root.

+ +

In cfx, the entry point defaults to "main.js", and is located in the "lib" directory under the add-on's root.

+
+ +

Save the file.

+ +

Next, create a directory called "data" in your add-on's root, and save these three icon files to the "data" directory:

+ + + + + + + + + + + + + + + + +
icon-16.png
icon-32.png
icon-64.png
+ +

Back at the command prompt, type:

+ +
jpm run
+ +

This is the jpm command to run a new instance of Firefox with your add-on installed.

+ +

If Firefox can not be located, you may need to provide the path to it (example in Ubuntu):

+ + +
jpm run -b /usr/bin/firefox
+ +

When Firefox launches, in the top-right corner of the browser you'll see an icon with the Firefox logo. Click the icon, and a new tab will open with http://www.mozilla.org/ loaded into it.

+ +

That's all this add-on does. It uses two SDK modules: the action button module, which enables you to add buttons to the browser, and the tabs module, which enables you to perform basic operations with tabs. In this case, we've created a button whose icon is the Firefox icon, and added a click handler that loads the Mozilla home page in a new tab.

+ +

Try editing this file. For example, we could change the page that gets loaded:

+ +
var buttons = require('sdk/ui/button/action');
+var tabs = require("sdk/tabs");
+
+var button = buttons.ActionButton({
+  id: "mozilla-link",
+  label: "Visit Mozilla",
+  icon: {
+    "16": "./icon-16.png",
+    "32": "./icon-32.png",
+    "64": "./icon-64.png"
+  },
+  onClick: handleClick
+});
+
+function handleClick(state) {
+  tabs.open("https://developer.mozilla.org/");
+}
+ +

At the command prompt, execute jpm run again. This time clicking it takes you to https://developer.mozilla.org/.

+ +

Packaging the add-on

+ +

When you've finished the add-on and are ready to distribute it, you'll need to package it as an XPI file. This is the installable file format for Firefox add-ons. You can distribute XPI files yourself or publish them to https://addons.mozilla.org so other users can download and install them.

+ +

To build an XPI, just execute the command jpm xpi from the add-on's directory:

+ +
jpm xpi
+
+ +

You should see a message like:

+ +
JPM info Successfully created xpi at /path/to/getting-started/@getting-started.xpi
+
+ +

To test that this worked, try installing the XPI file in your own Firefox installation. You can do this by pressing the Ctrl+O key combination (Cmd+O on Mac) from within Firefox, or selecting the "Open" item from Firefox's "File" menu. This will bring up a file selection dialog: navigate to the "@getting-started.xpi" file, open it and follow the prompts to install the add-on.

+ +

Summary

+ +

In this tutorial we've built and packaged an add-on using three commands:

+ + + +

These are the three main commands you'll use when developing SDK add-ons. There's comprehensive reference documentation covering all the commands you can use and all the options they take.

+ +

The add-on code itself uses two SDK modules, action button and tabs. There's reference documentation for all the high-level and low-level APIs in the SDK.

+ +

What's next?

+ +

To get a feel for some of the things you can do with the SDK APIs, try working through some of the tutorials.

+ +

 

diff --git a/files/zh-cn/mozilla/add-ons/sdk/tutorials/index.html b/files/zh-cn/mozilla/add-ons/sdk/tutorials/index.html new file mode 100644 index 0000000000..03173c3219 --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/tutorials/index.html @@ -0,0 +1,144 @@ +--- +title: 教程 +slug: Mozilla/Add-ons/SDK/Tutorials +tags: + - 插件SDK +translation_of: Archive/Add-ons/Add-on_SDK/Tutorials +--- +

{{LegacyAddonsNotice}}{{AddonSidebar}}

+ +

本文列出了许多关于如何通过SDK完成特定任务要求的实际动手的文章。

+ +
+

开发之旅

+ +
+
+
+
安装
+
在Windows, OS X和Linux上下载,安装,初始化SDK工具。
+
+ +
+
常见问题
+
解决常见问题的建议和获取更多帮助。
+
+
+ +
+
+
快速开始
+
走马观花地使用SDK开发一个简单的扩展
+
+
+
+ +
+

创建用户交互界面

+ +
+
+
+
添加一个工具按钮
+
添加一个按钮到火狐Add-on工具栏。
+
添加一个菜单选项到火狐
+
添加多个菜单选项到火狐主菜单。
+
+
+ +
+
+
显示一个弹出对话框
+
通过HTML和JavaScript实现并显示一个弹窗对话框。
+
添加一个上下文菜单
+
添加一个上下文菜单(一般都是右键菜单)到火狐浏览器
+
+
+
+ +
+

与浏览器交互

+ +
+
+
+
打开Web页面
+
在一个新的浏览器选项卡里打开一个Web页面或窗口使用tabs模块并获取内容。
+
监听页面加载
+
当新页面载入时使用tabs模块得到通知并获取页面内容。
+
+
+ +
+
+
获取打开的选项卡(Tab)列表
+
使用tabs模块遍历当前打开的tab,并获取其内容。
+
+
+
+ +
+

更改网页

+ +
+
+
+
更改网页通过URL
+
基于URL过滤网页:当载入的页面的URL与过滤器匹配时执行特定的脚本。
+
+
+ +
+
+
修改页面内容
+
动态加载脚本到当前页面。
+
+
+
+ +
+

开发技术

+ +
+
+
+
日志
+
在终端中记录日志以便调试。
+
创建可复用的模块
+
拆分扩展程序为多个分离的模块,可以使开发调试和维护更加简单。封装你的模块使其成为可复用的包,以便其他开发者可以再次使用。
+
单元测试
+
使用SDK的测试框架书写和进行单元测试。
+
Chrome授权
+
获得Components对象,使你的扩展程序能够加载和使用任何XPCOM对象。
+
创建事件目标
+
使你定义的对象能够响应相关事件。
+
+
+ +
+
+
监听载入和卸载
+
当你的扩展程序被加载和卸载时获得通知,并从终端传递参数给扩展程序。
+
使用第三方模块
+
安装和使用与SDK无关额外的模块
+
本地化
+
书写本地化代码.
+
移动开发
+
为Andriod上的火狐浏览器开发扩展程序。
+
扩展调试
+
调试扩展应用的JavaScript。
+
+
+
+ +
+

打包

+ +
+
+
+
扩展应用:Annotator
+
一起开发一个相对复杂的扩展应用。
+
+
+
diff --git a/files/zh-cn/mozilla/add-ons/sdk/tutorials/installation/index.html b/files/zh-cn/mozilla/add-ons/sdk/tutorials/installation/index.html new file mode 100644 index 0000000000..4313836979 --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/tutorials/installation/index.html @@ -0,0 +1,135 @@ +--- +title: 安装 +slug: Mozilla/Add-ons/SDK/Tutorials/Installation +tags: + - cfx安装 + - 配置cfx环境 +translation_of: Mozilla/Add-ons/SDK/Tools/jpm#Installation +--- +

先决条件

+ +

请不要把任何相关的开发工具和扩展程序(命名推荐小写英文和数字,不推荐特殊符号等),放到有空格的目录(如Progaram Files\Python),会导致意想不到的错误,无法进行后续操作。(最新开发SDK可能修复了该错误,参见https://github.com/mozilla/addon-sdk/pull/1738)

+ +

开发 Add-on SDK,你需要:

+ + + +

另外,可以从它GitHub repository库中得到最新的开发版本

+ +

为AMO开发扩展

+ +

如果提交到AMO只有最新发布或许使用。

+ +

git archive 需要扩展一些Git属性占位符

+ +
git checkout 1.16
+
+git archive 1.16 python-lib/cuddlefish/_version.py | tar -xvf -
+ +

通过Homebrew自动安装到Mac OS X

+ +

使用以下命令通过 homebrew来安装SDK插件工具:

+ +
brew install mozilla-addon-sdk
+ +

安装到Mac OS X/Linux

+ +

无论你选择哪个方式都要解压缩文件的内容作为根路径,并通过shell/命令提示符切换到SDK的根目录下例如:

+ +
tar -xf addon-sdk.tar.gz
+cd addon-sdk
+
+ +

如果你是Bash的用户,则继续运行(大多数人都是的):

+ +
source bin/activate
+
+ +

如果你是一个非Bash的用户你应该运行

+ +
bash bin/activate
+
+ +

命令提示符现在应该有一个包含SDK的目录名称的新的前缀

+ +
(addon-sdk)~/mozilla/addon-sdk >
+
+ +

安装到Windows

+ +

同样解压缩文件,并通过命令符进入到SDK根目录下,例如:

+ +
7z.exe x addon-sdk.zip
+cd addon-sdk
+
+ +

接着运行激活命令:

+ +
bin\activate
+
+ +

同样可以看到命令提示符现在应该有一个包含SDK的目录名称新的前缀

+ +
(C:\Users\mozilla\sdk\addon-sdk) C:\Users\Work\sdk\addon-sdk>
+
+ +

SDK 的虚拟环境

+ +

当命令提示符出现新的前缀表明你的已经搭建好了SDK的运行环境,那么你就可以使用Add-on SDK来开发命令行工具

+ +

任何时候,你都可以通过运行 deactivate 命令停用虚拟环境.

+ +

配置好的虚拟环境是特定于这个特定的命令提示符。如果您关闭命令提示符, 它会关闭运行环境,你需要source bin/activate bin\activate 在一个新的命令提示符重新激活它。如果你打开一个新命令提示符SDK将不会被激活在新的提示

+ +

可以将SDK的多个副本拷贝在磁盘上的不同位置,并在它们之间切换,甚至可以让他们同时激活运行在不同的命令提示

+ +

制作启动项

+ +

所有 activate 的作用是通过设置环境变量,使位于顶层 bin 目录下的脚本 位于当前命令符下,制作的启动项 ,通过永久环境中的这些变量设置,以便每一个新的命令提示符下都能读取它们那么就不需要每次都去打开新的命令提示符来激活 activate

+ +

因为变量精确设置可能随SDK发布新版本的变化,所以最好是指激活脚本来确定哪些变量需要设置。激活使用不同的脚本设置bash环境不同的变量LinuxMAC OS X和Windows环境

+ +

Windows

+ +

在Windows上,使用 bin\activate\activate.bat批处理脚本,也可以使用命令行setx工具或控制面板激活永久使用

+ +

Linux/Mac OS X

+ +

在 Linux 和 Mac OS X,使用source bin/activate/activate bash 脚本, 你可以 ~/.bashrc ( Linux) 或~/.bashprofile (Mac OS X) 来激活。

+ +

作为替代,你可以在 ~/bin 目录中创建到cfx 程序的符号链接

+ +
ln -s PATH_TO_SDK/bin/cfx ~/bin/cfx
+
+ +

完整性检查

+ +

在shell提示符运行:

+ +
cfx
+
+ +

它会产生下面信息,这里是第一行内容,后面大量的使用信息

+ +
Usage: cfx [options] [command]
+
+ +

这是 CFX命令行程序界面加载项的SDK可以使用它来启动Firefox和测试插件,打包附加分发,查看​​文档和运行单元测试

+ +

出现问题?

+ +

尝试通过故障排除页面来解决遇到的问题。

+ +

下一步

+ +

接下来, 开始学习 cfx 教程, 其中介绍了如何使用CFX的工具来创建附加组件

diff --git a/files/zh-cn/mozilla/add-ons/sdk/tutorials/l10n/index.html b/files/zh-cn/mozilla/add-ons/sdk/tutorials/l10n/index.html new file mode 100644 index 0000000000..5083be5b5c --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/tutorials/l10n/index.html @@ -0,0 +1,381 @@ +--- +title: Localization +slug: Mozilla/Add-ons/SDK/Tutorials/l10n +tags: + - Add-on SDK + - 本地化 +translation_of: Archive/Add-ons/Add-on_SDK/Tutorials/l10n +--- +

该SDK支持本地化字符串出现在:

+ + + +

目前为止还不支持本地化CSS和content scripts。

+ +

本地化字符串

+ +

翻译后的字符串都保存在你的add-on扩展目录下一个名为 "locale"的目录 ,每个本地化地域对应一个文件。这些文件:

+ + + +

假设你的附加组件包含一个单一的本地化字符串,用英语表示为“Hello!”,你想提供英语和法语的本地化支持。

+ +

你需要添加两个文件到"locale"目录:

+ +
my-addon/
+         data
+         lib
+         locale/
+                en-US.properties
+                fr-FR.properties
+
+ +

"en-US.properties" 文件中内容:

+ +
hello_id= Hello!
+
+ +

"fr-FR.properties" 文件中内容:

+ +
hello_id= Bonjour !
+
+ +

现在,每当你的JavaScript或HTML向本地化系统请求hello_id标识的翻译,它将获得与当前区域语言一致的翻译。

+ +

在HTML中使用本地化字符串

+ +
+

本例使用的 action button API需要 Firefox 29 或者更高版本。

+
+ +

要从HTML中引用本地化字符串,需要添加一个 data-l10n-id 的属性,到你想本地化的字符串所属的HTML标签中,然后为该属性指定一个ID值(To reference localized strings from HTML, add a data-l10n-id attribute to the HTML tag where you want the localized string to appear, and assign the identifier to it):

+ +
<html>
+  <body>
+    <h1 data-l10n-id="hello_id"></h1>
+  </body>
+</html>
+
+ +

然后你就可以使用这个HTML文件来建立你的界面, 比如插入一个 panel 面板:

+ +
var button = require("sdk/ui/button/action").ActionButton({
+  id: "localized-hello",
+  label: "Localized hello",
+  icon: "./icon-16.png",
+  onClick: function() {
+    hello.show();
+  }
+});
+
+var hello = require("sdk/panel").Panel({
+  height: 75,
+  width: 150,
+  contentURL: require("sdk/self").data.url("my-panel.html")
+});
+ +

“en-US”和“fr-FR”提供了翻译标识为hello_id字符串的本地化文件,面板将根据当前区域语言设置,显示“Hello!”或者“Bonjour!”:

+ +

+ +

翻译文本会插入到具有data-l10n-id属性集的节点中。任何之前存在的内容只是被替换掉了。(The translation is inserted into the node which has the data-l10n-id attribute set. Any previously existing content is just replaced.)

+ +

本地化字符串只能作为text文本插入, 所以你不能使用下面的语句插入HTML:

+ +
hello_id= <blink>Hello!</blink>
+
+ +

Localizing Element Attributes

+ +
这是 Firefox 39 上的新功能
+ +


+ 你可以在properties文件中,通过设置 l10n-id.attributeName 的值,本地化某些具有 l10n-id属性的元素的属性值。像这样(You can localize certain attributes of elements with an l10n-id by setting its value with l10n-id.attributeName in the properties file like):
+  

+ +
hello_id.accesskey= H
+ +

可以支持以下几个属性:

+ + + +

更多的 ARIA 属性aria-label, aria-valuetextaria-moz-hint 的本地化将通过在Firefox OS上同样的别名被支持(Further the localization of the ARIA attributes aria-label, aria-valuetext and aria-moz-hint are supported with the same aliases as on Firefox OS):

+ + + +

在JavaScript代码中使用本地化字符串

+ +

为了在主附加组件代码中引用本地化字符串,你需要这样做:

+ +
var _ = require("sdk/l10n").get;
+console.log(_("hello_id!"));
+ +

指定的 "_" 并不是必需的,但是作为 gettext 工具的默认约定,这能更好的配合其他默认使用 "_" 来表示本地化字符串的现有工具。

+ +
    +
  1. 导入 l10n 模块,然后指定 "_" (下划线)为模块的 get 函数。
  2. +
  3. 把所有涉及本地化的字符串放到 _() 函数中包括起来。
  4. +
+ +

如果你运行它,你会看到输出为预期的当前设置的区域语言:

+ +
info: Hello!
+
+ +
info: Bonjour !
+
+ +

注意你不能在content scripts中 require() 一个模块,所以目前还不能在content script 中引用本地化字符串。

+ +

复数

+ +

 l10n 模快支持复数形式,不同的语言有不同的复数形态。例如,英语有两种形式:相对于"one"的单数形式,和对于"everything else, including zero"的复数形式:

+ +
one tomato
+no tomatoes
+two tomatoes
+
+ +

但是俄罗斯语对于以 1 结尾(除了11)的数字、以 2-4 结尾(除了12-14)的数字和其他数字,有着不同的复数形态:

+ +
один помидор     // one tomato
+два помидора     // two tomatoes
+пять помидоров   // five tomatoes
+
+ +

SDK使用 Unicode CLDR 数据描述由不同的语言使用的不同复数形式。

+ +

Unicode CLDR 复数形式

+ +

Unicode CLDR项目定义了用于描述一个特定语言的多个规则的一种方案。在这个方案中一种语言对应最多有六种不同的范围的数字,有以下类别区分:zero(零个),one(一个),two(两个),few(几个),many(很多),other(其他)。(The Unicode CLDR project defines a scheme for describing a particular language's plural rules. In this scheme a language maps each distinct range of numbers on to one of up to six forms, identified by the following categories: zero, one, two, few, many, and other.)

+ +

英语有两种复数形式,可以表示为 "1" 映射到 "one" 和 "everything else" 映射到 "other"的形式(English has two forms, which can be described by mapping "1" to "one" and "everything else" to "other"):

+ +
one   → n is 1;
+other → everything else
+
+ +

俄罗斯语有四种形式,可以使用以下方式表示:

+ +
one   → n mod 10 is 1 and n mod 100 is not 11;
+few   → n mod 10 in 2..4 and n mod 100 not in 12..14;
+many  → n mod 10 is 0 or n mod 10 in 5..9 or n mod 100 in 11..14;
+other → everything else
+
+ +

所有语言的多个规则可以在CLDR的 语言复数规则 页面查到 (即使这个规则表相对于 CLDR XML source 已经过时了).

+ +

SDK中的复数形式

+ +

代码中,在 _()函数中的本地化ID参数之后提供一个额外的参数,用来表示代表多少个项的本地化字符串(In the code, you supply an extra parameter alongside the identifier, describing how many items there are):

+ +
var _ = require("sdk/l10n").get;
+console.log(_("tomato_id"));
+console.log(_("tomato_id", 1));
+console.log(_("tomato_id", 2));
+console.log(_("tomato_id", 5));
+console.log(_("tomato_id", .5));
+ +

.properties 文件中通过使用 CLDR 关键字,对于每种语言可能的复数形式你可以自定义不同的本地化字符串。所以对于英语可以有两种本地化(注意"other" 分类不是 CDLR 关键字)。(In the .properties file for each language you can define a different localization for each plural form possible in that language, using the CLDR keywords. So in English we could have two plural localizations (note that the "other" category does not take the CLDR keyword))

+ +
# en-US translations
+tomato_id[one]= %d tomato
+tomato_id= %d tomatoes
+
+ +

俄罗斯语中可以定义四种本地化的复数形式:

+ +
# ru-RU translations
+tomato_id[one]= %d помидор
+tomato_id[few]= %d помидора
+tomato_id[many]= %d помидоров
+tomato_id= %d помидоры
+
+ +

The localization module itself understands the CLDR definitions for each language, enabling it to map between, for example, "2" in the code and "few" in the ru-RU.properties file. Then it retrieves and returns the localization appropriate for the count you supplied.

+ +

Placeholders

+ +

The l10n module supports placeholders, allowing you to insert a string which should not be localized into one which is. The following "en-US" and "fr-FR" ".properties" files include placeholders:

+ +
# en-US translations
+hello_id= Hello %s!
+
+ +
# fr-FR translations
+hello_id= Bonjour %s !
+
+ +

To use placeholders, supply the placeholder string after the identifier:

+ +
var _ = require("sdk/l10n").get;
+console.log(_("hello_id", "Bob"));
+console.log(_("hello_id", "Alice"));
+ +

In the "en-US" locale, this gives us:

+ +
info: Hello Bob!
+info: Hello Alice!
+
+ +

In "fr-FR" we get:

+ +
info: Bonjour Bob !
+info: Bonjour Alice !
+
+ +

Ordering Placeholders

+ +

When a localizable string can take two or more placeholders, translators can define the order in which placeholders are inserted, without affecting the code.

+ +

Primarily, this is important because different languages have different rules for word order. Even within the same language, though, translators should have the freedom to define word order.

+ +

For example, suppose we want to include a localized string naming a person's home town. There are two placeholders: the name of the person and the name of the home town:

+ +
var _ = require("sdk/l10n").get;
+console.log(_("home_town_id", "Bob", "London"));
+ +

An English translator might want to choose between the following:

+ +
"<town_name> is <person_name>'s home town."
+
+ +
"<person_name>'s home town is <town_name>"
+
+ +

To choose the first option, the .properties file can order the placeholders as follows:

+ +
home_town_id= %2s is %1s's home town.
+
+ +

This gives us the following output:

+ +
info: London is Bob's home town.
+
+ +

在首选项设置中的本地化字符串

+ +

通过加入一个 "preferences" 字段的结构到你的附加组件的 "package.json" 文件中,你可以为你的附加组件定义首选项选项,用户可以在Firefox的 Add-ons Manager 看到和编辑它。

+ +

Preferences (首选项)有一个必需的title标题项和一个可选的description描述项 这些字符串将出现在 Add-ons Manager中,来帮助向用户解释各个首选项设置的意义。

+ + + +

例如, 假设你的 "package.json" 中只定义了一个设置选项:

+ +
{
+    "preferences": [
+        {
+            "type": "string",
+            "name": "monster_name",
+            "value": "Gerald",
+            "title": "Name"
+        }
+    ],
+    "name": "monster-builder",
+    "license": "MPL 2.0",
+    "author": "me",
+    "version": "0.1",
+    "fullName": "Monster Builder",
+    "id": "monster-builder@me.org",
+    "description": "Build your own monster"
+}
+
+ +

在你的"en-US.properties"文件中, 应该包括下面两个项:

+ +
monster_name_title= Name
+monster_name_description= What is the monster's name?
+
+ +

在你的"fr-FR.properties"文件中, 应该包括下面两个法语的翻译项:

+ +
monster_name_title= Nom
+monster_name_description= Quel est le nom du monstre ?
+
+ +

现在,当浏览器的区域设置为 "en-US", 用户会在 Add-ons Manager看到这样:

+ +

+ +

当浏览器区域设置为 "fr-FR", 用户会看到:

+ +

+ +

下拉菜单menulist和单选按钮radio的类型有多个选项,每一个选项的标签属性都会展示给用户。如果本地化文件中有一项是以前缀是"{name} _options" 为键的键值对,其中"{name}"是选项的标签名字,该键值对的值就是一个选项标签的本地化字符串。(The menulist and the radio preference types have options. The label attribute of each option is displayed to the user. If the locale file has a entry with the value of the label attribute prefixed with "{name}_options." as its key, where {name} is the name of the preference, its value is used as a localized label.)

+ +

Using Identifiers

+ +

If the localization system can't find an entry for a particular identifier using the current locale, then it just returns the identifier itself.

+ +

This has the nice property that you can write localizable, fully functional add-ons without having to write any locale files. You can just use the default language strings as your identifier, and subsequently supply .properties files for all the additional locales you want to support.

+ +

For example, in the case above you could use "Hello!" as the identifier, and just have one .properties file for the "fr-FR" locale:

+ +
Hello!= Bonjour !
+
+ +

Then when the locale is "en-US", the system would fail to find a .properties file, and return "Hello!".

+ +

However, this approach makes it difficult to maintain an add-on which has many localizations, because you're using the default language strings both as user interface strings and as keys to look up your translations. This means that if you want to change the wording of a string in the default language, or fix a typo, then you break all your locale files.

+ +

Locale Updater

+ +

The locale updater add-on makes it easier to update locale files. Once you've installed it, open the Add-on Manager, and you'll see a see a new button labeled "Update l10n" next to each add-on you've installed:

+ +

+ +

Click the button and you'll be prompted for a new .properties file for that add-on. If you provide a new file, the add-on's locale data will be updated with the new file.

+ +

Limitations

+ +

The current localization support is a first step towards full support, and contains a number of limitations.

+ + + +

See Also - for developers looking to localize non-SDK add-ons

+ + diff --git a/files/zh-cn/mozilla/add-ons/sdk/tutorials/list_open_tabs/index.html b/files/zh-cn/mozilla/add-ons/sdk/tutorials/list_open_tabs/index.html new file mode 100644 index 0000000000..8a4985806e --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/tutorials/list_open_tabs/index.html @@ -0,0 +1,72 @@ +--- +title: 列出打开的标签页 +slug: Mozilla/Add-ons/SDK/Tutorials/List_Open_Tabs +tags: + - Add-on SDK +translation_of: Archive/Add-ons/Add-on_SDK/Tutorials/List_Open_Tabs +--- +

{{AddonSidebar}}

+ +
+

学习本教程之前你需要学习 jpm 基础

+
+ +

列出打开的标签页,你可以遍历 tabs 对象本身。

+ +

下面的 add-on 添加一个 action button 当用户单击该按钮时,该日志将在打开的标签页中记录:

+ +
require("sdk/ui/button/action").ActionButton({
+  id: "list-tabs",
+  label: "List Tabs",
+  icon: "./icon-16.png",
+  onClick: listTabs
+});
+
+function listTabs() {
+  var tabs = require("sdk/tabs");
+  for (let tab of tabs)
+    console.log(tab.url);
+}
+
+ +
+

注意:为此你需要一个按钮图标,以"icon-16.png"的文件名保存到你的 add-on 的"data"目录下。你可以从这里下载图标:

+
+ +

运行该 add-on,加载一对标签页,并点击按钮,你会看到在控制台输出如下的内容:

+ +
info: http://www.mozilla.org/en-US/about/
+info: http://www.bbc.co.uk/
+
+ +
+

你不能直接访问到标签页中的任何宿主内容(具体概念请查阅相关内容:JavaScript 本地对象、内置对象、宿主对象 )。

+ +

为了访问标签页的内容,你需要使用 tab.attach() 添加一个脚本,此 add-on 加载加载一个页面,然后将一个脚本附加到所有打开的标签页,该脚本将向标签页的文档添加红色边框:

+
+ +
require("sdk/ui/button/action").ActionButton({
+  id: "list-tabs",
+  label: "List Tabs",
+  icon: "./icon-16.png",
+  onClick: listTabs
+});
+
+function listTabs() {
+  var tabs = require("sdk/tabs");
+  for (let tab of tabs)
+    runScript(tab);
+}
+
+function runScript(tab) {
+  tab.attach({
+    contentScript: "document.body.style.border = '5px solid red';"
+  });
+}
+
+ +

学习更多

+ +

要了解更多关于SDK中标签如何工作, 查看 tabs API reference

+ +

要了解更多关于在标签中运行脚本, 查看 tutorial on using tab.attach()

diff --git a/files/zh-cn/mozilla/add-ons/sdk/tutorials/listen_for_page_load/index.html b/files/zh-cn/mozilla/add-ons/sdk/tutorials/listen_for_page_load/index.html new file mode 100644 index 0000000000..815cfd42c5 --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/tutorials/listen_for_page_load/index.html @@ -0,0 +1,42 @@ +--- +title: 监听页面加载 +slug: Mozilla/Add-ons/SDK/Tutorials/Listen_for_Page_Load +translation_of: Archive/Add-ons/Add-on_SDK/Tutorials/Listen_for_Page_Load +--- +

{{AddonSidebar}}

+ +
学习本教程之前你需要了解 jpm 基础
+ +

你可以使用 tabs 模块来获取关于新页面加载的通知。下面的附加组件监听标签页内建的 ready 事件,并且记录下每一个标签加载时的URL:

+ +
require("sdk/tabs").on("ready", logURL);
+
+function logURL(tab) {
+  console.log(tab.url);
+}
+
+ +
+

你会在浏览器控制台,而非 Web 控制台中,找到这些输出的内容。

+
+ +

你不能直接访问标签页里面的内容。

+ +

为了访问标签页内容,你需要使用 tab.attach() 把一个脚本附到标签页上。这个示例给每一个打开后的标签页附上了一个脚本。这个脚本给标签页的 document 加上了一个红色边框:

+ +
require("sdk/tabs").on("ready", runScript);
+
+function runScript(tab) {
+  tab.attach({
+    contentScript: "if (document.body) document.body.style.border = '5px solid red';"
+  });
+}
+
+ +

(本示例仅仅表示:可以像这样实现一些功能,而你应当使用 page-mod,并且指定匹配模式为 "*"。)

+ +

了解更多

+ +

想要了解更多关于如何在SDK中处理标签页的内容,请看 tabs API 参考。你能够监听其他一些标签页事件,包括 openclose、和 activate

+ +

想要了解更多关于在标签页中运行脚本的事情,请看 tab.attach() 使用教程

diff --git a/files/zh-cn/mozilla/add-ons/sdk/tutorials/modifying_the_page_hosted_by_a_tab/index.html b/files/zh-cn/mozilla/add-ons/sdk/tutorials/modifying_the_page_hosted_by_a_tab/index.html new file mode 100644 index 0000000000..5fd51fd8f7 --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/tutorials/modifying_the_page_hosted_by_a_tab/index.html @@ -0,0 +1,109 @@ +--- +title: 修改标签页中页面 +slug: Mozilla/Add-ons/SDK/Tutorials/Modifying_the_Page_Hosted_by_a_Tab +tags: + - Add-on SDK +translation_of: Archive/Add-ons/Add-on_SDK/Tutorials/Modifying_the_Page_Hosted_by_a_Tab +--- +
+

为了进一步学习本教程,你需要安装 Add-on SDK 和学习 cfx的基本使用

+ +

这篇教程使用动作按钮API,需要Firefox 29或更新版本。

+
+ +

为了修改特定标签页中的页面, 可以使用 tab对象的attach() 方法加载一个script脚本到页面中。因为他们的工作是和Web内容进行交互,所以这些脚本被称为content scripts(内容脚本)。

+ +

这是个简单的示例:

+ +
var button = require("sdk/ui/button/action").ActionButton({
+  id: "style-tab",
+  label: "Style Tab",
+  icon: "./icon-16.png",
+  onClick: function() {
+    require("sdk/tabs").activeTab.attach({
+      contentScript: 'document.body.style.border = "5px solid red";'
+    });
+  }
+});
+ +

要运行这个示例你必须保存一个名为”icon-16.png“的图标文件在你的Add-on目录下的”data”目录下。你可以下载这个图标:

+ +

这个插件创建一个按钮,其中包含Mozilla的图标作为一个图标。这个按钮产生一个点击事件处理程序,处理事件中将获取当前活动标签页和加载一个脚本到该标签页中的页面。使用contentscript选项指定加载的脚本,该脚本只绘制一个红色边框页。

+ +

然后在浏览器窗口中打开任何网页,点击按钮 。你会看到一个红色的边界出现在页面中, 就像这样:

+ +

+ +

保持Content Script在一个单独的文件中

+ +

在上面的例子中我们的content script作为一个字符串来直接使用.除非脚本非常简单,你应该保持脚本作为一个单独的文件。这使得代码更容易维护、调试和审查。

+ +

比如,我们把上面的脚本代码保存在Add-on目录下的data目录中并取名为my-script.js,在代码中可以这样加载脚本:

+ +
var self = require("sdk/self");
+
+var button = require("sdk/ui/button/action").ActionButton({
+  id: "style-tab",
+  label: "Style Tab",
+  icon: "./icon-16.png",
+  onClick: function() {
+    require("sdk/tabs").activeTab.attach({
+      contentScriptFile: self.data.url("my-script.js")
+    });
+  }
+});
+
+ +

你可以加载多个脚本,同时这些脚本可以直接相互作用。所以你可以加载 jQuery, 然后在你的其他 content script使用它。

+ +

与Content Script传递信息

+ +

你的扩展插件脚本Add-on script和内容脚本content script 不能直接访问对方的变量和函数,但他们之间可以互相发送消息。

+ +

从一方发送消息到另外一方, 发送方需要调用 port.emit()发送消息, 同时接收方使用port.on()接收消息。

+ + + +

让我们重写上面的例子来从附加内容脚本 content script 传递一个消息。现在content script 需要像下面这样:

+ +
// "self" is a global object in content scripts
+// Listen for a "drawBorder"
+self.port.on("drawBorder", function(color) {
+  document.body.style.border = "5px solid " + color;
+});
+
+ +

在 add-on script 扩展脚本中,我们使用 attach()方法返回的对象向 content script 中发送一个“drawBorder”消息:

+ +
var self = require("sdk/self");
+var tabs = require("sdk/tabs");
+
+var button = require("sdk/ui/button/action").ActionButton({
+  id: "style-tab",
+  label: "Style Tab",
+  icon: "./icon-16.png",
+  onClick: function() {
+    var worker = tabs.activeTab.attach({
+      contentScriptFile: self.data.url("my-script.js")
+    });
+    worker.port.emit("drawBorder", "red");
+  }
+});
+
+ +

名为 drawBorder 的消息并不是一个内置的消息, 而是通过 port.emit()方法自定义的。

+ +

注入 CSS

+ +

不像 page-mod API, tab.attach() 不允许你直接注入CSS到页面中。

+ +

你需要使用 JavaScript 来修改页面的样式,就像前面的示例那样。

+ +

学习更多

+ +

要了解更多关于在SDK中标签页的使用, 可以查看打开一个网页教程,  列出打开的标签页教程, 和tabs API 参考手册.

+ +

要学习更多关于content scripts, 查看 content scripts guide.

diff --git a/files/zh-cn/mozilla/add-ons/sdk/tutorials/modifying_web_pages_based_on_url/index.html b/files/zh-cn/mozilla/add-ons/sdk/tutorials/modifying_web_pages_based_on_url/index.html new file mode 100644 index 0000000000..6e5e46c532 --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/tutorials/modifying_web_pages_based_on_url/index.html @@ -0,0 +1,210 @@ +--- +title: Modifying Web Pages Based on URL +slug: Mozilla/Add-ons/SDK/Tutorials/Modifying_Web_Pages_Based_on_URL +translation_of: Archive/Add-ons/Add-on_SDK/Tutorials/Modifying_Web_Pages_Based_on_URL +--- +
开始本教程之前,您必须安装好 SDK,并且学习 cfx 的基本的使用方法。
+ +

要修改任何页面匹配特定的模式(比如,“http://example.org/”)当它们加载后,使用page-mod模块。

+ +

要创建 page-mod,您必须指定两件事:

+ + + +
+

content scripts为内容脚本,只能使用普通浏览器支持的 JS,不能使用 add-on 的API

+
+ +

这里有一个范例。内容脚本提供contentScript选项,地址样本提供include选项:

+ +
// Import the page-mod API
+var pageMod = require("sdk/page-mod");
+
+// Create a page mod
+// It will run a script whenever a ".org" URL is loaded
+// The script replaces the page contents with a message
+pageMod.PageMod({
+  include: "*.org",
+  contentScript: 'document.body.innerHTML = ' +
+                 ' "<h1>Page matches ruleset</h1>";'
+});
+
+ +

试试吧:

+ + + +

您将看到:

+ +

+ +

指定匹配模式

+ +

匹配模式使用 match-pattern 语法。您可以通过单一的匹配字符串,或者数组。

+ +

把内容脚本放在独立的文件中

+ +

在上面的范例中我们通过字符串来实现内容脚本。除非是用作简单的例子,通常情况下您应该将内容脚本放在独立文件中,这将使您的代码更易维护、调式和查看。

+ +

要这样做,您需要:

+ + + +

例如,如果我们将内容脚本保存在 data 目录下,命名为 my-script.js,在main.js中,我们应该这么写:

+ +
// Import the page-mod API
+var pageMod = require("sdk/page-mod");
+// Import the self API
+var self = require("sdk/self");
+
+// Create a page mod
+// It will run a script whenever a ".org" URL is loaded
+// The script replaces the page contents with a message
+pageMod.PageMod({
+  include: "*.org",
+  contentScriptFile: self.data.url("my-script.js")
+});
+ +

加载多个Content Scripts

+ +

您可以加载更多脚本,且脚本可以相互交互。所以,您可以使用jQuery重写 my-script.js:

+ +
$("body").html("<h1>Page matches ruleset</h1>");
+
+ +

然后下载 jQuery 库到您的扩展开发目录的 data 目录下,并且将 jQuery 和 my-script 一起加载(确保先加载 jQuery 库):

+ +
// Import the page-mod API,加载 add-on 的 page-mod API
+var pageMod = require("sdk/page-mod");
+// Import the self API, 加载 add-on 的 self API
+var self = require("sdk/self");
+
+// Create a page mod
+// It will run a script whenever a ".org" URL is loaded
+// The script replaces the page contents with a message
+// 创建 page mod,匹配 “.org” URL
+pageMod.PageMod({
+  include: "*.org",
+  contentScriptFile: [self.data.url("jquery-1.7.min.js"), self.data.url("my-script.js")]
+});
+
+ +

您也可以在同一 page-mod 中同时使用 contentScriptcontentScriptFile 。如果您这么做的话,contentScript的脚本将会先加载。(应该是contentScriptFile的先加载吧?)

+ +
// Import the page-mod API
+var pageMod = require("sdk/page-mod");
+// Import the self API
+var self = require("sdk/self");
+
+// Create a page mod
+// It will run a script whenever a ".org" URL is loaded
+// The script replaces the page contents with a message
+pageMod.PageMod({
+  include: "*.org",
+  contentScriptFile: self.data.url("jquery-1.7.min.js"),
+  contentScript: '$("body").html("<h1>Page matches ruleset</h1>");'
+});
+
+ +

注意,您不能直接加载网站上的脚本。脚本必须从 data 目录中加载。

+ +

与 Content Script 通信

+ +

Your add-on script and the content script can't directly access each other's variables or call each other's functions, but they can send each other messages.

+ +

从 Content Script 与 main.js 通信,发送方使用 port.emit() ,接收方使用 port.on() 监听.

+ + + +

Let's rewrite the example above to pass a message from the add-on to the content script. The message will contain the new content to insert into the document. The content script now needs to look like this:

+ +
// "self" is a global object in content scripts
+// Listen for a message, and replace the document's
+// contents with the message payload.
+self.port.on("replacePage", function(message) {
+  document.body.innerHTML = "<h1>" + message + "</h1>";
+});
+
+ +

In the add-on script, we'll send the content script a message inside onAttach:

+ +
// Import the page-mod API
+var pageMod = require("sdk/page-mod");
+// Import the self API
+var self = require("sdk/self");
+
+// Create a page mod
+// It will run a script whenever a ".org" URL is loaded
+// The script replaces the page contents with a message
+pageMod.PageMod({
+  include: "*.org",
+  contentScriptFile: self.data.url("my-script.js"),
+  // Send the content script a message inside onAttach
+  onAttach: function(worker) {
+    worker.port.emit("replacePage", "Page matches ruleset");
+  }
+});
+
+ +

The replacePage message isn't a built-in message: it's a message defined by the add-on in the port.emit() call.

+ +

注入 CSS

+ +
+

请注意,本节中描述的功能是实验性的:我们很可能继续支持的功能,但可能需要改变的细节。

+
+ +

Rather than injecting JavaScript into a page, you can inject CSS by setting the page-mod's contentStyle option:

+ +
var pageMod = require("sdk/page-mod").PageMod({
+  include: "*",
+  contentStyle: "body {" +
+                "  border: 5px solid green;" +
+                "}"
+});
+
+ +

As with contentScript, there's a corresponding contentStyleFile option that's given the URL of a CSS file in your "data" directory, and it is good practice to use this option in preference to contentStyle if the CSS is at all complex:

+ +
var pageMod = require("sdk/page-mod").PageMod({
+  include: "*",
+  contentStyleFile: require("sdk/self").data.url("my-style.css")
+});
+
+ +

You can't currently use relative URLs in style sheets loaded with contentStyle or contentStyleFile. If you do, the files referenced by the relative URLs will not be found.

+ +

To learn more about this, and read about a workaround, see the relevant section in the page-mod API documentation.

+ +

Learning More

+ +

To learn more about page-mod, see its API reference page. In particular, the PageMod constructor takes several additional options to control its behavior:

+ + + +

To learn more about content scripts in general, see the content scripts guide.

diff --git a/files/zh-cn/mozilla/add-ons/sdk/tutorials/open_a_web_page/index.html b/files/zh-cn/mozilla/add-ons/sdk/tutorials/open_a_web_page/index.html new file mode 100644 index 0000000000..7ff9ba7883 --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/tutorials/open_a_web_page/index.html @@ -0,0 +1,52 @@ +--- +title: 打开Web页面 +slug: Mozilla/Add-ons/SDK/Tutorials/Open_a_Web_Page +translation_of: Archive/Add-ons/Add-on_SDK/Tutorials/Open_a_Web_Page +--- +{{AddonSidebar}} + +
学习本教程之前你需要学习 jpm 基础
+ +

打开一个新的网页,你可以使用 tabs 模块:

+ +
var tabs = require("sdk/tabs");
+tabs.open({
+"http://www.example.com");
+
+ +

这个函数是异步的,所以你不能立即获取一个可以检查的标签对象。要做到这一点,通过一个回调函数为open()。将回调函数赋值给 onready 属性,并将通过标签作为参数:

+ +
var tabs = require("sdk/tabs");
+tabs.open({
+  url: "http://www.example.com",
+  onReady: function onReady(tab) {
+    console.log(tab.title);
+  }
+});
+
+ +
+

尽管这样,你还是不能直接访问到标签页中的任何宿主内容(具体概念请查阅相关内容:JavaScript 本地对象、内置对象、宿主对象 )。

+
+ +

+要访问标签页的内容,你需要使用 tab.attach()把一个脚本添加到该标签页。此add-on加载加载一个页面,然后将一个脚本附加到该页,该将向页面添加红色边框:

+ +
var tabs = require("sdk/tabs");
+tabs.open({
+  url: "http://www.example.com",
+  onReady: runScript
+});
+
+function runScript(tab) {
+  tab.attach({
+    contentScript: "document.body.style.border = '5px solid red';"
+  });
+}
+
+ +

学习更多

+ +

要了解更多关于SDK中标签如何工作, 查看 tabs API reference.

+ +

要了解更多关于在标签中运行脚本, 查看 tutorial on using tab.attach().

diff --git a/files/zh-cn/mozilla/add-ons/sdk/tutorials/troubleshooting/index.html b/files/zh-cn/mozilla/add-ons/sdk/tutorials/troubleshooting/index.html new file mode 100644 index 0000000000..afa3cc0cd3 --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/tutorials/troubleshooting/index.html @@ -0,0 +1,39 @@ +--- +title: Troubleshooting +slug: Mozilla/Add-ons/SDK/Tutorials/Troubleshooting +tags: + - add-on sdk 安装指南 + - add-on sdk安装解惑 +translation_of: Archive/Add-ons/Add-on_SDK/Tutorials/Troubleshooting +--- +

{{AddonSidebar}}

+ +

如果你的SDK安装和运行遇到了问题,不要慌!本页面列出了一些基本点,来帮助你追踪你的问题。

+ +

检查你的 Firefox

+ +

jpm 会搜索你系统中 Firefox 常见的地址,jpm 也许不能找到火狐安装在哪,或者你有多个地方安装了火狐,jpm 也许找错了地方。这种情况下,你需要使用 jpm--binary 选项。参看 jpm 指南以获取更多信息

+ +

当你运行 jpm 来测试你的 add-on 或者运行单元测试时,它会打印出 Firefox 或 XULRunner 二进制文件的地址,所有你可以检查一下它的输出内容来做确认。

+ +

检查你的文本控制台

+ +

当你的代码代码和SKD的API产生错误时,他们会被记录到文本控制台。这应该和你运行 jpm 命令的是同一控制台或shell

+ +

搜索已知的问题

+ +

也许有人已经遇到过和你一样的问题了。其他用户经常发布问题到 项目邮件列表。你也可以浏览已知问题列表或者搜索特定的关键词。

+ +

与项目团队和用户组交流

+ +

SDK的用户和项目团队成员对问题和建议在 项目的邮件列表.  别人可能有与你相同的问题,所以试着搜索列表。也欢迎你发表问题.

+ +

你也可以与其他SDK用户在 Mozilla的 IRC 网络#jetpack 聊天室聊天.

+ +

如果你想报告SDK的bug,我们非常欢迎!您将需要创建一个 Bugzilla 的帐号,Bugzilla 是 Mozilla 的 bug 追踪系统。

+ +
 
+ +
 
+ +
 
diff --git a/files/zh-cn/mozilla/add-ons/sdk/tutorials/unit_testing/index.html b/files/zh-cn/mozilla/add-ons/sdk/tutorials/unit_testing/index.html new file mode 100644 index 0000000000..63cd86da13 --- /dev/null +++ b/files/zh-cn/mozilla/add-ons/sdk/tutorials/unit_testing/index.html @@ -0,0 +1,102 @@ +--- +title: Unit Testing +slug: Mozilla/Add-ons/SDK/Tutorials/Unit_testing +translation_of: Archive/Add-ons/Add-on_SDK/Tutorials/Unit_testing +--- +
+

学习本教程你将需要安装SDK, 学习 基本操作cfx,和学习过编写可重复模块(writing reusable modules).

+
+
+

如果你在使用 jpm 而不是 cfx, 请看关于cfx, 而且重点看加载测试模块(loading modules from test code).

+
+

SDK提供了一个框架,为你的代码创建和运行单元测试.接下来我们将演示如何写一个关于Base64 模块的单元测试.

+

一个Base64模块例子

+

在一个网页中, 你可以进行Base64的加密和解密,通过使用函数btoa() and atob() .不幸的是这些函数依附在window对象: 由于这个对象在你的add-on(插件) main 的代码里不是有效对象,所以 atob() and btoa() 也不是有效的. 因此我们将展示如何在这个平台上创建一个base64模块 .

+

To begin with, create a new directory, navigate to it, and run cfx init. Now create a new file in "lib" called "base64.js", and give it the following contents:

+
const { atob, btoa } = require("chrome").Cu.import("resource://gre/modules/Services.jsm", {});
+
+exports.atob = a => atob(a);
+exports.btoa = b => btoa(b);
+
+

This code exports two functions, which just call the platform's btoa() and atob() functions. To show the module in use, edit the "main.js" file as follows:

+
var base64 = require("./base64");
+
+var button = require("sdk/ui/button/action").ActionButton({
+  id: "base64",
+  label: "base64",
+  icon: "./icon-16.png",
+  onClick: function() {
+    encoded = base64.btoa("hello");
+    console.log(encoded);
+    decoded = base64.atob(encoded);
+    console.log(decoded);
+  }
+});
+

To run this example you'll also have to have an icon file named "icon-16.png" saved in your add-ons "data" directory. You could download this icon: .

+

Now "main.js" imports the base64 module and calls its two exported functions. If we run the add-on and click the button, we should see the following logging output:

+
info: aGVsbG8=
+info: hello
+
+

Testing the Base64 Module

+

Navigate to the add-on's test directory and delete the test-main.js file. In its place create a file called test-base64.js with the following contents:

+
var base64 = require("./base64");
+
+exports["test atob"] = function(assert) {
+      assert.ok(base64.atob("aGVsbG8=") == "hello", "atob works");
+}
+
+exports["test btoa"] = function(assert) {
+  assert.ok(base64.btoa("hello") == "aGVsbG8=", "btoa works");
+}
+
+exports["test empty string"] = function(assert) {
+  assert.throws(function() {
+                  base64.atob();
+                },
+                "empty string check works");
+}
+
+require("sdk/test").run(exports);
+
+

This file: exports three functions, each of which expects to receive a single argument which is an assert object. assert is supplied by the test/assert module and implements the CommonJS Unit Testing specification.

+ +

At this point your add-on ought to look like this:

+
  /base64
+      package.json
+      README.md
+      /doc
+          main.md
+      /lib
+          main.js
+          base64.js
+      /test
+          test-base64.js
+
+

Now execute cfx --verbose test from the add-on's root directory. You should see something like this:

+
Running tests on Firefox 13.0/Gecko 13.0 ({ec8030f7-c20a-464f-9b0e-13a3a9e97384}) under darwin/x86.
+info: executing 'test-base64.test atob'
+info: pass: atob works
+info: executing 'test-base64.test btoa'
+info: pass: btoa works
+info: executing 'test-base64.test empty string'
+info: pass: empty string check works
+
+3 of 3 tests passed.
+Total time: 5.172589 seconds
+Program terminated successfully.
+
+

What happens here is that cfx test:

+

Note the hyphen after "test" in the module name. cfx test will include a module called "test-myCode.js", but will exclude modules called "test_myCode.js" or "testMyCode.js".

+ +

Obviously, you don't have to pass the --verbose option to cfx if you don't want to; doing so just makes the output easier to read.

diff --git "a/files/zh-cn/mozilla/add-ons/sdk/tutorials/\346\227\245\345\277\227/index.html" "b/files/zh-cn/mozilla/add-ons/sdk/tutorials/\346\227\245\345\277\227/index.html" new file mode 100644 index 0000000000..e581a0811c --- /dev/null +++ "b/files/zh-cn/mozilla/add-ons/sdk/tutorials/\346\227\245\345\277\227/index.html" @@ -0,0 +1,62 @@ +--- +title: 日志 +slug: Mozilla/Add-ons/SDK/Tutorials/日志 +translation_of: Archive/Add-ons/Add-on_SDK/Tutorials/Logging +--- +

{{AddonSidebar}}

+ +
学习本教程之前你需要学习 jpm 基础.
+ +

DOM console 对象对调试 Javascript 非常有帮助。但是由于扩展程序无法访问 DOM 对象,sdk 提供了一个拥有大部分 DOM console 对象方法的全局 console 对象,包括打印错误日车、警告和数据信息的方法。你无需 require() 任何模块,就可以直接使用 console 对象。

+ +

使用 console.log() 方法来打印信息:

+ +
console.log("Hello World");
+
+ +

尝试:

+ + + +

Firefox 将会启动,并在你执行 jpm run 的命令行窗口显示下面的信息:

+ +
console.log: console: Hello world
+
+ +

在内容脚本(conent script)中使用 console

+ +

与 addon 主代码一样,你可以在内容脚本中直接使用 console 对象。下面这个扩展在内容脚本中调用了 console.log() 方法,作用是在控制台打印出每个打开的标签页内的 HTML 内容:

+ +
require("sdk/tabs").on("ready", function(tab) {
+  tab.attach({
+    contentScript: "console.log(document.body.innerHTML);"
+  });
+});
+
+ +

控制台输出

+ +

如果你是在命令行启动你的扩展(例如:执行 jpm runjpm test),那么控制台信息将在你使用的命令行界面中显示。

+ +

如果你将扩展安装到了 Firefox 中,控制台信息将显示在 Firefox 浏览器控制台中。

+ +

但请注意,默认情况下,任何已经安装的扩展不会在错误控制台中输出任何信息,包括使用扩展构建程序安装的扩展或者使用其它工具例如:Extension Auto-installer

+ +

关于此项内容的更多信息请参阅控制台参考文档 “日志等级”。

+ +

更多

+ +

完整的 console API,请看 API 参考文档

diff --git "a/files/zh-cn/mozilla/add-ons/sdk/tutorials/\346\267\273\345\212\240\344\270\200\344\270\252\350\217\234\345\215\225\351\241\271/index.html" "b/files/zh-cn/mozilla/add-ons/sdk/tutorials/\346\267\273\345\212\240\344\270\200\344\270\252\350\217\234\345\215\225\351\241\271/index.html" new file mode 100644 index 0000000000..77d743e806 --- /dev/null +++ "b/files/zh-cn/mozilla/add-ons/sdk/tutorials/\346\267\273\345\212\240\344\270\200\344\270\252\350\217\234\345\215\225\351\241\271/index.html" @@ -0,0 +1,49 @@ +--- +title: 添加菜单项 +slug: Mozilla/Add-ons/SDK/Tutorials/添加一个菜单项 +translation_of: Archive/Add-ons/Add-on_SDK/Tutorials/Add_a_Context_Menu_Item +--- +
+

学习本章前,您要先 安装SDK 和学习 cfx的基本用法

+
+

右键菜单模块添加右键菜单项或子菜单

+

下面的例子是增加了一个新的上下文菜单项。当页面被选中时才会显示该菜单项,选择的部分会被发送到main.js的add-on代码中,它只是记录:

+
var contextMenu = require("sdk/context-menu");
+ var menuItem = contextMenu.Item({
+  label: "Log Selection",
+  context: contextMenu.SelectionContext(),
+  contentScript: 'self.on("click", function () {' +
+                 '  var text = window.getSelection().toString();' +
+                 '  self.postMessage(text);' +
+                 '});',
+  onMessage: function (selectionText) {
+    console.log(selectionText);
+  }
+});
+

Try it: run the add-on, load a web page, select some text and right-click. You should see the new item appear:

+

试一试:运行该扩展,加载一个网页,选中一些文本并右键单击。你应该能看到新的项目出现:

+

+

点击,选中的文本记录到控制台:

+
info: elephantine lizard
+
+

细节

+


+ 这个add-on所有的操作是构建一个上下文菜单项。你不需要添加它:一旦你已经建立了项目,它会自动添加在正确的上下文。在这种情况下,构造函数接受四个选项:labelcontext,和contentscript,onMessage。

+

label

+


+ 标签是字符串的显示。

+

context

+


+ 上下文应该在不同的情境中显示它该做的显示。上下文菜单模块提供了一些简单的内置的上下文,包括selectioncontext(),这意味着:当页面被选中的时候将会显示菜单项。
+ 如果这些简单的背景是不够的,你可以使用脚本定义更复杂的环境。

+

contentScript

+

这将一个脚本项目。在这种情况下,脚本侦听用户点击该项目,然后选定文本用消息发送到add-on。

+

onMessage

+

onMessage属性提供附加的代码来响应来自连接到上下文菜单项脚本报文的一种方法。在这种情况下,它只是记录选定的文本。

+

所以:
+ 用户点击项目
+ 内容脚本的点击事件触发,和内容脚本检索选定的文本和发送邮件的附件
+ 附加的消息事件触发,并附加代码的处理函数是通过选定的文本,它的日志
+  

+

获取更多

+

如果想获取更多信息关于context-menu模块,查看context-menu API reference.

-- cgit v1.2.3-54-g00ecf