Permalink
Browse files

Resolve merge conflicts for the 1.14 release.

  • Loading branch information...
1 parent 42893f9 commit ad58ee0e01d0b7e00a5baa4717262fa35a9e2ecf @KWierso KWierso committed Mar 26, 2013
Showing with 7,838 additions and 1,870 deletions.
  1. +5 −0 README
  2. +120 −44 app-extension/bootstrap.js
  3. +2 −2 app-extension/install.rdf
  4. +0 −1 bin/activate.bat
  5. +1 −1 bin/cfx
  6. +4 −2 data/test-iframe.js
  7. +6 −0 doc/dev-guide-source/cfx-tool.md
  8. +29 −26 doc/dev-guide-source/guides/events.md
  9. +214 −113 doc/dev-guide-source/package-spec.md
  10. +6 −2 doc/dev-guide-source/tutorials/annotator/overview.md
  11. +12 −85 doc/dev-guide-source/tutorials/annotator/storing.md
  12. +1 −1 doc/dev-guide-source/tutorials/event-targets.md
  13. +7 −1 doc/dev-guide-source/tutorials/installation.md
  14. +9 −6 doc/module-source/sdk/context-menu.md
  15. +14 −11 doc/module-source/sdk/frame/hidden-frame.md
  16. +134 −0 doc/module-source/sdk/indexed-db.md
  17. +10 −0 doc/module-source/sdk/page-mod.md
  18. +19 −0 doc/module-source/sdk/panel.md
  19. +157 −47 doc/module-source/sdk/private-browsing.md
  20. +8 −0 doc/module-source/sdk/selection.md
  21. +9 −0 doc/module-source/sdk/self.md
  22. +6 −26 doc/module-source/sdk/simple-storage.md
  23. +4 −2 doc/module-source/sdk/system/events.md
  24. +17 −0 doc/module-source/sdk/tabs.md
  25. +11 −2 doc/module-source/sdk/tabs/utils.md
  26. +9 −0 doc/module-source/sdk/widget.md
  27. +20 −0 doc/module-source/sdk/window/utils.md
  28. +30 −13 doc/module-source/sdk/windows.md
  29. +0 −1 doc/static-files/base.html
  30. +3 −31 examples/annotator/lib/main.js
  31. +29 −3 lib/sdk/addon-page.js
  32. +1 −2 lib/sdk/addon/runner.js
  33. +93 −53 lib/sdk/console/plain-text.js
  34. +2 −2 lib/sdk/console/traceback.js
  35. +9 −2 lib/sdk/content/symbiont.js
  36. +49 −6 lib/sdk/content/worker.js
  37. +7 −4 lib/sdk/context-menu.js
  38. +58 −0 lib/sdk/core/disposable.js
  39. +42 −6 lib/sdk/core/heritage.js
  40. +4 −3 lib/sdk/core/promise.js
  41. +7 −16 lib/sdk/deprecated/tab-browser.js
  42. +7 −2 lib/sdk/deprecated/unit-test-finder.js
  43. +40 −13 lib/sdk/deprecated/unit-test.js
  44. +24 −8 lib/sdk/deprecated/window-utils.js
  45. +117 −118 lib/sdk/frame/hidden-frame.js
  46. +17 −3 lib/sdk/frame/utils.js
  47. +65 −0 lib/sdk/indexed-db.js
  48. +5 −7 lib/sdk/io/data.js
  49. +1 −1 lib/sdk/l10n/html.js
  50. +81 −9 lib/sdk/loader/cuddlefish.js
  51. +1 −1 lib/sdk/loader/sandbox.js
  52. +8 −3 lib/sdk/page-mod.js
  53. +31 −59 lib/sdk/panel.js
  54. +52 −0 lib/sdk/panel/window.js
  55. +52 −7 lib/sdk/private-browsing.js
  56. +49 −26 lib/sdk/private-browsing/utils.js
  57. +33 −0 lib/sdk/private-browsing/window/utils.js
  58. +47 −27 lib/sdk/selection.js
  59. +3 −3 lib/sdk/self.js
  60. +123 −6 lib/sdk/system/xul-app.js
  61. +5 −1 lib/sdk/tabs/common.js
  62. +26 −2 lib/sdk/tabs/helpers.js
  63. +1 −0 lib/sdk/tabs/namespace.js
  64. +63 −6 lib/sdk/tabs/tab-fennec.js
  65. +25 −3 lib/sdk/tabs/tab-firefox.js
  66. +7 −9 lib/sdk/tabs/tab.js
  67. +45 −4 lib/sdk/tabs/tabs-firefox.js
  68. +7 −9 lib/sdk/tabs/tabs.js
  69. +75 −10 lib/sdk/tabs/utils.js
  70. +2 −2 lib/sdk/test.js
  71. +17 −8 lib/sdk/test/assert.js
  72. +132 −7 lib/sdk/test/harness.js
  73. +43 −1 lib/sdk/test/httpd.js
  74. +44 −1 lib/sdk/test/loader.js
  75. +2 −1 lib/sdk/test/runner.js
  76. +8 −0 lib/sdk/util/array.js
  77. +9 −0 lib/sdk/util/deprecate.js
  78. +77 −81 lib/sdk/widget.js
  79. +14 −2 lib/sdk/window/browser.js
  80. +41 −0 lib/sdk/window/helpers.js
  81. +114 −15 lib/sdk/window/utils.js
  82. +7 −9 lib/sdk/windows.js
  83. +7 −3 lib/sdk/windows/dom.js
  84. +3 −4 lib/sdk/windows/fennec.js
  85. +25 −4 lib/sdk/windows/firefox.js
  86. +1 −0 lib/sdk/windows/loader.js
  87. +12 −51 lib/sdk/windows/tabs-fennec.js
  88. +21 −8 lib/sdk/windows/tabs-firefox.js
  89. +112 −0 lib/test.js
  90. +25 −13 lib/toolkit/loader.js
  91. +83 −13 python-lib/cuddlefish/__init__.py
  92. +32 −14 python-lib/cuddlefish/manifest.py
  93. +1 −1 python-lib/cuddlefish/packaging.py
  94. +2 −0 python-lib/cuddlefish/prefs.py
  95. +1 −0 python-lib/cuddlefish/property_parser.py
  96. +2 −2 python-lib/cuddlefish/rdf.py
  97. +80 −12 python-lib/cuddlefish/runner.py
  98. +3 −2 python-lib/cuddlefish/templates.py
  99. +11 −10 python-lib/cuddlefish/tests/test_init.py
  100. +18 −18 python-lib/cuddlefish/tests/test_linker.py
  101. +3 −3 python-lib/cuddlefish/tests/test_xpi.py
  102. +12 −2 python-lib/cuddlefish/xpi.py
  103. +6 −0 test/addons/l10n/locale/en-GB.properties
  104. +4 −0 test/addons/l10n/main.js
  105. +2 −2 test/{test-layout-change.js → addons/layout-change/main.js}
  106. +3 −0 test/addons/layout-change/package.json
  107. +20 −0 test/addons/packed/main.js
  108. +4 −0 test/addons/packed/package.json
  109. +20 −0 test/addons/private-browsing-supported/main.js
  110. +6 −0 test/addons/private-browsing-supported/package.json
  111. +144 −0 test/addons/private-browsing-supported/test-global-private-browsing.js
  112. +111 −0 test/addons/private-browsing-supported/test-page-mod.js
  113. +16 −0 test/addons/private-browsing-supported/test-panel.js
  114. +164 −0 test/addons/private-browsing-supported/test-private-browsing.js
  115. +458 −0 test/addons/private-browsing-supported/test-selection.js
  116. +32 −0 test/addons/private-browsing-supported/test-tabs.js
  117. +255 −0 test/addons/private-browsing-supported/test-windows.js
  118. +18 −0 test/addons/unpacked/main.js
  119. +4 −0 test/addons/unpacked/package.json
  120. +1 −1 test/commonjs-test-adapter/asserts.js
  121. +1 −1 test/modules/tiger.js
  122. +6 −5 test/pagemod-test-helpers.js
  123. +250 −0 test/private-browsing/global.js
  124. +23 −8 test/{private-browsing-helper.js → private-browsing/helper.js}
  125. +27 −0 test/private-browsing/tabs.js
  126. +77 −0 test/private-browsing/windows.js
  127. +76 −29 test/tabs/test-fennec-tabs.js
  128. +48 −0 test/tabs/test-firefox-tabs.js
  129. +91 −2 test/test-addon-page.js
  130. +2 −2 test/test-app-strings.js
  131. +47 −16 test/test-content-worker.js
  132. +132 −8 test/test-context-menu.js
  133. +43 −26 test/test-deprecate.js
  134. +166 −0 test/test-disposable.js
  135. +9 −10 test/test-event-core.js
  136. +13 −0 test/test-events.js
  137. +4 −1 test/test-heritage.js
  138. +43 −0 test/test-hidden-frame.js
  139. +3 −1 test/test-httpd.js
  140. +132 −0 test/test-indexed-db.js
  141. +9 −16 test/test-loader.js
  142. +1 −1 test/test-net-url.js
  143. +6 −10 test/test-observer-service.js
  144. +33 −4 test/test-packaging.js
  145. +162 −31 test/test-page-mod.js
  146. +219 −6 test/test-panel.js
  147. +88 −13 test/test-plain-text-console.js
  148. +121 −195 test/test-private-browsing.js
  149. +66 −18 test/test-request.js
  150. +314 −24 test/test-selection.js
  151. +3 −0 test/test-self.js
  152. +2 −2 test/test-simple-prefs.js
  153. +216 −0 test/test-system-events.js
  154. +6 −8 test/test-tab-browser.js
  155. +70 −0 test/test-tab-utils.js
  156. +2 −1 test/test-tab.js
  157. +95 −2 test/test-tabs-common.js
  158. +60 −0 test/test-test-loader.js
  159. +3 −3 test/test-traceback.js
  160. +28 −5 test/test-unit-test.js
  161. +6 −11 test/test-unload.js
  162. +0 −11 test/test-url.js
  163. +25 −35 test/test-widget.js
  164. +152 −0 test/test-window-utils-global-private-browsing.js
  165. +227 −0 test/test-window-utils-private-browsing.js
  166. +40 −78 test/test-window-utils.js
  167. +29 −9 test/test-window-utils2.js
  168. +0 −46 test/test-windows-private-browsing.js
  169. +115 −15 test/test-xul-app.js
  170. +137 −76 test/windows/test-firefox-windows.js
View
5 README
@@ -38,3 +38,8 @@ Bugs
* file a bug: https://bugzilla.mozilla.org/enter_bug.cgi?product=Add-on%20SDK
+Style Guidelines
+--------------------
+
+* https://github.com/mozilla/addon-sdk/wiki/Coding-style-guide
+
View
@@ -19,10 +19,20 @@ const resourceHandler = ioService.getProtocolHandler('resource').
const systemPrincipal = CC('@mozilla.org/systemprincipal;1', 'nsIPrincipal')();
const scriptLoader = Cc['@mozilla.org/moz/jssubscript-loader;1'].
getService(Ci.mozIJSSubScriptLoader);
+const prefService = Cc['@mozilla.org/preferences-service;1'].
+ getService(Ci.nsIPrefService).
+ QueryInterface(Ci.nsIPrefBranch);
+const appInfo = Cc["@mozilla.org/xre/app-info;1"].
+ getService(Ci.nsIXULAppInfo);
+const vc = Cc["@mozilla.org/xpcom/version-comparator;1"].
+ getService(Ci.nsIVersionComparator);
+
const REASON = [ 'unknown', 'startup', 'shutdown', 'enable', 'disable',
'install', 'uninstall', 'upgrade', 'downgrade' ];
+const bind = Function.call.bind(Function.bind);
+
let loader = null;
let unload = null;
let cuddlefishSandbox = null;
@@ -53,29 +63,6 @@ function readURI(uri) {
return data;
}
-// Utility function that converts cfx-py generated paths to a
-// module ids.
-function path2id(path) {
- // Strips out `/lib` and `.js` from package/lib/path.js
- return path.replace(/([^\/]*)\/lib/, '$1').replace(/.js$/, '');
-}
-// Utility function that takes old manifest format and creates a manifest
-// in a new format: https://github.com/mozilla/addon-sdk/wiki/JEP-Linker
-function manifestV2(manifest) {
- return Object.keys(manifest).reduce(function(result, path) {
- let entry = manifest[path];
- let id = path2id(path);
- let requirements = entry.requirements || {};
- result[id] = {
- requirements: Object.keys(requirements).reduce(function(result, path) {
- result[path] = path2id(requirements[path].path);
- return result;
- }, {})
- };
- return result
- }, {});
-}
-
// We don't do anything on install & uninstall yet, but in a future
// we should allow add-ons to cleanup after uninstall.
function install(data, reason) {}
@@ -95,6 +82,15 @@ function startup(data, reasonCode) {
let id = options.jetpackID;
let name = options.name;
+
+ // Clean the metadata
+ options.metadata[name]['permissions'] = options.metadata[name]['permissions'] || {};
+
+ // freeze the permissionss
+ Object.freeze(options.metadata[name]['permissions']);
+ // freeze the metadata
+ Object.freeze(options.metadata[name]);
+
// Register a new resource 'domain' for this addon which is mapping to
// XPI's `resources` folder.
// Generate the domain name by using jetpack ID, which is the extension ID
@@ -113,28 +109,93 @@ function startup(data, reasonCode) {
resourceHandler.setSubstitution(domain, resourcesURI);
// Create path to URLs mapping supported by loader.
- let paths = Object.keys(options.metadata).reduce(function(result, name) {
- result[name + '/'] = prefixURI + name + '/lib/'
- result[name + '/tests/'] = prefixURI + name + '/tests/'
- return result
- }, {
+ let paths = {
// Relative modules resolve to add-on package lib
'./': prefixURI + name + '/lib/',
- 'toolkit/': 'resource://gre/modules/toolkit/',
- '': 'resources:///modules/'
- });
+ './tests/': prefixURI + name + '/tests/',
+ '': 'resource://gre/modules/commonjs/'
+ };
+
+ // Maps addon lib and tests ressource folders for each package
+ paths = Object.keys(options.metadata).reduce(function(result, name) {
+ result[name + '/'] = prefixURI + name + '/lib/'
+ result[name + '/tests/'] = prefixURI + name + '/tests/'
+ return result;
+ }, paths);
+
+ // We need to map tests folder when we run sdk tests whose package name
+ // is stripped
+ if (name == 'addon-sdk')
+ paths['tests/'] = prefixURI + name + '/tests/';
+
+ let useBundledSDK = options['force-use-bundled-sdk'];
+ if (!useBundledSDK) {
+ try {
+ useBundledSDK = prefService.getBoolPref("extensions.addon-sdk.useBundledSDK");
+ }
+ catch (e) {
+ // Pref doesn't exist, allow using Firefox shipped SDK
+ }
+ }
+
+ // Starting with Firefox 21.0a1, we start using modules shipped into firefox
+ // Still allow using modules from the xpi if the manifest tell us to do so.
+ // And only try to look for sdk modules in xpi if the xpi actually ship them
+ if (options['is-sdk-bundled'] &&
+ (vc.compare(appInfo.version, '21.0a1') < 0 || useBundledSDK)) {
+ // Maps sdk module folders to their resource folder
+ paths[''] = prefixURI + 'addon-sdk/lib/';
+ // test.js is usually found in root commonjs or SDK_ROOT/lib/ folder,
+ // so that it isn't shipped in the xpi. Keep a copy of it in sdk/ folder
+ // until we no longer support SDK modules in XPI:
+ paths['test'] = prefixURI + 'addon-sdk/lib/sdk/test.js';
+ }
+
+ // Retrieve list of module folder overloads based on preferences in order to
+ // eventually used a local modules instead of files shipped into Firefox.
+ let branch = prefService.getBranch('extensions.modules.' + id + '.path');
+ paths = branch.getChildList('', {}).reduce(function (result, name) {
+ // Allows overloading of any sub folder by replacing . by / in pref name
+ let path = name.substr(1).split('.').join('/');
+ // Only accept overloading folder by ensuring always ending with `/`
+ if (path) path += '/';
+ let fileURI = branch.getCharPref(name);
+
+ // On mobile, file URI has to end with a `/` otherwise, setSubstitution
+ // takes the parent folder instead.
+ if (fileURI[fileURI.length-1] !== '/')
+ fileURI += '/';
+
+ // Maps the given file:// URI to a resource:// in order to avoid various
+ // failure that happens with file:// URI and be close to production env
+ let resourcesURI = ioService.newURI(fileURI, null, null);
+ let resName = 'extensions.modules.' + domain + '.commonjs.path' + name;
+ resourceHandler.setSubstitution(resName, resourcesURI);
+
+ result[path] = 'resource://' + resName + '/';
+ return result;
+ }, paths);
// Make version 2 of the manifest
- let manifest = manifestV2(options.manifest);
+ let manifest = options.manifest;
// Import `cuddlefish.js` module using a Sandbox and bootstrap loader.
- let cuddlefishURI = prefixURI + options.loader;
+ let cuddlefishPath = 'loader/cuddlefish.js';
+ let cuddlefishURI = 'resource://gre/modules/commonjs/sdk/' + cuddlefishPath;
+ if (paths['sdk/']) { // sdk folder has been overloaded
+ // (from pref, or cuddlefish is still in the xpi)
+ cuddlefishURI = paths['sdk/'] + cuddlefishPath;
+ }
+ else if (paths['']) { // root modules folder has been overloaded
+ cuddlefishURI = paths[''] + 'sdk/' + cuddlefishPath;
+ }
+
cuddlefishSandbox = loadSandbox(cuddlefishURI);
let cuddlefish = cuddlefishSandbox.exports;
// Normalize `options.mainPath` so that it looks like one that will come
// in a new version of linker.
- let main = path2id(options.mainPath);
+ let main = options.mainPath;
unload = cuddlefish.unload;
loader = cuddlefish.Loader({
@@ -173,11 +234,12 @@ function startup(data, reasonCode) {
profileMemory: options.profileMemory,
stopOnError: options.stopOnError,
verbose: options.verbose,
+ parseable: options.parseable,
}
}
});
- let module = cuddlefish.Module('addon-sdk/sdk/loader/cuddlefish', cuddlefishURI);
+ let module = cuddlefish.Module('sdk/loader/cuddlefish', cuddlefishURI);
let require = cuddlefish.Require(loader, module);
require('sdk/addon/runner').startup(reason, {
@@ -204,8 +266,13 @@ function loadSandbox(uri) {
// correctly
sandbox.exports = {};
sandbox.module = { uri: uri, exports: sandbox.exports };
- sandbox.require = function () {
- throw new Error("Bootstrap sandbox `require` method isn't implemented.");
+ sandbox.require = function (id) {
+ if (id !== "chrome")
+ throw new Error("Bootstrap sandbox `require` method isn't implemented.");
+
+ return Object.freeze({ Cc: Cc, Ci: Ci, Cu: Cu, Cr: Cr, Cm: Cm,
+ CC: bind(CC, Components), components: Components,
+ ChromeWorker: ChromeWorker });
};
scriptLoader.loadSubScript(uri, sandbox, 'UTF-8');
return sandbox;
@@ -228,12 +295,16 @@ function shutdown(data, reasonCode) {
if (loader) {
unload(loader, reason);
unload = null;
- // Avoid leaking all modules when something goes wrong with one particular
- // module. Do not clean it up immediatly in order to allow executing some
- // actions on addon disabling.
- // We need to keep a reference to the timer, otherwise it is collected
- // and won't ever fire.
- nukeTimer = setTimeout(nukeModules, 1000);
+
+ // Don't waste time cleaning up if the application is shutting down
+ if (reason != "shutdown") {
+ // Avoid leaking all modules when something goes wrong with one particular
+ // module. Do not clean it up immediatly in order to allow executing some
+ // actions on addon disabling.
+ // We need to keep a reference to the timer, otherwise it is collected
+ // and won't ever fire.
+ nukeTimer = setTimeout(nukeModules, 1000);
+ }
}
};
@@ -253,8 +324,13 @@ function nukeModules() {
}
loader = null;
- // Unload sandbox used to evaluate loader.js
+ // both `toolkit/loader` and `system/xul-app` are loaded as JSM's via
+ // `cuddlefish.js`, and needs to be unloaded to avoid memory leaks, when
+ // the addon is unload.
+
unloadSandbox(cuddlefishSandbox.loaderSandbox);
+ unloadSandbox(cuddlefishSandbox.xulappSandbox);
+
// Bug 764840: We need to unload cuddlefish otherwise it will stay alive
// and keep a reference to this compartment.
unloadSandbox(cuddlefishSandbox);
@@ -17,8 +17,8 @@
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
- <em:minVersion>18.0</em:minVersion>
- <em:maxVersion>19.*</em:maxVersion>
+ <em:minVersion>19.0</em:minVersion>
+ <em:maxVersion>20.*</em:maxVersion>
</Description>
</em:targetApplication>
View
@@ -84,7 +84,6 @@ set PYTHONVERSION=
set key=
set reg=
set _tokens=
-cd "%VIRTUAL_ENV%"
python -c "from jetpack_sdk_env import welcome; welcome()"
GOTO :EOF
View
@@ -17,7 +17,7 @@ if 'CUDDLEFISH_ROOT' not in os.environ:
# add our own python-lib path to the python module search path.
python_lib_dir = os.path.join(cuddlefish_root, "python-lib")
if python_lib_dir not in sys.path:
- sys.path.append(python_lib_dir)
+ sys.path.insert(0, python_lib_dir)
# now export to env so sub-processes get it too
if 'PYTHONPATH' not in os.environ:
View
@@ -1,9 +1,11 @@
-var count = 0
+var count = 0;
setTimeout(function() {
window.addEventListener("message", function(msg) {
- if (++count > 1) self.postMessage(msg.data);
+ if (++count > 1) {
+ self.postMessage(msg.data);
+ }
else msg.source.postMessage(msg.data, '*');
});
@@ -784,6 +784,12 @@ documentation. Use `cfx testcfx -v` for the specific list of tests.
This accepts the same options as `cfx test`.
+### cfx testaddons ###
+
+This will run a number of test add-ons that are packaged with the SDK.
+
+This accepts the same options as `cfx test`.
+
### cfx testpkgs ###
This will test all of the available CommonJS packages. Note that the number
@@ -51,19 +51,15 @@ is specific to an event emitter and is included with its documentation.
whenever the event occurs. The arguments that will be passed to the listener
are specific to an event type and are documented with the event emitter.
-For example, the following add-on registers two listeners with the
-[`private-browsing`](modules/sdk/private-browsing.html) module to
-listen for the `start` and `stop` events, and logs a string to the console
-reporting the change:
+For example, the following add-on registers a listener with the
+[`tabs`](modules/sdk/tabs.html) module to
+listen for the `ready` event, and logs a string to the console
+reporting the event:
- var pb = require("sdk/private-browsing");
-
- pb.on("start", function() {
- console.log("Private browsing is on");
- });
+ var tabs = require("sdk/tabs");
- pb.on("stop", function() {
- console.log("Private browsing is off");
+ tabs.on("ready", function () {
+ console.log("tab loaded");
});
It is not possible to enumerate the set of listeners for a given event.
@@ -73,9 +69,8 @@ the event.
### Adding Listeners in Constructors ###
-Event emitters may be modules, as is the case for the
-`private-browsing` events, or they may be objects returned by
-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
@@ -126,28 +121,36 @@ 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 private-browsing's `start`
-event, enter and exit private browsing, then remove the first listener and
-enter private browsing again.
+In the following add-on, we add two listeners to the
+[`tabs` module's `ready` event](modules/sdk/tabs.html#ready).
+One of the handler functions removes the listener again.
- var pb = require("sdk/private-browsing");
+Then we open two tabs.
+
+ var tabs = require("sdk/tabs");
function listener1() {
console.log("Listener 1");
- pb.removeListener("start", listener1);
+ tabs.removeListener("ready", listener1);
}
function listener2() {
console.log("Listener 2");
}
- pb.on("start", listener1);
- pb.on("start", listener2);
+ 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:
- pb.activate();
- pb.deactivate();
- pb.activate();
+<pre>
+info: tabevents: Listener 1
+info: tabevents: Listener 2
+info: tabevents: Listener 2
+</pre>
-Removing listeners is optional since they will be removed in any case
-when the application or add-on is unloaded.
+Listeners will be removed automatically when the add-on is unloaded.
Oops, something went wrong.

0 comments on commit ad58ee0

Please sign in to comment.