Bug 731779: Integrate the Add-on SDK loader and API libraries into Firefox (uplifting from addon-sdk a16bbd5772880b578a939eeb65102bca6560d494)

This commit is contained in:
Dave Townsend 2013-02-01 13:17:34 -08:00
Родитель a3425a057a
Коммит 37d1fc5b0d
790 изменённых файлов: 96215 добавлений и 0 удалений

5
addon-sdk/source/.gitattributes поставляемый Normal file
Просмотреть файл

@ -0,0 +1,5 @@
.gitignore export-ignore
.hgignore export-ignore
.hgtags export-ignore
.gitattributes export-ignore
python-lib/cuddlefish/_version.py export-subst

20
addon-sdk/source/.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1,20 @@
local.json
python-lib/cuddlefish/app-extension/components/jetpack.xpt
testdocs.tgz
jetpack-sdk-docs.tgz
.test_tmp/
doc/dev-guide/
doc/index.html
doc/modules/
doc/status.md5
packages/*
# Python
*.pyc
# OSX
*.DS_Store
# Windows
*Thumbs.db

Просмотреть файл

@ -0,0 +1,14 @@
syntax: glob
local.json
python-lib/cuddlefish/app-extension/components/jetpack.xpt
testdocs.tgz
jetpack-sdk-docs.tgz
.test_tmp
jetpack-sdk-docs
# These should really be in a global .hgignore, but such a thing
# seems ridiculously confusing to set up, so we'll include some
# common intermediate files here.
*.pyc
*~
*.DS_Store

64
addon-sdk/source/.hgtags Normal file
Просмотреть файл

@ -0,0 +1,64 @@
39c45211aa250d43f6e54b776a60ef07a7a4cbb6 jep-31-examples
0120642297b217680e75e68c4005a93b16466001 0.1rc1
1fd7993ff2cec39c5948a1b2535b78c2d4c65858 0.1rc2
530e51d02922ad05fd43e8cf113618068216b0cf 0.1
ed11a9e3ae23aefd56ac88042b9816887d10a04f 0.2rc1
f70ab8998abdce809bda93408435f83648909cdd 0.2rc2
f70ab8998abdce809bda93408435f83648909cdd 0.2
436968c6ec833d9c18a15426be26595a44b79283 0.3rc1
a3b091d29607f0bcecf77fe27801ba901142f1c9 0.3
06031b56312ad7f1880a9620b4d26ce253a23b23 0.3rc2
a3b091d29607f0bcecf77fe27801ba901142f1c9 0.3
4b08c8a8c1eef71ac3aa54e004b68e87049ca56b 0.3
6884e54fd6a1cfa9ec63910c46d2cbf51495b584 0.3rc3
4b08c8a8c1eef71ac3aa54e004b68e87049ca56b 0.3
cd70b7140ec20689255f1248512a1e5b9cf90a32 0.3
d9d733666ca8738e9665ba816d5d12f41fc0d216 0.4rc1
8d2871fc10df16740273f53ef05815310da4b210 0.4
201e60065b1aef23259a71756f93207cffa2abd5 0.4rc2
8d2871fc10df16740273f53ef05815310da4b210 0.4
c0e5bbdcafad4572a5de22e533543bf3330a869e 0.4
201e60065b1aef23259a71756f93207cffa2abd5 0.4rc2
a8dceaefd6f3376b7bc1619cccd854591b76ecc0 0.4rc2
c0e5bbdcafad4572a5de22e533543bf3330a869e 0.4
dfa3bdfedb12d743b5e0ac19d0ecf116b567a0b3 0.4
b519a0848585e2a665a14aac2fb6f15c0a839e6c 0.4rc3
dfa3bdfedb12d743b5e0ac19d0ecf116b567a0b3 0.4
05acdf4987c28d8cc1d5a12d87e71b1445fdd6b7 0.4
04b5afe48d1cee819d9f0af72ed16556a075b67d 0.5rc1
1968150561097bcfeeb28a75801ef881d2fcbbf5 0.5
3236e68e6f2e36353e47f01a9b8acfce56686263 0.5rc2
1968150561097bcfeeb28a75801ef881d2fcbbf5 0.5
0097da78aa23be8b7a5a811c4be0e3e35395af43 0.5
be7016807a69c910ac31ff068a6c2bde0a25c9d4 0.5rc3
0097da78aa23be8b7a5a811c4be0e3e35395af43 0.5
83e97107e4a381b898c780b32e30e48b0e4d37f3 0.5
182a405aa393dbd412e671e8516d2decd8e8d80c 0.6rc1
96799a3d96abfdb1408f9b8e30479668f1feed70 0.6
c5f6cdfa36b2ad33d8017e6844992de0b0a6c0e0 0.6rc2
96799a3d96abfdb1408f9b8e30479668f1feed70 0.6
65c393fbc6af7db79a4a2e903d06b17e90d254bf 0.6
3e747e3e307a4e853d468b03357f96b2172bad37 0.7rc1
b0e97e12eb9115f08127cc447d459f871c5046ed 0.7
37288479d249168668abd60a907620fa7f593128 0.7rc2
b0e97e12eb9115f08127cc447d459f871c5046ed 0.7
db0eabc310619d00ce47372ecaafba7d2539b601 0.7
37288479d249168668abd60a907620fa7f593128 0.7rc2
b90bedb83525d8f32ebb4d3833e8d91efa991b6d 0.7rc2
db0eabc310619d00ce47372ecaafba7d2539b601 0.7
e6ea2e5c5274cbbb0278dc976844dbf51818a95e 0.7
b90bedb83525d8f32ebb4d3833e8d91efa991b6d 0.7rc2
9b978d823509de5d52ca222b9907f72591c68c5a 0.7rc2
e6ea2e5c5274cbbb0278dc976844dbf51818a95e 0.7
cd71e1e77c93adf169b9f38184b1f95d423876ba 0.7
67e3fd285fa955898a2641226c86725365d68b95 0.8rc1
b2f9405db6d74c21601ca9c250887411c837396a 0.8
67e3fd285fa955898a2641226c86725365d68b95 0.8rc1
56e8179ff5b467959ddf33978558d7eb5346ee53 0.8rc1
b2f9405db6d74c21601ca9c250887411c837396a 0.8
51a22a186b1c7d8e7292a5ec9670752dad223ca1 0.8
d150cca77cfef60dee2f2fe7c892fd7fc5ce83c4 0.9rc1
1e106beb57f65fabc32682f277de51a8101c919b 0.9
173d56fa8cc1c5622eae70f1ae88ef789de4722b 0.9rc2
1e106beb57f65fabc32682f277de51a8101c919b 0.9
cde12eefd178d7a0ee7d5e9ae77f3089bcbdc6ec 0.9

30
addon-sdk/source/LICENSE Normal file
Просмотреть файл

@ -0,0 +1,30 @@
The files which make up the SDK are developed by Mozilla and licensed
under the MPL 2.0 (http://mozilla.org/MPL/2.0/), with the exception of the
components listed below, which are made available by their authors under
the licenses listed alongside.
syntaxhighlighter
------------------
doc/static-files/syntaxhighlighter
Made available under the MIT license.
jQuery
------
examples/reddit-panel/data/jquery-1.4.4.min.js
examples/annotator/data/jquery-1.4.2.min.js
Made available under the MIT license.
simplejson
----------
python-lib/simplejson
Made available under the MIT license.
Python Markdown
---------------
python-lib/markdown
Made available under the BSD license.
LibraryDetector
---------------
examples/library-detector/data/library-detector.js
Made available under the MIT license.

45
addon-sdk/source/README Normal file
Просмотреть файл

@ -0,0 +1,45 @@
Add-on SDK README
==================
Before proceeding, please make sure you've installed Python 2.5,
2.6, or 2.7 (if it's not already on your system):
http://python.org/download/
Note that Python 3.0 and 3.1 are not supported in this release.
For Windows users, MozillaBuild (https://wiki.mozilla.org/MozillaBuild)
will install the correct version of Python and the MSYS package, which
will make it easier to work with the SDK.
To get started, first enter the same directory that this README file
is in (the SDK's root directory) using a shell program. On Unix systems
or on Windows with MSYS, you can execute the following command:
source bin/activate
Windows users using cmd.exe should instead run:
bin\activate.bat
Then run:
cfx docs
This should start a documentation server and open a web browser
with further instructions.
If you get an error when running cfx or have any other problems getting
started, see the "Troubleshooting" guide at:
https://addons.mozilla.org/en-US/developers/docs/sdk/latest/dev-guide/tutorials/troubleshooting.html
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

Просмотреть файл

@ -0,0 +1,11 @@
[App]
Vendor=Varma
Name=Test App
Version=1.0
BuildID=20060101
Copyright=Copyright (c) 2009 Atul Varma
ID=xulapp@toolness.com
[Gecko]
MinVersion=1.9.2.0
MaxVersion=2.0.*

275
addon-sdk/source/app-extension/bootstrap.js поставляемый Normal file
Просмотреть файл

@ -0,0 +1,275 @@
/* vim:set ts=2 sw=2 sts=2 expandtab */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// @see http://mxr.mozilla.org/mozilla-central/source/js/src/xpconnect/loader/mozJSComponentLoader.cpp
'use strict';
// IMPORTANT: Avoid adding any initialization tasks here, if you need to do
// something before add-on is loaded consider addon/runner module instead!
const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu,
results: Cr, manager: Cm } = Components;
const ioService = Cc['@mozilla.org/network/io-service;1'].
getService(Ci.nsIIOService);
const resourceHandler = ioService.getProtocolHandler('resource').
QueryInterface(Ci.nsIResProtocolHandler);
const systemPrincipal = CC('@mozilla.org/systemprincipal;1', 'nsIPrincipal')();
const scriptLoader = Cc['@mozilla.org/moz/jssubscript-loader;1'].
getService(Ci.mozIJSSubScriptLoader);
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;
let nukeTimer = null;
// Utility function that synchronously reads local resource from the given
// `uri` and returns content string.
function readURI(uri) {
let ioservice = Cc['@mozilla.org/network/io-service;1'].
getService(Ci.nsIIOService);
let channel = ioservice.newChannel(uri, 'UTF-8', null);
let stream = channel.open();
let cstream = Cc['@mozilla.org/intl/converter-input-stream;1'].
createInstance(Ci.nsIConverterInputStream);
cstream.init(stream, 'UTF-8', 0, 0);
let str = {};
let data = '';
let read = 0;
do {
read = cstream.readString(0xffffffff, str);
data += str.value;
} while (read != 0);
cstream.close();
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) {}
function uninstall(data, reason) {}
function startup(data, reasonCode) {
try {
let reason = REASON[reasonCode];
// URI for the root of the XPI file.
// 'jar:' URI if the addon is packed, 'file:' URI otherwise.
// (Used by l10n module in order to fetch `locale` folder)
let rootURI = data.resourceURI.spec;
// TODO: Maybe we should perform read harness-options.json asynchronously,
// since we can't do anything until 'sessionstore-windows-restored' anyway.
let options = JSON.parse(readURI(rootURI + './harness-options.json'));
let id = options.jetpackID;
let name = options.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
// by stripping common characters that doesn't work as a domain name:
let uuidRe =
/^\{([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\}$/;
let domain = id.
toLowerCase().
replace(/@/g, '-at-').
replace(/\./g, '-dot-').
replace(uuidRe, '$1');
let prefixURI = 'resource://' + domain + '/';
let resourcesURI = ioService.newURI(rootURI + '/resources/', null, null);
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
}, {
// Relative modules resolve to add-on package lib
'./': prefixURI + name + '/lib/',
'toolkit/': 'resource://gre/modules/toolkit/',
'': 'resources:///modules/'
});
// Make version 2 of the manifest
let manifest = manifestV2(options.manifest);
// Import `cuddlefish.js` module using a Sandbox and bootstrap loader.
let cuddlefishURI = prefixURI + options.loader;
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);
unload = cuddlefish.unload;
loader = cuddlefish.Loader({
paths: paths,
// modules manifest.
manifest: manifest,
// Add-on ID used by different APIs as a unique identifier.
id: id,
// Add-on name.
name: name,
// Add-on version.
version: options.metadata[name].version,
// Add-on package descriptor.
metadata: options.metadata[name],
// Add-on load reason.
loadReason: reason,
prefixURI: prefixURI,
// Add-on URI.
rootURI: rootURI,
// options used by system module.
// File to write 'OK' or 'FAIL' (exit code emulation).
resultFile: options.resultFile,
// File to write stdout.
logFile: options.logFile,
// Arguments passed as --static-args
staticArgs: options.staticArgs,
// Arguments related to test runner.
modules: {
'@test/options': {
allTestModules: options.allTestModules,
iterations: options.iterations,
filter: options.filter,
profileMemory: options.profileMemory,
stopOnError: options.stopOnError,
verbose: options.verbose,
parseable: options.parseable,
}
}
});
let module = cuddlefish.Module('addon-sdk/sdk/loader/cuddlefish', cuddlefishURI);
let require = cuddlefish.Require(loader, module);
require('sdk/addon/runner').startup(reason, {
loader: loader,
main: main,
prefsURI: rootURI + 'defaults/preferences/prefs.js'
});
} catch (error) {
dump('Bootstrap error: ' + error.message + '\n' +
(error.stack || error.fileName + ': ' + error.lineNumber) + '\n');
throw error;
}
};
function loadSandbox(uri) {
let proto = {
sandboxPrototype: {
loadSandbox: loadSandbox,
ChromeWorker: ChromeWorker
}
};
let sandbox = Cu.Sandbox(systemPrincipal, proto);
// Create a fake commonjs environnement just to enable loading loader.js
// correctly
sandbox.exports = {};
sandbox.module = { uri: uri, exports: sandbox.exports };
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;
}
function unloadSandbox(sandbox) {
if ("nukeSandbox" in Cu)
Cu.nukeSandbox(sandbox);
}
function setTimeout(callback, delay) {
let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
timer.initWithCallback({ notify: callback }, delay,
Ci.nsITimer.TYPE_ONE_SHOT);
return timer;
}
function shutdown(data, reasonCode) {
let reason = REASON[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);
}
};
function nukeModules() {
nukeTimer = null;
// module objects store `exports` which comes from sandboxes
// We should avoid keeping link to these object to avoid leaking sandboxes
for (let key in loader.modules) {
delete loader.modules[key];
}
// Direct links to sandboxes should be removed too
for (let key in loader.sandboxes) {
let sandbox = loader.sandboxes[key];
delete loader.sandboxes[key];
// Bug 775067: From FF17 we can kill all CCW from a given sandbox
unloadSandbox(sandbox);
}
loader = null;
// 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);
cuddlefishSandbox = null;
}

Просмотреть файл

@ -0,0 +1,33 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
<Description about="urn:mozilla:install-manifest">
<em:id>xulapp@toolness.com</em:id>
<em:version>1.0</em:version>
<em:type>2</em:type>
<em:bootstrap>true</em:bootstrap>
<em:unpack>false</em:unpack>
<!-- Firefox -->
<em:targetApplication>
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>18.0</em:minVersion>
<em:maxVersion>21.0a1</em:maxVersion>
</Description>
</em:targetApplication>
<!-- Front End MetaData -->
<em:name>Test App</em:name>
<em:description>Harness for tests.</em:description>
<em:creator>Mozilla Corporation</em:creator>
<em:homepageURL></em:homepageURL>
<em:optionsType></em:optionsType>
<em:updateURL></em:updateURL>
</Description>
</RDF>

Просмотреть файл

@ -0,0 +1,86 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# This file must be used with "source bin/activate" *from bash*
# you cannot run it directly
deactivate () {
if [ -n "$_OLD_VIRTUAL_PATH" ] ; then
PATH="$_OLD_VIRTUAL_PATH"
export PATH
unset _OLD_VIRTUAL_PATH
fi
# This should detect bash and zsh, which have a hash command that must
# be called to get it to forget past commands. Without forgetting
# past commands the $PATH changes we made may not be respected
if [ -n "$BASH" -o -n "$ZSH_VERSION" ] ; then
hash -r
fi
if [ -n "$_OLD_VIRTUAL_PS1" ] ; then
PS1="$_OLD_VIRTUAL_PS1"
export PS1
unset _OLD_VIRTUAL_PS1
fi
if [ -n "$_OLD_PYTHONPATH" ] ; then
PYTHONPATH="$_OLD_PYTHONPATH"
export PYTHONPATH
unset _OLD_PYTHONPATH
fi
unset CUDDLEFISH_ROOT
unset VIRTUAL_ENV
if [ ! "$1" = "nondestructive" ] ; then
# Self destruct!
unset deactivate
fi
}
# unset irrelavent variables
deactivate nondestructive
_OLD_PYTHONPATH="$PYTHONPATH"
_OLD_VIRTUAL_PATH="$PATH"
VIRTUAL_ENV="`pwd`"
if [ "x$OSTYPE" = "xmsys" ] ; then
CUDDLEFISH_ROOT="`pwd -W | sed s,/,\\\\\\\\,g`"
PATH="`pwd`/bin:$PATH"
# msys will convert any env vars with PATH in it to use msys
# form and will unconvert before launching
PYTHONPATH="`pwd -W`/python-lib;$PYTHONPATH"
else
CUDDLEFISH_ROOT="$VIRTUAL_ENV"
PYTHONPATH="$VIRTUAL_ENV/python-lib:$PYTHONPATH"
PATH="$VIRTUAL_ENV/bin:$PATH"
fi
VIRTUAL_ENV="`pwd`"
export CUDDLEFISH_ROOT
export PYTHONPATH
export PATH
_OLD_VIRTUAL_PS1="$PS1"
if [ "`basename \"$VIRTUAL_ENV\"`" = "__" ] ; then
# special case for Aspen magic directories
# see http://www.zetadev.com/software/aspen/
PS1="[`basename \`dirname \"$VIRTUAL_ENV\"\``] $PS1"
else
PS1="(`basename \"$VIRTUAL_ENV\"`)$PS1"
fi
export PS1
# This should detect bash and zsh, which have a hash command that must
# be called to get it to forget past commands. Without forgetting
# past commands the $PATH changes we made may not be respected
if [ -n "$BASH" -o -n "$ZSH_VERSION" ] ; then
hash -r
fi
python -c "from jetpack_sdk_env import welcome; welcome()"

Просмотреть файл

@ -0,0 +1,134 @@
@echo off
rem This Source Code Form is subject to the terms of the Mozilla Public
rem License, v. 2.0. If a copy of the MPL was not distributed with this
rem file, You can obtain one at http://mozilla.org/MPL/2.0/.
set VIRTUAL_ENV=%~dp0
set VIRTUAL_ENV=%VIRTUAL_ENV:~0,-5%
set CUDDLEFISH_ROOT=%VIRTUAL_ENV%
SET PYTHONKEY=SOFTWARE\Python\PythonCore
rem look for 32-bit windows and python, or 64-bit windows and python
SET PYTHONVERSION=2.7
call:CheckPython PYTHONINSTALL %PYTHONKEY%\%PYTHONVERSION%\InstallPath
if "%PYTHONINSTALL%" NEQ "" goto FoundPython
SET PYTHONVERSION=2.6
call:CheckPython PYTHONINSTALL %PYTHONKEY%\%PYTHONVERSION%\InstallPath
if "%PYTHONINSTALL%" NEQ "" goto FoundPython
SET PYTHONVERSION=2.5
call:CheckPython PYTHONINSTALL %PYTHONKEY%\%PYTHONVERSION%\InstallPath
if "%PYTHONINSTALL%" NEQ "" goto FoundPython
if not defined ProgramFiles(x86) goto win32
rem look for 32-bit python on 64-bit windows
SET PYTHONKEY=SOFTWARE\Wow6432Node\Python\PythonCore
SET PYTHONVERSION=2.7
call:CheckPython PYTHONINSTALL %PYTHONKEY%\%PYTHONVERSION%\InstallPath
if "%PYTHONINSTALL%" NEQ "" goto FoundPython
SET PYTHONVERSION=2.6
call:CheckPython PYTHONINSTALL %PYTHONKEY%\%PYTHONVERSION%\InstallPath
if "%PYTHONINSTALL%" NEQ "" goto FoundPython
SET PYTHONVERSION=2.5
call:CheckPython PYTHONINSTALL %PYTHONKEY%\%PYTHONVERSION%\InstallPath
if "%PYTHONINSTALL%" NEQ "" goto FoundPython
:win32
SET PYTHONVERSION=
set PYTHONKEY=
echo Warning: Failed to find Python installation directory
goto :EOF
:FoundPython
if defined _OLD_PYTHONPATH (
set PYTHONPATH=%_OLD_PYTHONPATH%
)
if not defined PYTHONPATH (
set PYTHONPATH=;
)
set _OLD_PYTHONPATH=%PYTHONPATH%
set PYTHONPATH=%VIRTUAL_ENV%\python-lib;%PYTHONPATH%
if not defined PROMPT (
set PROMPT=$P$G
)
if defined _OLD_VIRTUAL_PROMPT (
set PROMPT=%_OLD_VIRTUAL_PROMPT%
)
set _OLD_VIRTUAL_PROMPT=%PROMPT%
set PROMPT=(%VIRTUAL_ENV%) %PROMPT%
if defined _OLD_VIRTUAL_PATH goto OLDPATH
goto SKIPPATH
:OLDPATH
PATH %_OLD_VIRTUAL_PATH%
:SKIPPATH
set _OLD_VIRTUAL_PATH=%PATH%
PATH %VIRTUAL_ENV%\bin;%PYTHONINSTALL%;%PATH%
set PYTHONKEY=
set PYTHONINSTALL=
set PYTHONVERSION=
set key=
set reg=
set _tokens=
python -c "from jetpack_sdk_env import welcome; welcome()"
GOTO :EOF
:CheckPython
::CheckPython(retVal, key)
::Reads the registry at %2% and checks if a Python exists there.
::Checks both HKLM and HKCU, then checks the executable actually exists.
SET key=%2%
SET "%~1="
SET reg=reg
if defined ProgramFiles(x86) (
rem 32-bit cmd on 64-bit windows
if exist %WINDIR%\sysnative\reg.exe SET reg=%WINDIR%\sysnative\reg.exe
)
rem On Vista+, the last line of output is:
rem (default) REG_SZ the_value
rem (but note the word "default" will be localized.
rem On XP, the last line of output is:
rem <NO NAME>\tREG_SZ\tthe_value
rem (not sure if "NO NAME" is localized or not!)
rem SO: we use ")>" as the tokens to split on, then nuke
rem the REG_SZ and any tabs or spaces.
FOR /F "usebackq tokens=2 delims=)>" %%A IN (`%reg% QUERY HKLM\%key% /ve 2^>NUL`) DO SET "%~1=%%A"
rem Remove the REG_SZ
set PYTHONINSTALL=%PYTHONINSTALL:REG_SZ=%
rem Remove tabs (note the literal \t in the next line
set PYTHONINSTALL=%PYTHONINSTALL: =%
rem Remove spaces.
set PYTHONINSTALL=%PYTHONINSTALL: =%
if exist %PYTHONINSTALL%\python.exe goto :EOF
rem It may be a 32bit Python directory built from source, in which case the
rem executable is in the PCBuild directory.
if exist %PYTHONINSTALL%\PCBuild\python.exe (set "PYTHONINSTALL=%PYTHONINSTALL%\PCBuild" & goto :EOF)
rem Or maybe a 64bit build directory.
if exist %PYTHONINSTALL%\PCBuild\amd64\python.exe (set "PYTHONINSTALL=%PYTHONINSTALL%\PCBuild\amd64" & goto :EOF)
rem And try HKCU
FOR /F "usebackq tokens=2 delims=)>" %%A IN (`%reg% QUERY HKCU\%key% /ve 2^>NUL`) DO SET "%~1=%%A"
set PYTHONINSTALL=%PYTHONINSTALL:REG_SZ=%
set PYTHONINSTALL=%PYTHONINSTALL: =%
set PYTHONINSTALL=%PYTHONINSTALL: =%
if exist %PYTHONINSTALL%\python.exe goto :EOF
if exist %PYTHONINSTALL%\PCBuild\python.exe (set "PYTHONINSTALL=%PYTHONINSTALL%\PCBuild" & goto :EOF)
if exist %PYTHONINSTALL%\PCBuild\amd64\python.exe (set "PYTHONINSTALL=%PYTHONINSTALL%\PCBuild\amd64" & goto :EOF)
rem can't find it here, so arrange to try the next key
set PYTHONINSTALL=
GOTO :EOF

Просмотреть файл

@ -0,0 +1,99 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
$Env:VIRTUAL_ENV = (gl);
$Env:CUDDLEFISH_ROOT = $Env:VIRTUAL_ENV;
# http://stackoverflow.com/questions/5648931/powershell-test-if-registry-value-exists/5652674#5652674
Function Test-RegistryValue {
param(
[Alias("PSPath")]
[Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
[String]$Path
,
[Parameter(Position = 1, Mandatory = $true)]
[String]$Name
,
[Switch]$PassThru
)
process {
if (Test-Path $Path) {
$Key = Get-Item -LiteralPath $Path
if ($Key.GetValue($Name, $null) -ne $null) {
if ($PassThru) {
Get-ItemProperty $Path $Name
} else {
$true
}
} else {
$false
}
} else {
$false
}
}
}
$WINCURVERKEY = 'HKLM:SOFTWARE\Microsoft\Windows\CurrentVersion';
$WIN64 = (Test-RegistryValue $WINCURVERKEY 'ProgramFilesDir (x86)');
if($WIN64) {
$PYTHONKEY='HKLM:SOFTWARE\Wow6432Node\Python\PythonCore';
}
else {
$PYTHONKEY='HKLM:SOFTWARE\Python\PythonCore';
}
$Env:PYTHONVERSION = '';
$Env:PYTHONINSTALL = '';
foreach ($version in @('2.6', '2.5', '2.4')) {
if (Test-RegistryValue "$PYTHONKEY\$version\InstallPath" '(default)') {
$Env:PYTHONVERSION = $version;
}
}
if ($Env:PYTHONVERSION) {
$Env:PYTHONINSTALL = (Get-Item "$PYTHONKEY\$version\InstallPath)").'(default)';
}
if ($Env:PYTHONINSTALL) {
$Env:Path += ";$Env:PYTHONINSTALL";
}
if (Test-Path Env:_OLD_PYTHONPATH) {
$Env:PYTHONPATH = $Env:_OLD_PYTHONPATH;
}
else {
$Env:PYTHONPATH = '';
}
$Env:_OLD_PYTHONPATH=$Env:PYTHONPATH;
$Env:PYTHONPATH= "$Env:VIRTUAL_ENV\python-lib;$Env:PYTHONPATH";
if (Test-Path Function:_OLD_VIRTUAL_PROMPT) {
Set-Content Function:Prompt (Get-Content Function:_OLD_VIRTUAL_PROMPT);
}
else {
function global:_OLD_VIRTUAL_PROMPT {}
}
Set-Content Function:_OLD_VIRTUAL_PROMPT (Get-Content Function:Prompt);
function global:prompt { "($Env:VIRTUAL_ENV) $(_OLD_VIRTUAL_PROMPT)"; };
if (Test-Path Env:_OLD_VIRTUAL_PATH) {
$Env:PATH = $Env:_OLD_VIRTUAL_PATH;
}
else {
$Env:_OLD_VIRTUAL_PATH = $Env:PATH;
}
$Env:Path="$Env:VIRTUAL_ENV\bin;$Env:Path"
[System.Console]::WriteLine("Note: this PowerShell SDK activation script is experimental.")
python -c "from jetpack_sdk_env import welcome; welcome()"

33
addon-sdk/source/bin/cfx Normal file
Просмотреть файл

@ -0,0 +1,33 @@
#! /usr/bin/env python
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import os
import sys
# set the cuddlefish "root directory" for this process if it's not already
# set in the environment
cuddlefish_root = os.path.dirname(os.path.dirname(os.path.realpath(sys.argv[0])))
if 'CUDDLEFISH_ROOT' not in os.environ:
os.environ['CUDDLEFISH_ROOT'] = cuddlefish_root
# 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.insert(0, python_lib_dir)
# now export to env so sub-processes get it too
if 'PYTHONPATH' not in os.environ:
os.environ['PYTHONPATH'] = python_lib_dir
elif python_lib_dir not in os.environ['PYTHONPATH'].split(os.pathsep):
paths = os.environ['PYTHONPATH'].split(os.pathsep)
paths.insert(0, python_lib_dir)
os.environ['PYTHONPATH'] = os.pathsep.join(paths)
import cuddlefish
if __name__ == '__main__':
cuddlefish.run()

Просмотреть файл

@ -0,0 +1,6 @@
@echo off
rem This Source Code Form is subject to the terms of the Mozilla Public
rem License, v. 2.0. If a copy of the MPL was not distributed with this
rem file, You can obtain one at http://mozilla.org/MPL/2.0/.
python "%VIRTUAL_ENV%\bin\cfx" %*

Просмотреть файл

@ -0,0 +1,23 @@
@echo off
rem This Source Code Form is subject to the terms of the Mozilla Public
rem License, v. 2.0. If a copy of the MPL was not distributed with this
rem file, You can obtain one at http://mozilla.org/MPL/2.0/.
if defined _OLD_VIRTUAL_PROMPT (
set "PROMPT=%_OLD_VIRTUAL_PROMPT%"
)
set _OLD_VIRTUAL_PROMPT=
if defined _OLD_VIRTUAL_PATH (
set "PATH=%_OLD_VIRTUAL_PATH%"
)
set _OLD_VIRTUAL_PATH=
if defined _OLD_PYTHONPATH (
set "PYTHONPATH=%_OLD_PYTHONPATH%"
)
set _OLD_PYTHONPATH=
set CUDDLEFISH_ROOT=
:END

Просмотреть файл

@ -0,0 +1,14 @@
#!/bin/bash
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
source ./bin/activate
if [ type -P xvfb-run ]
then
xvfb-run cfx $*
else
cfx $*
fi
deactivate

Просмотреть файл

@ -0,0 +1,364 @@
#!/usr/bin/env python
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import os
import signal
import threading
import urllib2, urllib
import zipfile
import tarfile
import subprocess
import optparse
import sys, re
#import win32api
class SDK:
def __init__(self):
try:
# Take the current working directory
self.default_path = os.getcwd()
if sys.platform == "win32":
self.mswindows = True
else:
self.mswindows = False
# Take the default home path of the user.
home = os.path.expanduser('~')
# The following are the parameters that can be used to pass a dynamic URL, a specific path or a binry. The binary is not used yet. It will be used in version 2.0
# If a dynamic path is to be mentioned, it should start with a '/'. For eg. "/Desktop"
parser = optparse.OptionParser()
parser.add_option('-u', '--url', dest = 'url', default = 'https://ftp.mozilla.org/pub/mozilla.org/labs/jetpack/addon-sdk-latest.zip')
parser.add_option('-p', '--path', dest = 'path', default = self.default_path)
parser.add_option('-b', '--binary', dest = 'binary')#, default='/Applications/Firefox.app')
(options, args) = parser.parse_args()
# Get the URL from the parameter
self.link = options.url
# Set the base path for the user. If the user supplies the path, use the home variable as well. Else, take the default path of this script as the installation directory.
if options.path!=self.default_path:
if self.mswindows:
self.base_path = home + str(options.path).strip() + '\\'
else:
self.base_path = home + str(options.path).strip() + '/'
else:
if self.mswindows:
self.base_path = str(options.path).strip() + '\\'
else:
self.base_path = str(options.path).strip() + '/'
assert ' ' not in self.base_path, "You cannot have a space in your home path. Please remove the space before you continue."
print('Your Base path is =' + self.base_path)
# This assignment is not used in this program. It will be used in version 2 of this script.
self.bin = options.binary
# if app or bin is empty, dont pass anything
# Search for the .zip file or tarball file in the URL.
i = self.link.rfind('/')
self.fname = self.link[i+1:]
z = re.search('zip',self.fname,re.I)
g = re.search('gz',self.fname,re.I)
if z:
print 'zip file present in the URL.'
self.zip = True
self.gz = False
elif g:
print 'gz file present in the URL'
self.gz = True
self.zip = False
else:
print 'zip/gz file not present. Check the URL.'
return
print("File name is =" + self.fname)
# Join the base path and the zip/tar file name to crate a complete Local file path.
self.fpath = self.base_path + self.fname
print('Your local file path will be=' + self.fpath)
except AssertionError, e:
print e.args[0]
sys.exit(1)
# Download function - to download the SDK from the URL to the local machine.
def download(self,url,fpath,fname):
try:
# Start the download
print("Downloading...Please be patient!")
urllib.urlretrieve(url,filename = fname)
print('Download was successful.')
except ValueError: # Handles broken URL errors.
print 'The URL is ether broken or the file does not exist. Please enter the correct URL.'
raise
except urllib2.URLError: # Handles URL errors
print '\nURL not correct. Check again!'
raise
# Function to extract the downloaded zipfile.
def extract(self, zipfilepath, extfile):
try:
# Timeout is set to 30 seconds.
timeout = 30
# Change the directory to the location of the zip file.
try:
os.chdir(zipfilepath)
except OSError:
# Will reach here if zip file doesnt exist
print 'O/S Error:' + zipfilepath + 'does not exist'
raise
# Get the folder name of Jetpack to get the exact version number.
if self.zip:
try:
f = zipfile.ZipFile(extfile, "r")
except IOError as (errno, strerror): # Handles file errors
print "I/O error - Cannot perform extract operation: {1}".format(errno, strerror)
raise
list = f.namelist()[0]
temp_name = list.split('/')
print('Folder Name= ' +temp_name[0])
self.folder_name = temp_name[0]
elif self.gz:
try:
f = tarfile.open(extfile,'r')
except IOError as (errno, strerror): # Handles file errors
print "I/O error - Cannot perform extract operation: {1}".format(errno, strerror)
raise
list = f.getnames()[0]
temp_name = list.split('/')
print('Folder Name= ' +temp_name[0])
self.folder_name = temp_name[0]
print ('Starting to Extract...')
# Timeout code. The subprocess.popen exeutes the command and the thread waits for a timeout. If the process does not finish within the mentioned-
# timeout, the process is killed.
kill_check = threading.Event()
if self.zip:
# Call the command to unzip the file.
if self.mswindows:
zipfile.ZipFile.extractall(f)
else:
p = subprocess.Popen('unzip '+extfile, stdout=subprocess.PIPE, shell=True)
pid = p.pid
elif self.gz:
# Call the command to untar the file.
if self.mswindows:
tarfile.TarFile.extractall(f)
else:
p = subprocess.Popen('tar -xf '+extfile, stdout=subprocess.PIPE, shell=True)
pid = p.pid
#No need to handle for windows because windows automatically replaces old files with new files. It does not ask the user(as it does in Mac/Unix)
if self.mswindows==False:
watch = threading.Timer(timeout, kill_process, args=(pid, kill_check, self.mswindows ))
watch.start()
(stdout, stderr) = p.communicate()
watch.cancel() # if it's still waiting to run
success = not kill_check.isSet()
# Abort process if process fails.
if not success:
raise RuntimeError
kill_check.clear()
print('Extraction Successful.')
except RuntimeError:
print "Ending the program"
sys.exit(1)
except:
print "Error during file extraction: ", sys.exc_info()[0]
raise
# Function to run the cfx testall comands and to make sure the SDK is not broken.
def run_testall(self, home_path, folder_name):
try:
timeout = 500
self.new_dir = home_path + folder_name
try:
os.chdir(self.new_dir)
except OSError:
# Will reach here if the jetpack 0.X directory doesnt exist
print 'O/S Error: Jetpack directory does not exist at ' + self.new_dir
raise
print '\nStarting tests...'
# Timeout code. The subprocess.popen exeutes the command and the thread waits for a timeout. If the process does not finish within the mentioned-
# timeout, the process is killed.
kill_check = threading.Event()
# Set the path for the logs. They will be in the parent directory of the Jetpack SDK.
log_path = home_path + 'tests.log'
# Subprocess call to set up the jetpack environment and to start the tests. Also sends the output to a log file.
if self.bin != None:
if self.mswindows:
p = subprocess.Popen("bin\\activate && cfx testall -a firefox -b \"" + self.bin + "\"" , stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
proc_handle = p._handle
(stdout,stderr) = p.communicate()
else:
p = subprocess.Popen('. bin/activate; cfx testall -a firefox -b ' + self.bin , stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
pid = p.pid
(stdout,stderr) = p.communicate()
elif self.bin == None:
if self.mswindows:
p=subprocess.Popen('bin\\activate && cfx testall -a firefox > '+log_path, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
proc_handle = p._handle
(stdout,stderr) = p.communicate()
else:
p = subprocess.Popen('. bin/activate; cfx testall -a firefox > '+log_path, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
pid = p.pid
(stdout,stderr) = p.communicate()
#Write the output to log file
f=open(log_path,"w")
f.write(stdout+stderr)
f.close()
#Watchdog for timeout process
if self.mswindows:
watch = threading.Timer(timeout, kill_process, args=(proc_handle, kill_check, self.mswindows))
else:
watch = threading.Timer(timeout, kill_process, args=(pid, kill_check, self.mswindows))
watch.start()
watch.cancel() # if it's still waiting to run
success = not kill_check.isSet()
if not success:
raise RuntimeError
kill_check.clear()
if p.returncode!=0:
print('\nAll tests were not successful. Check the test-logs in the jetpack directory.')
result_sdk(home_path)
#sys.exit(1)
raise RuntimeError
else:
ret_code=result_sdk(home_path)
if ret_code==0:
print('\nAll tests were successful. Yay \o/ . Running a sample package test now...')
else:
print ('\nThere were errors during the tests.Take a look at logs')
raise RuntimeError
except RuntimeError:
print "Ending the program"
sys.exit(1)
except:
print "Error during the testall command execution:", sys.exc_info()[0]
raise
def package(self, example_dir):
try:
timeout = 30
print '\nNow Running packaging tests...'
kill_check = threading.Event()
# Set the path for the example logs. They will be in the parent directory of the Jetpack SDK.
exlog_path = example_dir + 'test-example.log'
# Subprocess call to test the sample example for packaging.
if self.bin!=None:
if self.mswindows:
p = subprocess.Popen('bin\\activate && cfx run --pkgdir examples\\reading-data --static-args="{\\"quitWhenDone\\":true}" -b \"" + self.bin + "\"' , stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
proc_handle = p._handle
(stdout, stderr) = p.communicate()
else:
p = subprocess.Popen('. bin/activate; cfx run --pkgdir examples/reading-data --static-args=\'{\"quitWhenDone\":true}\' -b ' + self.bin , stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
pid = p.pid
(stdout, stderr) = p.communicate()
elif self.bin==None:
if self.mswindows:
p = subprocess.Popen('bin\\activate && cfx run --pkgdir examples\\reading-data --static-args="{\\"quitWhenDone\\":true}"', stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
proc_handle = p._handle
(stdout, stderr) = p.communicate()
else:
p = subprocess.Popen('. bin/activate; cfx run --pkgdir examples/reading-data --static-args=\'{\"quitWhenDone\":true}\' ', stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
pid = p.pid
(stdout, stderr) = p.communicate()
#Write the output to log file
f=open(exlog_path,"w")
f.write(stdout+stderr)
f.close()
#Watch dog for timeout process
if self.mswindows:
watch = threading.Timer(timeout, kill_process, args=(proc_handle, kill_check, self.mswindows))
else:
watch = threading.Timer(timeout, kill_process, args=(pid, kill_check, self.mswindows))
watch.start()
watch.cancel() # if it's still waiting to run
success = not kill_check.isSet()
if not success:
raise RuntimeError
kill_check.clear()
if p.returncode != 0:
print('\nSample tests were not executed correctly. Check the test-example log in jetpack diretory.')
result_example(example_dir)
raise RuntimeError
else:
ret_code=result_example(example_dir)
if ret_code==0:
print('\nAll tests pass. The SDK is working! Yay \o/')
else:
print ('\nTests passed with warning.Take a look at logs')
sys.exit(1)
except RuntimeError:
print "Ending program"
sys.exit(1)
except:
print "Error during running sample tests:", sys.exc_info()[0]
raise
def result_sdk(sdk_dir):
log_path = sdk_dir + 'tests.log'
print 'Results are logged at:' + log_path
try:
f = open(log_path,'r')
# Handles file errors
except IOError :
print 'I/O error - Cannot open test log at ' + log_path
raise
for line in reversed(open(log_path).readlines()):
if line.strip()=='FAIL':
print ('\nOverall result - FAIL. Look at the test log at '+log_path)
return 1
return 0
def result_example(sdk_dir):
exlog_path = sdk_dir + 'test-example.log'
print 'Sample test results are logged at:' + exlog_path
try:
f = open(exlog_path,'r')
# Handles file errors
except IOError :
print 'I/O error - Cannot open sample test log at ' + exlog_path
raise
#Read the file in reverse and check for the keyword 'FAIL'.
for line in reversed(open(exlog_path).readlines()):
if line.strip()=='FAIL':
print ('\nOverall result for Sample tests - FAIL. Look at the test log at '+exlog_path)
return 1
return 0
def kill_process(process, kill_check, mswindows):
print '\nProcess Timedout. Killing the process. Please Rerun this script.'
if mswindows:
win32api.TerminateProcess(process, -1)
else:
os.kill(process, signal.SIGKILL)
kill_check.set()# tell the main routine to kill. Used SIGKILL to hard kill the process.
return
if __name__ == "__main__":
obj = SDK()
obj.download(obj.link,obj.fpath,obj.fname)
obj.extract(obj.base_path,obj.fname)
obj.run_testall(obj.base_path,obj.folder_name)
obj.package(obj.base_path)

Просмотреть файл

@ -0,0 +1,13 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<html>
<head>
<meta charset="UTF-8">
<title>Add-on Page</title>
</head>
<body>
<p>This is an add-on page test!</p>
</body>
</html>

Просмотреть файл

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generator: Adobe Illustrator 12.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 51448) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" [
<!ENTITY ns_svg "http://www.w3.org/2000/svg">
<!ENTITY ns_xlink "http://www.w3.org/1999/xlink">
]>
<svg version="1.1" id="svg2997" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200" viewBox="-0.5 -0.5 200 200" overflow="visible" enable-background="new -0.5 -0.5 200 200" xml:space="preserve">
<path id="path85" fill="#FFFFFF" d="M-2559.936,6359.65c19.127,26.489,46.371,47.744,80.988,44.707 C-2479.339,6371.648-2523.898,6352.641-2559.936,6359.65z"/>
<path id="path93" fill="#FFFFFF" d="M-2634.014,6073.506c4.082-7.232,2.571-17.81,3.831-26.684 c-9.019,4.635-15.506,14.72-24.551,18.641C-2652.134,6070.818-2640.447,6070.829-2634.014,6073.506z"/>
<path id="path95" fill="#FFFFFF" d="M-2687.179,6018.109l-18.654,18.212c4.158,3.301,9.676,8.132,17.11,6.36L-2687.179,6018.109z"/>
<path id="path97" fill="#FFFFFF" d="M-2731.117,5998.391c-4.356-0.363-14.41,20.156-18.785,19.857l14.762,8.728 C-2730.055,6027.281-2732.816,6001.961-2731.117,5998.391L-2731.117,5998.391z"/>
<path id="path99" fill="#FFFFFF" d="M-2776.505,5989.09l4.736-11.668c-10.245,0.813-15.511,20.68-21.719,26.244 C-2776.37,6007.109-2775.727,6011.81-2776.505,5989.09z"/>
<path id="path101" fill="#FFFFFF" d="M-2825.16,5962.186l-18.405,31.539l17.091,3.962L-2825.16,5962.186L-2825.16,5962.186z"/>
<path id="path103" fill="#FFFFFF" d="M-2872.173,5967.636l-21.059,16.139C-2872.035,5992.07-2870.821,5995.181-2872.173,5967.636z"/>
<path id="path105" fill="#FFFFFF" d="M-2922.987,5980.398c-3.084-9.172-0.18-20.226,0.396-29.914 c-4.952,9.422-16.338,16.166-17.745,27.299C-2935.489,5981.802-2928.796,5980.021-2922.987,5980.398L-2922.987,5980.398z"/>
<g>
<path id="path9" d="M70.165,21.946C62.09,27.292,49.432,38.35,43.382,44.795c1.491,0.208,3.068-0.957,4.751-1.179l11.635-3.192 c-4.833,4.151-15.001,11.47-17.946,17.234c3.632-1.309,11.254-5.699,15.043-6.059c-3.309,3.038-10.214,8.801-12.378,12.469 c0.261,0.124,0.08,0.445,0.12,0.669c2.846-1.635,9.763-5.91,12.9-6.199c-3.229,3.484-9.169,10.193-10.47,14.688 c2.804-1.913,9.856-5.792,12.944-6.969c-0.703,1.773-2.262,3.09-3.276,4.864c-1.399,2.453-4.711,5.246-4.834,8.161 c2.46-2.375,5.51-4.474,8.571-6.148c-1.365,2.947-3.876,5.384-4.561,8.892c2.007-2.02,5.813-3.071,8.399-4.005l-0.312,0.311 c-0.224,0.191-3.136,2.335-5.121,4.727c1.765-0.114,0.449-0.334,2.707,0.18l-0.043,0.114 c-0.154,13.494,25.501,18.427,41.224,17.743c3.314-6.419,1.525-16.789,10.088-21.247c5.764-3.067,6.416-4.742,13.068-3.491 c3.596,0.674,11.461,3.392,15.227,2.534c3.514-2.033,5.221-2.928,8.332-5.37c3.01-4.574-23.988-19.398-2.164-0.171 c5.236,1.121,6.588,0.954,8.139-1.376c0.693-1.88,2.348-6.877,3.859-10.133l0.107-1.76c-1.467-4.414-1.111-11.965-1.852-13.053 c-4.842-6.713-22.34-7.764-30.256-16.596c1.35-4.384-1.482-7.997-4.367-10.738c-4.332-3.187-10.313-1.722-14.925-0.399 c-5.255-1.099-10.117-2.986-15.233-4.48C81.381,13.626,70.165,21.946,70.165,21.946L70.165,21.946z"/>
<path id="path11" fill="#FF1A00" d="M103.517,25.68l-3.771,1.438c-0.453-0.901,0.676-1.757,1.126-2.599 c0.322-0.616,1.247-0.961,1.465-1.705c-4.877-0.786-9.486-3.178-14.688-2.354c-5.563,0.878-8.802,3.003-12.005,5.54 c-1.617,0.885-2.396,2.142-3.968,3.201c-1.92,1.729-2.755,5.176-3.233,5.833c2.785-1.206,5.587-1.026,7.479-1.663 c-7.115,4.447-8.358,13.134-6.39,20.38l-0.116,0.159c-2.391-1.429-3.083-4.065-3.745-6.371c-0.471,1.515-0.538,3.44-0.859,5.132 c-0.764-0.713-1.184-1.734-1.61-2.644c-0.799,7.668,2.938,13.913,7.521,19.966c3.068,4.058-2.923,6.744-1.512,11.838l7.206-0.404 c0.929-0.348,0.64-0.223,0.64-0.223c-1.806,3.613-11.365,6.352-13.201,8.417c0.513,0.425,8.917-2.164,11.991-2.687l-9.045,5.112 c0.899,0.277-0.028,3.311,1.946,3.023c12.508,3.713,26.597,8.866,28.792,3.488c1.831-3.419,4.541-9.21,4.122-12.49l1.941-2.949 c-2.59,0.643-5.199,1.691-7.202,3.823c-0.599-1.256,0.342-2.763,0.81-3.951c1.343-2.41,4.026-3.686,6.151-5.056l-4.464-1.976 c-2.25-3.864-6.523-7.047-6.639-11.586l1.057,0.36c4.069,4.621,9.854,8.371,15.647,8.639c-3.943-0.338,16.471-2.743,15.271-4.472 c2.17,3.121,8.729,4.432,13.52,6.071c2.201-0.792,3.162-0.566,4.816-2.057c-0.705-0.765-1.795-1.39-2.844-1.787 c-6.295-1.738-21.162-4.796-24.365-10.986c-0.604,2.637,1.838,4.812,4.566,7.061l-5.873-0.826 c-10.331-4.782-9.083-12.984-6.813-20.78l0.785,6.508l2.025,3.548c3.851-6.966,1.835-6.1-5.898-15.494l5.832,2.228 c2.663,2.142,7.04,5.845,10.921,8.417c10.453,6.845,15.625,11.381,28.891,12.311l4.092,0.908c2.896-7.59-2.719-14.597-1.021-3.989 c-2.166-2.631-4.283-5.066-3.842-6.439c-0.76-1.147,3.143-4.014,2.811-5.319c-3.83-3.894-20.389-5.984-29.381-14.907 c-0.264,1.492,4.158,5.354,4.924,6.683c-8.918,0.088-14.174-2.651-15.127-5.542c-0.195-0.481,0.047-2.623,0.998-3.313 c1.287-0.672,2.541-1.693,4.02-1.83c-2.316-1.891-5.25,0.643-7.302,1.788c-1.594,2.737,0.889,5.788,1.202,8.014 c-2.175-0.549-5.111-4.378-5.471-6.782c-3.306,0.362-6.34-0.065-8.881-1.974c2.243-0.492,4.56-1.331,6.906-1.755 c6.143-1.34,12.598-7.655,18.803-2.72l-2.889-3.672C113.848,21.654,108.812,24.342,103.517,25.68L103.517,25.68z"/>
<path id="path13" d="M83.254,28.459c-1.649,1.924-3.302,3.85-3.687,6.437l0.369,0.434c1.102-1.326,2.753-2.437,4.274-3.207 c0.002,2.726,1.76,5.399,4.11,6.713l0.395,0.028c-0.498-2.879-0.166-6.399,0.96-8.998c1.793-1.508,4.151-2.106,5.668-3.692 c-3.797-0.888-7.411-0.136-10.658,1.1L83.254,28.459L83.254,28.459z"/>
<path id="path15" fill="#FFFFFF" d="M121.379,36.471c-1.561-2.157-3.777-3.888-6.594-3.642 C114.814,35.491,118.443,37.043,121.379,36.471z"/>
<path id="path17" d="M98.854,36.815c-2.445,0.187-4.736,0.622-7.041,1.224c1.017,0.937,2.302,1.252,3.266,2.129 c-3.017,1.137-6.176,1.861-8.813,4.186l0.546,0.325c4.878-1.015,10.645-0.929,15.732,0.16l0.294-0.212l-2.948-5.361l4.533-0.981 c-2.405-2.249-5.156-4.46-8.322-5.422C96.585,34.171,97.917,35.479,98.854,36.815L98.854,36.815L98.854,36.815z"/>
<path id="path19" d="M116.564,42.189c2.406,0.445,5.064,0.503,7.357,0.074c-3.787-1.932-8.625-1.552-11.639-4.944 C112.852,39.099,114.086,41.968,116.564,42.189z"/>
<path id="path21" d="M80.567,55.828c0.943,3.023-2.23,6.707,0.312,9.485c2.165,2.522,4.81,4.614,7.354,6.464l-0.225-0.943 c-2.266-2.647-4.349-5.569-5.396-8.716c2.656,0.928,5.463,2.216,8.496,2.646c-3.416-5.104-9.678-9.984-7.319-16.97 c0.129-1.154,1.655-2.041,1.271-3.113C81.49,47.408,79.996,51.435,80.567,55.828L80.567,55.828z"/>
<path id="path23" fill="#FFFFFF" d="M127.412,59.77c-0.334,0.589-0.213,1.45-0.316,2.174c0.738-0.379,1.264-1.199,2-1.518 C128.883,59.988,127.934,59.986,127.412,59.77z"/>
<path id="path25" fill="#FFFFFF" d="M131.738,64.284l1.518-1.487c-0.336-0.267-0.785-0.661-1.395-0.518L131.738,64.284z"/>
<path id="path27" fill="#FFFFFF" d="M135.316,65.887c0.355,0.026,1.174-1.643,1.531-1.618l-1.203-0.711 C135.229,63.532,135.455,65.593,135.316,65.887z"/>
<path id="path29" fill="#FFFFFF" d="M139.012,66.644l-0.385,0.95c0.836-0.067,1.264-1.684,1.766-2.138 C139.002,65.177,138.949,64.793,139.012,66.644z"/>
<path id="path31" fill="#FFFFFF" d="M142.977,68.834l1.498-2.569l-1.395-0.32L142.977,68.834z"/>
<path id="path33" fill="#FFFFFF" d="M146.801,68.39l1.715-1.312C146.789,66.4,146.691,66.147,146.801,68.39z"/>
<path id="path35" fill="#FFFFFF" d="M150.938,67.35c0.254,0.748,0.02,1.647-0.031,2.436c0.404-0.766,1.332-1.313,1.445-2.221 C151.957,67.233,151.412,67.382,150.938,67.35L150.938,67.35z"/>
<path id="path37" d="M99.399,96.021c0.547,1.031,0.572,2.285,1.119,3.315c1.295-2.793,1.147-6.148-0.533-8.659 c-2.923-1.288-5.745-2.625-8.264-4.671C92.768,89.577,96.192,93.513,99.399,96.021L99.399,96.021z"/>
<path id="path71" d="M117.387,36.775c-0.953-0.248-1.51-1.317-1.25-2.393c0.268-1.079,1.252-1.75,2.207-1.502 c0.955,0.245,1.512,1.318,1.25,2.393C119.328,36.348,118.34,37.021,117.387,36.775z"/>
<path id="path75" d="M98.356,103.161l2.835-2.31c-2.758-1.791-7.426-5.004-9.95-7.387C93.224,97.589,93.284,102.152,98.356,103.161 z"/>
<path id="path2225" d="M57.859,84.722c11.552,15.175,27.262,21.19,46.412,19.542l-1.629-3.542 c-17.031,0.631-31.21-3.592-40.875-17.588L57.859,84.722z"/>
<path id="path2343" d="M10.063,170.061c2.903,0.027,5.809-0.039,8.712,0.014l-0.375,2.293l-5.096,0.02l-0.021,3.24h4.088v2.342 h-4.088v6.334h-3.22V170.061z"/>
<path id="path3315" d="M30.648,184.227c-2.242-0.338-3.854-1.586-4.75-3.68c-0.511-1.191-0.656-2.105-0.596-3.748 c0.061-1.664,0.254-2.451,0.886-3.605c0.814-1.488,1.881-2.344,3.555-2.846c0.877-0.266,1.065-0.291,2.274-0.293 c1.095-0.004,1.446,0.029,2.078,0.201c2.303,0.627,3.802,2.172,4.426,4.559c0.288,1.105,0.265,3.764-0.042,4.846 c-0.697,2.463-2.385,4.098-4.674,4.527C33.093,184.322,31.412,184.342,30.648,184.227z M32.991,181.963 c0.926-0.281,1.579-1.137,1.876-2.463c0.205-0.91,0.238-3.596,0.058-4.547c-0.335-1.762-1.328-2.654-2.957-2.656 c-1.589-0.002-2.456,0.793-2.912,2.67c-0.214,0.883-0.238,3.438-0.041,4.396c0.279,1.357,0.877,2.211,1.761,2.512 C31.381,182.082,32.455,182.123,32.991,181.963z"/>
<path id="flowRoot5258" d="M47.563,170.053h3.446v8.381c0,1.154,0.179,1.98,0.537,2.48c0.364,0.492,0.955,0.883,1.772,0.883 c0.823,0,1.414-0.391,1.772-0.883c0.364-0.5,0.546-1.326,0.546-2.48v-8.381h3.445v8.381c0,1.979-0.474,3.451-1.423,4.418 c-0.948,0.967-2.396,1.451-4.34,1.451c-1.939,0-3.383-0.484-4.331-1.451c-0.949-0.967-1.423-2.439-1.423-4.418V170.053"/>
<path id="path6262" d="M68.606,170.053l3.509,0.051c1.381,2.793,2.899,5.531,4.078,8.41l0.197,0.504l-0.049-1.01l-0.18-7.951h2.948 v14.246h-3.182l-0.363-0.758c-1.15-2.398-3.13-6.629-3.602-7.697c-0.302-0.684-0.524-1.137-0.495-1.012 c0.029,0.127,0.083,2.309,0.119,4.848l0.065,4.619h-3.047V170.053z"/>
<path id="text8203" d="M92.398,182.191l0.931,0.002c0.986,0,1.843-1.076,2.366-1.844c0.522-0.766,0.977-1.873,0.977-3.322 c0-1.424-0.431-2.496-0.905-3.213c-0.469-0.725-1.17-1.424-2.104-1.424l-1.265-0.002v9.805 M88.878,184.303v-14.25h4.209 c1.173,0,2.12,0.104,2.84,0.311c0.72,0.201,1.357,0.529,1.912,0.986c0.8,0.65,1.393,1.455,1.776,2.41 c0.39,0.949,0.584,2.084,0.584,3.404c0,1.223-0.2,2.328-0.6,3.314c-0.4,0.986-0.981,1.813-1.744,2.482 c-0.544,0.475-1.155,0.818-1.832,1.031c-0.678,0.207-1.558,0.311-2.641,0.311H88.878"/>
<path id="path9182" d="M106.307,184.277l5.092-14.225h3.457c1.617,4.746,3.281,9.477,4.885,14.225l-3.457,0.023l-1.084-3.391 l-4.433,0.002l-1.083,3.389C108.559,184.289,107.431,184.324,106.307,184.277z M113.051,173.643l-1.51,4.887h2.873 C113.82,176.934,113.57,175.254,113.051,173.643z"/>
<path id="path10154" d="M127.924,172.535h-3.867v-2.48c3.799,0,7.6-0.004,11.4,0c-0.002,0-0.529,2.479-0.529,2.479l-3.748,0.002 v11.768h-3.256V172.535z"/>
<path id="path11126" d="M142.326,170.053h3.471v14.242h-3.471V170.053z"/>
<path id="path14040" d="M159.949,184.227c-2.24-0.338-3.854-1.586-4.748-3.68c-0.512-1.191-0.656-2.105-0.598-3.748 c0.061-1.664,0.254-2.451,0.885-3.605c0.816-1.488,1.883-2.344,3.557-2.846c0.877-0.266,1.064-0.291,2.275-0.293 c1.094-0.004,1.445,0.029,2.076,0.201c2.305,0.627,3.803,2.172,4.426,4.559c0.289,1.105,0.266,3.764-0.041,4.846 c-0.697,2.463-2.387,4.098-4.676,4.527C162.395,184.322,160.713,184.342,159.949,184.227z M162.293,181.963 c0.926-0.281,1.578-1.137,1.875-2.463c0.205-0.91,0.24-3.596,0.059-4.547c-0.336-1.762-1.328-2.654-2.957-2.656 c-1.59-0.002-2.457,0.793-2.912,2.67c-0.215,0.883-0.238,3.438-0.041,4.396c0.279,1.357,0.877,2.211,1.762,2.512 C160.682,182.082,161.756,182.123,162.293,181.963z"/>
<path id="path2224" d="M176.656,170.053l3.563,0.051c1.402,2.793,2.945,5.531,4.143,8.41l0.199,0.504l-0.049-1.01l-0.182-7.951 h2.992v14.246h-3.23l-0.369-0.758c-1.168-2.398-3.178-6.629-3.658-7.697c-0.305-0.684-0.531-1.137-0.502-1.012 c0.031,0.127,0.086,2.309,0.121,4.848l0.066,4.619h-3.094V170.053z"/>
<g id="g3173" transform="translate(-0.4495808,1251.722)">
<path id="path2320" d="M185.373-1089.163c-1.527-0.85-2.496-1.666-3.189-2.689l-0.402-0.596l-0.586,0.516 c-0.816,0.725-1.141,0.965-1.887,1.406c-1.727,1.02-4.043,1.439-6.92,1.252c-5.955-0.385-9.082-3.238-9.49-8.66 c-0.182-2.412,0.223-4.607,1.158-6.289c1.281-2.305,3.789-3.914,7.313-4.695c1.803-0.398,3.266-0.545,5.977-0.594l2.516-0.049 l-0.049-1.311c-0.027-0.723-0.09-1.543-0.137-1.824c-0.25-1.477-0.871-2.223-2.035-2.445c-0.902-0.17-1.32-0.18-2.357-0.053 c-2.006,0.246-4.098,1.066-6.678,2.623c-0.691,0.416-1.297,0.744-1.346,0.725c-0.096-0.037-3.463-5.682-3.461-5.799 c0.008-0.166,2.094-1.316,3.582-1.973c4.023-1.775,6.543-2.377,9.953-2.377c3.17,0,5.359,0.533,7.127,1.736 c0.693,0.473,1.736,1.551,2.115,2.186c0.35,0.586,0.777,1.646,1.012,2.51l0.189,0.691l-0.041,4.766 c-0.021,2.619-0.041,6.43-0.041,8.467c0,4.178,0,4.18,0.633,5.414c0.273,0.537,0.498,0.814,1.266,1.57 c0.855,0.842,0.922,0.93,0.809,1.059c-0.068,0.076-1.029,1.184-2.137,2.461c-1.109,1.275-2.057,2.328-2.107,2.338 C186.108-1088.786,185.756-1088.953,185.373-1089.163z M175.903-1095.453c1.229-0.154,2.246-0.641,3.182-1.518l0.508-0.477 l0.055-3.168l0.053-3.168l-1.316,0.057c-4.154,0.178-5.707,0.842-6.377,2.729c-0.213,0.596-0.295,2.16-0.146,2.787 c0.334,1.418,1.457,2.533,2.768,2.748c0.246,0.039,0.486,0.078,0.531,0.082C175.203-1095.374,175.539-1095.408,175.903-1095.453z M137.623-1089.718c-1.318-0.205-2.459-0.68-3.502-1.459c-1.109-0.826-1.762-1.803-2.238-3.348 c-0.445-1.441-0.436-1.025-0.49-18.475c-0.051-15.838-0.057-16.158-0.236-18.186c-0.102-1.135-0.162-2.086-0.137-2.111 c0.074-0.074,8.055-1.891,8.154-1.857c0.098,0.037,0.248,1.135,0.361,2.654c0.162,10.801,0.012,21.605,0.156,32.406 c0.063,2.619,0.086,2.936,0.24,3.373c0.379,1.078,0.902,1.469,1.963,1.465c0.336,0,0.662-0.018,0.727-0.039 c0.105-0.033,0.479,1.133,1.371,4.289l0.162,0.578l-1.271,0.334C141.034-1089.607,139.186-1089.476,137.623-1089.718z M154.555-1089.644c-1.066-0.125-1.982-0.391-2.85-0.822c-2.164-1.08-3.219-2.633-3.713-5.475 c-0.066-0.375-0.109-5.193-0.146-16.57c-0.053-15.98-0.055-16.049-0.25-18.383c-0.117-1.43-0.164-2.367-0.119-2.408 c0.102-0.088,8.1-1.91,8.152-1.857c0.088,0.088,0.268,1.711,0.348,3.148c0.213,11.125-0.035,22.258,0.162,33.383 c0.055,1.18,0.107,1.611,0.238,1.984c0.221,0.625,0.525,1.014,0.945,1.199c0.457,0.203,1.182,0.281,1.516,0.166 c0.174-0.063,0.283-0.066,0.316-0.014c0.078,0.129,1.402,4.816,1.369,4.85c-0.016,0.018-0.504,0.164-1.084,0.328 C158.041-1089.722,155.75-1089.501,154.555-1089.644z M67.676-1089.716c-0.134-0.018-0.61-0.076-1.059-0.127 c-0.447-0.053-1.237-0.215-1.756-0.363c-5.568-1.582-9.046-6.182-9.853-13.021c-0.124-1.047-0.123-3.988,0.001-5.09 c0.362-3.213,1.4-6.119,2.997-8.387c1.083-1.537,2.861-3.086,4.462-3.889c2.021-1.016,3.78-1.402,6.368-1.402 c2.15,0,3.536,0.219,5.156,0.816c3.931,1.449,7.106,5.004,8.254,9.238c0.922,3.398,0.905,8.645-0.037,12.051 c-0.744,2.691-2.024,4.861-3.966,6.725s-4.086,2.918-6.7,3.297C70.775-1089.757,68.143-1089.654,67.676-1089.716z M70.77-1096.027 c1.815-0.824,2.693-2.672,3.095-6.512c0.153-1.465,0.177-5.111,0.041-6.512c-0.375-3.879-1.335-5.748-3.356-6.531 c-1.055-0.41-2.505-0.303-3.577,0.262c-1.823,0.959-2.647,2.99-2.926,7.207c-0.158,2.404-0.013,5.633,0.343,7.572 c0.475,2.602,1.225,3.77,2.859,4.457c0.768,0.322,1.166,0.387,2.144,0.344C70.09-1095.769,70.293-1095.812,70.77-1096.027z M10.314-1116.745c-0.13-1.063-0.376-2.029-0.667-2.621c-0.147-0.301-0.224-0.547-0.182-0.584c0.09-0.078,7.021-1.965,7.22-1.965 c0.204,0,0.671,0.98,0.915,1.92c0.112,0.432,0.204,0.855,0.204,0.939c0,0.086,0.027,0.152,0.061,0.152 c0.034-0.002,0.391-0.277,0.794-0.615c1.52-1.27,3.532-2.127,5.465-2.324c2.115-0.217,4.02,0.1,5.551,0.92 c0.98,0.527,2.146,1.512,2.768,2.336l0.488,0.646l0.314-0.326c0.76-0.789,2.256-1.92,3.307-2.496 c0.898-0.494,2.17-0.893,3.413-1.074c1.114-0.16,3.312-0.063,4.384,0.197c1.185,0.287,2.204,0.719,2.971,1.26 c1.574,1.109,2.172,2.082,2.584,4.207c0.172,0.885,0.174,1.025,0.203,13.373l0.029,12.479H42.15v-10.934 c0-7.029-0.03-11.18-0.084-11.623c-0.198-1.623-0.574-2.096-1.798-2.268c-1.429-0.199-3.438,0.574-5.267,2.025l-0.667,0.529 l0,22.27h-7.729c-0.473-7.383,0.652-15.438-0.186-22.727c-0.296-1.432-0.807-1.955-2.059-2.107 c-1.462-0.178-3.452,0.498-5.153,1.75l-0.664,0.488l-0.005,22.596h-7.979C10.282-1099.062,11.051-1108.048,10.314-1116.745z M86.536-1095.492l14.459-19.949l-13.248-0.043v-5.779h23.043v5.578c-4.503,6.535-9.129,12.986-13.598,19.545l14.179,0.037 l-0.059,0.182l-1.888,5.559l-22.899,0.041L86.536-1095.492z M116.735-1120.853l0.184-0.041c2.588-0.43,5.186-0.809,7.775-1.227 l0.266-0.045v31.844h-8.225V-1120.853z M120.276-1124.939c-1.963-0.195-3.682-1.678-4.188-3.611 c-0.703-2.688,0.707-5.361,3.273-6.201c0.484-0.158,0.754-0.191,1.592-0.191c1.578,0,2.482,0.357,3.508,1.381 c0.986,0.986,1.412,2.068,1.418,3.586c0.004,1.563-0.406,2.584-1.457,3.637C123.332-1125.245,121.934-1124.773,120.276-1124.939z"/>
</g>
</g>
</svg>

После

Ширина:  |  Высота:  |  Размер: 16 KiB

Двоичные данные
addon-sdk/source/data/moz_favicon.ico Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 1.4 KiB

Просмотреть файл

@ -0,0 +1 @@
div { border: 10px solid black; }

Просмотреть файл

@ -0,0 +1,5 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// test-content-symbiont

Просмотреть файл

@ -0,0 +1,5 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
self.on("context", function () true);

Просмотреть файл

@ -0,0 +1,20 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<script>
window.addEventListener("message", function(msg) {
parent.postMessage(msg.data, "*");
});
parent.postMessage({
first: "a string",
second: ["an", "array"],
third: {an: 'object'}
}, '*');
</script>
</head>
<body>
<h1>Inner iframe</h1>
</body>
</html>

Просмотреть файл

@ -0,0 +1,9 @@
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
<iframe id="inner" src="about:blank"></iframe>
</body>
</html>

Просмотреть файл

@ -0,0 +1,11 @@
var count = 0
setTimeout(function() {
window.addEventListener("message", function(msg) {
if (++count > 1) self.postMessage(msg.data);
else msg.source.postMessage(msg.data, '*');
});
document.getElementById('inner').src = iframePath;
}, 0);

Просмотреть файл

@ -0,0 +1,6 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
const TEST_VALUE = 11;

Просмотреть файл

@ -0,0 +1 @@
Hello, ゼロ!

Просмотреть файл

@ -0,0 +1,12 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<html>
<head>
<meta charset="UTF-8">
<title>Page Mod test</title>
</head>
<body>
<p id="paragraph">Lorem ipsum dolor sit amet.</p>
</body>
</html>

Просмотреть файл

@ -0,0 +1,13 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<html>
<head>
<meta charset="UTF-8">
<title>Page Worker test</title>
</head>
<body>
<p id="paragraph">Lorem ipsum dolor sit amet.</p>
</body>
</html>

Просмотреть файл

@ -0,0 +1,29 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// get title directly
self.postMessage(["equal", document.title, "Page Worker test",
"Correct page title accessed directly"]);
// get <p> directly
let p = document.getElementById("paragraph");
self.postMessage(["ok", !!p, "<p> can be accessed directly"]);
self.postMessage(["equal", p.firstChild.nodeValue,
"Lorem ipsum dolor sit amet.",
"Correct text node expected"]);
// Modify page
let div = document.createElement("div");
div.setAttribute("id", "block");
div.appendChild(document.createTextNode("Test text created"));
document.body.appendChild(div);
// Check back the modification
div = document.getElementById("block");
self.postMessage(["ok", !!div, "<div> can be accessed directly"]);
self.postMessage(["equal", div.firstChild.nodeValue,
"Test text created", "Correct text node expected"]);
self.postMessage(["done"]);

Просмотреть файл

@ -0,0 +1,18 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<html>
<head>
<meta charset="UTF-8">
<title>Worker test</title>
</head>
<body>
<p id="paragraph">Lorem ipsum dolor sit amet.</p>
<script>
addon.port.on('addon-to-document', function (arg) {
addon.port.emit('document-to-addon', arg);
});
</script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,13 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<html>
<head>
<meta charset="UTF-8">
<title>foo</title>
</head>
<body>
<p>bar</p>
</body>
</html>

Просмотреть файл

@ -0,0 +1,908 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# cfx #
The `cfx` command-line tool gives you access to the SDK documentation and
development servers as well as testing, running, and building add-ons.
`cfx` usage is:
<pre>
cfx [options] command [command-specific options]
</pre>
"Options" are global options applicable to the tool itself or to all
commands (for example `--help`). `cfx` supports the following global options:
<pre>
-h, --help - show a help message and exit
-v, --verbose - enable lots of output
</pre>
"Command-specific options" are only
applicable to a subset of the commands.
## Supported Commands ##
### cfx docs ###
This command displays the documentation for the SDK. The documentation is
shipped with the SDK in [Markdown](http://daringfireball.net/projects/markdown/)
format. The first time this command is executed, and any time after the
Markdown files on disk have changed, `cfx docs` will generate a set of HTML
pages from them and launch a web browser to display them. If the Markdown files
haven't changed, `cfx docs` just launches a browser initialized to the set of
generated pages.
To regenerate the documentation associated with a single file, you can
specify the file as an argument. For example:
<pre>
cfx docs doc/dev-guide-source/addon-development/cfx-tool.md
</pre>
This command will regenerate only the HTML page you're reading.
This is useful if you're iteratively editing a single file, and don't want to wait for cfx to
regenerate the complete documentation tree.
### cfx init ####
Create a new directory called "my-addon", change into it, and run `cfx init`.
This command will create an skeleton add-on, as a starting point for your
own add-on development, with the following file structure:
<ul class="tree">
<li>my-addon
<ul>
<li>data</li>
<li>docs
<ul><li>main.md</li></ul>
</li>
<li>lib
<ul><li>main.js</li></ul>
</li>
<li>package.json</li>
<li>README.md</li>
<li>tests
<ul><li>test-main.js</li></ul>
</li>
</ul>
</li>
</ul>
<div style="clear:both"></div>
### cfx run ###
This command is used to run the add-on. Called with no options it looks for a
file called `package.json` in the current directory, loads the corresponding
add-on, and runs it under the version of Firefox it finds in the platform's
default install path.
#### Supported Options #####
You can point `cfx run` at a different `package.json` file using the
`--pkgdir` option, and pass arguments to your add-on using the
`--static-args` option.
You can specify a different version of the
<a href="dev-guide/glossary.html#host-application">host application</a>
using the `--binary` option, passing in the path to the application binary to
run. The path may be specified as a full path or may be relative to the current
directory. But note that the version must be 4.0b7 or later.
`cfx run` runs the host application with a new
[profile](http://support.mozilla.com/en-US/kb/profiles). You can specify an
existing profile using the `--profiledir` option, and this gives you access to
that profile's history, bookmarks, and other add-ons. This enables you to run
your add-on alongside debuggers like [Firebug](http://getfirebug.com/).
See <a href="dev-guide/cfx-tool.html#profiledir">
"Using --profiledir"</a> for more information.
<table>
<colgroup>
<col width="30%">
<col width="70%">
</colgroup>
<tr>
<td>
<code>-b BINARY, --binary=BINARY</code>
</td>
<td>
Use the host application binary specified in BINARY. BINARY may be specified as
a full path or as a path relative to the current directory.
</td>
</tr>
<tr>
<td>
<code>--binary-args=CMDARGS</code>
</td>
<td>
<p>Pass <a href="http://kb.mozillazine.org/Command_line_arguments">extra
arguments</a> to the binary being executed (for example, Firefox).</p>
<p>For example, to pass the
<code>-jsconsole</code> argument to Firefox, which will launch the
<a href="https://developer.mozilla.org/en/Error_Console">JavaScript
Error Console</a>, try the following:</p>
<pre>cfx run --binary-args -jsconsole</pre>
<p>To pass multiple arguments, or arguments containing spaces, quote them:</p>
<pre>cfx run --binary-args '-url "www.mozilla.org" -jsconsole'</pre>
</td>
</tr>
<tr>
<td>
<code>--extra-packages=EXTRA_PACKAGES</code>
</td>
<td>
Extra packages to include, specified as a comma-separated list of package
names.
</td>
</tr>
<tr>
<td>
<code>-g CONFIG, --use-config=CONFIG</code>
</td>
<td>
Pass a set of options by
<a href="dev-guide/cfx-tool.html#configurations">referencing a named configuration</a>.
</td>
</tr>
<tr>
<td>
<code>-p PROFILEDIR, --profiledir=PROFILEDIR</code>
</td>
<td>
<p>Use an existing
<a href="http://support.mozilla.com/en-US/kb/profiles">profile</a>
located in PROFILEDIR. PROFILEDIR may be specified as
a full path or as a path relative to the current directory.</p>
<p>See <a href="dev-guide/cfx-tool.html#profiledir">
"Using --profiledir"</a> for more information.
</td>
</tr>
<tr>
<td>
<code>--pkgdir=PKGDIR</code>
</td>
<td>
Use an add-on located in PKGDIR. PKGDIR may be specified as
a full path or as a path relative to the current directory.
</td>
</tr>
<tr>
<td>
<code>--static-args=STATIC_ARGS</code>
</td>
<td>
<a href="dev-guide/cfx-tool.html#arguments">Pass arguments to your add-on</a>,
in JSON format.
</td>
</tr>
</table>
#### Experimental Options ####
<table>
<colgroup>
<col width="30%">
<col width="70%">
</colgroup>
<tr>
<td>
<code>-a APP, --app=APP</code>
</td>
<td>
By default, <code>cfx run</code> uses Firefox as the
<a href="dev-guide/glossary.html#host-application">host application</a>.
This option enables you to select a different host. You can specify
"firefox", "xulrunner", "fennec", or "thunderbird". But note that at
present only Firefox is supported.
</td>
</tr>
<tr>
<td>
<code>--no-run</code>
</td>
<td>
<p>With this option <code>cfx</code> will not execute the command, but
will print out the command that it would have used to execute the
command.</p>
<p>For example, if you type:</p>
<pre>
cfx run ---no-run</pre>
<p>you will see something like:</p>
<pre>
To launch the application, enter the following command:
/path/to/firefox/firefox-bin -profile
/path/to/profile/tmpJDNlP6.mozrunner -foreground -no-remote</pre>
<p>This enables you to run the add-on without going through
<code>cfx</code>, which might be useful if you want to run it
inside a debugger like GDB.</p>
</td>
</tr>
<tr>
<td>
<code>--templatedir=TEMPLATEDIR</code>
</td>
<td>
The <code>cfx run</code> command constructs the add-on using a extension
template which you can find under the SDK root, in
<code>app-extension</code>.
Use the <code>--templatedir</code> option to specify a different template.
TEMPLATEDIR may be specified as a full path or as a path relative to the
current directory.
</td>
</tr>
</table>
#### Internal Options ####
<table>
<colgroup>
<col width="30%">
<col width="70%">
</colgroup>
<tr>
<td>
<code>--addons=ADDONS</code>
</td>
<td>
Paths of add-ons to install, comma-separated. ADDONS may be specified as
a full path or as a path relative to the current directory.
</td>
</tr>
<tr>
<td>
<code>--e10s</code>
</td>
<td>
If this option is set then the add-on runs in a separate process.
This option is currently not implemented.
</td>
</tr>
<tr>
<td>
<code>--keydir=KEYDIR</code>
</td>
<td>
Supply a different location for
<a href="dev-guide/guides/program-id.html">signing keys</a>.
KEYDIR may be specified as a full path or as a path relative to the
current directory.
</td>
</tr>
</table>
### cfx test ###
Run available tests for the specified package.
<span class="aside">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".</span>
Called with no options this command will look for a file called `package.json`
in the current directory. If `package.json` exists, `cfx` will load the
corresponding add-on, load from the `tests` directory
any modules that start with the word `test-` and run the unit tests
they contain.
See the
[tutorial on unit testing](dev-guide/tutorials/unit-testing.html) and the
[reference documentation for the `assert` module](modules/sdk/test/assert.html)
for details.
#### Supported Options #####
As with `cfx run` you can use options to control which host application binary
version to use, and to select a profile.
You can also control which tests are run: you
can test dependent packages, filter the tests by name and run tests multiple
times.
<table>
<colgroup>
<col width="30%">
<col width="70%">
</colgroup>
<tr>
<td>
<code>-b BINARY, --binary=BINARY</code>
</td>
<td>
Use the host application binary specified in BINARY. BINARY may be specified as
a full path or as a path relative to the current directory.
</td>
</tr>
<tr>
<td>
<code>--binary-args=CMDARGS</code>
</td>
<td>
<p>Pass <a href="http://kb.mozillazine.org/Command_line_arguments">extra
arguments</a> to the binary being executed (for example, Firefox).</p>
<p>For example, to pass the
<code>-jsconsole</code> argument to Firefox, which will launch the
<a href="https://developer.mozilla.org/en/Error_Console">JavaScript
Error Console</a>, try the following:</p>
<pre>cfx run --binary-args -jsconsole</pre>
<p>To pass multiple arguments, or arguments containing spaces, quote them:</p>
<pre>cfx run --binary-args '-url "www.mozilla.org" -jsconsole'</pre>
</td>
</tr>
<tr>
<td>
<code>--dependencies</code>
</td>
<td>
Load and run any tests that are included with modules that your package
depends on.
<br>
For example: if your add-on depends on modules from the SDK, then
<code>cfx</code> will run the unit tests for the SDK's modules as well
as yours.
</td>
</tr>
<tr>
<td>
<code>-f FILENAME[:TESTNAME], --filter=FILENAME[:TESTNAME]</code>
</td>
<td>
Only run tests whose filenames match FILENAME and
optionally match TESTNAME, both regexps (test, testall, testex, testpkgs)
<br>
For example: if you specify <code>--filter data</code>, then
<code>cfx</code> will only run tests in those modules whose name contain
the string "data".
</td>
</tr>
<tr>
<td>
<code>-g CONFIG, --use-config=CONFIG</code>
</td>
<td>
Pass a set of options by
<a href="dev-guide/cfx-tool.html#configurations">referencing a named configuration</a>.
</td>
</tr>
<tr>
<td>
<code>-p PROFILEDIR, --profiledir=PROFILEDIR</code>
</td>
<td>
<p>Use an existing
<a href="http://support.mozilla.com/en-US/kb/profiles">profile</a>
located in PROFILEDIR. PROFILEDIR may be specified as
a full path or as a path relative to the current directory.</p>
<p>See <a href="dev-guide/cfx-tool.html#profiledir">
"Using --profiledir"</a> for more information.
</td>
</tr>
<tr>
<td>
<code>--times=ITERATIONS</code>
</td>
<td>
Execute tests ITERATIONS number of times.
</td>
</tr>
</table>
#### Experimental Options ####
<table>
<colgroup>
<col width="30%">
<col width="70%">
</colgroup>
<tr>
<td>
<code>-a APP, --app=APP</code>
</td>
<td>
By default, <code>cfx test</code> uses Firefox as the
<a href="dev-guide/glossary.html#host-application">host application</a>.
This option enables you to select a different host. You can specify
"firefox", "xulrunner", "fennec", or "thunderbird". But note that at
present only Firefox is supported.
</td>
</tr>
<tr>
<td>
<code>--no-run</code>
</td>
<td>
<p>With this option <code>cfx</code> will not execute the command, but
will print out the command that it would have used to execute the
command.</p>
<p>For example, if you type:</p>
<pre>
cfx run ---no-run</pre>
<p>you will see something like:</p>
<pre>
To launch the application, enter the following command:
/path/to/firefox/firefox-bin -profile
/path/to/profile/tmpJDNlP6.mozrunner -foreground -no-remote</pre>
<p>This enables you to run the add-on without going through
<code>cfx</code>, which might be useful if you want to run it
inside a debugger like GDB.</p>
</td>
</tr>
<tr>
<td>
<code>--use-server</code>
</td>
<td>
Run tests using a server previously started with <code>cfx develop</code>.
</td>
</tr>
</table>
#### Internal Options ####
<table>
<colgroup>
<col width="30%">
<col width="70%">
</colgroup>
<tr>
<td>
<code>--addons=ADDONS</code>
</td>
<td>
Paths of add-ons to install, comma-separated.
ADDONS may be specified as full paths or relative to the
current directory.
</td>
</tr>
<tr>
<td>
<code>--e10s</code>
</td>
<td>
If this option is set then the add-on runs in a separate process.
This option is currently not implemented.
</td>
</tr>
<tr>
<td>
<code>--keydir=KEYDIR</code>
</td>
<td>
Supply a different location for
<a href="dev-guide/guides/program-id.html">signing keys</a>.
KEYDIR may be specified as a full path or as a path relative to the
current directory.
</td>
</tr>
<tr>
<td>
<code>--logfile=LOGFILE</code>
</td>
<td>
Log console output to the file specified by LOGFILE.
LOGFILE may be specified as a full path or as a path relative to the
current directory.
</td>
</tr>
<tr>
<td>
<code>--profile-memory=PROFILEMEMORY</code>
</td>
<td>
If this option is given and PROFILEMEMORY is any non-zero integer, then
<code>cfx</code> dumps detailed memory usage information to the console
when the tests finish.
</td>
</tr>
<tr>
<td>
<code>--test-runner-pkg=TEST_RUNNER_PKG</code>
</td>
<td>
Name of package containing test runner program. Defaults to
<code>test-harness</code>.
</td>
</tr>
</table>
### cfx xpi ###
This tool is used to package your add-on as an
[XPI](https://developer.mozilla.org/en/XPI) file, which is the install file
format for Mozilla add-ons.
Called with no options, this command looks for a file called `package.json` in
the current directory and creates the corresponding XPI file.
Once you have built an XPI file you can distribute your add-on by submitting
it to [addons.mozilla.org](http://addons.mozilla.org).
#### updateURL and updateLink ####
If you choose to host the XPI yourself you should enable the host application
to find new versions of your add-on.
To do this, include a URL in the XPI called the
[updateURL](https://developer.mozilla.org/en/install_manifests#updateURL): the
host application will go here to get information about updates. At the
`updateURL` you host a file in the
[update RDF](https://developer.mozilla.org/en/extension_versioning,_update_and_compatibility#Update_RDF_Format)
format: among other things, this includes another URL called `updateLink` which
points to the updated XPI itself.
The `--update-link` and `--update-url` options simplify this process.
Both options take a URL as an argument.
The `--update-link` option builds an update RDF alongside the XPI, and embeds
the supplied URL in the update RDF as the value of `updateLink`.
The `--update-url` option embeds the supplied URL in the XPI file, as the value
of `updateURL`.
Note that as the [add-on documentation](https://developer.mozilla.org/en/extension_versioning,_update_and_compatibility#Securing_Updates)
explains, you should make sure the update procedure for your add-on is secure,
and this usually involves using HTTPS for the links.
So if we run the following command:
<pre>
cfx xpi --update-link https://example.com/addon/latest
--update-url https://example.com/addon/update_rdf
</pre>
`cfx` will create two files:
* an XPI file which embeds
`https://example.com/addon/update_rdf` as the value of `updateURL`
* an RDF file which embeds `https://example.com/addon/latest` as the value of
`updateLink`.
#### Supported Options ####
As with `cfx run` you can point `cfx` at a different `package.json` file using
the `--pkgdir` option. You can also embed arguments in the XPI using the
`--static-args` option: if you do this the arguments will be passed to your
add-on whenever it is run.
<table>
<colgroup>
<col width="50%">
<col width="50%">
</colgroup>
<tr>
<td>
<code>--extra-packages=EXTRA_PACKAGES</code>
</td>
<td>
Extra packages to include, specified as a comma-separated list of package
names.
</td>
</tr>
<tr>
<td>
<code>-g CONFIG, --use-config=CONFIG</code>
</td>
<td>
Pass a set of options by
<a href="dev-guide/cfx-tool.html#configurations">referencing a named configuration</a>.
</td>
</tr>
<tr>
<td>
<code>--pkgdir=PKGDIR</code>
</td>
<td>
Use an add-on located in PKGDIR.
PKGDIR may be specified as a full path or as a path relative to the
current directory.
</td>
</tr>
<tr>
<td>
<code>--static-args=STATIC_ARGS</code>
</td>
<td>
<a href="dev-guide/cfx-tool.html#arguments">Pass arguments to your add-on</a>,
in JSON format.
</td>
</tr>
<tr>
<td>
<code>--update-link=UPDATE_LINK</code>
</td>
<td>
Build an
<a href="https://developer.mozilla.org/en/extension_versioning,_update_and_compatibility#Update_RDF_Format">update RDF</a>
alongside the XPI file, and embed the URL supplied in UPDATE_LINK in it as
the value of <code>updateLink</code>.
</td>
</tr>
<tr>
<td>
<code>--update-link=UPDATE_URL</code>
</td>
<td>
Embed the URL supplied in UPDATE_URL in the XPI file, as the value
of <code>updateURL</code>.
</td>
</tr>
</table>
#### Experimental Options ####
<table>
<colgroup>
<col width="50%">
<col width="50%">
</colgroup>
<tr>
<td>
<code>--templatedir=TEMPLATEDIR</code>
</td>
<td>
The <code>cfx xpi</code> command constructs the add-on using a extension
template which you can find under the SDK root, in
<code>app-extension</code>.
Use the <code>--templatedir</code> option to specify a different template.
TEMPLATEDIR may be specified as a full path or as a path relative to the
current directory.
</td>
</tr>
</table>
#### Internal Options ####
<table>
<colgroup>
<col width="50%">
<col width="50%">
</colgroup>
<tr>
<td>
<code>--keydir=KEYDIR</code>
</td>
<td>
Supply a different location for
<a href="dev-guide/guides/program-id.html">signing keys</a>.
KEYDIR may be specified as a full path or as a path relative to the
current directory.
</td>
</tr>
</table>
## Experimental Commands ##
### cfx develop ###
This initiates an instance of a host application in development mode,
and allows you to pipe commands into it from another shell without
having to constantly restart it. Aside from convenience, for SDK
Platform developers this has the added benefit of making it easier to
detect leaks.
For example, in shell A, type:
<pre>
cfx develop
</pre>
In shell B, type:
<pre>
cfx test --use-server
</pre>
This will send `cfx test --use-server` output to shell A. If you repeat the
command in shell B, `cfx test --use-server` output will appear again in shell A
without restarting the host application.
`cfx develop` doesn't take any options.
## Internal Commands ##
### cfx sdocs ###
Executing this command builds a static HTML version of the SDK documentation
that can be hosted on a web server without the special application support
required by `cfx docs`.
#### Options ####
<table>
<colgroup>
<col width="50%">
<col width="50%">
</colgroup>
<tr>
<td>
<code>--baseurl=BASEURL</code>
</td>
<td>
The root of the static docs tree, for example:
<code>http://example.com/sdk-docs/</code>.
</td>
</tr>
</table>
### cfx testcfx ###
This will run a number of tests on the cfx tool, including tests against the
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
of tests run and their success depends on what application they are run
with, and which binary is used.
This accepts the same options as `cfx test`.
### cfx testex ###
This will test all available example code. Note that the number
of tests run and their success depends on what application they are run
with, and which binary is used.
This accepts the same options as `cfx test`.
### cfx testall ###
This will test *everything*: the cfx tool, all available CommonJS packages,
and all examples.
This accepts the same options as `cfx test`.
## <a name="profiledir">Using --profiledir</a> ##
By default, `cfx run` and `cfx test` use a new profile each time they
are executed. This means that any profile-specific data entered from
one run of `cfx` will not, by default, be available in the next run.
This includes, for example, any extra add-ons you installed, or your
history, or any data stored using the
[simple-storage](modules/sdk/simple-storage.html) API.
To make `cfx` use a specific profile, pass the `--profiledir` option,
specifying the path to the profile you wish to use.
If you give `--profiledir` a path to a nonexistent profile, `cfx`
will create a profile there for you. So you just have to make up
a path and name the first time, and keep using it:
<pre>
cfx run --profiledir="~/addon-dev/profiles/boogaloo"
</pre>
The path must contain at least one "/" (although you may specify
just "./dir").
## <a name="configurations">Using Configurations</a> ##
The `--use-config` option enables you to specify a set of options as a named
configuration in a file, then pass them to `cfx` by referencing the named set.
You define configurations in a file called `local.json` which should live
in the root directory of your SDK. Configurations are listed under a key called
"configs".
Suppose your the following `local.json` is as follows:
<pre>
{
"configs": {
"ff40": ["-b", "/usr/bin/firefox-4.0"]
}
}
</pre>
You can run:
<pre>
cfx test --use-config=ff40
</pre>
And it would be equivalent to:
<pre>
cfx test -a firefox -b /usr/bin/firefox-4.0
</pre>
This method of defining configuration options can be used for all of the `run`,
build, and test tools. If "default" is defined in the `local.json` cfx will use
that configuration unless otherwise specified.
## <a name="arguments">Passing Static Arguments</a> ##
You can use the cfx `--static-args` option to pass arbitrary data to your
program. This may be especially useful if you run cfx from a script.
The value of `--static-args` must be a JSON string. The object encoded by the
JSON becomes the `staticArgs` property of the
[`system` module](modules/sdk/system.html).
The default value of
`--static-args` is `"{}"` (an empty object), so you don't have to worry about
checking whether `staticArgs` exists in `system`.
For example, if your add-on looks like this:
var system = require("sdk/system");
console.log(system.staticArgs.foo);
And you run cfx like this:
<pre>
cfx run --static-args="{ \"foo\": \"Hello from the command line\" }"
</pre>
Then your console should contain this:
<pre>
info: my-addon: Hello from the command line
</pre>
The `--static-args` option is recognized by two of the package-specific
commands: `run` and `xpi`. When used with the `xpi` command, the JSON is
packaged with the XPI's harness options and will therefore be used whenever the
program in the XPI is run.

Просмотреть файл

@ -0,0 +1,46 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# console #
The `console` object enables your add-on to log messages. If you have started
the host application for your add-on from the command line (for example, by
executing `cfx run` or `cfx test`) then these messages appear in the command
shell you used. If the add-on has been installed in the host application, then
the messages appear in the host application's
[Error Console](https://developer.mozilla.org/en/Error_Console).
The `console` object has the following methods:
<code>console.**log**(*object*[, *object*, ...])</code>
Logs an informational message to the shell.
Depending on the console's underlying implementation and user interface,
you may be able to introspect into the properties of non-primitive objects
that are logged.
<code>console.**info**(*object*[, *object*, ...])</code>
A synonym for `console.log()`.
<code>console.**warn**(*object*[, *object*, ...])</code>
Logs a warning message.
<code>console.**error**(*object*[, *object*, ...])</code>
Logs an error message.
<code>console.**debug**(*object*[, *object*, ...])</code>
Logs a debug message.
<code>console.**exception**(*exception*)</code>
Logs the given exception instance as an error, outputting information
about the exception's stack traceback if one is available.
<code>console.**trace**()</code>
Logs a stack trace at the point this function is called.

Просмотреть файл

@ -0,0 +1,68 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Credits #
We'd like to thank our many Jetpack project contributors! They include:
* Adamantium
* Ehsan Akhgari
* arky
* [Heather Arthur](https://github.com/harthur)
* Dietrich Ayala
* [Romain B](https://github.com/Niamor)
* Will Bamberg
* Zbigniew Braniecki
* Daniel Buchner
* James Burke
* [Shane Caraveo](https://github.com/mixedpuppy)
* [Matěj Cepl](https://github.com/mcepl)
* Hernán Rodriguez Colmeiro
* [David Creswick](https://github.com/dcrewi)
* dexter
* [Matteo Ferretti (ZER0)](https://github.com/ZER0)
* fuzzykiller
* [Marcio Galli](https://github.com/taboca)
* [Ben Gillbanks](http://www.iconfinder.com/browse/iconset/circular_icons/)
* Felipe Gomes
* Irakli Gozalishvili
* Luca Greco
* Mark Hammond
* Lloyd Hilaiel
* Bobby Holley
* Eric H. Jung
* Hrishikesh Kale
* Wes Kocher
* Edward Lee
* Myk Melez
* Zandr Milewski
* Noelle Murata
* Joe R. Nassimian ([placidrage](https://github.com/placidrage))
* Nick Nguyen
* [ongaeshi](https://github.com/ongaeshi)
* Paul OShannessy
* l.m.orchard
* Alexandre Poirot
* Nickolay Ponomarev
* Aza Raskin
* Till Schneidereit
* Justin Scott
* Ayan Shah
* [skratchdot](https://github.com/skratchdot)
* [Mihai Sucan](https://github.com/mihaisucan)
* Clint Talbert
* Thomas
* Dave Townsend
* Peter Van der Beken
* Atul Varma
* [Erik Vold](https://github.com/erikvold)
* Vladimir Vukicevic
* Brian Warner
* [Henri Wiechers](https://github.com/hwiechers)
* Drew Willcoxon
* Piotr Zalewa
* [David Guo](https://github.com/dglol)
* [Nils Maier](https://github.com/nmaier)
* [Louis-Rémi Babé](https://github.com/louisremi)
* [Matthias Tylkowski](https://github.com/tylkomat)

Просмотреть файл

@ -0,0 +1,73 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Glossary #
This glossary contains a list of terms used in the Add-on SDK.
__Add-on__: A software package that adds functionality to a Mozilla application,
which can be built with either Mozilla's traditional add-on platform or the SDK.
__Add-on SDK__: A toolchain and associated applications for developing add-ons.
__API Utils__: A small, self-contained set of low-level modules that forms
the base functionality for the SDK. The library can be "bootstrapped" into
any Mozilla application or add-on.
__CFX__: A command-line build, testing, and packaging tool for SDK-based code.
__CommonJS__: A specification for a cross-platform JavaScript module
system and standard library. [Web site](http://commonjs.org/).
__Extension__: Synonym for Add-on.
__Globals__: The set of global variables and objects provided
to all modules, such as `console` and `memory`. Includes
CommonJS globals like `require` and standard JavaScript globals such
as `Array` and `Math`.
<span><a name="host-application">__Host Application__:</a> Add-ons are executed in
the context of a host application, which is the application they are extending.
Firefox and Thunderbird are the most obvious hosts for Mozilla add-ons, but
at present only Firefox is supported as a host for add-ons developed using the
Add-on SDK.</span>
__Jetpack Prototype__: A Mozilla Labs experiment that predated and inspired
the SDK. The SDK incorporates many ideas and some code from the prototype.
__Loader__: An object capable of finding, evaluating, and
exposing CommonJS modules to each other in a given security context,
while providing each module with necessary globals and
enforcing security boundaries between the modules as necessary. It's
entirely possible for Loaders to create new Loaders.
__Low-Level Module__: A module with the following properties:
* Has "chrome" access to the Mozilla platform (e.g. `Components.classes`)
and all globals.
* Is reloadable without leaking memory.
* Logs full exception tracebacks originating from client-provided
callbacks (i.e., does not allow the exceptions to propagate into
Mozilla platform code).
* Can exist side-by-side with multiple instances and versions of
itself.
* Contains documentation on security concerns and threat modeling.
__Module__: A CommonJS module that is either a Low-Level Module
or an Unprivileged Module.
__Package__: A directory structure containing modules,
documentation, tests, and related metadata. If a package contains
a program and includes proper metadata, it can be built into
a Mozilla application or add-on.
__Program__: A module named `main` that optionally exports
a `main()` function. This module is intended either to start an application for
an end-user or add features to an existing application.
__Unprivileged Module__: A CommonJS module that may be run
without unrestricted access to the Mozilla platform, and which may use
all applicable globals that don't require chrome privileges.
[Low-Level Module Best Practices]: dev-guide/module-development/best-practices.html

Просмотреть файл

@ -0,0 +1,164 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Accessing the DOM #
This page talks about the access content scripts have to DOM objects
in the pages they are attached to.
## XRayWrapper ##
Content scripts need to be able to access DOM objects in arbitrary web
pages, but this could cause two potential security problems:
1. JavaScript values from the content script could be accessed by the page,
enabling a malicious page to steal data or call privileged methods.
2. a malicious page could redefine standard functions and properties of DOM
objects so they don't do what the add-on expects.
To deal with this, content scripts access DOM objects using
`XRayWrapper`, (also known as
[`XPCNativeWrapper`](https://developer.mozilla.org/en/XPCNativeWrapper)).
These wrappers give the user access to the native values of DOM functions
and properties, even if they have been redefined by a script.
For example: the page below redefines `window.confirm()` to return
`true` without showing a confirmation dialog:
<script type="syntaxhighlighter" class="brush: html"><![CDATA[
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html lang='en' xml:lang='en' xmlns="http://www.w3.org/1999/xhtml">
<head>
<script>
window.confirm = function(message) {
return true;
}
&lt;/script>
</head>
</html>
</script>
But thanks to the wrapper, a content script which calls
`window.confirm()` will get the native implementation:
var widgets = require("sdk/widget");
var tabs = require("sdk/tabs");
var data = require("sdk/self").data;
var widget = widgets.Widget({
id: "transfer",
label: "Transfer",
content: "Transfer",
width: 100,
onClick: function() {
tabs.activeTab.attach({
// native implementation of window.confirm will be used
contentScript: "console.log(window.confirm('Transfer all my money?'));"
});
}
});
tabs.open(data.url("xray.html"));
The wrapper is transparent to content scripts: as far as the content script
is concerned, it is accessing the DOM directly. But because it's not, some
things that you might expect to work, won't. For example, if the page includes
a library like [jQuery](http://www.jquery.com), or any other page script
adds other objects to any DOM nodes, they won't be visible to the content
script. So to use jQuery you'll typically have to add it as a content script,
as in [this example](dev-guide/guides/content-scripts/reddit-example.html).
### XRayWrapper Limitations ###
There are some limitations with accessing objects through XRayWrapper.
First, XRayWrappers don't inherit from JavaScript's
[`Object`](https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object),
so methods like `valueOf`, `toSource`, and `watch` are not available.
This issue is being tracked as
[bug 787013](https://bugzilla.mozilla.org/show_bug.cgi?id=787013).
Second, you can't access the prototype of an object through an XRayWrapper.
Consider a script like this:
window.HTMLElement.prototype.foo = 'bar';
window.alert(window.document.body.foo);
Run as a normal page script, this will work fine. But if you execute it as
a content script you'll see an error like:
<pre>
TypeError: window.HTMLElement.prototype is undefined
</pre>
This issue is being tracked as
[bug 787070](https://bugzilla.mozilla.org/show_bug.cgi?id=787070).
The main effect of this is that certain features of the
[Prototype JavaScript framework](http://www.prototypejs.org/) don't work
if it is loaded as a content script. As a workaround you can
disable these features by setting
`Prototype.BrowserFeatures.SpecificElementExtensions` to `false`
in `prototype.js`:
<pre>
if (Prototype.Browser.MobileSafari)
Prototype.BrowserFeatures.SpecificElementExtensions = false;
+// Disable element extension in addon-sdk content scripts
+Prototype.BrowserFeatures.SpecificElementExtensions = false;
</pre>
## Adding Event Listeners ##
You can listen for DOM events in a content script just as you can in a normal
page script, but there's one important difference: if you define an event
listener by passing it as a string into
[`setAttribute()`](https://developer.mozilla.org/en/DOM/element.setAttribute),
then the listener is evaluated in the page's context, so it will not have
access to any variables defined in the content script.
For example, this content script will fail with the error "theMessage is not
defined":
var theMessage = "Hello from content script!";
anElement.setAttribute("onclick", "alert(theMessage);");
So using `setAttribute()` is not recommended. Instead, add a listener by
assignment to
[`onclick`](https://developer.mozilla.org/en/DOM/element.onclick) or by using
[`addEventListener()`](https://developer.mozilla.org/en/DOM/element.addEventListener),
in either case defining the listener as a function:
var theMessage = "Hello from content script!";
anElement.onclick = function() {
alert(theMessage);
};
anotherElement.addEventListener("click", function() {
alert(theMessage);
});
Note that with both `onclick` assignment and `addEventListener()`, you must
define the listener as a function. It cannot be defined as a string, whether
in a content script or in a page script.
## unsafeWindow ##
If you really need direct access to the underlying DOM, you can use the
global `unsafeWindow` object.
To see the difference, try editing the example above
so the content script uses `unsafeWindow.confirm()` instead of
`window.confirm()`.
Avoid using `unsafeWindow` if possible: it is the same concept as
Greasemonkey's unsafeWindow, and the
[warnings for that](http://wiki.greasespot.net/UnsafeWindow) apply equally
here. Also, `unsafeWindow` isn't a supported API, so it could be removed or
changed in a future version of the SDK.

Просмотреть файл

@ -0,0 +1,246 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Communicating With Other Scripts #
This section of the guide explains how content scripts can
communicate with:
* [your `main.js` file](dev-guide/guides/content-scripts/communicating-with-other-scripts.html#main.js),
or any other modules in your add-on
* [other content scripts loaded by your add-on](dev-guide/guides/content-scripts/communicating-with-other-scripts.html#Content Scripts)
* [page scripts](dev-guide/guides/content-scripts/communicating-with-other-scripts.html#Page Scripts) (that is, scripts embedded in the web page or
included using `<script>` tags)
## main.js ##
Your content scripts can communicate with your add-on's "main.js"
(or any other modules you're written for your add-on) by sending it messages,
using either the `port.emit()` API or the `postMessage()` API. See the
articles on
[using `postMessage()`](dev-guide/guides/content-scripts/using-postmessage.html)
and
[using `port`](dev-guide/guides/content-scripts//using-port.html) for details.
## Content Scripts ##
Content scripts loaded into the same document can interact
with each other directly as well as with the web content itself. However,
content scripts which have been loaded into different documents
cannot interact with each other.
For example:
* if an add-on creates a single `panel` object and loads several content
scripts into the panel, then they can interact with each other
* if an add-on creates two `panel` objects and loads a script into each
one, they can't interact with each other.
* if an add-on creates a single `page-mod` object and loads several content
scripts into the page mod, then only content scripts associated with the
same page can interact with each other: if two different matching pages are
loaded, content scripts attached to page A cannot interact with those attached
to page B.
The web content has no access to objects created by the content script, unless
the content script explicitly makes them available.
## Page Scripts ##
If a page includes its own scripts using `<script>` tags,
either embedded in the page or linked to it using the `src` attribute, there
are a couple of ways a content script can communicate with it:
* using the [DOM `postMessage()` API](dev-guide/guides/content-scripts/communicating-with-other-scripts.html#Using the DOM postMessage API)
* using [custom DOM events](dev-guide/guides/content-scripts/communicating-with-other-scripts.html#Using Custom DOM Events)
### Using the DOM postMessage API ###
You can communicate between the content script and page scripts using
[`window.postMessage()`](https://developer.mozilla.org/en/DOM/window.postMessage),
but there's a twist: in early versions of the SDK, the global `postMessage()`
function in content scripts was used for communicating between the content
script and the main add-on code. Although this has been
[deprecated in favor of `self.postMessage`](https://wiki.mozilla.org/Labs/Jetpack/Release_Notes/1.0b5#Major_Changes),
the old globals are still supported, so you can't currently use
`window.postMessage()`. You must use `document.defaultView.postMessage()`
instead.
#### Messaging From Content Script To Page Script ####
Suppose we have a page called "listen.html" hosted at "my-domain.org", and we want to send messages
from the add-on to a script embedded in that page.
In the main add-on code, we have a
[`page-mod`](modules/sdk/page-mod.html) that attaches the content script
"talk.js" to the right page:
var data = require("sdk/self").data;
var pageMod = require("sdk/page-mod");
pageMod.PageMod({
include: "http://my-domain.org/listen.html",
contentScriptFile: data.url("talk.js")
});
The "talk.js" content script uses `document.defaultView.postMessage()` to send
the message to the page:
document.defaultView.postMessage("Message from content script", "http://my-domain.org/");
The second argument may be '*' which will allow communication with any domain.
Finally, "listen.html" uses `window.addEventListener()` to listen for
messages from the content script:
<script type="syntaxhighlighter" class="brush: html"><![CDATA[
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;&lt;/head&gt;
&lt;body&gt;
&lt;script&gt;
window.addEventListener('message', function(event) {
window.alert(event.data);
}, false);
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
]]>
</script>
#### Messaging From Page Script To Content Script ####
Sending messages from the page script to the content script is just
the same, but in reverse.
Here "main.js" creates a [`page-mod`](modules/sdk/page-mod.html)
that attaches "listen.js" to the web page:
var data = require("sdk/self").data;
var pageMod = require("sdk/page-mod");
pageMod.PageMod({
include: "http://my-domain.org/talk.html",
contentScriptFile: data.url("listen.js")
});
The web page "talk.html" embeds a script that uses `window.postMessage()`
to send the content script a message when the user clicks a button:
<script type="syntaxhighlighter" class="brush: html"><![CDATA[
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;&lt;/head&gt;
&lt;body&gt;
&lt;script&gt;
function sendMessage() {
window.postMessage("Message from page script", "http://my-domain.org/");
}
&lt;/script&gt;
&lt;button onclick="sendMessage()"&gt;Send Message&lt;/button&gt;
&lt;/body&gt;
&lt;/html&gt;
</script>
Finally, the content script "listen.js" uses
`document.defaultView.addEventListener()` to listen for messages from the page
script:
document.defaultView.addEventListener('message', function(event) {
console.log(event.data);
console.log(event.origin);
}, false);
### Using Custom DOM Events ###
As an alternative to using `postMessage()` you can use
[custom DOM events](https://developer.mozilla.org/en/DOM/CustomEvent)
to communicate between page scripts and content scripts.
#### Messaging From Content Script To Page Script ####
Here's an example showing how to use custom DOM events to send a message
from a content script to a page script.
First, "main.js" will create a [`page-mod`](modules/sdk/page-mod.html)
that will attach "talk.js" to the target web page:
var data = require("sdk/self").data;
var pageMod = require("sdk/page-mod");
pageMod.PageMod({
include: "http://my-domain.org/listen.html",
contentScriptFile: data.url("talk.js")
});
Next, "talk.js" creates and dispatches a custom event, passing the payload
in the `detail` parameter to `initCustomEvent()`:
<!-- This comment is used to terminate the Markdown list above -->
var event = document.createEvent('CustomEvent');
event.initCustomEvent("addon-message", true, true, { hello: 'world' });
document.documentElement.dispatchEvent(event);
Finally "listen.html" listens for the new event and examines its
`detail` attribute to retrieve the payload:
<script type="syntaxhighlighter" class="brush: html"><![CDATA[
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;&lt;/head&gt;
&lt;body&gt;
&lt;script&gt;
document.documentElement.addEventListener("addon-message", function(event) {
window.alert(JSON.stringify(event.detail))
}, false);
&lt;/script&gt;
&lt;/body&gt;
&lt;/html&gt;
</script>
#### Messaging From Page Script to Content Script ####
Sending messages using custom DOM events from the page script
to the content script is just the same, but in reverse.
Again, "main.js" creates a [`page-mod`](modules/sdk/page-mod.html)
to target the page we are interested in:
var data = require("sdk/self").data;
var pageMod = require("sdk/page-mod");
pageMod.PageMod({
include: "http://my-domain.org/talk.html",
contentScriptFile: data.url("listen.js")
});
The web page "talk.html" creates and dispatches a custom DOM event,
using `initCustomEvent()`'s `detail` parameter to supply the payload:
<script type="syntaxhighlighter" class="brush: html"><![CDATA[
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;&lt;/head&gt;
&lt;body&gt;
&lt;script&gt;
function sendMessage() {
var event = document.createEvent('CustomEvent');
event.initCustomEvent("addon-message", true, true, { hello: 'world' });
document.documentElement.dispatchEvent(event);
}
&lt;/script&gt;
&lt;button onclick="sendMessage()"&gt;Send Message&lt;/button&gt;
&lt;/body&gt;
&lt;/html&gt;
</script>
Finally, the content script "listen.js" listens for the new event
and retrieves the payload from its `detail` attribute:
document.documentElement.addEventListener("addon-message", function(event) {
console.log(JSON.stringify(event.detail));
}, false);

Просмотреть файл

@ -0,0 +1,93 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Content Scripts #
Almost all interesting add-ons will need to interact with web content or the
browser's user interface. For example, they may need to access and modify the
content of web pages or be notified when the user clicks a link.
The SDK provides several core modules to support this:
**[panel](modules/sdk/panel.html)**<br>
Create a dialog that can host web content.
**[page-worker](modules/sdk/page-worker.html)**<br>
Retrieve a page and access its content, without displaying it to the user.
**[page-mod](modules/sdk/page-mod.html)**<br>
Execute scripts in the context of selected web pages.
**[widget](modules/sdk/widget.html)**<br>
Host an add-on's user interface, including web content.
**[context-menu](modules/sdk/context-menu.html)**<br>
Add items to the browser's context menu.
Firefox is moving towards a model in which it uses separate
processes to display the UI, handle web content, and execute add-ons. The main
add-on code will run in the add-on process and will not have direct access to
any web content.
This means that an add-on which needs to interact with web content needs to be
structured in two parts:
* the main script runs in the add-on process
* any code that needs to interact with web content is loaded into the web
content process as a separate script. These separate scripts are called
_content scripts_.
A single add-on may use multiple content scripts, and content scripts loaded
into the same context can interact directly with each other as well as with
the web content itself. See the chapter on
<a href="dev-guide/guides/content-scripts/communicating-with-other-scripts.html">
communicating with other scripts</a>.
The add-on script and content script can't directly access each other's state.
Instead, you can define your own events which each side can emit, and the
other side can register listeners to handle them. The interfaces are similar
to the event-handling interfaces described in the
[Working with Events](dev-guide/guides/events.html) guide.
The diagram below shows an overview of the main components and their
relationships. The gray fill represents code written by the add-on developer.
<img class="image-center" src="static-files/media/content-scripting-overview.png"
alt="Content script events">
This might sound complicated but it doesn't need to be. The following add-on
uses the [page-mod](modules/sdk/page-mod.html) module to replace the
content of any web page in the `.co.uk` domain by executing a content script
in the context of that page:
var pageMod = require("sdk/page-mod");
pageMod.PageMod({
include: ["*.co.uk"],
contentScript: 'document.body.innerHTML = ' +
'"<h1>this page has been eaten</h1>";'
});
In this example the content script is supplied directly to the page mod via
the `contentScript` option in its constructor, and does not need to be
maintained as a separate file at all.
The next few chapters explain content scripts in detail:
* [Loading Content Scripts](dev-guide/guides/content-scripts/loading.html):
how to attach content scripts to web pages, and how to control the point at
which they are executed
* [Accessing the DOM](dev-guide/guides/content-scripts/accessing-the-dom.html):
detail about the access content scripts get to the DOM
* [Communicating With Other Scripts](dev-guide/guides/content-scripts/communicating-with-other-scripts.html):
detail about how content scripts can communicate with "main.js", with other
content scripts, and with scripts loaded by the web page itself
* [Communicating Using <code>port</code>](dev-guide/guides/content-scripts/using-port.html):
how to communicate between your add-on and its content scripts using the
<code>port</code> object
* [Communicating using <code>postMessage()</code>](dev-guide/guides/content-scripts/using-postmessage.html):
how to communicate between your add-on and its content scripts using the
<code>postMessage()</code> API
* [Example](dev-guide/guides/content-scripts/reddit-example.html):
a simple example add-on using content scripts

Просмотреть файл

@ -0,0 +1,79 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Loading Content Scripts #
The constructors for content-script-using objects such as panel and page-mod
define a group of options for loading content scripts:
<pre>
contentScript string, array
contentScriptFile string, array
contentScriptWhen string
contentScriptOptions object
</pre>
We have already seen the `contentScript` option, which enables you to pass
in the text of the script itself as a string literal. This version of the API
avoids the need to maintain a separate file for the content script.
The `contentScriptFile` option enables you to pass in the local file URL from
which the content script will be loaded. To supply the file
"my-content-script.js", located in the /data subdirectory under your add-on's
root directory, use a line like:
// "data" is supplied by the "self" module
var data = require("sdk/self").data;
...
contentScriptFile: data.url("my-content-script.js")
Both `contentScript` and `contentScriptFile` accept an array of strings, so you
can load multiple scripts, which can also interact directly with each other in
the content process:
// "data" is supplied by the "self" module
var data = require("sdk/self").data;
...
contentScriptFile:
[data.url("jquery-1.4.2.min.js"), data.url("my-content-script.js")]
Scripts specified using contentScriptFile are loaded before those specified
using contentScript. This enables you to load a JavaScript library like jQuery
by URL, then pass in a simple script inline that can use jQuery.
<div class="warning">
<p>Unless your content script is extremely simple and consists only of a
static string, don't use <code>contentScript</code>: if you do, you may
have problems getting your add-on approved on AMO.</p>
<p>Instead, keep the script in a separate file and load it using
<code>contentScriptFile</code>. This makes your code easier to maintain,
secure, debug and review.</p>
</div>
The `contentScriptWhen` option specifies when the content script(s) should be
loaded. It takes one of three possible values:
* "start" loads the scripts immediately after the document element for the
page is inserted into the DOM. At this point the DOM content hasn't been
loaded yet, so the script won't be able to interact with it.
* "ready" loads the scripts after the DOM for the page has been loaded: that
is, at the point the
[DOMContentLoaded](https://developer.mozilla.org/en/Gecko-Specific_DOM_Events)
event fires. At this point, content scripts are able to interact with the DOM
content, but externally-referenced stylesheets and images may not have finished
loading.
* "end" loads the scripts after all content (DOM, JS, CSS, images) for the page
has been loaded, at the time the
[window.onload event](https://developer.mozilla.org/en/DOM/window.onload)
fires.
The default value is "end".
The `contentScriptOptions` is a json that is exposed to content scripts as a read
only value under `self.options` property.
Any kind of jsonable value (object, array, string, etc.) can be used here.

Просмотреть файл

@ -0,0 +1,71 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Reddit Example #
This example add-on creates a panel containing the mobile version of Reddit.
When the user clicks on the title of a story in the panel, the add-on opens
the linked story in a new tab in the main browser window.
To accomplish this the add-on needs to run a content script in the context of
the Reddit page which intercepts mouse clicks on each title link and fetches the
link's target URL. The content script then needs to send the URL to the add-on
script.
This is the complete add-on script:
var data = require("sdk/self").data;
var reddit_panel = require("sdk/panel").Panel({
width: 240,
height: 320,
contentURL: "http://www.reddit.com/.mobile?keep_extension=True",
contentScriptFile: [data.url("jquery-1.4.4.min.js"),
data.url("panel.js")]
});
reddit_panel.port.on("click", function(url) {
require("sdk/tabs").open(url);
});
require("sdk/widget").Widget({
id: "open-reddit-btn",
label: "Reddit",
contentURL: "http://www.reddit.com/static/favicon.ico",
panel: reddit_panel
});
This code supplies two content scripts to the panel's constructor in the
`contentScriptFile` option: the jQuery library and the script that intercepts
link clicks.
Finally, it registers a listener to the user-defined `click` event which in
turn passes the URL into the `open` function of the
[tabs](modules/sdk/tabs.html) module.
This is the `panel.js` content script that intercepts link clicks:
$(window).click(function (event) {
var t = event.target;
// Don't intercept the click if it isn't on a link.
if (t.nodeName != "A")
return;
// Don't intercept the click if it was on one of the links in the header
// or next/previous footer, since those links should load in the panel itself.
if ($(t).parents('#header').length || $(t).parents('.nextprev').length)
return;
// Intercept the click, passing it to the addon, which will load it in a tab.
event.stopPropagation();
event.preventDefault();
self.port.emit('click', t.toString());
});
This script uses jQuery to interact with the DOM of the page and the
`self.port.emit` function to pass URLs back to the add-on script.
See the `examples/reddit-panel` directory for the complete example (including
the content script containing jQuery).

Просмотреть файл

@ -0,0 +1,183 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Communicating using "port" #
To enable add-on scripts and content scripts to communicate with each other,
each end of the conversation has access to a `port` object which defines two
functions:
**`emit()`** is used to emit an event. It may be called with any number of
parameters, but is most likely to be called with a name for the event and
an optional payload. The payload can be any value that is
<a href = "dev-guide/guides/content-scripts/using-port.html#json_serializable">serializable to JSON</a>
port.emit("myEvent", myEventPayload);
**`on()`** takes two parameters: the name of the event and a function to handle it:
port.on("myEvent", function handleMyEvent(myEventPayload) {
// Handle the event
});
Here's simple add-on that sends a message to a content script using `port`:
var tabs = require("sdk/tabs");
var alertContentScript = "self.port.on('alert', function(message) {" +
" window.alert(message);" +
"})";
tabs.on("ready", function(tab) {
worker = tab.attach({
contentScript: alertContentScript
});
worker.port.emit("alert", "Message from the add-on");
});
tabs.open("http://www.mozilla.org");
We could depict the interface between add-on code and content script code like
this:
<img class="image-center" src="static-files/media/content-scripting-events.png"
alt="Content script events">
Events are asynchronous: that is, the sender does not wait for a reply from
the recipient but just emits the event and continues processing.
## Accessing `port` in the Content Script ##
<span class="aside">Note that the global `self` object is completely
different from the [`self` module](modules/sdk/self.html), which
provides an API for an add-on to access its data files and ID.</span>
In the content script the `port` object is available as a property of the
global `self` object. Thus, to emit an event from a content script:
self.port.emit("myContentScriptEvent", myContentScriptEventPayload);
To receive an event from the add-on code:
self.port.on("myAddonEvent", function(myAddonEventPayload) {
// Handle the event
});
Compare this to the technique used to receive _built-in_ events in the
content script. For example, to receive the `context` event in a content script
associated with a [context menu](modules/sdk/context-menu.html)
object, you would call the `on` function attached to the global `self` object:
self.on("context", function() {
// Handle the event
});
So the `port` property is essentially used here as a namespace for
user-defined events.
## Accessing `port` in the Add-on Script ##
In the add-on code, the channel of communication between the add-on and a
particular content script context is encapsulated by the `worker` object. Thus
the `port` object for communicating with a content script is a property of the
corresponding `worker` object.
However, the worker is not exposed to add-on code in quite the same way
in all modules. The `panel` and `page-worker` objects integrate the
worker API directly. So to receive events from a content script associated
with a panel you use `panel.port.on()`:
var panel = require("sdk/panel").Panel({
contentScript: "self.port.emit('showing', 'panel is showing');"
});
panel.port.on("showing", function(text) {
console.log(text);
});
panel.show();
Conversely, to emit user-defined events from your add-on you can just call
`panel.port.emit()`:
var panel = require("sdk/panel").Panel({
contentScript: "self.port.on('alert', function(text) {" +
" console.log(text);" +
"});"
});
panel.show();
panel.port.emit("alert", "panel is showing");
The `panel` and `page-worker` objects only host a single page at a time,
so each distinct page object only needs a single channel of communication
to its content scripts. But some modules, such as `page-mod`, might need to
handle multiple pages, each with its own context in which the content scripts
are executing, so it needs a separate channel (worker) for each page.
So `page-mod` does not integrate the worker API directly: instead, each time a
content script is attached to a page, the worker associated with the page is
supplied to the page-mod in its `onAttach` function. By supplying a target for
this function in the page-mod's constructor you can register to receive
events from the content script, and take a reference to the worker so as to
emit events to it.
var pageModScript = "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);" +
"});"
var pageMod = require('sdk/page-mod').PageMod({
include: ['*'],
contentScript: pageModScript,
onAttach: function(worker) {
worker.port.on('click', function(html) {
worker.port.emit('warning', 'Do not click this again');
});
}
});
In the add-on above there are two user-defined events:
* `click` is sent from the page-mod to the add-on, when the user clicks an
element in the page
* `warning` sends a silly string back to the page-mod
## <a name="json_serializable">JSON-Serializable Values</a> ##
The payload for an event can be any JSON-serializable value. When events are
sent their payloads are automatically serialized, and when events are received
their payloads are automatically deserialized, so you don't need to worry
about serialization.
However, you _do_ have to ensure that the payload can be serialized to JSON.
This means that it needs to be a string, number, boolean, null, array of
JSON-serializable values, or an object whose property values are themselves
JSON-serializable. This means you can't send functions, and if the object
contains methods they won't be encoded.
For example, to include an array of strings in the payload:
var pageModScript = "self.port.emit('loaded'," +
" [" +
" document.location.toString()," +
" document.title" +
" ]" +
");"
var pageMod = require('page-mod').PageMod({
include: ['*'],
contentScript: pageModScript,
onAttach: function(worker) {
worker.port.on('loaded', function(pageInfo) {
console.log(pageInfo[0]);
console.log(pageInfo[1]);
});
}
});

Просмотреть файл

@ -0,0 +1,184 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Communicating using "postMessage()" #
As an alternative to user-defined events content modules support the built-in
`message` event. In most cases user-defined events are preferable to message
events. However, the `context-menu` module does not support user-defined
events, so to send messages from a content script to the add-on via a context
menu object, you must use message events.
## Handling Message Events in the Content Script ##
To send a message from a content script, you use the `postMessage` function of
the global `self` object:
self.postMessage(contentScriptMessage);
This takes a single parameter, the message payload, which may be any
<a href = "dev-guide/guides/content-scripts/using-port.html#json_serializable">JSON-serializable value</a>.
To receive a message from the add-on script, use `self`'s `on` function:
self.on("message", function(addonMessage) {
// Handle the message
});
Like all event-registration functions, this takes two parameters: the name
of the event, and the handler function. The handler function is passed the
message payload.
## Handling Message Events in the Add-on Script ##
To send a message to a content script, use the worker's `postMessage`
function. Again, `panel` and `page` integrate `worker` directly:
// Post a message to the panel's content scripts
panel.postMessage(addonMessage);
However, for `page-mod` objects you need to listen to the `onAttach` event
and use the worker supplied to that:
var pageMod = require('sdk/page-mod').PageMod({
include: ['*'],
contentScript: pageModScript,
onAttach: function(worker) {
worker.postMessage(addonMessage);
}
});
To receive messages from a content script, use the worker's `on` function.
To simplify this most content modules provide an `onMessage` property as an
argument to the constructor:
panel = require("sdk/panel").Panel({
onMessage: function(contentScriptMessage) {
// Handle message from the content script
}
});
## Timing Issues Using postMessage ##
Content scripts are loaded according to the value of the
[`contentScriptWhen`](dev-guide/guides/content-scripts/loading.html)
option: until that point is reached, any attempt to send a message to
the script using `postMessage()` will trigger an exception, probably
this:
<span class="aside">
This is a generic message which is emitted whenever we try to
send a message to a content script, but can't find the worker
which is supposed to receive it.
</span>
<pre>
Error: Couldn't find the worker to receive this message. The script may not be initialized yet, or may already have been unloaded.
</pre>
So code like this, where we create a panel and then
synchronously send it a message using `postMessage()`, will not work:
var data = require("sdk/self").data;
var panel = require("sdk/panel").Panel({
contentURL: "http://www.bbc.co.uk/mobile/index.html",
contentScriptFile: data.url("panel.js")
});
panel.postMessage("hi from main.js");
[`port.emit()`](dev-guide/guides/content-scripts/using-port.html)
queues messages until the content script is ready to receive them,
so the equivalent code using `port.emit()` will work:
var data = require("sdk/self").data;
var panel = require("sdk/panel").Panel({
contentURL: "http://www.bbc.co.uk/mobile/index.html",
contentScriptFile: data.url("panel.js")
});
panel.port.emit("hi from main.js");
## Message Events Versus User-Defined Events ##
You can use message events as an alternative to user-defined events:
var pageModScript = "window.addEventListener('mouseover', function(event) {" +
" self.postMessage(event.target.toString());" +
"}, false);";
var pageMod = require('sdk/page-mod').PageMod({
include: ['*'],
contentScript: pageModScript,
onAttach: function(worker) {
worker.on('message', function(message) {
console.log('mouseover: ' + message);
});
}
});
The reason to prefer user-defined events is that as soon as you need to send
more than one type of message, then both sending and receiving messages gets
more complex.
Suppose the content script wants to send `mouseout` events as well as
`mouseover`. Now we have to embed the event type in the message payload, and
implement a switch function in the receiver to dispatch the message:
var pageModScript = "window.addEventListener('mouseover', function(event) {" +
" self.postMessage({" +
" kind: 'mouseover'," +
" element: event.target.toString()" +
" });" +
"}, false);" +
"window.addEventListener('mouseout', function(event) {" +
" self.postMessage({" +
" kind: 'mouseout'," +
" element: event.target.toString()" +
" });" +
" }, false);"
var pageMod = require('sdk/page-mod').PageMod({
include: ['*'],
contentScript: pageModScript,
onAttach: function(worker) {
worker.on('message', function(message) {
switch(message.kind) {
case 'mouseover':
console.log('mouseover: ' + message.element);
break;
case 'mouseout':
console.log('mouseout: ' + message.element);
break;
}
});
}
});
Implementing the same add-on with user-defined events is shorter and more
readable:
var pageModScript = "window.addEventListener('mouseover', function(event) {" +
" self.port.emit('mouseover', event.target.toString());" +
"}, false);" +
"window.addEventListener('mouseout', function(event) {" +
" self.port.emit('mouseout', event.target.toString());" +
"}, false);";
var pageMod = require('sdk/page-mod').PageMod({
include: ['*'],
contentScript: pageModScript,
onAttach: function(worker) {
worker.port.on('mouseover', function(message) {
console.log('mouseover :' + message);
});
worker.port.on('mouseout', function(message) {
console.log('mouseout :' + message);
});
}
});

Просмотреть файл

@ -0,0 +1,153 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Working with Events #
The Add-on SDK supports event-driven programming through its
[`EventEmitter`](modules/sdk/deprecated/events.html) framework.
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.
<span class="aside">
We talk about content
scripts in more detail in the
[Working with Content Scripts](dev-guide/guides/content-scripts/index.html)
guide.</span>
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:
* **listening to built-in events** emitted by objects in the SDK, such as tabs
opening, pages loading, mouse clicks
* **sending and receiving user-defined events** between content scripts and
add-on code
This guide only covers the first of these; the second is explained in the
[Working with Content Scripts](dev-guide/guides/content-scripts/index.html)
guide.
## Adding Listeners ##
You can add a listener to an event emitter by calling its `on(type, listener)`
method.
It takes two parameters:
* **`type`**: the type of event we are interested in, identified by a string.
Many event emitters may emit more than one type of event: for example, a browser
window might emit both `open` and `close` events. The list of valid event types
is specific to an event emitter and is included with its documentation.
* **`listener`**: the listener itself. This is a function which will be called
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:
var pb = require("sdk/private-browsing");
pb.on("start", function() {
console.log("Private browsing is on");
});
pb.on("stop", function() {
console.log("Private browsing is off");
});
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.
### 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.
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 [`widget`](modules/sdk/widget.html) object emits
an event when the widget is clicked.
The following add-on creates a widget and assigns a listener to the
`onClick` property of the `options` object supplied to the widget's
constructor. The listener loads the Google home page:
var widgets = require("sdk/widget");
var tabs = require("sdk/tabs");
widgets.Widget({
id: "google-link",
label: "Widget with an image and a click handler",
contentURL: "http://www.google.com/favicon.ico",
onClick: function() {
tabs.open("http://www.google.com/");
}
});
This is exactly equivalent to constructing the widget and then calling the
widget's `on()` method:
var widgets = require("sdk/widget");
var tabs = require("sdk/tabs");
var widget = widgets.Widget({
id: "google-link-alternative",
label: "Widget with an image and a click handler",
contentURL: "http://www.google.com/favicon.ico"
});
widget.on("click", function() {
tabs.open("http://www.google.com/");
});
## 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 private-browsing's `start`
event, enter and exit private browsing, then remove the first listener and
enter private browsing again.
var pb = require("sdk/private-browsing");
function listener1() {
console.log("Listener 1");
pb.removeListener("start", listener1);
}
function listener2() {
console.log("Listener 2");
}
pb.on("start", listener1);
pb.on("start", listener2);
pb.activate();
pb.deactivate();
pb.activate();
Removing listeners is optional since they will be removed in any case
when the application or add-on is unloaded.

Просмотреть файл

@ -0,0 +1,103 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Firefox Compatibility #
One of the promises the SDK makes is to maintain compatibility for its
["supported" or "high-level" APIs](modules/high-level-modules.html):
meaning that code written against them will not need to change as new
versions of Firefox are released.
This ties the SDK release cycle into the Firefox release cycle because
the SDK must absorb any changes made to Firefox APIs. The SDK
and Firefox both release every 6 weeks, and the releases are precisely
staggered: so the SDK releases three weeks before Firefox. Each SDK
release is tested against, and marked as compatible with, two
versions of Firefox:
* the currently shipping Firefox version at the time the SDK is released
* the Beta Firefox version at the time the SDK is released - which,
because SDK and Firefox releases are staggered, will become the
currently shipping Firefox three week later
Add-ons built using a particular version of the SDK are marked
as being compatible with those two versions of Firefox, meaning
that in the
[`targetApplication` field of the add-on's install.rdf](https://developer.mozilla.org/en/Install.rdf#targetApplication):
* the `minVersion` is set to the currently shipping Firefox
* the `maxVersion` is set to the current Firefox Beta
See the
[SDK Release Schedule](https://wiki.mozilla.org/Jetpack/SDK_2012_Release_Schedule)
for the list of all SDK releases scheduled for 2012, along with the Firefox
versions they are compatible with.
## "Compatible By Default" ##
<span class="aside">There are exceptions to the "compatible by default" rule:
add-ons with binary XPCOM components, add-ons that have their compatibility
set to less than Firefox 4, and add-ons that are repeatedly reported as
incompatible, which are added to a compatibility override list.
</span>
From Firefox 10 onwards, Firefox treats add-ons as
"compatible by default": that is, even if the Firefox installing the
add-on is not inside the range defined in `targetApplication`,
Firefox will happily install it.
For example, although an add-on developed using version 1.6 of the SDK will be
marked as compatible with only versions 11 and 12 of Firefox, users of
Firefox 10 will still be able to install it.
But before version 10, Firefox assumed that add-ons were incompatible unless
they were marked as compatible in the `targetApplication` field: so an add-on
developed using SDK 1.6 will not install on Firefox 9.
## Changing minVersion and maxVersion Values ##
The `minVersion` and `maxVersion` values that are written into add-ons
generated with the SDK are taken from the template file found at:
<pre>
app-extension/install.rdf
</pre>
If you need to create add-ons which are compatible with a wider range of
Firefox releases, you can edit this file to change the
`minVersion...maxVersion` range.
Obviously, you should be careful to test the resulting add-on on the extra
versions of Firefox you are including, because the version of the SDK you
are using will not have been tested against them.
## Repacking Add-ons ##
Suppose you create an add-on using version 1.6 of the SDK, and upload it to
[https://addons.mozilla.org/](https://addons.mozilla.org/). It's compatible
with versions 11 and 12 of Firefox, and indicates that in its
`minVersion...maxVersion` range.
Now Firefox 13 is released. Suppose Firefox 13 does not change any of the
APIs used by version 1.6 of the SDK. In this case the add-on will still
work with Firefox 13.
But if Firefox 13 makes some incompatible changes, then the add-on will
no longer work. In this case, we'll notify developers, who will need to:
* download and install a new version of the SDK
* rebuild their add-on using this version of the SDK
* update their XPI on [https://addons.mozilla.org/](https://addons.mozilla.org/)
with the new version.
If you created the add-on using the [Add-on Builder](https://builder.addons.mozilla.org/)
rather than locally using the SDK, then it will be repacked automatically
and you don't have to do this.
### Future Plans ###
The reason add-ons need to be repacked when Firefox changes is that the
SDK modules used by an add-on are packaged as part of the add-on, rather
than part of Firefox. In the future we plan to start shipping SDK modules
in Firefox, and repacking will not be needed any more.

Просмотреть файл

@ -0,0 +1,190 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Guides #
This page lists more theoretical in-depth articles about the SDK.
<hr>
<h2><a name="sdk-infrastructure">SDK Infrastructure</a></h2>
<table class="catalog">
<colgroup>
<col width="50%">
<col width="50%">
</colgroup>
<tr>
<td>
<h4><a href="dev-guide/guides/modules.html">Module structure of the SDK</a></h4>
The SDK, and add-ons built using it, are of composed from reusable JavaScript modules. This
explains what these modules are, how to load modules, and how the SDK's module
tree is structured.
</td>
<td>
<h4><a href="dev-guide/guides/program-id.html">Program ID</a></h4>
The Program ID is a unique identifier for your add-on. This guide
explains how it's created, what it's used for and how to define your
own.
</td>
</tr>
<tr>
<td>
</td>
<td>
<h4><a href="dev-guide/guides/firefox-compatibility.html">Firefox compatibility</a></h4>
Working out which Firefox releases a given SDK release is
compatible with, and dealing with compatibility problems.
</td>
</tr>
<tr>
<td>
<h4><a href="dev-guide/guides/stability.html">SDK API lifecycle</a></h4>
Definition of the lifecycle for the SDK's APIs, including the stability
ratings for APIs.
</td>
<td>
</td>
</tr>
</table>
<hr>
<h2><a name="sdk-idioms">SDK Idioms</a></h2>
<table class="catalog">
<colgroup>
<col width="50%">
<col width="50%">
</colgroup>
<tr>
<td>
<h4><a href="dev-guide/guides/events.html">Working With Events</a></h4>
Write event-driven code using the the SDK's event emitting framework.
</td>
<td>
<h4><a href="dev-guide/guides/two-types-of-scripts.html">Two Types of Scripts</a></h4>
This article explains the differences between the APIs
available to your main add-on code and those available
to content scripts.
</td>
</tr>
</table>
<hr>
<h2><a name="content-scripts">Content Scripts</a></h2>
<table class="catalog">
<colgroup>
<col width="50%">
<col width="50%">
</colgroup>
<tr>
<td>
<h4><a href="dev-guide/guides/content-scripts/index.html">Introducing content scripts</a></h4>
An overview of content scripts.
</td>
<td>
<h4><a href="dev-guide/guides/content-scripts/loading.html">Loading content scripts</a></h4>
Load content scripts into web pages, specified either as strings
or in separate files, and how to control the point at which they are
executed.
</td>
</tr>
<tr>
<td>
<h4><a href="dev-guide/guides/content-scripts/accessing-the-dom.html">Accessing the DOM</a></h4>
Detail about the access content scripts get to the DOM.
</td>
<td>
<h4><a href="dev-guide/guides/content-scripts/communicating-with-other-scripts.html">Communicating with other scripts</a></h4>
Detail about how content scripts can communicate with "main.js", with other
content scripts, and with scripts loaded by the web page itself.
</td>
</tr>
<tr>
<td>
<h4><a href="dev-guide/guides/content-scripts/using-port.html">Using "port"</a></h4>
Communicating between a content script and the rest of your add-on
using the <code>port</code> object.
</td>
<td>
<h4><a href="dev-guide/guides/content-scripts/using-postmessage.html">Using "postMessage()"</a></h4>
Communicating between a content script and the rest of your add-on
using the <code>postMessage()</code> API, and a comparison between
this technique and the <code>port</code> object.
</td>
</tr>
<tr>
<td>
<h4><a href="dev-guide/guides/content-scripts/reddit-example.html">Reddit example</a></h4>
A simple add-on which uses content scripts.
</td>
</tr>
</table>
<hr>
<h2><a name="xul-migration">XUL Migration</a></h2>
<table class="catalog">
<colgroup>
<col width="50%">
<col width="50%">
</colgroup>
<tr>
<td>
<h4><a href="dev-guide/guides/xul-migration.html">XUL Migration Guide</a></h4>
Techniques to help port a XUL add-on to the SDK.
</td>
<td>
<h4><a href="dev-guide/guides/sdk-vs-xul.html">XUL versus the SDK</a></h4>
A comparison of the strengths and weaknesses of the SDK,
compared to traditional XUL-based add-ons.
</td>
</tr>
<tr>
<td>
<h4><a href="dev-guide/guides/library-detector.html">Porting Example</a></h4>
A walkthrough of porting a relatively simple XUL-based
add-on to the SDK.
</td>
<td>
</td>
</tr>
</table>

Просмотреть файл

@ -0,0 +1,218 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Porting the Library Detector #
This example walks through the process of porting a XUL-based add-on to the
SDK. It's a very simple add-on and a good candidate for porting because
there are suitable SDK APIs for all its features.
<img class="image-right" src="static-files/media/librarydetector/library-detector.png" alt="Library Detector Screenshot" />
The add-on is Paul Bakaus's
[Library Detector](https://addons.mozilla.org/en-US/firefox/addon/library-detector/).
The Library Detector tells you which JavaScript frameworks the current
web page is using. It does this by checking whether particular objects
that those libraries add to the global window object are defined.
For example, if `window.jQuery` is defined, then the page has loaded
[jQuery](http://jquery.com/).
For each library that it finds, the library detector adds an icon
representing that library to the status bar. It adds a tooltip to each
icon, which contains the library name and version.
You can browse and run the ported version in the SDK's `examples` directory.
### How the Library Detector Works ###
All the work is done inside a single file,
[`librarydetector.xul`](http://code.google.com/p/librarydetector/source/browse/trunk/chrome/content/librarydetector.xul)
This contains:
<ul>
<li>a XUL overlay</li>
<li>a script</li>
</ul>
The XUL overlay adds a `box` element to the browser's status bar:
<script type="syntaxhighlighter" class="brush: html"><![CDATA[
&lt;statusbar id="status-bar"&gt; &lt;box orient="horizontal" id="librarydetector"&gt; &lt;/box&gt; &lt;/statusbar&gt;
]]>
</script>
The script does everything else.
The bulk of the script is an array of test objects, one for each library.
Each test object contains a function called `test()`: if the
function finds the library, it defines various additional properties for
the test object, such as a `version` property containing the library version.
Each test also contains a `chrome://` URL pointing to the icon associated with
its library.
The script listens to [gBrowser](https://developer.mozilla.org/en/Code_snippets/Tabbed_browser)'s
`DOMContentLoaded` event. When this is triggered, the `testLibraries()`
function builds an array of libraries by iterating through the tests and
adding an entry for each library which passes.
Once the list is built, the `switchLibraries()` function constructs a XUL
`statusbarpanel` element for each library it found, populates it with the
icon at the corresponding `chrome://` URL, and adds it to the box.
Finally, it listens to gBrowser's `TabSelect` event, to update the contents
of the box for that window.
### Content Script Separation ###
The test objects in the original script need access to the DOM window object,
so in the SDK port, they need to run in a content script. In fact, they need
access to the un-proxied DOM window, so they can see the objects added by
libraries, so well need to use the experimental [unsafeWindow](dev-guide/guides/content-scripts/accessing-the-dom.html#unsafeWindow)
The main add-on script, `main.js`, will use a
[`page-mod`](modules/sdk/page-mod.html)
to inject the content script into every new page.
The content script, which we'll call `library-detector.js`, will keep most of
the logic of the `test` functions intact. However, instead of maintaining its
own state by listening for `gBrowser` events and updating the
user interface, the content script will just run when it's loaded, collect
the array of library names, and post it back to `main.js`:
function testLibraries() {
var win = unsafeWindow;
var libraryList = [];
for(var i in LD_tests) {
var passed = LD_tests[i].test(win);
if (passed) {
var libraryInfo = {
name: i,
version: passed.version
};
libraryList.push(libraryInfo);
}
}
self.postMessage(libraryList);
}
testLibraries();
`main.js` responds to that message by fetching the tab
corresponding to that worker using
[`worker.tab`](modules/sdk/content/worker.html#tab), and adding
the array of library names to that tab's `libraries` property:
pageMod.PageMod({
include: "*",
contentScriptWhen: 'end',
contentScriptFile: (data.url('library-detector.js')),
onAttach: function(worker) {
worker.on('message', function(libraryList) {
if (!worker.tab.libraries) {
worker.tab.libraries = [];
}
libraryList.forEach(function(library) {
if (worker.tab.libraries.indexOf(library) == -1) {
worker.tab.libraries.push(library);
}
});
if (worker.tab == tabs.activeTab) {
updateWidgetView(worker.tab);
}
});
}
});
The content script is executed once for every `window.onload` event, so
it will run multiple times when a single page containing multiple iframes
is loaded. So `main.js` needs to filter out any duplicates, in case
a page contains more than one iframe, and those iframes use the same library.
### Implementing the User Interface ###
#### Showing the Library Array ####
The [`widget`](modules/sdk/widget.html) module is a natural fit
for displaying the library list. We'll specify its content using HTML, so we
can display an array of icons. The widget must be able to display different
content for different windows, so we'll use the
[`WidgetView`](modules/sdk/widget.html) object.
`main.js` will create an array of icons corresponding to the array of library
names, and use that to build the widget's HTML content dynamically:
function buildWidgetViewContent(libraryList) {
widgetContent = htmlContentPreamble;
libraryList.forEach(function(library) {
widgetContent += buildIconHtml(icons[library.name],
library.name + "<br>Version: " + library.version);
});
widgetContent += htmlContentPostamble;
return widgetContent;
}
function updateWidgetView(tab) {
var widgetView = widget.getView(tab.window);
if (!tab.libraries) {
tab.libraries = [];
}
widgetView.content = buildWidgetViewContent(tab.libraries);
widgetView.width = tab.libraries.length * ICON_WIDTH;
}
`main.js` will
use the [`tabs`](modules/sdk/tabs.html) module to update the
widget's content when necessary (for example, when the user switches between
tabs):
tabs.on('activate', function(tab) {
updateWidgetView(tab);
});
tabs.on('ready', function(tab) {
tab.libraries = [];
});
#### Showing the Library Detail ####
The XUL library detector displayed the detailed information about each
library on mouseover in a tooltip: we can't do this using a widget, so
instead will use a panel. This means we'll need two additional content
scripts:
* one in the widget's context, which listens for icon mouseover events
and sends a message to `main.js` containing the name of the corresponding
library:
<pre><code>
function setLibraryInfo(element) {
self.port.emit('setLibraryInfo', element.target.title);
}
var elements = document.getElementsByTagName('img');
for (var i = 0; i &lt; elements.length; i++) {
elements[i].addEventListener('mouseover', setLibraryInfo, false);
}
</code></pre>
* one in the panel, which updates the panel's content with the library
information:
<pre><code>
self.on("message", function(libraryInfo) {
window.document.body.innerHTML = libraryInfo;
});
</code></pre>
Finally `main.js` relays the library information from the widget to the panel:
<pre><code>
widget.port.on('setLibraryInfo', function(libraryInfo) {
widget.panel.postMessage(libraryInfo);
});
</code></pre>
<img class="image-center" src="static-files/media/librarydetector/panel-content.png" alt="Updating panel content" />

Просмотреть файл

@ -0,0 +1,231 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Modules in the SDK #
[CommonJS](http://wiki.commonjs.org/wiki/CommonJS) is the underlying
infrastructure for both the SDK and the add-ons you build using the SDK.
A CommonJS module is a piece of reusable JavaScript: it exports certain
objects which are thus made available to dependent code. CommonJS defines:
* an object called `exports` which contains all the objects which a CommonJS
module wants to make available to other modules
* a function called `require` which a module can use to import the `exports`
object of another module.
![CommonJS modules](static-files/media/commonjs-modules.png)
Except for [scripts that interact directly with web content](dev-guide/guides/content-scripts/index.html),
all the JavaScript code you'll write or use when developing add-ons using
the SDK is part of a CommonJS module, including:
* [SDK modules](dev-guide/guides/modules.html#SDK Modules):
the JavaScript modules which the SDK provides, such as
[`panel`](modules/sdk/panel.html) and [page-mod](modules/sdk/page-mod.html).
* [Local modules](dev-guide/guides/modules.html#Local Modules):
each of the JavaScript files under your add-ons "lib" directory.
* [External modules](dev-guide/guides/modules.html#External Modules):
reusable modules developed and maintained outside the SDK, but usable by
SDK-based add-ons.
## SDK Modules ##
All the modules supplied with the SDK can be found in the "lib"
directory under the SDK root. The following diagram shows a reduced view
of the SDK tree, with the "lib" directory highlighted.
<ul class="tree">
<li>addon-sdk
<ul>
<li>app-extension</li>
<li>bin</li>
<li>data</li>
<li>doc</li>
<li>examples</li>
<li class="highlight-tree-node">lib
<ul>
<li>sdk
<ul>
<li>core
<ul>
<li>heritage.js</li>
<li>namespace.js</li>
</ul>
</li>
<li>panel.js</li>
<li>page-mod.js</li>
</ul>
</li>
<li>toolkit
<ul>
<li>loader.js</li>
</ul>
</li>
</ul>
</li>
<li>python-lib</li>
<li>test</li>
</ul>
</li>
</ul>
All modules that are specifically intended for users of the
SDK are stored under "lib" in the "sdk" directory.
[High-level modules](dev-guide/high-level-apis.html) like
[`panel`](modules/sdk/panel.html) and
[`page-mod`](modules/sdk/page-mod.html) are directly underneath
the "sdk" directory.
[Low-level modules](dev-guide/low-level-apis.html) like
[`heritage`](modules/sdk/core/heritage.html) and
[`namespace`](modules/sdk/core/heritage.html) are grouped in subdirectories
of "sdk" such as "core".
Very generic, platform-agnostic modules that are shared with other
projects, such as [`loader`](modules/toolkit/loader.html), are stored
in "toolkit".
<div style="clear:both"></div>
To use SDK modules, you can pass `require` a complete path from
(but not including) the "lib" directory to the module you want to use.
For high-level modules this is just `sdk/<module_name>`, and for low-level
modules it is `sdk/<path_to_module>/<module_name>`:
// load the high-level "tabs" module
var tabs = require("sdk/tabs");
// load the low-level "uuid" module
var uuid = require('sdk/util/uuid');
For high-level modules only, you can also pass just the name of the module:
var tabs = require("tabs");
However, this is ambiguous, as it could also refer to a local module in your
add-on named `tabs`. For this reason it is better to use the full path from
"lib".
## Local Modules ##
At a minimum, an SDK-based add-on consists of a single module
named `main.js`, but you can factor your add-on's code into a collection
of separate CommonJS modules. Each module is a separate file stored under your
add-on's "lib" directory, and exports the objects you want to make available
to other modules in your add-on. See the tutorial on
[creating reusable modules](dev-guide/tutorials/reusable-modules.html) for
more details.
To import a local module, specify a path relative to the importing module.
For example, the following add-on contains an additional module directly under
"lib", and other modules under subdirectories of "lib":
<ul class="tree">
<li>my-addon
<ul>
<li>lib
<ul>
<li>main.js</li>
<li>password-dialog.js</li>
<li>secrets
<ul>
<li>hash.js</li>
</ul>
</li>
<li>storage
<ul>
<li>password-store.js</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
To import modules into `main`:
// main.js code
var dialog = require("./password-dialog");
var hash = require("./secrets/hash");
To import modules into `password-store`:
// password-store.js code
var dialog = require("../password-dialog");
var hash = require("../secrets/hash");
<div style="clear:both"></div>
For backwards compatibility, you may also omit the leading "`./`"
or "`../`" characters, treating the path as an absolute path from
your add-on's "lib" directory:
var dialog = require("password-dialog");
var hash = require("secrets/hash");
This form is not recommended for new code, because the behavior of `require`
is more complex and thus less predictable than if you specify the target
module explicitly using a relative path.
## External Modules ##
As well as using the SDK's modules and writing your own, you
can use modules that have been developed outside the SDK and made
available to other add-on authors.
There's a list of these
["community-developed modules"](https://github.com/mozilla/addon-sdk/wiki/Community-developed-modules)
in the SDK's GitHub Wiki, and to learn how to use them, see
the tutorial on
[using external modules to add menu items to Firefox](dev-guide/tutorials/adding-menus.html).
To import external modules, treat them like local modules:
copy them somewhere under your add-ons "lib" directory and
reference them with a path relative to the importing module.
For example, this add-on places external modules in a "dependencies"
directory:
<ul class="tree">
<li>my-addon
<ul>
<li>lib
<ul>
<li>main.js</li>
<li>dependencies
<ul>
<li>geolocation.js</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
It can then load them in the same way it would load a local module.
For example, to load from `main`:
// main.js code
var geo = require("./dependencies/geolocation");
<div style="clear:both"></div>
## Freezing ##
The SDK
[freezes](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/freeze)
the `exports` object returned by `require`. So a if you import a module using
`require`, you can't change the properties of the object returned:
self = require("sdk/self");
// Attempting to define a new property
// will fail, or throw an exception in strict mode
self.foo = 1;
// Attempting to modify an existing property
// will fail, or throw an exception in strict mode
self.data = "foo";

Просмотреть файл

@ -0,0 +1,33 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# The Program ID #
The Program ID is a unique identifier for your add-on. When you package your
add-on for distribution using `cfx xpi`, it will become the
[ID field in the add-on's Install Manifest](https://developer.mozilla.org/en/install.rdf#id).
The ID is used for a variety
of purposes. For example: [addons.mozilla.org](http://addons.mozilla.org) uses
it to distinguish between new add-ons and updates to existing add-ons, and the
[`simple-storage`](modules/sdk/simple-storage.html) module uses it
to figure out which stored data belongs to which add-on.
It is read from the `id` key in your add-on's [`package.json`](dev-guide/package-spec.html) file.
`cfx init` does not create this key, so if you don't set it yourself, the
first time you execute `cfx run` or `cfx xpi`, then `cfx` will create an
ID for you, and will show a message like this:
<pre>
No 'id' in package.json: creating a new ID for you.
package.json modified: please re-run 'cfx run'
</pre>
The ID generated by `cfx` in this way is a randomly-generated string, but
you can define your own ID by editing the `package.json` file
directly. In particular, you can use the `extensionname@example.org` format
described in the
[Install Manifest documentation](https://developer.mozilla.org/en/install.rdf#id).
However, you can't use the
[GUID-style](https://developer.mozilla.org/en/Generating_GUIDs) format.

Просмотреть файл

@ -0,0 +1,107 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# SDK and XUL Comparison #
## Advantages of the SDK ##
<table>
<colgroup>
<col width="20%">
<col width="80%">
</colgroup>
<tr>
<td> <strong><a name="simplicity">Simplicity</a></strong></td>
<td><p>The SDK provides high-level JavaScript APIs to simplify many
common tasks in add-on development, and tool support which greatly simplifies
the process of developing, testing, and packaging an add-on.</p>
</td>
</tr>
<tr>
<td> <strong><a name="compatibility">Compatibility</a></strong></td>
<td><p>Although we can't promise we'll never break a High-Level API,
maintaining compatibility across Firefox versions is a top priority for us.</p>
<p>We've designed the APIs to be forward-compatible with the new
<a href="https://wiki.mozilla.org/Electrolysis/Firefox">multiple process architecture</a>
(codenamed Electrolysis) planned for Firefox.</p>
<p>We also expect to support both desktop and mobile Firefox using a single
edition of the SDK: so you'll be able to write one extension and have it work
on both products.</p></td>
</tr>
<tr>
<td> <strong><a name="security">Security</a></strong></td>
<td><p>If they're not carefully designed, Firefox add-ons can open the browser
to attack by malicious web pages. Although it's possible to write insecure
add-ons using the SDK, it's not as easy, and the damage that a compromised
add-on can do is usually more limited.</p></td>
</tr>
<tr>
<td> <strong><a name="restartlessness">Restartlessness</a></strong></td>
<td><p>Add-ons built with the SDK are can be installed without having
to restart Firefox.</p>
<p>Although you can write
<a href="https://developer.mozilla.org/en/Extensions/Bootstrapped_extensions">
traditional add-ons that are restartless</a>, you can't use XUL overlays in
them, so most traditional add-ons would have to be substantially rewritten
anyway.</p></td>
</tr>
<tr>
<td> <strong><a name="ux_best_practice">User Experience Best Practices</a></strong></td>
<td><p>The UI components available in the SDK are designed to align with the usability
guidelines for Firefox, giving your users a better, more consistent experience.</p></td>
</tr>
<tr>
<td> <strong><a name="mobile_support">Mobile Support</a></strong></td>
<td><p>Starting in SDK 1.5, we've added experimental support for developing
add-ons on the new native version of Firefox Mobile. See the
<a href="dev-guide/tutorials/mobile.html">tutorial on mobile development<a>.</p></td>
</tr>
</table>
## Advantages of XUL-based Add-ons ##
<table>
<colgroup>
<col width="20%">
<col width="80%">
</colgroup>
<tr>
<td><strong><a name="ui_flexibility">User interface flexibility</a></strong></td>
<td><p>XUL overlays offer a great deal of options for building a UI and
integrating it into the browser. Using only the SDK's supported APIs you have
much more limited options for your UI.</p></td>
</tr>
<tr>
<td><strong><a name="xpcom_access">XPCOM</a></strong></td>
<td><p>Traditional add-ons have access to a vast amount of Firefox
functionality via XPCOM. The SDK's supported APIs expose a relatively
small set of this functionality.</p></td>
</tr>
</table>
### Low-level APIs and Third-party Modules ###
That's not the whole story. If you need more flexibility than the SDK's
High-Level APIs provide, you can use its Low-level APIs to load
XPCOM objects directly or to manipulate the DOM directly as in a
traditional
<a href="https://developer.mozilla.org/en/Extensions/Bootstrapped_extensions">bootstrapped extension</a>.
Alternatively, you can load third-party modules, which extend the SDK's
core APIs.
Note that by doing this you lose some of the benefits of programming
with the SDK including simplicity, compatibility, and to a lesser extent
security.

Просмотреть файл

@ -0,0 +1,104 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# SDK API Lifecycle #
Developers using the SDK's APIs need to know how far they can trust that
a given API will not change in future releases. At the same time, developers
maintaining and extending the SDK's APIs need to be able to introduce new
APIs that aren't yet fully proven, and to retire old APIs when they're
no longer optimal or supported by the underlying platform.
The API lifecycle aims to balance these competing demands. It has two
main components:
* a [stability index](dev-guide/guides/stability.html#Stability Index)
that defines how stable each module is
* a [deprecation process](dev-guide/guides/stability.html#Deprecation Process)
that defines when and how stable SDK APIs can be changed or removed from
future versions of the SDK while giving developers enough time to update
their code.
## Stability Index ##
The stability index is adopted from
[node.js](http://nodejs.org/api/documentation.html#documentation_stability_index).
The SDK uses only three of the six values defined by node.js:
<table>
<tr>
<td>Experimental</td>
<td>The module is not yet stabilized.
You can try it out and provide feedback, but we may change or remove
it in future versions without having to pass through a formal
deprecation process.</td>
</tr>
<tr>
<td>Stable</td>
<td>The module is a fully-supported part of
the SDK. We will avoid breaking backwards compatibility unless absolutely
necessary. If we do have to make backwards-incompatible changes, we will
go through the formal deprecation process.</td>
</tr>
<tr>
<td>Deprecated</td>
<td>We plan to change this module, and backwards compatibility
should not be expected. Dont start using it, and plan to migrate away from
this module to its replacement.</td>
</tr>
</table>
The stability index for each module is written into that modules
metadata structure, and is displayed at the top of each module's
documentation page.
## Deprecation Process ##
### Deprecation ###
In the chosen release, the SDK team will communicate the module's deprecation:
* update the module's stability index to be "deprecated"
* include a deprecation notice in the
[release notes](https://wiki.mozilla.org/Labs/Jetpack/Release_Notes),
the [Add-ons blog](https://blog.mozilla.org/addons/), and the
[Jetpack Google group](https://groups.google.com/forum/?fromgroups#!forum/mozilla-labs-jetpack).
The deprecation notice should point developers at a migration guide.
### Migration ###
The deprecation period defaults to 18 weeks (that is, three releases)
although in some cases, generally those out of our control, it might
be shorter than this.
During this time, the module will be in the deprecated state. The SDK
team will track usage of deprecated modules on
[addons.mozilla.org](https://addons.mozilla.org/en-US/firefox/) and support
developers migrating their code. The SDK will continue to provide warnings:
* API documentation will inform users that the module is deprecated.
* Attempts to use a deprecated module at runtime will log an error to
the error console.
* The AMO validator will throw errors when deprecated modules are used,
and these add-ons will therefore fail AMO review.
All warnings should include links to further information about what to
use instead of the deprecated module and when the module will be completely
removed.
### Removal ###
The target removal date is 18 weeks after deprecation. In preparation for
this date the SDK team will decide whether to go ahead with removal: this
will depend on how many developers have successfully migrated from the
deprecated module, and on how urgently the module needs to be removed.
If it's OK to remove the module, it will be removed. The SDK team will
remove the corresponding documentation, and communicate the removal in
the usual ways: the [release notes](https://wiki.mozilla.org/Labs/Jetpack/Release_Notes),
the [Add-ons blog](https://blog.mozilla.org/addons/), and the
[Jetpack Google group](https://groups.google.com/forum/?fromgroups#!forum/mozilla-labs-jetpack).
If it's not OK to remove it, the team will continue to support migration
and aim to remove the module in the next release.

Просмотреть файл

@ -0,0 +1,119 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Two Types of Scripts #
On the web, JavaScript executes in the context of a web page, and has access to
that page's DOM content. This enables you to call functions like:
window.alert("Hello there");
In an add-on's main scripts you can't do that, because the add-on code does
not execute in the context of a page, and the DOM is therefore not available.
If you need to access the DOM of a particular page, you need to use a
content script.
So there are two distinct sorts of JavaScript scripts you might include
in your add-on and they have access to different sets of APIs. In the SDK
documentation we call one sort "add-on code" and the other sort "content
scripts".
## Add-on Code ##
This is the place where the main logic of your add-on is implemented.
Your add-on is implemented as a collection of one or more
[CommonJS modules](dev-guide/guides/modules.html). Each module
is supplied as a script stored under the `lib` directory under your add-on's
root directory.
Minimally you'll have a single module implemented by a script called
"main.js", but you can include additional modules in `lib`, and import them
using the `require()` function. To learn how to implement and import your own
modules, see the tutorial on
[Implementing Reusable Modules](dev-guide/tutorials/reusable-modules.html).
## Content Scripts ##
While your add-on will always have a "main.js" module, you will only need
to write content scripts if your add-on needs to manipulate web content.
Content scripts are injected into web pages using APIs defined by some of the
SDK's modules such as `page-mod`, `panel` and `widget`.
Content scripts may be supplied as literal strings or maintained in separate
files and referenced by filename. If they are stored in separate files you
should store them under the `data` directory under your add-on's root.
To learn all about content scripts read the
[Working with Content Scripts](dev-guide/guides/content-scripts/index.html)
guide.
## API Access for Add-on Code and Content Scripts ##
The table below summarizes the APIs that are available to each type of
script.
<table>
<colgroup>
<col width="70%">
<col width="15%">
<col width="15%">
</colgroup>
<tr>
<th>API</th>
<th>Add-on code</th>
<th>Content script</th>
</tr>
<tr>
<td>The global objects defined in the core JavaScript language, such as
<code>Math</code>, <code>Array</code>, and <code>JSON</code>. See the
<a href= "https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects">reference at MDN</a>.
</td>
<td class="check"></td>
<td class="check"></td>
</tr>
<tr>
<td><p>The <code>require()</code> and <code>exports</code> globals defined
by version 1.0 of the
<a href="http://wiki.commonjs.org/wiki/Modules/1.0">CommonJS Module Specification</a>.
You use <code>require()</code> to import functionality from another module,
and <code>exports</code> to export functionality from your module.</p>
If <code>require()</code> is available, then so are the modules supplied in the
SDK.
</td>
<td class="check"></td>
<td class="cross"></td>
</tr>
<tr>
<td>The <a href="dev-guide/console.html">console</a>
global supplied by the SDK.
</td>
<td class="check"></td>
<td class="check"></td>
</tr>
<tr>
<td>Globals defined by the
<a href="http://dev.w3.org/html5/spec/Overview.html">HTML5</a> specification,
such as <code>window</code>, <code>document</code>, and
<code>localStorage</code>.
</td>
<td class="cross"></td>
<td class="check"></td>
</tr>
<tr>
<td>The <code>self</code> global, used for communicating between content
scripts and add-on code. See the guide to
<a href="dev-guide/guides/content-scripts/using-port.html">communicating with content scripts</a>
for more details.
</td>
<td class="cross"></td>
<td class="check"></td>
</tr>
</table>

Просмотреть файл

@ -0,0 +1,325 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# XUL Migration Guide #
This guide aims to help you migrate a XUL-based add-on to the SDK.
First we'll outline how to decide whether
<a href="dev-guide/guides/xul-migration.html#should-you-migrate">
your add-on is a good candidate for migration</a> via a
[comparison of the benefits and limitations of the SDK versus XUL development](dev-guide/guides/sdk-vs-xul.html).
Next, we'll look at some of the main tasks involved in migrating:
* <a href="dev-guide/guides/xul-migration.html#content-scripts">
working with content scripts</a>
* <a href="dev-guide/guides/xul-migration.html#supported-apis">
using the SDK's supported APIs</a>
* how to
go beyond the supported APIs when necessary, by:
* <a href="dev-guide/guides/xul-migration.html#third-party-packages">
using third party modules</a>
* <a href="dev-guide/guides/xul-migration.html#low-level-apis">
using the SDK's low-level APIs</a>
* <a href="dev-guide/guides/xul-migration.html#xpcom">
getting direct access to XPCOM</a>
Finally, we'll walk through a
<a href="dev-guide/guides/xul-migration.html#library-detector">
simple example</a>.
## <a name="should-you-migrate">Should You Migrate?</a> ##
See this [comparison of the benefits and limitations of SDK development
and XUL development](dev-guide/guides/sdk-vs-xul.html).
Whether you should migrate a particular add-on is largely a matter of
how well the SDK's
<a href="dev-guide/guides/xul-migration.html#supported-apis">
supported APIs</a> meet its needs.
* If your add-on can accomplish everything it needs using only the
supported APIs, it's a good candidate for migration.
* If your add-on needs a lot of help from third party packages, low-level
APIs, or XPCOM, then the cost of migrating is high, and may not be worth
it at this point.
* If your add-on only needs a little help from those techniques, and can
accomplish most of what it needs using the supported APIs, then it might
still be worth migrating: we'll add more supported APIs in future releases
to meet important use cases.
## <a name="content-scripts">Content Scripts</a> ##
In a XUL-based add-on, code that uses XPCOM objects, code that manipulates
the browser chrome, and code that interacts with web pages all runs in the
same context. But the SDK makes a distinction between:
* **add-on scripts**, which can use the SDK APIs, but are not able to interact
with web pages
* **content scripts**, which can access web pages, but do not have access to
the SDK's APIs
Content scripts and add-on scripts communicate by sending each other JSON
messages: in fact, the ability to communicate with the add-on scripts is the
only extra privilege a content script is granted over a normal remote web
page script.
A XUL-based add-on will need to be reorganized to respect this distinction.
Suppose an add-on wants to make a cross-domain XMLHttpRequest based on some
data extracted from a web page. In a XUL-based extension you would implement
all this in a single script. An SDK-based equivalent would need to be
structured like this:
* the main add-on code (1) attaches a content script to the page, and (2)
registers a listener function for messages from the content script
* the content script (3) extracts the data from the page and (4) sends
it to the main add-on code in a message
* the main add-on code (5) receives the message and (6) sends the request,
using the SDK's [`request`](modules/sdk/request.html) API
<img class="image-center" src="static-files/media/xul-migration-cs.png"
alt="Content script organization">
There are two related reasons for this design. The first is security: it
reduces the risk that a malicious web page will be able to access privileged
APIs. The second is the need to be compatible with the multi-process architecture
planned for Firefox: after this is implemented in Firefox, all add-ons will
need to use a similar pattern, so it's likely that a XUL-based add-on will
need to be rewritten anyway.
There's much more information on content scripts in the
[Working With Content Scripts](dev-guide/guides/content-scripts/index.html) guide.
## <a name="supported-apis">Using the Supported APIs</a> ##
The SDK provides a set of high level APIs
providing some basic user interface components and functionality commonly
required by add-ons. Because we expect to keep these APIs compatible as new versions
of Firefox are released, we call them the "supported" APIs.
See the [tutorials](dev-guide/tutorials/index.html)
and the [High-Level API reference](modules/high-level-modules.html).
If the supported APIs do what you need, they're the best option: you get the
benefits of compatibility across Firefox releases and of the SDK's security
model.
APIs like [`widget`](modules/sdk/widget.html) and
[`panel`](modules/sdk/panel.html) are very generic and with the
right content can be used to replace many specific XUL elements. But there are
some notable limitations in the SDK APIs and even a fairly simple UI may need
some degree of redesign to work with them.
Some limitations are the result of intentional design choices. For example,
widgets always appear by default in the
[add-on bar](https://developer.mozilla.org/en/The_add-on_bar) (although users
may relocate them by
[toolbar customization](http://support.mozilla.com/en-US/kb/how-do-i-customize-toolbars))
because it makes for a better user experience for add-ons to expose their
interfaces in a consistent way. In such cases it's worth considering
changing your user interface to align with the SDK APIs.
Some limitations only exist because we haven't yet implemented the relevant
APIs: for example, there's currently no way to add items to the browser's main
menus using the SDK's supported APIs.
Many add-ons will need to make some changes to their user interfaces if they
are to use only the SDK's supported APIs, and add-ons which make drastic
changes to the browser chrome will very probably need more than the SDK's
supported APIs can offer.
Similarly, the supported APIs expose only a small fraction of the full range
of XPCOM functionality.
## <a name="third-party-packages">Using Third Party Packages</a> ##
The SDK is extensible by design: developers can create new modules filling gaps
in the SDK, and package them for distribution and reuse. Add-on developers can
install these packages and use the new modules.
If you can find a third party package that does what you want, this is a great
way to use features not supported in the SDK without having to use the
low-level APIs.
See the
[guide to adding Firefox menu items](dev-guide/tutorials/adding-menus.html).
Some useful third party packages are
[collected in the Jetpack Wiki](https://wiki.mozilla.org/Jetpack/Modules).
Note, though, that by using third party packages you're likely to lose the
security and compatibility benefits of using the SDK.
## <a name="low-level-apis">Using the Low-level APIs</a> ##
<span class="aside">
But note that unlike the supported APIs, low-level APIs do not come with a
compatibility guarantee, so we do not expect code using them will necessarily
continue to work as new versions of Firefox are released.
</span>
In addition to the High-Level APIs, the SDK includes a number of
[Low-Level APIs](modules/low-level-modules.html) some of which, such
[`xhr`](modules/sdk/net/xhr.html) and
[`window/utils`](modules/sdk/window/utils.html), expose powerful
browser capabilities.
In this section we'll use low-level modules how to:
* modify the browser chrome using dynamic manipulation of the DOM
* directly access the [tabbrowser](https://developer.mozilla.org/en/XUL/tabbrowser)
object
### <a name="browser-chrome">Modifying the Browser Chrome</a> ###
The [`window/utils`](modules/sdk/window/utils.html) module gives
you direct access to chrome windows, including the browser's chrome window.
Here's a really simple example add-on that modifies the browser chrome using
`window/utils`:
function removeForwardButton() {
var window = require("sdk/window/utils").getMostRecentBrowserWindow();
var forward = window.document.getElementById('forward-button');
var parent = window.document.getElementById('unified-back-forward-button');
parent.removeChild(forward);
}
var widgets = require("sdk/widget");
widgets.Widget({
id: "remove-forward-button",
label: "Remove the forward button",
contentURL: "http://www.mozilla.org/favicon.ico",
onClick: function() {
removeForwardButton();
}
});
There are more useful examples of this technique in the Jetpack Wiki's
collection of [third party modules](https://wiki.mozilla.org/Jetpack/Modules).
### <a name="accessing-tabbrowser">Accessing <a href="https://developer.mozilla.org/en/XUL/tabbrowser">tabbrowser</a> ###
The [`tabs/utils`](modules/sdk/tabs/utils.html) module gives
you direct access to the
[tabbrowser](https://developer.mozilla.org/en/XUL/tabbrowser) object and the
XUL tab objects it contains. This simple example modifies the selected tab's
CSS to enable the user to highlight the selected tab:
var widgets = require("sdk/widget");
var tabUtils = require("sdk/tabs/utils");
var self = require("sdk/self");
function highlightTab(tab) {
if (tab.style.getPropertyValue('background-color')) {
tab.style.setProperty('background-color','','important');
}
else {
tab.style.setProperty('background-color','rgb(255,255,100)','important');
}
}
var widget = widgets.Widget({
id: "tab highlighter",
label: "Highlight tabs",
contentURL: self.data.url("highlight.png"),
onClick: function() {
var window = require("sdk/window/utils").getMostRecentBrowserWindow();
highlightTab(tabUtils.getActiveTab(window));
}
});
### Security Implications ###
The SDK implements a security model in which an add-on only gets to access the
APIs it explicitly imports via `require()`. This is useful, because it means
that if a malicious web page is able to inject code into your add-on's
context, it is only able to use the APIs you have imported. For example, if
you have only imported the
[`notifications`](modules/sdk/notifications.html) module, then
even if a malicious web page manages to inject code into your add-on, it
can't use the SDK's [`file`](modules/sdk/io/file.html) module to
access the user's data.
But this means that the more powerful modules you `require()`, the greater
is your exposure if your add-on is compromised. Low-level modules like `xhr`,
`tab-browser` and `window-utils` are much more powerful than the modules in
`addon-kit`, so your add-on needs correspondingly more rigorous security
design and review.
## <a name="xpcom">Using XPCOM</a> ##
Finally, if none of the above techniques work for you, you can use the
`require("chrome")` statement to get direct access to the
[`Components`](https://developer.mozilla.org/en/Components_object) object,
which you can then use to load and access any XPCOM object.
The following complete add-on uses
[`nsIPromptService`](https://developer.mozilla.org/en/XPCOM_Interface_Reference/nsIPromptService)
to display an alert dialog:
var {Cc, Ci} = require("chrome");
var promptSvc = Cc["@mozilla.org/embedcomp/prompt-service;1"].
getService(Ci.nsIPromptService);
var widget = require("sdk/widget").Widget({
id: "xpcom example",
label: "Mozilla website",
contentURL: "http://www.mozilla.org/favicon.ico",
onClick: function() {
promptSvc.alert(null, "My Add-on", "Hello from XPCOM");
}
});
It's good practice to encapsulate code which uses XPCOM by
[packaging it in its own module](dev-guide/tutorials/reusable-modules.html).
For example, we could package the alert feature implemented above using a
script like:
var {Cc, Ci} = require("chrome");
var promptSvc = Cc["@mozilla.org/embedcomp/prompt-service;1"].
getService(Ci.nsIPromptService);
exports.alert = function(title, text) {
promptSvc.alert(null, title, text);
};
If we save this as "alert.js" in our add-on's `lib` directory, we can rewrite
`main.js` to use it as follows:
var widget = require("sdk/widget").Widget({
id: "xpcom example",
label: "Mozilla website",
contentURL: "http://www.mozilla.org/favicon.ico",
onClick: function() {
require("./alert").alert("My Add-on", "Hello from XPCOM");
}
});
One of the benefits of this is that we can control which parts of the add-on
are granted chrome privileges, making it easier to review and secure the code.
### Security Implications ###
We saw above that using powerful low-level modules like `tab-browser`
increases the damage that a malicious web page could do if it were able to
inject code into your add-ons context. This applies with even greater force
to `require("chrome")`, since this gives full access to the browser's
capabilities.
## <a name="library-detector">Example: Porting the Library Detector</a> ##
[Porting the Library Detector](dev-guide/guides/library-detector.html)
walks through the process of porting a XUL-based add-on to the
SDK. It's a very simple add-on and a good candidate for porting because
there are suitable SDK APIs for all its features.
Even so, we have to change its user interface slightly if we are to use only
the supported APIs.

Просмотреть файл

@ -0,0 +1,16 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# High-Level APIs #
Modules in this section implement high-level APIs for
building add-ons:
* creating user interfaces
* interacting with the web
* interacting with the browser
These modules are "supported": meaning that they are relatively
stable, and that we'll avoid making incompatible changes to them
unless absolutely necessary.

Просмотреть файл

@ -0,0 +1,155 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<h2 class="top">Welcome to the Add-on SDK!</h2>
Using the Add-on SDK you can create Firefox add-ons using standard Web
technologies: JavaScript, HTML, and CSS. The SDK includes JavaScript APIs which you can use to create add-ons, and tools for creating, running, testing, and packaging add-ons.
<hr>
## <a href="dev-guide/tutorials/index.html">Tutorials</a> ##
<table class="catalog">
<colgroup>
<col width="50%">
<col width="50%">
</colgroup>
<tr>
<td>
<h4><a href="dev-guide/tutorials/index.html#getting-started">Getting started</a></h4>
How to
<a href="dev-guide/tutorials/installation.html">install the SDK</a> and
<a href="dev-guide/tutorials/getting-started-with-cfx.html">use the cfx
tool</a> to develop, test, and package add-ons.
</td>
<td>
<h4><a href="dev-guide/tutorials/index.html#create-user-interfaces">Create user interface components</a></h4>
Create user interface components such as
<a href="dev-guide/tutorials/adding-toolbar-button.html">toolbar buttons</a>,
<a href="dev-guide/tutorials/adding-menus.html">menu items</a>, and
<a href="dev-guide/tutorials/display-a-popup.html">dialogs</a>
</td>
</tr>
<tr>
<td>
<h4><a href="dev-guide/tutorials/index.html#interact-with-the-browser">Interact with the browser</a></h4>
<a href="dev-guide/tutorials/open-a-web-page.html">Open web pages</a>,
<a href="dev-guide/tutorials/listen-for-page-load.html">listen for pages loading</a>, and
<a href="dev-guide/tutorials/list-open-tabs.html">list open pages</a>.
</td>
<td>
<h4><a href="dev-guide/tutorials/index.html#modify-web-pages">Modify web pages</a></h4>
<a href="dev-guide/tutorials/modifying-web-pages-url.html">Modify pages matching a URL pattern</a>
or <a href="dev-guide/tutorials/modifying-web-pages-tab.html">dynamically modify a particular tab</a>.
</td>
</tr>
<tr>
<td>
<h4><a href="dev-guide/tutorials/index.html#development-techniques">Development techniques</a></h4>
Learn about common development techniques, such as
<a href="dev-guide/tutorials/unit-testing.html">unit testing</a>,
<a href="dev-guide/tutorials/logging.html">logging</a>,
<a href="dev-guide/tutorials/reusable-modules.html">creating reusable modules</a>,
<a href="dev-guide/tutorials/l10n.html">localization</a>, and
<a href="dev-guide/tutorials/mobile.html">mobile development</a>.
</td>
<td>
<h4><a href="dev-guide/tutorials/index.html#putting-it-together">Putting it together</a></h4>
Walkthrough of the <a href="dev-guide/tutorials/annotator/index.html">Annotator</a> example add-on.
</td>
</tr>
</table>
<hr>
## <a href="dev-guide/guides/index.html">Guides</a> ##
<table class="catalog">
<colgroup>
<col width="50%">
<col width="50%">
</colgroup>
<tr>
<td>
<h4><a href="dev-guide/guides/index.html#sdk-infrastructure">SDK infrastructure</a></h4>
Aspects of the SDK's underlying technology:
<a href="dev-guide/guides/modules.html">Modules</a>, the
<a href="dev-guide/guides/program-id.html">Program ID</a>,
and the rules defining
<a href="dev-guide/guides/firefox-compatibility.html">Firefox compatibility</a>.
</td>
<td>
<h4><a href="dev-guide/guides/index.html#sdk-idioms">SDK idioms</a></h4>
The SDK's
<a href="dev-guide/guides/events.html">event framework</a> and the
<a href="dev-guide/guides/two-types-of-scripts.html">distinction between add-on scripts and content scripts</a>.
</td>
</tr>
<tr>
<td>
<h4><a href="dev-guide/guides/index.html#content-scripts">Content scripts</a></h4>
A <a href="dev-guide/guides/content-scripts/index.html">detailed guide to working with content scripts</a>,
including: how to load content scripts, which objects
content scripts can access, and how to communicate
between content scripts and the rest of your add-on.
</td>
<td>
<h4><a href="dev-guide/guides/index.html#xul-migration">XUL migration</a></h4>
A guide to <a href="dev-guide/guides/xul-migration.html">porting XUL add-ons to the SDK</a>.
This guide includes a
<a href="dev-guide/guides/sdk-vs-xul.html">comparison of the two toolsets</a> and a
<a href="dev-guide/guides/library-detector.html">worked example</a> of porting a XUL add-on.
</td>
</tr>
</table>
<hr>
## Reference ##
<table class="catalog">
<colgroup>
<col width="50%">
<col width="50%">
</colgroup>
<tr>
<td>
<h4><a href="modules/high-level-modules.html">High-Level APIs</a></h4>
Reference documentation for the high-level SDK APIs.
</td>
<td>
<h4><a href="modules/low-level-modules.html">Low-Level APIs</a></h4>
Reference documentation for the low-level SDK APIs.
</td>
</tr>
<tr>
<td>
<h4>Tools reference</h4>
Reference documentation for the
<a href="dev-guide/cfx-tool.html">cfx tool</a>
used to develop, test, and package add-ons, the
<a href="dev-guide/console.html">console</a>
global used for logging, and the
<a href="dev-guide/package-spec.html">package.json</a> file.
</td>
<td>
</td>
</tr>
</table>

Просмотреть файл

@ -0,0 +1,34 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Low-Level APIs #
Modules in this section implement low-level APIs. These
modules fall roughly into three categories:
* fundamental utilities such as
[collection](modules/sdk/platform/xpcom.html) and
[url](modules/sdk/url.html). Many add-ons are likely to
want to use modules from this category.
* building blocks for higher level modules, such as
[events](modules/sdk/deprecated/events.html),
[worker](modules/sdk/content/worker.html), and
[api-utils](modules/sdk/deprecated/api-utils.html). You're more
likely to use these if you are building your own modules that
implement new APIs, thus extending the SDK itself.
* privileged modules that expose powerful low-level capabilities
such as [tab-browser](modules/sdk/deprecated/tab-browser.html),
[xhr](modules/sdk/net/xhr.html), and
[xpcom](modules/sdk/platform/xpcom.html). You can use these
modules in your add-on if you need to, but should be aware that
the cost of privileged access is the need to take more elaborate
security precautions. In many cases these modules have simpler,
more restricted analogs among the "High-Level APIs" (for
example, [tabs](modules/sdk/tabs.html) or
[request](modules/sdk/request.html)).
These modules are still in active development, and we expect to
make incompatible changes to them in future releases.

Просмотреть файл

@ -0,0 +1,121 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Package Specification #
A *package* is a directory that, at minimum, contains a JSON file
called `package.json`. This file is also referred to as the
*package manifest*.
## The Package Manifest ##
`package.json` may contain the following keys:
* `name` - the name of the package. The package system will only load
one package with a given name. This name cannot contain spaces or periods.
The name defaults to the name of the parent directory. If the package is
ever built as an XPI and the `fullName` key is not present, this is
used as the add-on's `em:name` element in its `install.rdf`.
* `fullName` - the full name of the package. It can contain spaces. If
the package is ever built as an XPI, this is used as the add-on's
`em:name` element in its `install.rdf`.
* `description` - a String describing the package. If the package is
ever built as an XPI, this is used as the add-on's
`em:description` element in its `install.rdf`.
* `author` - the original author of the package. The author may be a
String including an optional URL in parentheses and optional email
address in angle brackets. If the package is ever built as an XPI,
this is used as the add-on's `em:creator` element in its
`install.rdf`.
* `translators` - an Array of Strings consisted of translators of the package.
* `contributors` - may be an Array of additional author Strings.
* `homepage` - the URL of the package's website.
* `icon` - the relative path from the root of the package to a
PNG file containing the icon for the package. By default, this
is `icon.png`. If the package is built as an XPI, this is used
as the add-on's icon to display in the Add-on Manager's add-ons list.
This key maps on to the
[`iconURL` entry in the Install Manifest](https://developer.mozilla.org/en/install_manifests#iconURL),
so the icon may be up to 48x48 pixels in size.
* `icon64` - the relative path from the root of the package to a
PNG file containing the icon64 for the package. By default, this
is `icon64.png`. If the package is built as an XPI, this is used
as the add-on's icon to display in the Addon Manager's add-on details view.
This key maps on to the
[`icon64URL` entry in the Install Manifest](https://developer.mozilla.org/en/install_manifests#icon64URL),
so the icon should be 64x64 pixels in size.
* `preferences` - *experimental*
An array of JSON objects that use the following keys `name`, `type`, `value`,
`title`, and `description`. These JSON objects will be used to automatically
create a preferences interface for the addon in the Add-ons Manager.
For more information see the documentation of [simple-prefs](modules/sdk/simple-prefs.html).
* `license` - the name of the license as a String, with an optional
URL in parentheses.
* `id` - a globally unique identifier for the package. When the package is
built as an XPI, this is used as the add-on's `em:id` element in its
`install.rdf`. See the
[Program ID page](dev-guide/guides/program-id.html).
* `version` - a String representing the version of the package. If the
package is ever built as an XPI, this is used as the add-on's
`em:version` element in its `install.rdf`.
* `dependencies` - a String or Array of Strings representing package
names that this package requires in order to function properly.
* `lib` - a String representing the top-level module directory provided in
this package. Defaults to `"lib"`.
* `tests` - a String representing the top-level module directory containing
test suites for this package. Defaults to `"tests"`.
* `packages` - a String or Array of Strings representing paths to
directories containing additional packages, defaults to
`"packages"`.
* `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"`.
* `harnessClassID` - a String in the GUID format:
`xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx`, where `x` represents a single
hexadecimal digit. It is used as a `classID` (CID) of the "harness service"
XPCOM component. Defaults to a random GUID generated by `cfx`.
## Documentation ##
A package may optionally contain a
[Markdown](http://daringfireball.net/projects/markdown/)-formatted file
called `README.md` in its root directory. Package-browsing tools may display
this file to developers.
Additionally, Markdown files can be placed in an optional `docs`
directory. When package-browsing tools are asked to show the
documentation for a module, they will look in this directory for a
`.md` file with the module's name. Thus, for instance, if a user
browses to a module at `lib/foo/bar.js`, the package-browsing tool
will look for a file at `docs/foo/bar.js` to represent the module's
API documentation.
## Data Resources ##
Packages may optionally contain a directory called `data` into which
arbitrary files may be placed, such as images or text files. The
URL for these resources may be reached using the
[self](modules/sdk/self.html) module.
[Markdown]: http://daringfireball.net/projects/markdown/
[non-bootstrapped XUL extension]: #guide/xul-extensions

Просмотреть файл

@ -0,0 +1,190 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<div id="cse" style="width: 100%;">Loading</div>
<script src="http://www.google.com/jsapi" type="text/javascript"></script>
<script type="text/javascript">
function parseQueryFromUrl () {
var queryParamName = "q";
var search = window.location.search.substr(1);
var parts = search.split('&');
for (var i = 0; i < parts.length; i++) {
var keyvaluepair = parts[i].split('=');
if (decodeURIComponent(keyvaluepair[0]) == queryParamName) {
return decodeURIComponent(keyvaluepair[1].replace(/\+/g, ' '));
}
}
return '';
}
google.load('search', '1', {language : 'en'});
google.setOnLoadCallback(function() {
var customSearchOptions = {};
var customSearchControl = new google.search.CustomSearchControl(
'017013284162333743052:rvlazd1zehe', customSearchOptions);
customSearchControl.setResultSetSize(google.search.Search.FILTERED_CSE_RESULTSET);
var options = new google.search.DrawOptions();
options.enableSearchResultsOnly();
customSearchControl.draw('cse', options);
var queryFromUrl = parseQueryFromUrl();
if (queryFromUrl) {
var searchBox = document.getElementById("search-box");
searchBox.value = queryFromUrl;
searchBox.focus();
searchBox.blur();
customSearchControl.execute(queryFromUrl);
}
}, true);
</script>
<link rel="stylesheet" href="http://www.google.com/cse/style/look/default.css" type="text/css" />
<style type="text/css">
#cse table, #cse tr, #cse td {
border: none;
}
.gsc-above-wrapper-area, .gsc-result-info-container {
border: none;
}
.gsc-table-cell-snippet-close {
border-color: #a0d0fb;
}
.gsc-resultsHeader {
display : none;
}
.gsc-control-cse {
font-family: "Trebuchet MS", sans-serif;
border-color: #F0F8FF;
background: none;
}
.gsc-tabHeader.gsc-tabhInactive {
border-color: #E9E9E9;
background-color: none;
}
.gsc-tabHeader.gsc-tabhActive {
border-top-color: #FF9900;
border-left-color: #E9E9E9;
border-right-color: #E9E9E9;
background-color: #FFFFFF;
}
.gsc-tabsArea {
border-color: #E9E9E9;
}
.gsc-webResult.gsc-result,
.gsc-results .gsc-imageResult {
border-color: #F0F8FF;
background-color: #FFFFFF;
}
.gsc-webResult.gsc-result:hover,
.gsc-webResult.gsc-result.gsc-promotion:hover,
.gsc-imageResult:hover {
border-color: #F0F8FF;
background-color: #FFFFFF;
}
.gs-webResult.gs-result a.gs-title:link,
.gs-webResult.gs-result a.gs-title:link b,
.gs-imageResult a.gs-title:link,
.gs-imageResult a.gs-title:link b {
color: #000000;
}
.gs-webResult.gs-result a.gs-title:visited,
.gs-webResult.gs-result a.gs-title:visited b,
.gs-imageResult a.gs-title:visited,
.gs-imageResult a.gs-title:visited b {
color: #000000;
}
.gs-webResult.gs-result a.gs-title:hover,
.gs-webResult.gs-result a.gs-title:hover b,
.gs-imageResult a.gs-title:hover,
.gs-imageResult a.gs-title:hover b {
color: #000000;
}
.gs-webResult.gs-result a.gs-title:active,
.gs-webResult.gs-result a.gs-title:active b,
.gs-imageResult a.gs-title:active,
.gs-imageResult a.gs-title:active b {
color: #000000;
}
.gsc-cursor-page {
color: #000000;
}
a.gsc-trailing-more-results:link {
color: #000000;
}
.gs-webResult .gs-snippet,
.gs-imageResult .gs-snippet,
.gs-fileFormatType {
color: #000000;
}
.gs-webResult div.gs-visibleUrl,
.gs-imageResult div.gs-visibleUrl {
color: #003595;
}
.gs-webResult div.gs-visibleUrl-short {
color: #003595;
}
.gs-webResult div.gs-visibleUrl-short {
display: none;
}
.gs-webResult div.gs-visibleUrl-long {
display: block;
}
.gs-promotion div.gs-visibleUrl-short {
display: none;
}
.gs-promotion div.gs-visibleUrl-long {
display: block;
}
.gsc-cursor-box {
border-color: #F0F8FF;
}
.gsc-results .gsc-cursor-box .gsc-cursor-page {
border-color: #F0F8FF;
background-color: #FFFFFF;
color: #000000;
}
.gsc-results .gsc-cursor-box .gsc-cursor-current-page {
border-color: #FF9900;
background-color: #FFFFFF;
color: #000000;
}
.gsc-webResult.gsc-result.gsc-promotion {
border-color: #336699;
background-color: #FFFFFF;
}
.gs-promotion a.gs-title:link,
.gs-promotion a.gs-title:link *,
.gs-promotion .gs-snippet a:link {
color: #00ffff;
}
.gs-promotion a.gs-title:visited,
.gs-promotion a.gs-title:visited *,
.gs-promotion .gs-snippet a:visited {
color: #0000CC;
}
.gs-promotion a.gs-title:hover,
.gs-promotion a.gs-title:hover *,
.gs-promotion .gs-snippet a:hover {
color: #0000CC;
}
.gs-promotion a.gs-title:active,
.gs-promotion a.gs-title:active *,
.gs-promotion .gs-snippet a:active {
color: #0000CC;
}
.gs-promotion .gs-snippet,
.gs-promotion .gs-title .gs-promotion-title-right,
.gs-promotion .gs-title .gs-promotion-title-right * {
color: #000000;
}
.gs-promotion .gs-visibleUrl,
.gs-promotion .gs-visibleUrl-short {
color: #008000;
}
</style>

Просмотреть файл

@ -0,0 +1,7 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Third-Party APIs #
This section lists modules which you've downloaded and added to your SDK installation.

Просмотреть файл

@ -0,0 +1,89 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Add a Context Menu Item #
<span class="aside">
To follow this tutorial you'll need to have
[installed the SDK](dev-guide/tutorials/installation.html)
and learned the
[basics of `cfx`](dev-guide/tutorials/getting-started-with-cfx.html).
</span>
To add items and submenus to the Firefox context menu, use the
[`context-menu`](modules/sdk/context-menu.html) module.
Here's an add-on that adds a new context menu item. The item is
displayed whenever something in the page is selected. When it's
clicked, the selection is sent to the main add-on code, which just
logs it:
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:
<img class="image-center" src="static-files/media/screenshots/context-menu-selection.png"></img>
Click it, and the selection is
[logged to the console](dev-guide/tutorials/logging.html):
<pre>
info: elephantine lizard
</pre>
All this add-on does is to construct a context menu item. You don't need
to add it: once you have constructed the item, it is automatically added
in the correct context. The constructor in this case takes four options:
`label`, `context`, `contentScript`, and `onMessage`.
### label ###
The `label` is just the string that's displayed.
### context ###
The `context` describes the circumstances in which the item should be
shown. The `context-menu` module provides a number of simple built-in
contexts, including this `SelectionContext()`, which means: display
the item when something on the page is selected.
If these simple contexts aren't enough, you can define more sophisticated
contexts using scripts.
### contentScript ###
This attaches a script to the item. In this case the script listens for
the user to click on the item, then sends a message to the add-on containing
the selected text.
### onMessage ###
The `onMessage` property provides a way for the add-on code to respond to
messages from the script attached to the context menu item. In this case
it just logs the selected text.
So:
1. the user clicks the item
2. the content script's `click` event fires, and the content script retrieves
the selected text and sends a message to the add-on
3. the add-on's `message` event fires, and the add-on code's handler function
is passed the selected text, which it logs
## Learning More ##
To learn more about the `context-menu` module, see the
[`context-menu` API reference](modules/sdk/context-menu.html).

Просмотреть файл

@ -0,0 +1,142 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Add a Menu Item to Firefox #
<span class="aside">
To follow this tutorial you'll need to have
[installed the SDK](dev-guide/tutorials/installation.html)
and learned the
[basics of `cfx`](dev-guide/tutorials/getting-started-with-cfx.html).
</span>
The SDK doesn't yet provide an API to add new menu items to Firefox.
But it's extensible by design, so anyone can build and publish
modules for add-on developers to use. Luckily, Erik Vold has written
a [`menuitems`](https://github.com/erikvold/menuitems-jplib) module
that enables us to add menu items.
This tutorial does double-duty. It describes the general method for
using an external, third-party module in your add-on, and it
describes how to add a menu item using the `menuitems` module in particular.
## Installing `menuitems` ##
First we'll download the `menuitems` package from
[https://github.com/erikvold/menuitems-jplib](https://github.com/erikvold/menuitems-jplib/zipball/51080383cbb0fe2a05f8992a8aae890f4c014176).
Third-party packages like `menuitems` can be installed in three
different places:
* in the `packages` directory under the SDK root. If you do this the package
is available to any other add-ons you're developing using that SDK instance,
and the package's documentation is visible through `cfx docs`.
* in a `packages` directory you create under your add-on's root: if you
do this, the package is only available to that add-on.
* in a directory indicated using the `packages` key in
your add-on's [package.json](dev-guide/package-spec.html). If you
do this, you may not keep any packages in your add-on's `packages`
directory, or they will not be found.
In this example we will install the package under the SDK root. From
the SDK root directory, execute something like the following commands:
<pre>
cd packages
tar -xf ../erikvold-menuitems-jplib-d80630c.zip
</pre>
Now if you run `cfx docs` you'll see a new section appear in the sidebar
labeled "Third-Party APIs", which lists the modules in the `menuitems`
package: this package contains a single module, also
called `menuitems`.
Click on the module name and you'll see API documentation for the module.
## Module Dependencies ##
If third-party modules only depend on SDK modules, you can use them right
away, but if they depend on other third-party modules, you'll have to install
those dependencies as well.
In the package's main directory you'll find a file called "package.json".
Open it up and look for an entry named "dependencies". The entry for the
`menuitems` package is:
<pre>
"dependencies": ["vold-utils"]
</pre>
This tells us that we need to install the `vold-utils` package,
which we can do by downloading it from
[https://github.com/erikvold/vold-utils-jplib](https://github.com/voldsoftware/vold-utils-jplib/zipball/1b2ad874c2d3b2070a1b0d43301aa3731233e84f)
and adding it under the `packages` directory alongside `menuitems`.
## Using `menuitems` ##
We can use the `menuitems` module in exactly the same way we use built-in
modules.
The documentation for the `menuitems` module tells us to we create a menu
item using `MenuItem()`. Of the options accepted by `MenuItem()`, we'll use
this minimal set:
* `id`: identifier for this menu item
* `label`: text the item displays
* `command`: function called when the user selects the item
* `menuid`: identifier for the item's parent element
* `insertbefore`: identifier for the item before which we want our item to
appear
Next, create a new add-on. Make a directory called 'clickme' wherever you
like, navigate to it and run `cfx init`. Open `lib/main.js` and add the
following code:
var menuitem = require("menuitems").Menuitem({
id: "clickme",
menuid: "menu_ToolsPopup",
label: "Click Me!",
onCommand: function() {
console.log("clicked");
},
insertbefore: "menu_pageInfo"
});
Next, we have to declare our dependency on the `menuitems` package.
In your add-on's `package.json` add the line:
<pre>
"dependencies": "menuitems"
</pre>
Note that due to
[bug 663480](https://bugzilla.mozilla.org/show_bug.cgi?id=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:
<pre>
"dependencies": ["menuitems", "addon-sdk"]
</pre>
Now we're done. Run the add-on and you'll see the new item appear in the
`Tools` menu: select it and you'll see `info: clicked` appear in the
console.
## Caveats ##
Eventually we expect the availability of a rich set of third party packages
will be one of the most valuable aspects of the SDK. Right now they're a great
way to use features not supported by the supported APIs without the
complexity of using the low-level APIs, but there are some caveats you should
be aware of:
* our support for third party packages is still fairly immature. One
consequence of this is that it's not always obvious where to find third-party
packages, although the
[Community Developed Modules](https://github.com/mozilla/addon-sdk/wiki/Community-developed-modules)
page in the SDK's GitHub Wiki lists a number of packages.
* because third party modules typically use low-level APIs, they may be broken
by new releases of Firefox.

Просмотреть файл

@ -0,0 +1,174 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Adding a Button to the Toolbar #
<span class="aside">
To follow this tutorial you'll need to have
[installed the SDK](dev-guide/tutorials/installation.html)
and learned the
[basics of `cfx`](dev-guide/tutorials/getting-started-with-cfx.html).
</span>
To add a button to the toolbar, use the
[`widget`](modules/sdk/widget.html) module.
Create a new directory, navigate to it, and execute `cfx init`.
Then open the file called "main.js" in the "lib" directory and
add the following code to it:
var widgets = require("sdk/widget");
var tabs = require("sdk/tabs");
var widget = widgets.Widget({
id: "mozilla-link",
label: "Mozilla website",
contentURL: "http://www.mozilla.org/favicon.ico",
onClick: function() {
tabs.open("http://www.mozilla.org/");
}
});
The widget is added to the "Add-on Bar" at the bottom of the browser window:
<img class="image-right" src="static-files/media/screenshots/widget-mozilla.png"
alt="Mozilla icon widget" />
You can't change the initial location for the widget, but the user can move
it to a different toolbar. The `id` attribute is mandatory, and is used to
remember the position of the widget, so you should not change it in subsequent
versions of the add-on.
Clicking the button opens [http://www.mozilla.org](http://www.mozilla.org).
<div style="clear:both"></div>
## Specifying the Icon ##
If you're using the widget to make a toolbar button, specify the icon to
display using `contentURL`: this may refer to a remote file as in the
example above, or may refer to a local file. The example below will load
an icon file called "my-icon.png" from the add-on's `data` directory:
var widgets = require("sdk/widget");
var tabs = require("sdk/tabs");
var self = require("sdk/self");
var widget = widgets.Widget({
id: "mozilla-link",
label: "Mozilla website",
contentURL: self.data.url("my-icon.png"),
onClick: function() {
tabs.open("http://www.mozilla.org/");
}
});
You can change the icon at any time by setting the widget's `contentURL`
property. However, 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](https://bugzilla.mozilla.org/show_bug.cgi?id=825434).
## Responding To the User ##
You can listen for `click`, `mouseover`, and `mouseout` events by passing
handler functions as the corresponding constructor options. The widget
example above assigns a listener to the `click` event using the `onClick`
option, and there are similar `onMouseover` and `onMouseout` options.
To handle user interaction in more detail, you can attach a content
script to the widget. 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.
Here's an example. The widget's built-in `onClick` property does not
distinguish between left and right mouse clicks, so to do this we need
to use a content script. The script looks like this:
window.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);
It uses the standard DOM `addEventListener()` function to listen for click
events, and handles them by sending the corresponding message to the main
add-on code. Note that the messages "left-click" and "right-click" are not
defined in the widget API itself, they're custom events defined by the add-on
author.
Save this script in your `data` directory as "click-listener.js".
Next, modify `main.js` to:
<ul>
<li>pass in the script by setting the <code>contentScriptFile</code>
property</li>
<li>listen for the new events:</li>
</ul>
var widgets = require("sdk/widget");
var tabs = require("sdk/tabs");
var self = require("sdk/self");
var widget = widgets.Widget({
id: "mozilla-link",
label: "Mozilla website",
contentURL: "http://www.mozilla.org/favicon.ico",
contentScriptFile: self.data.url("click-listener.js")
});
widget.port.on("left-click", function(){
console.log("left-click");
});
widget.port.on("right-click", function(){
console.log("right-click");
});
Now execute `cfx run` again, and try right- and left-clicking on the button.
You should see the corresponding string written to the command shell.
## Attaching a Panel ##
<!-- The icon the widget displays, shown in the screenshot, is taken from the
Circular icon set, http://prothemedesign.com/circular-icons/ which is made
available under the Creative Commons Attribution 2.5 Generic License:
http://creativecommons.org/licenses/by/2.5/ -->
<img class="image-right" src="static-files/media/screenshots/widget-panel-clock.png"
alt="Panel attached to a widget">
If you supply a `panel` object to the widget's constructor, then the panel
will be shown 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
});
To learn more about working with panels, see the tutorial on
[displaying a popup](dev-guide/tutorials/display-a-popup.html).
## Learning More ##
To learn more about the widget module, see its
[API reference documentation](modules/sdk/widget.html).
To learn more about content scripts, see the
[content scripts guide](dev-guide/guides/content-scripts/index.html).

Просмотреть файл

@ -0,0 +1,344 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Creating Annotations #
We'll use two objects to create annotations: a page-mod to find page elements
that the user can annotate, and a panel for the user to enter the annotation
text itself.
## Selector page-mod ##
### Selector Content Scripts ###
The content script for the selector page-mod uses [jQuery](http://jquery.com/)
to examine and manipulate the DOM.
Its main job is to maintain a "matched element": this is the page element that
is the current candidate for an annotation. The matched element is highlighted
and has a click handler bound to it which sends a message to the main add-on
code.
The selector page mod can be switched on and off using a message from the
main add-on code. It is initially off:
var matchedElement = null;
var originalBgColor = null;
var active = false;
function resetMatchedElement() {
if (matchedElement) {
$(matchedElement).css('background-color', originalBgColor);
$(matchedElement).unbind('click.annotator');
}
}
self.on('message', function onMessage(activation) {
active = activation;
if (!active) {
resetMatchedElement();
}
});
This selector listens for occurrences of the
[jQuery mouseenter](http://api.jquery.com/mouseenter/) event.
When a mouseenter event is triggered the selector checks whether the element
is eligible for annotation. An element is eligible if it, or one of its
ancestors in the DOM tree, has an attribute named `"id"`. The idea here is to
make it more likely that the annotator will be able to identify annotated
elements correctly later on.
If the page element is eligible for annotation, then the selector highlights
that element and binds a click handler to it. The click handler sends a message
called `show` back to the main add-on code. The `show` message contains: the URL
for the page, the ID attribute value, and the text content of the page element.
$('*').mouseenter(function() {
if (!active || $(this).hasClass('annotated')) {
return;
}
resetMatchedElement();
ancestor = $(this).closest("[id]");
matchedElement = $(this).first();
originalBgColor = $(matchedElement).css('background-color');
$(matchedElement).css('background-color', 'yellow');
$(matchedElement).bind('click.annotator', function(event) {
event.stopPropagation();
event.preventDefault();
self.port.emit('show',
[
document.location.toString(),
$(ancestor).attr("id"),
$(matchedElement).text()
]
);
});
});
Conversely, the add-on resets the matched element on
[mouseout](http://api.jquery.com/mouseout/):
$('*').mouseout(function() {
resetMatchedElement();
});
Save this code in a new file called `selector.js` in your add-on's `data`
directory.
Because this code uses jQuery, you'll need to
[download](http://docs.jquery.com/Downloading_jQuery) that as well, and save it in
`data`.
### Updating main.js ###
Go back to `main.js` and add the code to create the selector into the `main`
function:
var selector = pageMod.PageMod({
include: ['*'],
contentScriptWhen: 'ready',
contentScriptFile: [data.url('jquery-1.4.2.min.js'),
data.url('selector.js')],
onAttach: function(worker) {
worker.postMessage(annotatorIsOn);
selectors.push(worker);
worker.port.on('show', function(data) {
console.log(data);
});
worker.on('detach', function () {
detachWorker(this, selectors);
});
}
});
Make sure the name you use to load jQuery matches the name of the jQuery
version you downloaded.
The page-mod matches all pages, so each time the user loads a page the page-mod
emits the `attach` event, which will call the listener function we've assigned
to `onAttach`. The handler is passed a
[worker](modules/sdk/content/worker.html) object. Each worker
represents a channel of communication between the add-on code and any content
scripts running in that particular page context. For a more detailed discussion
of the way `page-mod` uses workers, see the
[page-mod documentation](modules/sdk/page-mod.html).
In the attach handler we do three things:
* send the content script a message with the current activation status
* add the worker to an array called `selectors` so we can send it messages
later on
* assign a message handler for messages from this worker. If the message is
`show` we will just log the content for the time being. If the message is
`detach` we remove the worker from the `selectors` array.
At the top of the file import the `page-mod` module and declare an array for
the workers:
var pageMod = require('sdk/page-mod');
var selectors = [];
Add `detachWorker`:
function detachWorker(worker, workerArray) {
var index = workerArray.indexOf(worker);
if(index != -1) {
workerArray.splice(index, 1);
}
}
Edit `toggleActivation` to notify the workers of a change in activation state:
function activateSelectors() {
selectors.forEach(
function (selector) {
selector.postMessage(annotatorIsOn);
});
}
function toggleActivation() {
annotatorIsOn = !annotatorIsOn;
activateSelectors();
return annotatorIsOn;
}
<span class="aside">We'll be using this URL in all our screenshots. Because
`cfx run` doesn't preserve browsing history, if you want to play along it's
worth taking a note of the URL.</span>
Save the file and execute `cfx run` again. Activate the annotator by clicking
the widget and load a page: the screenshot below uses
[http://blog.mozilla.com/addons/2011/02/04/
overview-amo-review-process/](http://blog.mozilla.com/addons/2011/02/04/overview-amo-review-process/).
You should see the highlight appearing when you move the mouse over certain elements:
<img class="image-center"
src="static-files/media/annotator/highlight.png" alt="Annotator Highlighting">
Click on the highlight and you should see something like this in the console
output:
<pre>
info: show
info: http://blog.mozilla.com/addons/2011/02/04/overview-amo-review-process/,
post-2249,When you submit a new add-on, you will have to choose between 2
review tracks: Full Review and Preliminary Review.
</pre>
## Annotation Editor Panel ##
So far we have a page-mod that can highlight elements and send information
about them to the main add-on code. Next we will create the editor panel,
which enables the user to enter an annotation associated with the selected
element.
We will supply the panel's content as an HTML file, and will also supply a
content script to execute in the panel's context.
So create a subdirectory under `data` called `editor`. This will contain
two files: the HTML content, and the content script.
### Annotation Editor HTML ###
The HTML is very simple:
<script type="syntaxhighlighter" class="brush: html"><![CDATA[
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>Annotation</title>
<style type="text/css" media="all">
body {
font: 100% arial, helvetica, sans-serif;
background-color: #F5F5F5;
}
textarea {
width: 180px;
height: 180px;
margin: 10px;
padding: 0px;
}
</style>
</head>
<body>
<textarea rows='10' cols='20' id='annotation-box'>
</textarea>
</body>
</html>
]]>
</script>
Save this inside `data/editor` as `annotation-editor.html`.
### Annotation Editor Content Script ###
In the corresponding content script we do two things:
* handle a message from the add-on code by giving the text area focus
* listen for the return key and when it is pressed, send the contents of the
text area to the add-on.
Here's the code:
var textArea = document.getElementById('annotation-box');
textArea.onkeyup = function(event) {
if (event.keyCode == 13) {
self.postMessage(textArea.value);
textArea.value = '';
}
};
self.on('message', function() {
var textArea = document.getElementById('annotation-box');
textArea.value = '';
textArea.focus();
});
Save this inside `data/editor` as `annotation-editor.js`.
### Updating main.js Again ###
Now we'll update `main.js` again to create the editor and use it.
First, import the `panel` module:
var panels = require('sdk/panel');
Then add the following code to the `main` function:
var annotationEditor = panels.Panel({
width: 220,
height: 220,
contentURL: data.url('editor/annotation-editor.html'),
contentScriptFile: data.url('editor/annotation-editor.js'),
onMessage: function(annotationText) {
if (annotationText) {
console.log(this.annotationAnchor);
console.log(annotationText);
}
annotationEditor.hide();
},
onShow: function() {
this.postMessage('focus');
}
});
We create the editor panel but don't show it.
We will send the editor panel the `focus` message when it is shown, so it will
give the text area focus. When the editor panel sends us its message we log the
message and hide the panel.
The only thing left is to link the editor to the selector. So edit the message
handler assigned to the selector so that on receiving the `show` message we
assign the content of the message to the panel using a new property
"annotationAnchor", and show the panel:
var selector = pageMod.PageMod({
include: ['*'],
contentScriptWhen: 'ready',
contentScriptFile: [data.url('jquery-1.4.2.min.js'),
data.url('selector.js')],
onAttach: function(worker) {
worker.postMessage(annotatorIsOn);
selectors.push(worker);
worker.port.on('show', function(data) {
annotationEditor.annotationAnchor = data;
annotationEditor.show();
});
worker.on('detach', function () {
detachWorker(this, selectors);
});
}
});
Execute `cfx run` again, activate the annotator, move your mouse over an
element and click the element when it is highlighted. You should see a panel
with a text area for a note:
<img class="image-center"
src="static-files/media/annotator/editor-panel.png" alt="Annotator Editor Panel">
<br>
Enter the note and press the return key: you should see console output like
this:
<pre>
info: http://blog.mozilla.com/addons/2011/02/04/overview-amo-review-process/,
post-2249,When you submit a new add-on, you will have to choose between 2
review tracks: Full Review and Preliminary Review.
info: We should ask for Full Review if possible.
</pre>
That's a complete annotation, and in the next section we'll deal with
[storing it](dev-guide/tutorials/annotator/storing.html).

Просмотреть файл

@ -0,0 +1,213 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Displaying Annotations #
In this chapter we'll use a page-mod to locate elements of web pages that have
annotations associated with them, and a panel to display the annotations.
## Matcher page-mod ##
### Matcher Content Script ###
The content script for the matcher page-mod is initialized with a list
of all the annotations that the user has created.
When a page is loaded the matcher searches the DOM for elements that match
annotations. If it finds any it binds functions to that element's
[mouseenter](http://api.jquery.com/mouseenter/) and
[mouseleave](http://api.jquery.com/mouseleave/) events to send messages to the
`main` module, asking it to show or hide the annotation.
Like the selector, the matcher also listens for the window's `unload` event
and on unload sends a `detach` message to the `main` module, so the add-on
can clean it up.
The complete content script is here:
self.on('message', function onMessage(annotations) {
annotations.forEach(
function(annotation) {
if(annotation.url == document.location.toString()) {
createAnchor(annotation);
}
});
$('.annotated').css('border', 'solid 3px yellow');
$('.annotated').bind('mouseenter', function(event) {
self.port.emit('show', $(this).attr('annotation'));
event.stopPropagation();
event.preventDefault();
});
$('.annotated').bind('mouseleave', function() {
self.port.emit('hide');
});
});
function createAnchor(annotation) {
annotationAnchorAncestor = $('#' + annotation.ancestorId);
annotationAnchor = $(annotationAnchorAncestor).parent().find(
':contains(' + annotation.anchorText + ')').last();
$(annotationAnchor).addClass('annotated');
$(annotationAnchor).attr('annotation', annotation.annotationText);
}
Save this in `data` as `matcher.js`.
### Updating main.js ###
First, initialize an array to hold workers associated with the matcher's
content scripts:
var matchers = [];
In the `main` function, add the code to create the matcher:
var matcher = pageMod.PageMod({
include: ['*'],
contentScriptWhen: 'ready',
contentScriptFile: [data.url('jquery-1.4.2.min.js'),
data.url('matcher.js')],
onAttach: function(worker) {
if(simpleStorage.storage.annotations) {
worker.postMessage(simpleStorage.storage.annotations);
}
worker.port.on('show', function(data) {
annotation.content = data;
annotation.show();
});
worker.port.on('hide', function() {
annotation.content = null;
annotation.hide();
});
worker.on('detach', function () {
detachWorker(this, matchers);
});
matchers.push(worker);
}
});
When a new page is loaded the function assigned to `onAttach` is called. This
function:
* initializes the content script instance with the current set of
annotations
* provides a handler for messages from that content script, handling the three
messages - `show`, `hide` and `detach` - that the content script might send
* adds the worker to an array, so we it can send messages back later.
Then in the module's scope implement a function to update the matcher's
workers, and edit `handleNewAnnotation` to call this new function when the
user enters a new annotation:
function updateMatchers() {
matchers.forEach(function (matcher) {
matcher.postMessage(simpleStorage.storage.annotations);
});
}
<br>
function handleNewAnnotation(annotationText, anchor) {
var newAnnotation = new Annotation(annotationText, anchor);
simpleStorage.storage.annotations.push(newAnnotation);
updateMatchers();
}
<br>
## Annotation panel ##
The annotation panel just shows the content of an annotation.
There are two files associated with the annotation panel:
* a simple HTML file to use as a template
* a simple content script to build the panel's content
These files will live in a new subdirectory of `data` which we'll call
`annotation`.
### Annotation panel HTML ###
<script type="syntaxhighlighter" class="brush: html"><![CDATA[
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>Annotation</title>
<style type="text/css" media="all">
body {
font: 100% arial, helvetica, sans-serif;
background-color: #F5F5F5;
}
div {
text-align:left;
}
</style>
</head>
<body>
<div id = "annotation">
</div>
</body>
</html>
]]>
</script>
Save this in `data/annotation` as `annotation.html`.
### Annotation panel Content Script ###
The annotation panel has a minimal content script that sets the text:
self.on('message', function(message) {
$('#annotation').text(message);
});
Save this in `data/annotation` as `annotation.js`.
### Updating main.js ###
Finally, update `main.js` with the code to construct the annotation panel:
var annotation = panels.Panel({
width: 200,
height: 180,
contentURL: data.url('annotation/annotation.html'),
contentScriptFile: [data.url('jquery-1.4.2.min.js'),
data.url('annotation/annotation.js')],
onShow: function() {
this.postMessage(this.content);
}
});
Execute `cfx run` one last time. Activate the annotator and enter an
annotation. You should see a yellow border around the item you annotated:
<img class="image-center"
src="static-files/media/annotator/matcher.png" alt="Annotator Matcher">
<br>
When you move your mouse over the item, the annotation should appear:
<img class="image-center"
src="static-files/media/annotator/annotation-panel.png" alt="Annotation Panel">
<br>
Obviously this add-on isn't complete yet. It could do with more beautiful
styling, it certainly needs a way to delete annotations, it should deal with
`OverQuota` more reliably, and the matcher could be made to match more
reliably.
But we hope this gives you an idea of the things that are possible with the
modules in the SDK.

Просмотреть файл

@ -0,0 +1,31 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Annotator: a More Complex Add-on #
In this tutorial we'll build an add-on that uses many of the SDK's
[high-level APIs](modules/high-level-modules.html).
The add-on is an annotator: it enables the user to select elements of web pages
and enter notes (annotations) associated with them. The annotator stores the
notes. Whenever the user loads a page containing annotated elements these
elements are highlighted, and if the user moves the mouse over an annotated
element its annotation is displayed.
Next we'll give a quick overview of the annotator's design, then go through
the implementation, step by step.
If you want to refer to the complete add-on you can find it under the
`examples` directory.
* [Design Overview](dev-guide/tutorials/annotator/overview.html)
* [Implementing the Widget](dev-guide/tutorials/annotator/widget.html)
* [Creating Annotations](dev-guide/tutorials/annotator/creating.html)
* [Storing Annotations](dev-guide/tutorials/annotator/storing.html)
* [Displaying Annotations](dev-guide/tutorials/annotator/displaying.html)

Просмотреть файл

@ -0,0 +1,63 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Annotator Design Overview #
The annotator uses content scripts to build user interfaces, get user input,
and examine the DOM of pages loaded by the user.
Meanwhile the `main` module contains the application logic and mediates
interactions between the different SDK objects.
We could represent the basic interactions between the `main` module and the
various content scripts like this:
<img class="image-center"
src="static-files/media/annotator/annotator-design.png" alt="Annotator Design">
## User Interface ##
The annotator's main user interface consists of a widget and three panels.
* The widget is used to switch the annotator on and off, and to display a list
of all the stored annotations.
* The **annotation-editor** panel enables the user to enter a new annotation.
* The **annotation-list** panel shows a list of all stored annotations.
* The **annotation** panel displays a single annotation.
Additionally, we use the `notifications` module to notify the user when the
add-on's storage quota is full.
## Working with the DOM ##
We'll use two page-mods to interact with the DOMs of pages that the user has
opened.
* The **selector** enables the user to choose an element to annotate.
It identifies page elements which are eligible for annotation, highlights them
on mouseover, and tells the main add-on code when the user clicks a highlighted
element.
* The **matcher** is responsible for finding annotated elements: it is
initialized with the list of annotations and searches web pages for the
elements they are associated with. It highlights any annotated elements that
are found. When the user moves the mouse over an annotated element
the matcher tells the main add-on code, which displays the annotation panel.
## Working with Data ##
We'll use the `simple-storage` module to store annotations.
Because we are recording potentially sensitive information, we want to prevent
the user creating annotations when in private browsing mode, so we'll use the
`private-browsing` module for that.
## Getting Started ##
Let's get started by creating a directory called "annotator". Navigate to it
and type `cfx init`.
Next, we will implement the
[widget](dev-guide/tutorials/annotator/widget.html).

Просмотреть файл

@ -0,0 +1,369 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Storing Annotations #
Now we are able to create annotations, let's store them using the
[`simple-storage`](modules/sdk/simple-storage.html) module. In
this chapter we will cover three topics relating to persistent storage:
* using `simple-storage` to persist objects
* handling exhaustion of the storage quota allocated to you
* respecting Private Browsing
## Storing New Annotations ##
In this section we are only touching the `main.js` file.
First, import the `simple-storage` module with a declaration like:
var simpleStorage = require('sdk/simple-storage');
In the module scope, initialize an array which will contain the stored annotations:
if (!simpleStorage.storage.annotations)
simpleStorage.storage.annotations = [];
Now we'll add a function to the module scope which deals with a new
annotation. The annotation is composed of the text the user entered and the
"annotation anchor", which consists of the URL, element ID and element content:
function handleNewAnnotation(annotationText, anchor) {
var newAnnotation = new Annotation(annotationText, anchor);
simpleStorage.storage.annotations.push(newAnnotation);
}
This function calls a constructor for an `Annotation` object, which we also
need to supply:
function Annotation(annotationText, anchor) {
this.annotationText = annotationText;
this.url = anchor[0];
this.ancestorId = anchor[1];
this.anchorText = anchor[2];
}
Now we need to link this code to the annotation editor, so that when the user
presses the return key in the editor, we create and store the new annotation:
var annotationEditor = panels.Panel({
width: 220,
height: 220,
contentURL: data.url('editor/annotation-editor.html'),
contentScriptFile: data.url('editor/annotation-editor.js'),
onMessage: function(annotationText) {
if (annotationText)
handleNewAnnotation(annotationText, this.annotationAnchor);
annotationEditor.hide();
},
onShow: function() {
this.postMessage('focus');
}
});
## Listing Stored Annotations ##
To prove that this works, let's implement the part of the add-on that displays
all the previously entered annotations. This is implemented as a panel that's
shown in response to the widget's `right-click` message.
The panel has three new files associated with it:
* a content-script which builds the panel content
* a simple HTML file used as a template for the panel's content
* a simple CSS file to provide some basic styling.
These three files can all go in a new subdirectory of `data` which we will call `list`.
### Annotation List Content Script ###
Here's the annotation list's content script:
self.on("message", function onMessage(storedAnnotations) {
var annotationList = $('#annotation-list');
annotationList.empty();
storedAnnotations.forEach(
function(storedAnnotation) {
var annotationHtml = $('#template .annotation-details').clone();
annotationHtml.find('.url').text(storedAnnotation.url)
.attr('href', storedAnnotation.url);
annotationHtml.find('.url').bind('click', function(event) {
event.stopPropagation();
event.preventDefault();
self.postMessage(storedAnnotation.url);
});
annotationHtml.find('.selection-text')
.text(storedAnnotation.anchorText);
annotationHtml.find('.annotation-text')
.text(storedAnnotation.annotationText);
annotationList.append(annotationHtml);
});
});
It builds the DOM for the panel from the array of annotations it is given.
The user will be able to click links in the panel, but we want to open them in
the main browser window rather than the panel. So the content script binds a
click handler to the links which will send the URL to the add-on.
Save this file in `data/list` as `annotation-list.js`.
### Annotation List HTML and CSS ###
Here's the HTML for the annotation list:
<pre class="brush: html">
&lt;html&gt;
&lt;head&gt;
&lt;meta http-equiv="Content-type" content="text/html; charset=utf-8" /&gt;
&lt;title&gt;Saved annotations&lt;/title&gt;
&lt;link rel="stylesheet" type="text/css" href="annotation-list.css" /&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;div id="annotation-list"&gt;
&lt;/div&gt;
&lt;div id="template"&gt;
&lt;div class="annotation-details"&gt;
&lt;a class="url"&gt;&lt;/a&gt;
&lt;div class="selection-text"&gt;&lt;/div&gt;
&lt;div class="annotation-text"&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
Here's the corresponding CSS:
<script type="syntaxhighlighter" class="brush: css"><![CDATA[
#annotation-list .annotation-details
{
padding: 10px;
margin: 10px;
border: solid 3px #EEE;
background-color: white;
}
#annotation-list .url, .selection-text, .annotation-text
{
padding: 5px;
margin: 5px;
}
#annotation-list .selection-text,#annotation-list .annotation-text
{
border: solid 1px #EEE;
}
#annotation-list .annotation-text
{
font-style: italic;
}
body
{
background-color: #F5F5F5;
font: 100% arial, helvetica, sans-serif;
}
h1
{
font-family: georgia,serif;
font-size: 1.5em;
text-align:center;
}
]]>
</script>
Save these in `data/list` as `annotation-list.html` and `annotation-list.css`
respectively.
### Updating main.js ###
Here's the code to create the panel, which can go in the `main` function.
var annotationList = panels.Panel({
width: 420,
height: 200,
contentURL: data.url('list/annotation-list.html'),
contentScriptFile: [data.url('jquery-1.4.2.min.js'),
data.url('list/annotation-list.js')],
contentScriptWhen: 'ready',
onShow: function() {
this.postMessage(simpleStorage.storage.annotations);
},
onMessage: function(message) {
require('sdk/tabs').open(message);
}
});
Since this panel's content script uses jQuery we will pass that in too: again,
make sure the name of it matches the version of jQuery you downloaded.
When the panel is shown we send it the array of stored annotations. When the
panel sends us a URL we use the `tabs` module to open it in a new tab.
Finally we need to connect this to the widget's `right-click` message:
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');
annotationList.show();
});
This time execute `cfx xpi` to build the XPI for the add-on, and install it in
Firefox. Activate the add-on, add an annotation, and then right-click the
widget. You should see something like this:
<img class="image-center"
src="static-files/media/annotator/annotation-list.png" alt="Annotation List">
<br>
<span class="aside">
Until now we've always run `cfx run` rather than building an XPI and installing
the add-on in Firefox. If the annotation does not reappear when you restart
Firefox, double check you installed the add-on and didn't just use `cfx run`
again.</span>
Restart Firefox, right-click the widget again, and check that the annotation
is still there.
## Responding To OverQuota events ##
Add-ons have a limited quota of storage space. If the add-on exits while
it is over quota, any data stored since the last time it was in quota will not
be persisted.
So we want to listen to the `OverQuota` event emitted by `simple-storage` and
respond to it. Add the following to your add-on's `main` function:
simpleStorage.on("OverQuota", function () {
notifications.notify({
title: 'Storage space exceeded',
text: 'Removing recent annotations'});
while (simpleStorage.quotaUsage > 1)
simpleStorage.storage.annotations.pop();
});
Because we use a notification to alert the user, we need to import the
`notifications` module:
var notifications = require("sdk/notifications");
(It should be obvious that this is an incredibly unhelpful way to deal with the
problem. A real add-on should give the user a chance to choose which data to
keep, and prevent the user from adding any more data until the add-on is back
under quota.)
## Respecting Private Browsing ##
Since annotations record the user's browsing history we should prevent the user
from creating annotations while the browser is in
[Private Browsing](http://support.mozilla.com/en-US/kb/Private%20Browsing) mode.
First let's import the `private-browsing` module into `main.js`:
var privateBrowsing = require('sdk/private-browsing');
We already have a variable `annotatorIsOn` that we use to indicate whether the
user can enter annotations. But we don't want to use that here, because we want
to remember the underlying state so that when they exit Private Browsing the
annotator is back in whichever state it was in before.
So we'll implement a function defining that to enter annotations, the annotator
must be active *and* Private Browsing must be off:
function canEnterAnnotations() {
return (annotatorIsOn && !privateBrowsing.isActive);
}
Next, everywhere we previously used `annotatorIsOn` directly, we'll call this
function instead:
function activateSelectors() {
selectors.forEach(
function (selector) {
selector.postMessage(canEnterAnnotations());
});
}
<br>
function toggleActivation() {
annotatorIsOn = !annotatorIsOn;
activateSelectors();
return canEnterAnnotations();
}
<br>
var selector = pageMod.PageMod({
include: ['*'],
contentScriptWhen: 'ready',
contentScriptFile: [data.url('jquery-1.4.2.min.js'),
data.url('selector.js')],
onAttach: function(worker) {
worker.postMessage(canEnterAnnotations());
selectors.push(worker);
worker.port.on('show', function(data) {
annotationEditor.annotationAnchor = data;
annotationEditor.show();
});
worker.on('detach', function () {
detachWorker(this, selectors);
});
}
});
We want to stop the user changing the underlying activation state when in
Private Browsing mode, so we'll edit `toggleActivation` again:
function toggleActivation() {
if (privateBrowsing.isActive) {
return false;
}
annotatorIsOn = !annotatorIsOn;
activateSelectors();
return canEnterAnnotations();
}
Finally, inside the `main` function, we'll add the following code to handle
changes in Private Browsing state by changing the icon and notifying the
selectors:
privateBrowsing.on('start', function() {
widget.contentURL = data.url('widget/pencil-off.png');
activateSelectors();
});
privateBrowsing.on('stop', function() {
if (canEnterAnnotations()) {
widget.contentURL = data.url('widget/pencil-on.png');
activateSelectors();
}
});
Try it: execute `cfx run`, and experiment with switching the annotator on and
off while in and out of Private Browsing mode.
Now we can create and store annotations, the last piece is to
[display them when the user loads the
page](dev-guide/tutorials/annotator/displaying.html).

Просмотреть файл

@ -0,0 +1,115 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Implementing the Widget #
We want the widget to do two things:
<span class="aside">
[Bug 634712](https://bugzilla.mozilla.org/show_bug.cgi?id=634712) asks that
the widget API should emit separate, or at least distinguishable, events for
left and right mouse clicks, and when it is fixed this widget won't need a
content script any more.</span>
* On a left-click, the widget should activate or deactivate the annotator.
* On a right-click, the widget should display a list of all the annotations
the user has created.
Because the widget's `click` event does not distinguish left and right mouse
clicks, we'll use a content script to capture the click events and send the
corresponding message back to our add-on.
The widget will have two icons: one to display when it's active, one to display
when it's inactive.
So there are three files we'll need to create: the widget's content script and
its two icons.
Inside the `data` subdirectory create another subdirectory `widget`. We'll
keep the widget's files here. (Note that this isn't mandatory: you could just
keep them all under `data`. But it seems tidier this way.)
## The Widget's Content Script ##
The widget's content script just listens for left- and right- mouse clicks and
posts the corresponding message to the add-on code:
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);
Save this in your `data/widget` directory as `widget.js`.
## The Widget's Icons ##
You can copy the widget's icons from here:
<img style="margin-left:40px; margin-right:20px;" src="static-files/media/annotator/pencil-on.png" alt="Active Annotator">
<img style="margin-left:20px; margin-right:20px;" src="static-files/media/annotator/pencil-off.png" alt="Inactive Annotator">
(Or make your own if you're feeling creative.) Save them in your `data/widget` directory.
## main.js ##
Now in the `lib` directory open `main.js` and add the following code:
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');
});
}
The annotator is inactive by default. It creates the widget and responds to
messages from the widget's content script by toggling its activation state.
<span class="aside">Note that due to
[bug 626326](https://bugzilla.mozilla.org/show_bug.cgi?id=626326) the add-on
bar's context menu is displayed, despite the `event.preventDefault()` call
in the widget's content script.</span>
Since we don't have any code to display annotations yet, we just log the
right-click events to the console.
Now from the `annotator` directory type `cfx run`. You should see the widget
in the add-on bar:
<div align="center">
<img src="static-files/media/annotator/widget-icon.png" alt="Widget Icon">
</div>
<br>
Left- and right-clicks should produce the appropriate debug output, and a
left-click should also change the widget icon to signal that it is active.
Next we'll add the code to
[create annotations](dev-guide/tutorials/annotator/creating.html).

Просмотреть файл

@ -0,0 +1,105 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<div class="warning">The API used to gain Chrome access is currently an
experimental feature of the SDK, and may change in future releases.</div>
# Chrome Authority #
## Using Chrome Authority ##
The most powerful low-level modules are run with "chrome privileges",
which gives them access to the infamous <code>Components</code> object, which
grants unfettered access to the host system. From this, the module can do
pretty much anything the browser is capable of. To obtain these privileges,
the module must declare its intent with a statement like the following:
var {Cc, Ci} = require("chrome");
The object returned by <code>require("chrome")</code>, when unpacked with the
"destructuring assignment" feature available in the Mozilla JS environment,
will provide the usual <code>Components.*</code> aliases:
<code>**Cc**</code>
An alias for `Components.classes`.
<code>**Ci**</code>
An alias for `Components.interfaces`.
<code>**Cu**</code>
An alias for `Components.utils`.
<code>**Cr**</code>
An alias for `Components.results`.
<code>**Cm**</code>
An alias for `Components.manager`.
<code>**components**</code>
An alias for `Components` itself (note the lower-case). From this you can
access less-frequently-used properties like `Components.stack` and
`Components.isSuccessCode`.
Note: the `require("chrome")` statement is the **only** way to access chrome
functionality and the `Components` API. The `Components` object should
**not** be accessed from modules. The SDK tools will emit a warning
if it sees module code which references `Components` directly.
Your modules should refrain from using chrome privileges unless they are
absolutely necessary. Chrome-authority-using modules must receive extra
security review, and most bugs in these modules are security-critical.
## Manifest Generation ##
The **manifest** is a list, included in the generated XPI, which
specifies which modules have requested `require()` access to which other
modules. It also records which modules have requested chrome access. This
list is generated by scanning all included modules for `require(XYZ)`
statements and recording the particular "XYZ" strings that they reference.
When the manifest implementation is complete the runtime loader will actually
prevent modules from `require()`ing modules that are not listed in the
manifest. Likewise, it will prevent modules from getting chrome authority
unless the manifest indicates that they have asked for it. This will ensure
that reviewers see the same authority restrictions that are enforced upon the
running code, increasing the effectiveness of the time spent reviewing the
add-on. (until this work is complete, modules may be able to sneak around these
restrictions).
The manifest is built with a simple regexp-based scanner, not a full
Javascript parser. Developers should stick to simple `require` statements,
with a single static string, one per line of code. If the scanner fails to
see a `require` entry, the manifest will not include that entry, and (once
the implementation is complete) the runtime code will get an exception.
For example, none of the following code will be matched by the manifest
scanner, leading to exceptions at runtime, when the `require()` call is
prohibited from importing the named modules:
// all of these will fail!
var xhr = require("x"+"hr");
var modname = "xpcom";
var xpcom = require(modname);
var one = require("one"); var two = require("two");
The intention is that developers use `require()` statements for two purposes:
to declare (to security reviewers) what sorts of powers the module wants to
use, and to control how those powers are mapped into the module's local
namespace. Their statements must therefore be clear and easy to parse. A
future manifest format may move the declaration portion out to a separate
file, to allow for more fine-grained expression of authority.
Commands that build a manifest, like "`cfx xpi`" or "`cfx run`", will scan
all included modules for use of `Cc`/`Ci` aliases (or the expanded
`Components.classes` forms). It will emit a warning if it sees the expanded
forms, or if it sees a use of e.g. "`Cc`" without a corresponding entry in
the `require("chrome")` statement. These warnings will serve to guide
developers to use the correct pattern. All module developers should heed the
warnings and correct their code until the warnings go away.

Просмотреть файл

@ -0,0 +1,151 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Display a Popup #
<span class="aside">
To follow this tutorial you'll need to have
[installed the SDK](dev-guide/tutorials/installation.html)
and learned the
[basics of `cfx`](dev-guide/tutorials/getting-started-with-cfx.html).
</span>
To display a popup dialog, use the
[`panel`](modules/sdk/panel.html) module. A panel's content is
defined using HTML. You can run content scripts in the panel: although the
script running in the panel can't directly access your main add-on code,
you can exchange messages between the panel script and the add-on code.
<img class="image-right" src="static-files/media/screenshots/text-entry-panel.png"
alt="Text entry panel">
In this tutorial we'll create an add-on that
[adds a widget to the toolbar](dev-guide/tutorials/adding-toolbar-button.html)
which displays a panel when clicked.
The panel just contains a
`<textarea>` element: when the user presses the `return` key, the contents
of the `<textarea>` is sent to the main add-on code.
The main add-on code
[logs the message to the console](dev-guide/tutorials/logging.html).
The add-on consists of three files:
* **`main.js`**: the main add-on code, that creates the widget and panel
* **`get-text.js`**: the content script that interacts with the panel content
* **`text-entry.html`**: the panel content itself, specified as HTML
<div style="clear:both"></div>
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({
width: 212,
height: 200,
contentURL: data.url("text-entry.html"),
contentScriptFile: data.url("get-text.js")
});
// Create a widget, and attach the panel to it, so the panel is
// shown when the user clicks the widget.
require("sdk/widget").Widget({
label: "Text entry",
id: "text-entry",
contentURL: "http://www.mozilla.org/favicon.ico",
panel: text_entry
});
// 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:
<pre class="brush: html">
&lt;html&gt;
&lt;head&gt;
&lt;style type="text/css" media="all"&gt;
textarea {
margin: 10px;
}
&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;textarea rows="10" cols="20" id="edit-box">&lt;/textarea&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
Try it out: "main.js" is saved in your add-on's `lib` directory,
and the other two files go in your add-on's `data` directory:
<pre>
my-addon/
data/
get-text.js
text-entry.html
lib/
main.js
</pre>
Run the add-on, click the widget, and you should see the panel.
Type some text and press "return" and you should see the output
in the console.
## Learning More ##
To learn more about the `panel` module, see the
[`panel` API reference](modules/sdk/panel.html).
To learn more about attaching panels to widgets, see the
[`widget` API reference](modules/sdk/widget.html).

Просмотреть файл

@ -0,0 +1,273 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Creating Event Targets #
<span class="aside">This tutorial describes the use of low-level APIs.
These APIs are still in active development, and we expect to make
incompatible changes to them in future releases.</span>
The [guide to event-driven programming with the SDK](dev-guide/guides/events.html)
describes how to consume events: that is, how to listen to events generated
by event targets. For example, you can listen to [`private-browsing`'s `start` event](modules/sdk/private-browsing.html#start) or the
[`Panel` object's `show` event](modules/sdk/panel.html#show).
With the SDK, it's also simple to implement your own event targets.
This is especially useful if you want to
[build your own modules](dev-guide/tutorials/reusable-modules.html),
either to organize your add-on better or to enable other developers to
reuse your code. If you use the SDK's event framework for your
event targets, users of your module can listen for events using the SDK's
standard event API.
In this tutorial we'll create part of a module to access the browser's
[Places API](https://developer.mozilla.org/en/Places). It will emit events
when the user adds and visits bookmarks, enabling users of the module
to listen for these events using the SDK's standard event API.
## Using the Places API ##
First, let's write some code using Places API that logs the URIs of
bookmarks the user adds.
Create a new directory called "bookmarks", navigate to it, and run `cfx init`.
Then open "lib/main.js" and add the following code:
var {Cc, Ci, Cu} = require("chrome");
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
var bookmarkService = Cc["@mozilla.org/browser/nav-bookmarks-service;1"]
.getService(Ci.nsINavBookmarksService);
var bookmarkObserver = {
onItemAdded: function(aItemId, aFolder, aIndex) {
console.log("added ", bookmarkService.getBookmarkURI(aItemId).spec);
},
onItemVisited: function(aItemId, aVisitID, time) {
console.log("visited ", bookmarkService.getBookmarkURI(aItemId).spec);
},
QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsINavBookmarkObserver])
};
exports.main = function() {
bookmarkService.addObserver(bookmarkObserver, false);
};
exports.onUnload = function() {
bookmarkService.removeObserver(bookmarkObserver);
}
Try running this add-on, adding and visiting bookmarks, and observing
the output in the console.
## Modules as Event Targets ##
We can adapt this code into a separate module that exposes the SDK's
standard event interface.
To do this we'll use the [`event/core`](modules/sdk/event/core.html)
module.
Create a new file in "lib" called "bookmarks.js", and add the following code:
var { emit, on, once, off } = require("sdk/event/core");
var {Cc, Ci, Cu} = require("chrome");
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
var bookmarkService = Cc["@mozilla.org/browser/nav-bookmarks-service;1"]
.getService(Ci.nsINavBookmarksService);
var bookmarkObserver = {
onItemAdded: function(aItemId, aFolder, aIndex) {
emit(exports, "added", bookmarkService.getBookmarkURI(aItemId).spec);
},
onItemVisited: function(aItemId, aVisitID, time) {
emit(exports, "visited", bookmarkService.getBookmarkURI(aItemId).spec);
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarkObserver])
};
bookmarkService.addObserver(bookmarkObserver, false);
exports.on = on.bind(null, exports);
exports.once = once.bind(null, exports);
exports.removeListener = function removeListener(type, listener) {
off(exports, type, listener);
};
This code implements a module which can emit `added` and `visited` events.
It duplicates the previous code, but with a few changes:
* import `emit()`, `on()`, `once()`, and `off()` from `event/core`
* replace listener functions with calls to `emit()`, passing the appropriate
event type
* export its own event API. This consists of three functions:
* `on()`: start listening for events or a given type
* `once()`: listen for the next occurrence of a given event, and then stop
* `removeListener()`: stop listening for events of a given type
The `on()` and `once()` exports delegate to the corresponding function from
`event/core`, and use `bind()` to pass the `exports` object itself as
the `target` argument to the underlying function. The `removeListener()`
function is implemented by calling the underlying `off()` function.
We can use this module in the same way we use any other module that emits
module-level events, such as
[`private-browsing`](modules/sdk/private-browsing.html). For example,
we can adapt "main.js" as follows:
var bookmarks = require("./bookmarks");
function logAdded(uri) {
console.log("added: " + uri);
}
function logVisited(uri) {
console.log("visited: " + uri);
}
exports.main = function() {
bookmarks.on("added", logAdded);
bookmarks.on("visited", logVisited);
};
exports.onUnload = function() {
bookmarks.removeListener("added", logAdded);
bookmarks.removeListener("visited", logVisited);
}
## Classes as Event Targets ##
Sometimes we want to emit events at the level of individual objects,
rather than at the level of the module.
To do this, we can inherit from the SDK's
[`EventTarget`](modules/sdk/event/target.html) class. `EventTarget`
provides an implementation of the functions needed to add and remove
event listeners: `on()`, `once()`, and `removeListener()`.
In this example, we could define a class `BookmarkManager` that inherits
from `EventTarget` and emits `added` and `visited` events.
Open "bookmarks.js" and replace its contents with this code:
var { emit } = require("sdk/event/core");
var { EventTarget } = require("sdk/event/target");
var { Class } = require("sdk/core/heritage");
var { merge } = require("sdk/util/object");
var {Cc, Ci, Cu} = require("chrome");
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
var bookmarkService = Cc["@mozilla.org/browser/nav-bookmarks-service;1"]
.getService(Ci.nsINavBookmarksService);
function createObserver(target) {
var bookmarkObserver = {
onItemAdded: function(aItemId, aFolder, aIndex) {
emit(target, "added", bookmarkService.getBookmarkURI(aItemId).spec);
},
onItemVisited: function(aItemId, aVisitID, time) {
emit(target, "visited", bookmarkService.getBookmarkURI(aItemId).spec);
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsINavBookmarkObserver])
};
bookmarkService.addObserver(bookmarkObserver, false);
}
var BookmarkManager = Class({
extends: EventTarget,
initialize: function initialize(options) {
EventTarget.prototype.initialize.call(this, options);
merge(this, options);
createObserver(this);
}
});
exports.BookmarkManager = BookmarkManager;
The code to interact with the Places API is the same here. However:
* we're now importing from four modules:
* [`event/core`](modules/sdk/event/core.html) gives us
`emit()`: note that we don't need `on`, `once`, or `off`,
since we will use `EventTarget` for adding and removing listeners
* [`event/target`](modules/sdk/event/target.html) gives us
`EventTarget`, which implements the interface for adding and removing
listeners
* [`heritage`](modules/sdk/core/heritage.html) gives us
`Class()`, which we can use to inherit from `EventTarget`
* `utils/object` gives us `merge()`, which just simplifies setting up the
`BookmarkManager`'s properties
* we use `Class` to inherit from `EventTarget`. In its `initialize()` function,
we:
* call the base class initializer
* use `merge()` to copy any supplied options into the newly created object
* call `createObserver()`, passing in the newly created object as the
event target
* `createObserver()` is the same as in the previous example, except that in
`emit()` we pass the newly created `BookmarkManager` as the event target
To use this event target we can create it and call the `on()`, `once()`, and
`removeListener()` functions that it has inherited:
var bookmarks = require("./bookmarks");
var bookmarkManager = bookmarks.BookmarkManager({});
function logAdded(uri) {
console.log("added: " + uri);
}
function logVisited(uri) {
console.log("visited: " + uri);
}
exports.main = function() {
bookmarkManager.on("added", logAdded);
bookmarkManager.on("visited", logVisited);
};
exports.onUnload = function() {
bookmarkManager.removeListener("added", logAdded);
bookmarkManager.removeListener("visited", logVisited);
}
### Implementing "onEvent" Options ###
Finally, most event targets accept options of the form "onEvent", where
"Event" is the capitalized form of the event type. For example, you
can listen to the
[`Panel` object's `show` event](modules/sdk/panel.html#show)
either by calling:
myPanel.on("show", listenerFunction);
or by passing the `onShow` option to `Panel`'s constructor:
var myPanel = require("sdk/panel").Panel({
onShow: listenerFunction,
contentURL: "https://en.wikipedia.org/w/index.php"
});
If your class inherits from `EventTarget`, options like this are automatically
handled for you. For example, given the implementation of `BookmarkManager`
above, your "main.js" could be rewritten like this:
var bookmarks = require("./bookmarks");
function logAdded(uri) {
console.log("added: " + uri);
}
function logVisited(uri) {
console.log("visited: " + uri);
}
var bookmarkManager = bookmarks.BookmarkManager({
onAdded: logAdded,
onVisited: logVisited
});
exports.onUnload = function() {
bookmarkManager.removeListener("added", logAdded);
bookmarkManager.removeListener("visited", logVisited);
}

Просмотреть файл

@ -0,0 +1,170 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<span class="aside">This tutorial assumes that you've read and followed the instructions in
the [installation guide](dev-guide/tutorials/installation.html), to
install and activate the SDK.</span>
# Getting Started With cfx #
To create add-ons using the SDK you'll have to get to know the `cfx`
command-line tool. It's what you'll use for testing and packaging add-ons.
There's comprehensive
[reference documentation](dev-guide/cfx-tool.html) covering
everything you can do using `cfx`, but in this tutorial we'll introduce the
three commands you need to get going:
* [`cfx init`](dev-guide/tutorials/getting-started-with-cfx.html#cfx-init)
: creates the skeleton structure for your add-on
* [`cfx run`](dev-guide/tutorials/getting-started-with-cfx.html#cfx-run)
: runs an instance of Firefox with your add-on installed
* [`cfx xpi`](dev-guide/tutorials/getting-started-with-cfx.html#cfx-xpi)
: build an installable [XPI](https://developer.mozilla.org/en/XPI) file to
distribute your add-on
## <a name="cfx-init">cfx init</a> ##
You use `cfx init` to create the basic skeleton for your add-on.
Create a new directory, navigate to it in your command shell, and run
`cfx init`:
<pre>
mkdir my-addon
cd my-addon
cfx init
</pre>
You don't have to create this directory under the SDK root: once you have
activated from the SDK root, `cfx` will remember where the SDK is, and you
will be able to use it from any directory.
You'll see some output like this:
<pre>
* lib directory created
* data directory created
* test directory created
* doc directory created
* README.md written
* package.json written
* test/test-main.js written
* lib/main.js written
* doc/main.md written
Your sample add-on is now ready for testing:
try "cfx test" and then "cfx run". Have fun!"
</pre>
## <a name="cfx-run">cfx run</a> ##
Use `cfx run` to run a new instance of Firefox with your add-on installed.
This is the command you'll use to test out your add-on while developing it.
The main code for an add-on is always kept in a file called `main.js` in your
add-on's `lib` directory. Open the `main.js` for this add-on, and
add the following code:
var widgets = require("sdk/widget");
var tabs = require("sdk/tabs");
var widget = widgets.Widget({
id: "mozilla-link",
label: "Mozilla website",
contentURL: "http://www.mozilla.org/favicon.ico",
onClick: function() {
tabs.open("http://www.mozilla.org/");
}
});
Now type:
<pre>
cfx run
</pre>
The first time you do this, you'll see a message like this:
<pre>
No 'id' in package.json: creating a new ID for you.
package.json modified: please re-run 'cfx run'
</pre>
<img class="image-right" src="static-files/media/screenshots/widget-mozilla.png"
alt="Mozilla icon widget" />
Run `cfx run` again, and it will run an instance of Firefox. In the
bottom-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/](http://www.mozilla.org/) loaded into it.
This add-on uses two SDK modules: the
[`widget`](modules/sdk/widget.html) module, which enables you
to add buttons to the browser, and the
[`tabs`](modules/sdk/tabs.html) module, which enables you to
perform basic operations with tabs. In this case, we've created a widget
whose icon is the Mozilla favicon, 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 icon displayed
and the URL that gets loaded:
var widgets = require("sdk/widget");
var tabs = require("sdk/tabs");
var widget = widgets.Widget({
id: "jquery-link",
label: "jQuery website",
contentURL: "http://www.jquery.com/favicon.ico",
onClick: function() {
tabs.open("http://www.jquery.com/");
}
});
<img class="image-right" src="static-files/media/screenshots/widget-jquery.png"
alt="jQuery icon widget" />
At the command prompt, execute `cfx run` again, and this time the icon is the
jQuery favicon, and clicking it takes you to
[http://www.jquery.com](http://www.jquery.com).
<div style="clear:both"></div>
## <a name="cfx-xpi">cfx xpi</a> ##
You'll use `cfx run` while developing your add-on, but once you're done with
that, you use `cfx xpi` to build an [XPI](https://developer.mozilla.org/en/XPI)
file. This is the installable file format for Firefox add-ons. You can
distribute XPI files yourself or publish them to
[http://addons.mozilla.org](http://addons.mozilla.org) so other users can
download and install it.
To build an XPI, just execute the command `cfx xpi` from the add-on's
directory:
<pre>
cfx xpi
</pre>
You should see a message like:
<pre>
Exporting extension to my-addon.xpi.
</pre>
The `my-addon.xpi` file can be found in the directory in which you ran
the command.
To test it, install it 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
`my-addon.xpi` file, open it and follow the prompts to install the
add-on.
Now you have the basic `cfx` commands, you can try out the
[SDK's features](dev-guide/tutorials/index.html).

Просмотреть файл

@ -0,0 +1,244 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Tutorials #
This page lists practical explanations of how to develop add-ons with
the SDK. The tutorials don't yet cover all the high-level APIs: see the sidebar
on the left for the full list of APIs.
<hr>
<h2><a name="getting-started">Getting Started</a></h2>
<table class="catalog">
<colgroup>
<col width="50%">
<col width="50%">
</colgroup>
<tr>
<td>
<h4><a href="dev-guide/tutorials/installation.html">Installation</a></h4>
Download, install, and initialize the SDK on Windows, OS X and Linux.
</td>
<td>
<h4><a href="dev-guide/tutorials/getting-started-with-cfx.html">Getting started with cfx</a></h4>
The basic <code>cfx</code> commands you need to start creating add-ons.
</td>
</tr>
<tr>
<td>
<h4><a href="dev-guide/tutorials/troubleshooting.html">Troubleshooting</a></h4>
Some pointers for fixing common problems and getting more help.
</td>
<td>
</td>
</tr>
</table>
<hr>
<h2><a name="create-user-interfaces">Create User Interfaces</a></h2>
<table class="catalog">
<colgroup>
<col width="50%">
<col width="50%">
</colgroup>
<tr>
<td>
<h4><a href="dev-guide/tutorials/adding-toolbar-button.html">Add a toolbar button</a></h4>
Attach a button to the Firefox Add-on toolbar.
</td>
<td>
<h4><a href="dev-guide/tutorials/display-a-popup.html">Display a popup</a></h4>
Display a popup dialog implemented with HTML and JavaScript.
</td>
</tr>
<tr>
<td>
<h4><a href="dev-guide/tutorials/adding-menus.html">Add a menu item to Firefox</a></h4>
Add items to Firefox's main menus.
</td>
<td>
<h4><a href="dev-guide/tutorials/add-a-context-menu-item.html">Add a context menu item</a></h4>
Add items to Firefox's context menu.
</td>
</tr>
</table>
<hr>
<h2><a name="interact-with-the-browser">Interact with the Browser</a></h2>
<table class="catalog">
<colgroup>
<col width="50%">
<col width="50%">
</colgroup>
<tr>
<td>
<h4><a href="dev-guide/tutorials/open-a-web-page.html">Open a web page</a></h4>
Open a web page in a new browser tab or window using the
<code><a href="modules/sdk/tabs.html">tabs</a></code> module, and access its content.
</td>
<td>
<h4><a href="dev-guide/tutorials/list-open-tabs.html">Get the list of open tabs</a></h4>
Use the <code><a href="modules/sdk/tabs.html">tabs</a></code>
module to iterate through the currently open tabs, and access their content.
</td>
</tr>
<tr>
<td>
<h4><a href="dev-guide/tutorials/listen-for-page-load.html">Listen for page load</a></h4>
Use the <code><a href="modules/sdk/tabs.html">tabs</a></code>
module to get notified when new web pages are loaded, and access their content.
</td>
<td>
</td>
</tr>
</table>
<hr>
<h2><a name="modify-web-pages">Modify Web Pages</a></h2>
<table class="catalog">
<colgroup>
<col width="50%">
<col width="50%">
</colgroup>
<tr>
<td>
<h4><a href="dev-guide/tutorials/modifying-web-pages-url.html">Modify web pages based on URL</a></h4>
Create filters for web pages based on their URL: whenever a web page
whose URL matches the filter is loaded, execute a specified script in it.
</td>
<td>
<h4><a href="dev-guide/tutorials/modifying-web-pages-tab.html">Modify the active web page</a></h4>
Dynamically load a script into the currently active web page.
</td>
</tr>
</table>
<hr>
<h2><a name="development-techniques">Development Techniques</a></h2>
<table class="catalog">
<colgroup>
<col width="50%">
<col width="50%">
</colgroup>
<tr>
<td>
<h4><a href="dev-guide/tutorials/logging.html">Logging</a></h4>
Log messages to the console for diagnostic purposes.
</td>
<td>
<h4><a href="dev-guide/tutorials/load-and-unload.html">Listen for load and unload</a></h4>
Get notifications when your add-on is loaded or unloaded by Firefox,
and pass arguments into your add-on from the command line.
</td>
</tr>
<tr>
<td>
<h4><a href="dev-guide/tutorials/reusable-modules.html">Creating third-party modules</a></h4>
Structure your add-on in separate modules to make it easier to develop, debug, and maintain.
Create reusable packages containing your modules, so other add-on developers can use them too.
</td>
<td>
<h4><a href="dev-guide/tutorials/adding-menus.html">Using third-party modules</a></h4>
Install and use additional modules which don't ship with the SDK itself.
</td>
</tr>
<tr>
<td>
<h4><a href="dev-guide/tutorials/unit-testing.html">Unit testing</a></h4>
Writing and running unit tests using the SDK's test framework.
</td>
<td>
<h4><a href="dev-guide/tutorials/l10n.html">Localization</a></h4>
Writing localizable code.
</td>
</tr>
<tr>
<td>
<h4><a href="dev-guide/tutorials/chrome.html">Chrome authority</a></h4>
Get access to the <a href="https://developer.mozilla.org/en/Components_object">Components</a>
object, enabling your add-on to load and use any XPCOM object.
</td>
<td>
<h4><a href="dev-guide/tutorials/mobile.html">Mobile development</a></h4>
Get set up to develop add-ons for Firefox Mobile on Android.
</td>
</tr>
<tr>
<td>
<h4><a href="dev-guide/tutorials/event-targets.html">Writing Event Targets</a></h4>
Enable the objects you define to emit their own events.
</td>
<td>
</td>
</tr>
</table>
<hr>
<h2><a name="putting-it-together">Putting It Together</a></h2>
<table class="catalog">
<colgroup>
<col width="50%">
<col width="50%">
</colgroup>
<tr>
<td>
<h4><a href="dev-guide/tutorials/annotator/index.html">Annotator add-on</a></h4>
A walkthrough of a relatively complex add-on.
</td>
<td>
</td>
</tr>
</table>

Просмотреть файл

@ -0,0 +1,160 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Installation #
## Prerequisites
To develop with the Add-on SDK, you'll need:
* [Python](http://www.python.org/) 2.5 or 2.6. Note that versions 3.0 and 3.1
of Python are not supported. Make sure that Python is in your path.
* A [compatible version of Firefox](dev-guide/guides/firefox-compatibility.html).
That's either: the version of Firefox shipping at the time the SDK shipped,
or the Beta version of Firefox at the time the SDK shipped. See the
[SDK Release Schedule](https://wiki.mozilla.org/Jetpack/SDK_2012_Release_Schedule)
to map SDK releases to Firefox releases.
* The SDK itself: you can obtain the latest stable version of the SDK as a
[tarball](https://ftp.mozilla.org/pub/mozilla.org/labs/jetpack/jetpack-sdk-latest.tar.gz)
or a [zip file](https://ftp.mozilla.org/pub/mozilla.org/labs/jetpack/jetpack-sdk-latest.zip).
Alternatively, you can get the latest development version from its
[GitHub repository](https://github.com/mozilla/addon-sdk).
## Installation on Mac OS X / Linux ##
Extract the file contents wherever you choose, and navigate to the root
directory of the SDK with a shell/command prompt. For example:
<pre>
tar -xf addon-sdk.tar.gz
cd addon-sdk
</pre>
Then run if you're a Bash user (most people are):
<pre>
source bin/activate
</pre>
And if you're a non-Bash user, you should run:
<pre>
bash bin/activate
</pre>
Your command prompt should now have a new prefix containing the name of the
SDK's root directory:
<pre>
(addon-sdk)~/mozilla/addon-sdk >
</pre>
## Installation on Windows ##
Extract the file contents wherever you choose, and navigate to the root
directory of the SDK with a shell/command prompt. For example:
<pre>
7z.exe x addon-sdk.zip
cd addon-sdk
</pre>
Then run:
<pre>
bin\activate
</pre>
Your command prompt should now have a new prefix containing the full path to
the SDK's root directory:
<pre>
(C:\Users\mozilla\sdk\addon-sdk) C:\Users\Work\sdk\addon-sdk>
</pre>
## SDK Virtual Environment ##
The new prefix to your command prompt indicates that your shell has entered
a virtual environment that gives you access to the Add-on SDK's command-line
tools.
At any time, you can leave a virtual environment by running `deactivate`.
The virtual environment is specific to this particular command prompt. If you
close this command prompt, it is deactivated and you need to type
`source bin/activate` or `bin\activate` in a new command prompt to reactivate
it. If you open a new command prompt, the SDK will not be active in the new
prompt.
You can have multiple copies of the SDK in different locations on disk and
switch between them, or even have them both activated in different command
prompts at the same time.
### Making `activate` Permanent ###
All `activate` does is to set a number of environment variables for the
current command prompt, using a script located in the top-level `bin`
directory. By setting these variables permanently in your environment so
every new command prompt reads them, you can make activation permanent. Then
you don't need to type `activate` every time you open up a new command prompt.
Because the exact set of variables may change with new releases of the SDK,
it's best to refer to the activation scripts to determine which variables need
to be set. Activation uses different scripts and sets different variables for
bash environments (Linux and Mac OS X) and for Windows environments.
#### Windows ####
On Windows, `bin\activate` uses `activate.bat`, and you can make activation
permanent using the command line `setx` tool or the Control Panel.
#### Linux/Mac OS X ####
On Linux and Mac OS X, `source bin/activate` uses the `activate` bash
script, and you can make activation permanent using your `~/.bashrc`
(on Linux) or `~/.bashprofile` (on Mac OS X).
As an alternative to this, you can create a symbolic link to the `cfx`
program in your `~/bin` directory:
<pre>
ln -s PATH_TO_SDK/bin/cfx ~/bin/cfx
</pre>
## Sanity Check ##
Run this at your shell prompt:
<pre>
cfx
</pre>
It should produce output whose first line looks something like this, followed by
many lines of usage information:
<pre>
Usage: cfx [options] [command]
</pre>
This is the `cfx` command-line program. It's your primary interface to the
Add-on SDK. You use it to launch Firefox and test your add-on, package your
add-on for distribution, view documentation, and run unit tests.
## cfx docs ##
If you're reading these documents online, try running `cfx docs`. This will
build the documentation for the SDK and display it in a browser.
## Problems? ##
Try the [Troubleshooting](dev-guide/tutorials/troubleshooting.html)
page.
## Next Steps ##
Next, take a look at the
[Getting Started With cfx](dev-guide/tutorials/getting-started-with-cfx.html)
tutorial, which explains how to create add-ons using the `cfx` tool.

Просмотреть файл

@ -0,0 +1,441 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Localization #
The SDK supports localization of strings appearing in:
* [your main add-on's JavaScript code](dev-guide/tutorials/l10n.html#Using Localized Strings in JavaScript)
* [HTML files packaged with your add-on](dev-guide/tutorials/l10n.html#Using Localized Strings in HTML)
* [the `title` and `description` fields of your add-on's preferences](dev-guide/tutorials/l10n.html#Using Localized Strings in Preferences).
It doesn't, yet, support localization of CSS or content scripts.
## Localized Strings ##
Translated strings are kept in a directory called "locale" under your
main add-on directory, one file for each locale. The files:
* use the [`.properties` format](http://en.wikipedia.org/wiki/.properties)
* are named "xx-YY.properties", where "xx-YY" is the [name of the locale](https://wiki.mozilla.org/L10n:Locale_Codes) in question
* contain one entry for each string you want to localize, consisting
of an identifier for the string and its translation in that locale,
in the format `identifier=translation`.
Suppose your add-on contains a single localizable string,
represented in English as "Hello!", and you want to supply US English
and French French localizations.
You'd add two files to the "locale" directory:
<pre>
my-addon/
data
lib
locale/
en-US.properties
fr-FR.properties
</pre>
"en.US.properties" contains this:
<pre>
hello_id= Hello!
</pre>
"fr.FR.properties" contains this:
<pre>
hello_id= Bonjour !
</pre>
Now whenever your JavaScript or HTML asks the localization system for
the translation of the `hello_id` identifier, it will get the correct
translation for the current locale.
## Using Localized Strings in HTML ##
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:
<pre class="brush: html">
&lt;html&gt;
&lt;body&gt;
&lt;h1 data-l10n-id="hello_id"&gt;&lt;/h1>&gt;
&lt;/body&gt;
&lt;/html&gt;
</pre>
Then you can use this HTML file to build your interface, for example
inside a panel:
var hello = require("sdk/panel").Panel({
height: 75,
width: 150,
contentURL: require("sdk/self").data.url("my-panel.html")
});
var widget = require("sdk/widget").Widget({
id: "mozilla-link",
label: "Mozilla website",
contentURL: "http://www.mozilla.org/favicon.ico",
panel: hello
});
<img class="image-right" src="static-files/media/screenshots/l10n-html-enUS.png"
alt="Example of panel containing US English text">
<img class="image-right" src="static-files/media/screenshots/l10n-html-frFR.png"
alt="Example of panel containing French French text">
Given locale files for "en-US" and "fr-FR" which provide translations
of `hello_id`, the panel will now display "Hello!" or "Bonjour !", according
to the current locale.
The translation is inserted into the node which has the `data-l10n-id`
attribute set. Any previously existing content is just replaced.
The string is inserted as text, so you can't insert HTML using a statement
like:
<pre>
hello_id= &lt;blink&gt;Hello!&lt;/blink&gt;
</pre>
## Using Localized Strings in JavaScript
To reference localized strings from your main add-on code, you do this:
var _ = require("sdk/l10n").get;
console.log(_("hello_id!"));
<span class="aside">Assigning to "`_`" in particular is not required, but
is a convention from the
[gettext](https://www.gnu.org/software/gettext/gettext.html) tools
and will make it possible to work with existing tools that expect "`_`"
to indicate localizable strings.</span>
1. Import the `l10n` module, and assign its `get` function to
"`_`" (underscore).
2. Wrap all references to localizable strings with the `_()`
function.
If you run it you'll see the expected output for the current locale:
<pre>
info: Hello!
</pre>
<pre>
info: Bonjour !
</pre>
Note that because you can't `require()` modules in content scripts,
you can't yet reference localized strings from content scripts.
### Plurals ###
The `l10n` module supports plural forms. Different languages have
different rules for the formation of plurals. For example,
English has two forms: a singular form for "one", and a plural form
for "everything else, including zero":
<pre>
one tomato
no tomatoes
two tomatoes
</pre>
But Russian has different forms for numbers ending in 1 (except 11),
numbers ending in 2-4 (except 12-14) and other numbers:
<pre>
один помидор // one tomato
два помидора // two tomatoes
пять помидоров // five tomatoes
</pre>
The SDK uses the [Unicode CLDR](http://cldr.unicode.org/index) data
to describe the different plural forms used by different languages.
#### Unicode CLDR Plural Forms ####
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*.
English has two forms, which can be described by mapping "1" to "one"
and "everything else" to "other":
<pre>
one → n is 1;
other → everything else
</pre>
Russian uses four forms, that can be described as follows:
<pre>
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
</pre>
Plural rules for all languages can be found in the CLDR
[Language Plural Rules](http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html)
page (although this table is out of date compared to the
[CLDR XML source](http://unicode.org/repos/cldr/trunk/common/supplemental/plurals.xml)).
#### Plural Forms in the SDK ####
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));
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):
<pre>
# en-US translations
tomato_id[one]= %d tomato
tomato_id= %d tomatoes
</pre>
In Russian we could have four plural localizations:
<pre>
# ru-RU translations
tomato_id[one]= %d помидор
tomato_id[few]= %d помидора
tomato_id[many]= %d помидоров
tomato_id= %d помидоры
</pre>
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:
<pre>
# en-US translations
hello_id= Hello %s!
</pre>
<pre>
# fr-FR translations
hello_id= Bonjour %s !
</pre>
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:
<pre>
info: Hello Bob!
info: Hello Alice!
</pre>
In "fr-FR" we get:
<pre>
info: Bonjour Bob !
info: Bonjour Alice !
</pre>
### 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:
<pre>
"&lt;town_name> is &lt;person_name>'s home town."
</pre>
<pre>
"&lt;person_name>'s home town is &lt;town_name>"
</pre>
To choose the first option, the `.properties` file can order the
placeholders as follows:
<pre>
home_town_id= %2s is %1s's home town.
</pre>
This gives us the following output:
<pre>
info: London is Bob's home town.
</pre>
## Using Localized Strings in Preferences ##
By including a
[`"preferences"` structure in your add-on's "package.json" file](modules/sdk/simple-prefs.html ), you can define
preferences for your add-on that the user can see and edit
using Firefox's
[Add-ons Manager](https://support.mozilla.org/en-US/kb/Using%20extensions%20with%20Firefox#w_how-to-change-extension-settings).
Preferences have mandatory `title` and optional `description` fields.
These are strings which appear alongside the preference in the Add-ons
Manager, to help explain to the user what the preference means.
* To provide the localized form of the preference title, include an
entry in your "properties" file whose identifier is the preference
name followed by `_title`, and whose value is the localized title.
* To provide the localized form of the preference description, include
an entry in your "properties" file whose identifier is the preference
name followed by `_description`, and whose value is the localized description.
For example, suppose your "package.json" defines a single preference:
<pre>
{
"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"
}
</pre>
In your "en-US.properties" file, include these two items:
<pre>
monster_name_title= Name
monster_name_description= What is the monster's name?
</pre>
In your "fr-FR.properties" file, include the French translation:
<pre>
monster_name_title= Nom
monster_name_description= Quel est le nom du monstre ?
</pre>
Now when the browser's locale is set to "en-US", users see this
in the Add-ons Manager:
<img class="image-center" src="static-files/media/screenshots/preference-us.png" alt="screenshot of US preference localization">
When the browser's locale is set to "fr-FR", they see this:
<img class="image-center" src="static-files/media/screenshots/preference-french.png" alt="screenshot of French preference localization">
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:
<pre>
Hello!= Bonjour !
</pre>
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](https://github.com/downloads/ochameau/locale-updater/locale-updater.xpi)
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:
<img class="align-center" src="static-files/media/screenshots/locale-updater.png"
alt="Add-on manager with locale updater 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.
## <a name="limitations">Limitations</a> ##
The current localization support is a first step towards full support,
and contains a number of limitations.
* There's no support for content scripts or CSS files: at
the moment, you can only localize strings appearing in JavaScript files
that can `require()` SDK modules and in HTML.
* The set of locale files is global across an add-on. This means that
a module isn't able to override a more general translation: so a module
`informal.js` can't specify that "hello_id" occurring in its code
should be localized to "Hi!".
* The SDK tools compile the locale files into a JSON format when
producing an XPI. This means that translators can't localize an add-on
given the XPI alone, but must be given access to the add-on source.
* The add-on developer must manually assemble the set of localizable
strings that make up the locale files. In a future release we'll add
a command to `cfx` that scans the add-on for localizable strings and
builds a template `.properties` file listing all the strings that need
to be translated.

Просмотреть файл

@ -0,0 +1,73 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# List Open Tabs #
<span class="aside">
To follow this tutorial you'll need to have
[installed the SDK](dev-guide/tutorials/installation.html)
and learned the
[basics of `cfx`](dev-guide/tutorials/getting-started-with-cfx.html).
</span>
To list the open tabs, you can iterate over the
[`tabs`](modules/sdk/tabs.html) object itself.
The following add-on adds a
[`widget`](modules/sdk/widget.html) that logs
the URLs of open tabs when the user clicks it:
var widget = require("sdk/widget").Widget({
id: "mozilla-link",
label: "Mozilla website",
contentURL: "http://www.mozilla.org/favicon.ico",
onClick: listTabs
});
function listTabs() {
var tabs = require("sdk/tabs");
for each (var tab in tabs)
console.log(tab.url);
}
If you run the add-on, load a couple of tabs, and click the
widget, you'll see output in the
[console](dev-guide/console.html) that looks like this:
<pre>
info: http://www.mozilla.org/en-US/about/
info: http://www.bbc.co.uk/
</pre>
You don't get direct access to any content hosted in the tab.
To access tab content you need to attach a script to the tab
using `tab.attach()`. This add-on attaches a script to all open
tabs. The script adds a red border to the tab's document:
var widget = require("sdk/widget").Widget({
id: "mozilla-link",
label: "Mozilla website",
contentURL: "http://www.mozilla.org/favicon.ico",
onClick: listTabs
});
function listTabs() {
var tabs = require("sdk/tabs");
for each (var tab in tabs)
runScript(tab);
}
function runScript(tab) {
tab.attach({
contentScript: "document.body.style.border = '5px solid red';"
});
}
## Learning More ##
To learn more about working with tabs in the SDK, see the
[`tabs` API reference](modules/sdk/tabs.html).
To learn more about running scripts in tabs, see the
[tutorial on using `tab.attach()`](dev-guide/tutorials/modifying-web-pages-tab.html).

Просмотреть файл

@ -0,0 +1,55 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Listen For Page Load #
<span class="aside">
To follow this tutorial you'll need to have
[installed the SDK](dev-guide/tutorials/installation.html)
and learned the
[basics of `cfx`](dev-guide/tutorials/getting-started-with-cfx.html).
</span>
You can get notifications about new pages loading using the
[`tabs`](modules/sdk/tabs.html) module. The following add-on
listens to the tab's built-in `ready` event and just logs the URL of each
tab as the user loads it:
require("sdk/tabs").on("ready", logURL);
function logURL(tab) {
console.log(tab.url);
}
You don't get direct access to any content hosted in the tab.
To access tab content you need to attach a script to the tab
using `tab.attach()`. This add-on attaches a script to all open
tabs. The script adds a red border to the tab's document:
require("sdk/tabs").on("ready", logURL);
function logURL(tab) {
runScript(tab);
}
function runScript(tab) {
tab.attach({
contentScript: "if (document.body) document.body.style.border = '5px solid red';"
});
}
(This example is only to show the idea: to implement something like this,
you should instead use
[`page-mod`](dev-guide/tutorials/modifying-web-pages-url.html),
and specify "*" as the match-pattern.)
## Learning More ##
To learn more about working with tabs in the SDK, see the
[`tabs` API reference](modules/sdk/tabs.html). You can listen
for a number of other tab events, including `open`, `close`, and `activate`.
To learn more about running scripts in tabs, see the
[tutorial on using `tab.attach()`](dev-guide/tutorials/modifying-web-pages-tab.html).

Просмотреть файл

@ -0,0 +1,103 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Listening for Load and Unload #
<span class="aside">
To follow this tutorial you'll need to have
[installed the SDK](dev-guide/tutorials/installation.html)
and learned the
[basics of `cfx`](dev-guide/tutorials/getting-started-with-cfx.html).
</span>
## exports.main() ##
Your add-on's `main.js` code is executed as soon as it is loaded. It is loaded
when it is installed, when it is enabled, or when Firefox starts.
If your add-on exports a function called `main()`, that function will be
called immediately after the overall `main.js` is evaluated, and after all
top-level `require()` statements have run (so generally after all dependent
modules have been loaded).
exports.main = function (options, callbacks) {};
`options` is an object describing the parameters with which your add-on was
loaded.
### options.loadReason ###
`options.loadReason` is one of the following strings
describing the reason your add-on was loaded:
<pre>
install
enable
startup
upgrade
downgrade
</pre>
### options.staticArgs ###
You can use the [`cfx`](dev-guide/cfx-tool.html)
`--static-args` option to pass arbitrary data to your
program.
The value of `--static-args` must be a JSON string. The object encoded by the
JSON becomes the `staticArgs` member of the `options` object passed as the
first argument to your program's `main` function. The default value of
`--static-args` is `"{}"` (an empty object), so you don't have to worry about
checking whether `staticArgs` exists in `options`.
For example, if your `main.js` looks like this:
exports.main = function (options, callbacks) {
console.log(options.staticArgs.foo);
};
And you run cfx like this:
<pre>
cfx run --static-args="{ \"foo\": \"Hello from the command line\" }"
</pre>
Then your console should contain this:
<pre>
info: Hello from the command line
</pre>
The `--static-args` option is recognized by `cfx run` and `cfx xpi`.
When used with `cfx xpi`, the JSON is packaged with the XPI's harness options
and will therefore be used whenever the program in the XPI is run.`
## exports.onUnload() ##
If your add-on exports a function called `onUnload()`, that function
will be called when the add-on is unloaded.
exports.onUnload = function (reason) {};
`reason` is one of the following strings describing the reason your add-on was
unloaded:
<span class="aside">But note that due to
[bug 627432](https://bugzilla.mozilla.org/show_bug.cgi?id=627432),
your `onUnload` listener will never be called with `uninstall`: it
will only be called with `disable`. See in particular
[comment 12 on that bug](https://bugzilla.mozilla.org/show_bug.cgi?id=627432#c12).</span>
<pre>
uninstall
disable
shutdown
upgrade
downgrade
</pre>
You don't have to use `exports.main()` or `exports.onUnload()`. You can just place
your add-on's code at the top level instead of wrapping it in a function
assigned to `exports.main()`. It will be loaded in the same circumstances, but
you won't get access to the `options` or `callbacks` arguments.

Просмотреть файл

@ -0,0 +1,67 @@
# Logging #
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<span class="aside">
To follow this tutorial you'll need to have
[installed the SDK](dev-guide/tutorials/installation.html)
and learned the
[basics of `cfx`](dev-guide/tutorials/getting-started-with-cfx.html).
</span>
The [DOM `console` object](https://developer.mozilla.org/en/DOM/console)
is useful for debugging JavaScript. Because DOM objects aren't available
to the main add-on code, the SDK provides its own global `console` object
with most of the same methods as the DOM `console`, including methods to
log error, warning, or informational messages. You don't have to
`require()` anything to get access to the console: it is automatically
made available to you.
The `console.log()` method prints an informational message:
console.log("Hello World");
Try it out:
* create a new directory, and navigate to it
* execute `cfx init`
* open "lib/main.js" and add the line above
* execute `cfx run`, then `cfx run` again
Firefox will start, and the following line will appear in the command
window you used to execute `cfx run`:
<pre>
info: Hello World!
</pre>
## `console` in Content Scripts ##
You can use the console in
[content scripts](dev-guide/guides/content-scripts/index.html) as well
as in your main add-on code. The following add-on logs the HTML content
of every tab the user loads, by calling `console.log()` inside a content
script:
require("sdk/tabs").on("ready", function(tab) {
tab.attach({
contentScript: "console.log(document.body.innerHTML);"
});
});
## `console` Output ##
If you are running your add-on from the command line (for example,
executing `cfx run` or `cfx test`) then the console's messages appear
in the command shell you used.
If you've installed the add-on in Firefox, or you're running the
add-on in the Add-on Builder, then the messages appear in Firefox's
[Error Console](https://developer.mozilla.org/en/Error_Console).
## Learning More ##
For the complete `console` API, see its
[API reference](dev-guide/console.html).

Просмотреть файл

@ -0,0 +1,350 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<div class="warning">Developing add-ons for Firefox Mobile is still
an experimental feature of the SDK. Although the SDK modules used are
stable, the setup instructions and cfx commands are likely to change.
</div>
# Developing for Firefox Mobile #
<span class="aside">
To follow this tutorial you'll need to have
[installed the SDK](dev-guide/tutorials/installation.html)
and learned the
[basics of `cfx`](dev-guide/tutorials/getting-started-with-cfx.html).
</span>
Mozilla has recently decided to
[reimplement the UI for Firefox Mobile on Android](http://starkravingfinkle.org/blog/2011/11/firefox-for-android-native-android-ui/)
using native Android widgets instead of XUL. With the add-on SDK you
can develop add-ons that run on this new version of Firefox Mobile as
well as on the desktop version of Firefox.
You can use the same code to target both desktop Firefox and Firefox
Mobile, and just specify some extra options to `cfx run`, `cfx test`,
and `cfx xpi` when targeting Firefox Mobile.
Right now not all modules are fully functional, but we're working on adding
support for more modules.
The [tables at the end of this guide](dev-guide/tutorials/mobile.html#modules-compatibility) list the modules that are currently supported on Firefox Mobile.
This tutorial explains how to run SDK add-ons on an Android
device connected via USB to your development machine.
We'll use the
[Android Debug Bridge](http://developer.android.com/guide/developing/tools/adb.html)
(adb) to communicate between the Add-on SDK and the device.
<img class="image-center" src="static-files/media/mobile-setup-adb.png"/>
It's possible to use the
[Android emulator](http://developer.android.com/guide/developing/tools/emulator.html)
to develop add-ons for Android without access to a device, but it's slow,
so for the time being it's much easier to use the technique described
below.
## Setting up the Environment ##
First you'll need an
[Android device capable of running the native version of
Firefox Mobile](https://wiki.mozilla.org/Mobile/Platforms/Android#System_Requirements).
Then:
* install the
[Nightly build of the native version of Firefox Mobile](https://wiki.mozilla.org/Mobile/Platforms/Android#Download_Nightly)
on the device.
* [enable USB debugging on the device (step 3 of this link only)](http://developer.android.com/guide/developing/device.html#setting-up)
On the development machine:
* install version 1.5 or higher of the Add-on SDK
* install the correct version of the
[Android SDK](http://developer.android.com/sdk/index.html)
for your device
* using the Android SDK, install the
[Android Platform Tools](http://developer.android.com/sdk/installing.html#components)
Next, attach the device to the development machine via USB.
Now open up a command shell. Android Platform Tools will have
installed `adb` in the "platform-tools" directory under the directory
in which you installed the Android SDK. Make sure the "platform-tools"
directory is in your path. Then type:
<pre>
adb devices
</pre>
You should see some output like:
<pre>
List of devices attached
51800F220F01564 device
</pre>
(The long hex string will be different.)
If you do, then `adb` has found your device and you can get started.
## Running Add-ons on Android ##
You can develop your add-on as normal, as long as you restrict yourself
to the supported modules.
When you need to run the add-on, first ensure that Firefox is not running
on the device. Then execute `cfx run` with some extra options:
<pre>
cfx run -a fennec-on-device -b /path/to/adb --mobile-app fennec --force-mobile
</pre>
See ["cfx Options for Mobile Development"](dev-guide/tutorials/mobile.html#cfx-options)
for the details of this command.
In the command shell, you should see something like:
<pre>
Launching mobile application with intent name org.mozilla.fennec
Pushing the addon to your device
Starting: Intent { act=android.activity.MAIN cmp=org.mozilla.fennec/.App (has extras) }
--------- beginning of /dev/log/main
--------- beginning of /dev/log/system
Could not read chrome manifest 'file:///data/data/org.mozilla.fennec/chrome.manifest'.
info: starting
info: starting
zerdatime 1329258528988 - browser chrome startup finished.
</pre>
This will be followed by lots of debug output.
On the device, you should see Firefox launch with your add-on installed.
`console.log()` output from your add-on is written to the command shell,
just as it is in desktop development. However, because there's a
lot of other debug output in the shell, it's not easy to follow.
The command `adb logcat` prints `adb`'s log, so you can filter the
debug output after running the add-on. For example, on Mac OS X
or Linux you can use a command like:
<pre>
adb logcat | grep info:
</pre>
Running `cfx test` is identical:
<pre>
cfx test -a fennec-on-device -b /path/to/adb --mobile-app fennec --force-mobile
</pre>
## <a name="cfx-options">cfx Options for Mobile Development</a> ##
As you see in the quote above, `cfx run` and `cfx test` need four options to
work on Android devices.
<table>
<colgroup>
<col width="30%">
<col width="70%">
</colgroup>
<tr>
<td>
<code>-a fennec-on-device</code>
</td>
<td>
This tells the Add-on SDK which application will host the
add-on, and should be set to "fennec-on-device" when running
an add-on on Firefox Mobile on a device.
</td>
</tr>
<tr>
<td>
<code>-b /path/to/adb</code>
</td>
<td>
<p>As we've seen, <code>cfx</code> uses the Android Debug Bridge (adb)
to communicate with the Android device. This tells <code>cfx</code>
where to find the <code>adb</code> executable.</p>
<p>You need to supply the full path to the <code>adb</code> executable.</p>
</td>
</tr>
<tr>
<td>
<code>--mobile-app</code>
</td>
<td>
<p>This is the name of the <a href="http://developer.android.com/reference/android/content/Intent.html">
Android intent</a>. Its value depends on the version of Firefox Mobile
that you're running on the device:</p>
<ul>
<li><code>fennec</code>: if you're running Nightly</li>
<li><code>fennec_aurora</code>: if you're running Aurora</li>
<li><code>firefox_beta</code>: if you're running Beta</li>
<li><code>firefox</code>: if you're running Release</li>
</ul>
<p>If you're not sure, run a command like this (on OS X/Linux, or the equivalent on Windows):</p>
<pre>adb shell pm list packages | grep mozilla</pre>
<p>You should see "package" followed by "org.mozilla." followed by a string.
The final string is the name you need to use. For example, if you see:</p>
<pre>package:org.mozilla.fennec</pre>
<p>...then you need to specify:</p>
<pre>--mobile-app fennec</pre>
<p>This option is not required if you have only one Firefox application
installed on the device.</p>
</td>
</tr>
<tr>
<td>
<code>--force-mobile</code>
</td>
<td>
<p>This is used to force compatibility with Firefox Mobile, and should
always be used when running on Firefox Mobile.</p>
</td>
</tr>
</table>
## Packaging Mobile Add-ons ##
To package a mobile add-on as an XPI, use the command:
<pre>
cfx xpi --force-mobile
</pre>
Actually installing the XPI on the device is a little tricky. The easiest way is
probably to copy the XPI somewhere on the device:
<pre>
adb push my-addon.xpi /mnt/sdcard/
</pre>
Then open Firefox Mobile and type this into the address bar:
<pre>
file:///mnt/sdcard/my-addon.xpi
</pre>
The browser should open the XPI and ask if you
want to install it.
Afterwards you can delete it using `adb` as follows:
<pre>
adb shell
cd /mnt/sdcard
rm my-addon.xpi
</pre>
<a name="modules-compatibility"></a>
## Module Compatibility
Modules not yet supported in Firefox Mobile are <span class="unsupported-on-mobile">highlighted</span> in the tables below.
### High-Level APIs ###
<ul class="module-list">
<li class="unsupported-on-mobile"><a href="modules/sdk/addon-page.html">addon-page</a></li>
<li><a href="modules/sdk/base64.html">base64</a></li>
<li class="unsupported-on-mobile"><a href="modules/sdk/clipboard.html">clipboard</a></li>
<li class="unsupported-on-mobile"><a href="modules/sdk/context-menu.html">context-menu</a></li>
<li><a href="modules/sdk/hotkeys.html">hotkeys</a></li>
<!-- test-l10n-locale, test-l10n-plural-rules -->
<li><a href="modules/sdk/l10n.html">l10n</a></li>
<li><a href="modules/sdk/notifications.html">notifications</a></li>
<!-- test-page-mod fails, but we know the module works -->
<li><a href="modules/sdk/page-mod.html">page-mod</a></li>
<li class="unsupported-on-mobile"><a href="modules/sdk/panel.html">panel</a></li>
<!-- test-passwords, test-passwords-utils (with exceptions / warning from js console) -->
<li><a href="modules/sdk/passwords.html">passwords</a></li>
<li class="unsupported-on-mobile"><a href="modules/sdk/private-browsing.html">private-browsing</a></li>
<li><a href="modules/sdk/querystring.html">querystring</a></li>
<li><a href="modules/sdk/request.html">request</a></li>
<li class="unsupported-on-mobile"><a href="modules/sdk/selection.html">selection</a></li>
<li><a href="modules/sdk/self.html">self</a></li>
<li><a href="modules/sdk/simple-prefs.html">simple-prefs</a></li>
<li><a href="modules/sdk/simple-storage.html">simple-storage</a></li>
<!-- test-tabs, test-tabs-common -->
<li><a href="modules/sdk/tabs.html">tabs</a></li>
<!-- test-timer -->
<li><a href="modules/sdk/timers.html">timers</a></li>
<li><a href="modules/sdk/url.html">url</a></li>
<li><a href="modules/sdk/widget.html">widget</a></li>
<!-- test-windows-common, test-windows -->
<li><a href="modules/sdk/windows.html">windows</a></li>
</ul>
### Low-Level APIs ###
<ul class="module-list">
<li><a href="modules/toolkit/loader.html">/loader</a></li>
<li><a href="dev-guide/tutorials/chrome.html">chrome</a></li>
<li><a href="modules/sdk/console/plain-text.html">console/plain-text</a></li>
<li><a href="modules/sdk/console/traceback.html">console/traceback</a></li>
<li class="unsupported-on-mobile"><a href="modules/sdk/content/content.html">content/content</a></li>
<li><a href="modules/sdk/content/loader.html">content/loader</a></li>
<li class="unsupported-on-mobile"><a href="modules/sdk/content/symbiont.html">content/symbiont</a></li>
<li class="unsupported-on-mobile"><a href="modules/sdk/content/worker.html">content/worker</a></li>
<li>core/disposable</li>
<li><a href="modules/sdk/core/heritage.html">core/heritage</a></li>
<li><a href="modules/sdk/core/namespace.html">core/namespace</a></li>
<li><a href="modules/sdk/core/promise.html">core/promise</a></li>
<li><a href="modules/sdk/deprecated/api-utils.html">deprecated/api-utils</a></li>
<li><a href="modules/sdk/deprecated/app-strings.html">deprecated/app-strings</a></li>
<li><a href="modules/sdk/deprecated/cortex.html">deprecated/cortex</a></li>
<li><a href="modules/sdk/deprecated/errors.html">deprecated/errors</a></li>
<li><a href="modules/sdk/deprecated/events.html">deprecated/events</a></li>
<li><a href="modules/sdk/deprecated/light-traits.html">deprecated/light-traits</a></li>
<li>deprecated/list</li>
<li><a href="modules/sdk/deprecated/observer-service.html">deprecated/observer-service</a></li>
<li class="unsupported-on-mobile"><a href="modules/sdk/deprecated/tab-browser.html">deprecated/tab-browser</a></li>
<!-- test-traits-core, test-traits -->
<li><a href="modules/sdk/deprecated/traits.html">deprecated/traits</a></li>
<li class="unsupported-on-mobile"><a href="modules/sdk/deprecated/window-utils.html">deprecated/window-utils</a></li>
<!-- test-dom -->
<li>dom/events</li>
<li><a href="modules/sdk/event/core.html">event/core</a></li>
<li><a href="modules/sdk/event/target.html">event/target</a></li>
<li><a href="modules/sdk/frame/hidden-frame.html">frame/hidden-frame</a></li>
<li><a href="modules/sdk/frame/utils.html">frame/utils</a></li>
<li><a href="modules/sdk/io/byte-streams.html">io/byte-streams</a></li>
<li><a href="modules/sdk/io/file.html">io/file</a></li>
<li><a href="modules/sdk/io/text-streams.html">io/text-streams</a></li>
<li>keyboard/observer</li>
<li>keyboard/utils</li>
<li>lang/functional</li>
<li>lang/type</li>
<li><a href="modules/sdk/loader/cuddlefish.html">loader/cuddlefish</a></li>
<li><a href="modules/sdk/loader/sandbox.html">loader/sandbox</a></li>
<li><a href="modules/sdk/net/url.html">net/url</a></li>
<li><a href="modules/sdk/net/xhr.html">net/xhr</a></li>
<li><a href="modules/sdk/page-mod/match-pattern.html">page-mod/match-pattern</a></li>
<li><a href="modules/sdk/platform/xpcom.html">platform/xpcom</a></li>
<!-- test-preferences-service, test-preferences-target -->
<li><a href="modules/sdk/preferences/service.html">preferences/service</a></li>
<li><a href="modules/sdk/system/environment.html">system/environment</a></li>
<!-- No test for `system/events`, assuming it works because other compatible modules are using it -->
<li><a href="modules/sdk/system/events.html">system/events</a></li>
<li>system/globals</li>
<!-- No test for `system/events`, assuming it works because other compatible modules are using it -->
<li><a href="modules/sdk/system/runtime.html">system/runtime</a></li>
<li><a href="modules/sdk/system/unload.html">system/unload</a></li>
<li><a href="modules/sdk/system/xul-app.html">system/xul-app</a></li>
<!-- No test for `assert`, assuming it works because the test are using it -->
<li><a href="modules/sdk/test/assert.html">test/assert</a></li>
<!-- No test for `harness`, assuming it works because the test are using it -->
<li><a href="modules/sdk/test/harness.html">test/harness</a></li>
<li><a href="modules/sdk/test/httpd.html">test/httpd</a></li>
<!-- No test for `runner`, assuming it works because the test are using it -->
<li><a href="modules/sdk/test/runner.html">test/runner</a></li>
<li>test/tmp-file</li>
<li>util/array</li>
<li><a href="modules/sdk/util/collection.html">util/collection</a></li>
<li><a href="modules/sdk/util/deprecate.html">util/deprecate</a></li>
<li><a href="modules/sdk/util/list.html">util/list</a></li>
<li>util/registry</li>
<li><a href="modules/sdk/util/uuid.html">util/uuid</a></li>
<!-- test-window-utils2 -->
<li><a href="modules/sdk/window/utils.html">window/utils</a></li>
</ul>

Просмотреть файл

@ -0,0 +1,153 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Modifying the Page Hosted by a Tab #
<span class="aside">
To follow this tutorial you'll need to have
[installed the SDK](dev-guide/tutorials/installation.html)
and learned the
[basics of `cfx`](dev-guide/tutorials/getting-started-with-cfx.html).
</span>
To modify the page hosted by a particular tab, load a script into it
using the `attach()` method of the
[tab](modules/sdk/tabs.html) object. Because their job is
to interact with web content, these scripts are called *content scripts*.
Here's a simple example:
var widgets = require("sdk/widget");
var tabs = require("sdk/tabs");
var widget = widgets.Widget({
id: "mozilla-link",
label: "Mozilla website",
contentURL: "http://www.mozilla.org/favicon.ico",
onClick: function() {
tabs.activeTab.attach({
contentScript:
'document.body.style.border = "5px solid red";'
})
}
});
This add-on creates a widget which contains the Mozilla favicon as an icon.
It has a click handler which fetches the active tab and loads a
script into the page hosted by the active tab. The script is specified using
`contentScript` option, and just draws
a red border around the page. Try it out:
* create a new directory and navigate to it
* run `cfx init`
* open the `lib/main.js` file, and add the code above
* run `cfx run`, then run `cfx run` again
You should see the Mozilla icon appear in the bottom-right corner of the
browser:
<img class="image-center" src="static-files/media/screenshots/widget-mozilla.png"
alt="Mozilla icon widget" />
Then open any web page in the browser window that opens, and click the
Mozilla icon. You should see a red border appear around the page, like this:
<img class="image-center" src="static-files/media/screenshots/tabattach-bbc.png"
alt="bbc.co.uk modded by tab.attach" />
## Keeping the Content Script in a Separate File ##
In the example above we've passed in the content script as a string. Unless
the script is extremely simple, you should instead maintain the script as a
separate file. This makes the code easier to maintain, debug, and review.
For example, if we save the script above under the add-on's `data` directory
in a file called `my-script.js`:
document.body.style.border = "5px solid red";
We can load this script by changing the add-on code like this:
var widgets = require("sdk/widget");
var tabs = require("sdk/tabs");
var self = require("sdk/self");
var widget = widgets.Widget({
id: "mozilla-link",
label: "Mozilla website",
contentURL: "http://www.mozilla.org/favicon.ico",
onClick: function() {
tabs.activeTab.attach({
contentScriptFile: self.data.url("my-script.js")
});
}
});
You can load more than one script, and the scripts can interact
directly with each other. So you can load [jQuery](http://jquery.com/),
and then your content script can use that.
## Communicating With the 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.
To send a
message from one side to the other, the sender calls `port.emit()` and
the receiver listens using `port.on()`.
* In the content script, `port` is a property of the global `self` object.
* In the add-on script, `tab-attach()` returns an object containing the
`port` property you use to send messages to the content script.
Let's rewrite the example above to pass a message from the add-on to
the content script. The content script now needs to look like this:
// "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;
});
In the add-on script, we'll send the content script a "drawBorder" message
using the object returned from `attach()`:
var widgets = require("sdk/widget");
var tabs = require("sdk/tabs");
var self = require("sdk/self");
var widget = widgets.Widget({
id: "mozilla-link",
label: "Mozilla website",
contentURL: "http://www.mozilla.org/favicon.ico",
onClick: function() {
worker = tabs.activeTab.attach({
contentScriptFile: self.data.url("my-script.js")
});
worker.port.emit("drawBorder", "red");
}
});
The `drawBorder` message isn't a built-in message, it's one that this
add-on defines in the `port.emit()` call.
## Injecting CSS ##
Unlike the [`page-mod`](dev-guide/tutorials/modifying-web-pages-url.html) API,
`tab.attach()` doesn't enable you to inject CSS directly into a page.
To modify the style of a page you have to use JavaScript, as in
the example above.
## Learning More ##
To learn more about working with tabs in the SDK, see the
[Open a Web Page](dev-guide/tutorials/open-a-web-page.html)
tutorial, the
[List Open Tabs](dev-guide/tutorials/list-open-tabs.html)
tutorial, and the [`tabs` API reference](modules/sdk/tabs.html).
To learn more about content scripts, see the
[content scripts guide](dev-guide/guides/content-scripts/index.html).

Просмотреть файл

@ -0,0 +1,242 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Modifying Web Pages Based on URL #
<span class="aside">
To follow this tutorial you'll need to have
[installed the SDK](dev-guide/tutorials/installation.html)
and learned the
[basics of `cfx`](dev-guide/tutorials/getting-started-with-cfx.html).
</span>
To modify any pages that match a particular pattern
(for example, "http://example.org/") as they are loaded, use the
[`page-mod`](modules/sdk/page-mod.html) module.
To create a page-mod you need to specify two things:
* one or more scripts to run. Because their job is to interact with web
content, these scripts are called *content scripts*.
* one or more patterns to match the URLs for the pages you want to modify
Here's a simple example. The content script is supplied as the `contentScript`
option, and the URL pattern is given as the `include` option:
// 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>";'
});
Try it out:
* create a new directory and navigate to it
* run `cfx init`
* open the `lib/main.js` file, and add the code above
* run `cfx run`, then run `cfx run` again
* open [ietf.org](http://www.ietf.org) in the browser window that opens
This is what you should see:
<img class="image-center" src="static-files/media/screenshots/pagemod-ietf.png"
alt="ietf.org eaten by page-mod" />
## Specifying the Match Pattern ##
The match pattern uses the
[`match-pattern`](modules/sdk/page-mod/match-pattern.html)
syntax. You can pass a single match-pattern string, or an array.
## Keeping the Content Script in a Separate File ##
In the example above we've passed in the content script as a string. Unless
the script is extremely simple, you should instead maintain the script as a
separate file. This makes the code easier to maintain, debug, and review.
To do this, you need to:
* save the script in your add-on's `data` directory
* use the `contentScriptFile` option instead of `contentScript`, and pass
it the URL for the script. The URL can be obtained using `self.data.url()`
For example, if we save the script above under the add-on's `data` directory
in a file called `my-script.js`:
document.body.innerHTML = "<h1>Page matches ruleset</h1>";
We can load this script by changing the page-mod code like this:
// 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")
});
## Loading Multiple Content Scripts ##
You can load more than one script, and the scripts can interact
directly with each other. So, for example, you could rewrite
`my-script.js` to use jQuery:
$("body").html("<h1>Page matches ruleset</h1>");
Then download jQuery to your add-on's `data` directory, and
load the script and jQuery together (making sure to load jQuery
first):
// 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"),
self.data.url("my-script.js")]
});
You can use both `contentScript` and `contentScriptFile`
in the same page-mod: if you do this, scripts loaded using
`contentScript` are loaded first:
// 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>");'
});
Note, though, that you can't load a script from a web site. The script
must be loaded from `data`.
## Communicating With the 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.
To send a
message from one side to the other, the sender calls `port.emit()` and
the receiver listens using `port.on()`.
* In the content script, `port` is a property of the global `self` object.
* In the add-on script, you need to listen for the `onAttach` event to get
passed an object that contains `port`.
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.
<div class="experimental">
## Injecting CSS ##
**Note that the feature described in this section is experimental
at the moment: we'll very probably continue to support the feature,
but details of the API might need to change.**
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](modules/sdk/page-mod.html#Working_with_Relative_URLs_in_CSS_Rules).
</div>
## Learning More ##
To learn more about `page-mod`, see its
[API reference page](modules/sdk/page-mod.html).
In particular, the `PageMod` constructor takes several additional options
to control its behavior:
* By default, content scripts are not attached to any tabs that are
already open when the page-mod is created, and are attached to iframes
as well as top-level documents. To control this behavior use the `attachTo`
option.
* Define read-only values accessible to content scripts using the
`contentScriptOptions` option.
* By default, content scripts are attached after all the content
(DOM, JS, CSS, images) for the page has been loaded, at the time the
[window.onload event](https://developer.mozilla.org/en/DOM/window.onload)
fires. To control this behavior use the `contentScriptWhen` option.
To learn more about content scripts in general, see the
[content scripts guide](dev-guide/guides/content-scripts/index.html).

Просмотреть файл

@ -0,0 +1,57 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Open a Web Page #
<span class="aside">
To follow this tutorial you'll need to have
[installed the SDK](dev-guide/tutorials/installation.html)
and learned the
[basics of `cfx`](dev-guide/tutorials/getting-started-with-cfx.html).
</span>
To open a new web page, you can use the
[`tabs`](modules/sdk/tabs.html) module:
var tabs = require("sdk/tabs");
tabs.open("http://www.example.com");
This function is asynchronous, so you don't immediately get back a
[`tab` object](modules/sdk/tabs.html#Tab) which you can examine.
To do this, pass a callback function into `open()`. The callback is assigned
to the `onReady` property, and will be passed the tab as an argument:
var tabs = require("sdk/tabs");
tabs.open({
url: "http://www.example.com",
onReady: function onReady(tab) {
console.log(tab.title);
}
});
Even then, you don't get direct access to any content hosted in the tab.
To access tab content you need to attach a script to the tab
using `tab.attach()`. This add-on loads a page, then attaches a script to
the page which adds a red border to it:
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';"
});
}
## Learning More ##
To learn more about working with tabs in the SDK, see the
[`tabs` API reference](modules/sdk/tabs.html).
To learn more about running scripts in tabs, see the
[tutorial on using `tab.attach()`](dev-guide/tutorials/modifying-web-pages-tab.html).

Просмотреть файл

@ -0,0 +1,408 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Creating Reusable Modules #
<span class="aside">
To follow this tutorial you'll need to have
[installed the SDK](dev-guide/tutorials/installation.html)
and learned the
[basics of `cfx`](dev-guide/tutorials/getting-started-with-cfx.html).
</span>
With the SDK you don't have to keep all your add-on in a single "main.js"
file. You can split your code into separate modules with clearly defined
interfaces between them. You then import and use these modules from other
parts of your add-on using the `require()` statement, in exactly that same
way that you import core SDK modules like
[`widget`](modules/sdk/widget.html) or
[`panel`](modules/sdk/panel.html).
It can often make sense to structure a larger or more complex add-on as a
collection of modules. This makes the design of the add-on easier to
understand and provides some encapsulation as each module will export only
what it chooses to, so you can change the internals of the module without
breaking its users.
Once you've done this, you can package the modules and distribute them
independently of your add-on, making them available to other add-on developers
and effectively extending the SDK itself.
In this tutorial we'll do exactly that with a module that exposes the
geolocation API in Firefox.
## Using Geolocation in an Add-on ##
Suppose we want to use the
[geolocation API built into Firefox](https://developer.mozilla.org/en/using_geolocation).
The SDK doesn't provide an API to access geolocation, but we can
[access the underlying XPCOM API using `require("chrome")`](dev-guide/guides/xul-migration.html#xpcom).
The following add-on adds a [button to the toolbar](dev-guide/tutorials/adding-toolbar-button.html):
when the user clicks the button, it loads the
[XPCOM nsIDOMGeoGeolocation](https://developer.mozilla.org/en/XPCOM_Interface_Reference/NsIDOMGeoGeolocation)
object, and retrieves the user's current position:
var {Cc, Ci} = require("chrome");
// Implement getCurrentPosition by loading the nsIDOMGeoGeolocation
// XPCOM object.
function getCurrentPosition(callback) {
var xpcomGeolocation = Cc["@mozilla.org/geolocation;1"]
.getService(Ci.nsIDOMGeoGeolocation);
xpcomGeolocation.getCurrentPosition(callback);
}
var widget = require("sdk/widget").Widget({
id: "whereami",
label: "Where am I?",
contentURL: "http://www.mozilla.org/favicon.ico",
onClick: function() {
getCurrentPosition(function(position) {
console.log("latitude: ", position.coords.latitude);
console.log("longitude: ", position.coords.longitude);
});
}
});
Try it out:
* create a new directory called "whereami" and navigate to it
* execute `cfx init`
* open "lib/main.js" and add the code above
* execute `cfx run`, then `cfx run` again
You should see a button added to the "Add-on Bar" at the bottom of
the browser window:
<img class="image-center" src="static-files/media/screenshots/widget-mozilla.png"
alt="Mozilla icon widget" />
Click the button, and after a short delay you should see output like
this in the console:
<pre>
info: latitude: 29.45799999
info: longitude: 93.0785269
</pre>
So far, so good. But the geolocation guide on MDN tells us that we must
[ask the user for permission](https://developer.mozilla.org/en/using_geolocation#Prompting_for_permission)
before using the API.
So we'll extend the add-on to include an adapted version of the code in
that MDN page:
<pre><code>
var activeBrowserWindow = require("sdk/window/utils").getMostRecentBrowserWindow();
var {Cc, Ci} = require("chrome");
// Ask the user to confirm that they want to share their location.
// If they agree, call the geolocation function, passing the in the
// callback. Otherwise, call the callback with an error message.
function getCurrentPositionWithCheck(callback) {
let pref = "extensions.whereami.allowGeolocation";
let message = "whereami Add-on wants to know your location."
let branch = Cc["@mozilla.org/preferences-service;1"]
.getService(Ci.nsIPrefBranch2);
if (branch.getPrefType(pref) === branch.PREF_STRING) {
switch (branch.getCharPref(pref)) {
case "always":
return getCurrentPosition(callback);
case "never":
return callback(null);
}
}
let done = false;
function remember(value, result) {
return function () {
done = true;
branch.setCharPref(pref, value);
if (result) {
getCurrentPosition(callback);
}
else {
callback(null);
}
}
}
let self = activeBrowserWindow.PopupNotifications.show(
activeBrowserWindow.gBrowser.selectedBrowser,
"geolocation",
message,
"geo-notification-icon",
{
label: "Share Location",
accessKey: "S",
callback: function (notification) {
done = true;
getCurrentPosition(callback);
}
}, [
{
label: "Always Share",
accessKey: "A",
callback: remember("always", true)
},
{
label: "Never Share",
accessKey: "N",
callback: remember("never", false)
}
], {
eventCallback: function (event) {
if (event === "dismissed") {
if (!done)
callback(null);
done = true;
PopupNotifications.remove(self);
}
},
persistWhileVisible: true
});
}
// Implement getCurrentPosition by loading the nsIDOMGeoGeolocation
// XPCOM object.
function getCurrentPosition(callback) {
var xpcomGeolocation = Cc["@mozilla.org/geolocation;1"]
.getService(Ci.nsIDOMGeoGeolocation);
xpcomGeolocation.getCurrentPosition(callback);
}
var widget = require("sdk/widget").Widget({
id: "whereami",
label: "Where am I?",
contentURL: "http://www.mozilla.org/favicon.ico",
onClick: function() {
getCurrentPositionWithCheck(function(position) {
if (!position) {
console.log("The user denied access to geolocation.");
}
else {
console.log("latitude: ", position.coords.latitude);
console.log("longitude: ", position.coords.longitude);
}
});
}
});
</code></pre>
This works fine: when we click the button, we get a notification box
asking for permission, and depending on our choice the add-on logs either
the position or an error message.
But the code is now somewhat long and complex, and if we want to do much
more in the add-on, it will be hard to maintain. So let's split the
geolocation code into a separate module.
## Creating a Separate Module ##
### Create `geolocation.js` ###
First create a new file in "lib" called "geolocation.js", and copy
everything except the widget code into this new file.
Next, add the following line somewhere in the new file:
exports.getCurrentPosition = getCurrentPositionWithCheck;
This defines the public interface of the new module. We export a single
a function to prompt the user for permission and get the current position
if they agree.
So "geolocation.js" should look like this:
<pre><code>
var activeBrowserWindow = require("sdk/window/utils").getMostRecentBrowserWindow();
var {Cc, Ci} = require("chrome");
// Ask the user to confirm that they want to share their location.
// If they agree, call the geolocation function, passing the in the
// callback. Otherwise, call the callback with an error message.
function getCurrentPositionWithCheck(callback) {
let pref = "extensions.whereami.allowGeolocation";
let message = "whereami Add-on wants to know your location."
let branch = Cc["@mozilla.org/preferences-service;1"]
.getService(Ci.nsIPrefBranch2);
if (branch.getPrefType(pref) === branch.PREF_STRING) {
switch (branch.getCharPref(pref)) {
case "always":
return getCurrentPosition(callback);
case "never":
return callback(null);
}
}
let done = false;
function remember(value, result) {
return function () {
done = true;
branch.setCharPref(pref, value);
if (result) {
getCurrentPosition(callback);
}
else {
callback(null);
}
}
}
let self = activeBrowserWindow.PopupNotifications.show(
activeBrowserWindow.gBrowser.selectedBrowser,
"geolocation",
message,
"geo-notification-icon",
{
label: "Share Location",
accessKey: "S",
callback: function (notification) {
done = true;
getCurrentPosition(callback);
}
}, [
{
label: "Always Share",
accessKey: "A",
callback: remember("always", true)
},
{
label: "Never Share",
accessKey: "N",
callback: remember("never", false)
}
], {
eventCallback: function (event) {
if (event === "dismissed") {
if (!done)
callback(null);
done = true;
PopupNotifications.remove(self);
}
},
persistWhileVisible: true
});
}
// Implement getCurrentPosition by loading the nsIDOMGeoGeolocation
// XPCOM object.
function getCurrentPosition(callback) {
var xpcomGeolocation = Cc["@mozilla.org/geolocation;1"]
.getService(Ci.nsIDOMGeoGeolocation);
xpcomGeolocation.getCurrentPosition(callback);
}
exports.getCurrentPosition = getCurrentPositionWithCheck;
</code></pre>
### Update `main.js` ###
Finally, update "main.js". First add a line to import the new module:
var geolocation = require("./geolocation");
When importing modules that are not SDK built in modules, it's a good
idea to specify the path to the module explicitly like this, rather than
relying on the module loader to find the module you intended.
Edit the widget's call to `getCurrentPositionWithCheck()` so it calls
the geolocation module's `getCurrentPosition()` function instead:
geolocation.getCurrentPosition(function(position) {
if (!position) {
Now "main.js" should look like this:
<pre><code>
var geolocation = require("./geolocation");
var widget = require("sdk/widget").Widget({
id: "whereami",
label: "Where am I?",
contentURL: "http://www.mozilla.org/favicon.ico",
onClick: function() {
geolocation.getCurrentPosition(function(position) {
if (!position) {
console.log("The user denied access to geolocation.");
}
else {
console.log("latitude: ", position.coords.latitude);
console.log("longitude: ", position.coords.longitude);
}
});
}
});
</code></pre>
## Packaging the Geolocation Module ##
So far, this is a useful technique for structuring your add-on.
But you can also package and distribute modules independently of
your add-on: then any other add-on developer can download your
module and use it in exactly the same way they use the SDK's built-in
modules.
### Code Changes ###
First we'll make a couple of changes to the code.
At the moment the message displayed in the prompt and the name of
the preference used to store the user's choice are hardcoded:
let pref = "extensions.whereami.allowGeolocation";
let message = "whereami Add-on wants to know your location."
Instead we'll use the `self` module to ensure that they are specific
to the add-on:
var addonName = require("sdk/self").name;
var addonId = require("sdk/self").id;
let pref = "extensions." + addonId + ".allowGeolocation";
let message = addonName + " Add-on wants to know your location."
### Repackaging ###
Next we'll repackage the geolocation module.
* create a new directory called "geolocation", and run `cfx init` in it.
* delete the "main.js" that `cfx` generated, and copy "geolocation.js"
there instead.
### Documentation ###
If you document your modules, people who install your package and
execute `cfx docs` will see the documentation
integrated with the SDK's own documentation.
You can document the geolocation module by creating a file called
"geolocation.md" in your package's "doc" directory. This file is also
written in Markdown, although you can optionally use some
[extended syntax](https://wiki.mozilla.org/Jetpack/SDK/Writing_Documentation#APIDoc_Syntax)
to document APIs.
Try it:
* add a "geolocation.md" under "doc"
* copy your geolocation package under the "packages" directory in the SDK root
* execute `cfx docs`
Once `cfx docs` has finished, you should see a new entry appear in the
sidebar called "Third-Party APIs", which lists the geolocation module.
### Editing "package.json" ###
The "package.json" file in your package's root directory contains metadata
for your package. See the
[package specification](dev-guide/package-spec.html) for
full details. If you intend to distribute the package, this is a good place
to add your name as the author, choose a distribution license, and so on.
## Learning More ##
To see some of the modules people have already developed, see the page of
[community-developed modules](https://github.com/mozilla/addon-sdk/wiki/Community-developed-modules).
To learn how to use third-party modules in your own code, see the
[tutorial on adding menu items](dev-guide/tutorials/adding-menus.html).

Просмотреть файл

@ -0,0 +1,5 @@
# Creating Event Emitters #
The [guide to event-driven programming with the SDK](dev-guide/guides/events.html) describes
how to consume events: that is, how to listen to events generated
by event-emitting objects.

Просмотреть файл

@ -0,0 +1,208 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Troubleshooting #
If you're having trouble getting the Add-on SDK up and running, don't panic!
This page lists some starting points that might help you track down your
problem.
Quarantine Problem on Mac OS X
------------------------------
On Mac OS X, you might see the following error when you try to run `cfx`:
<pre>
/path/to/sdk/bin/cfx: /usr/bin/env: bad interpreter: Operation not permitted
</pre>
This might be because the `cfx` executable file has been placed in quarantine
during download from the Internet.
To get it out of quarantine, use the `xattr -d` command, specifying
`com.apple.quarantine` as the name of the attribute to delete, and `cfx` as
the file from which to delete that attribute:
<pre>
xattr -d com.apple.quarantine /path/to/sdk/bin/cfx
</pre>
Check Your Python
-----------------
The SDK's `cfx` tool runs on Python. If you're having trouble getting `cfx` to
run at all, make sure you have Python correctly installed.
Try running the following from a command line:
<pre>
python --version
</pre>
`cfx` currently expects Python 2.5 or 2.6. Older and newer versions may or may
not work.
Check Your Firefox or XULRunner
-------------------------------
`cfx` searches well known locations on your system for Firefox or XULRunner.
`cfx` may not have found an installation, or if you have multiple installations,
`cfx` may have found the wrong one. In those cases you need to use `cfx`'s
`--binary` option. See the [cfx Tool][] guide for more information.
When you run `cfx` to test your add-on or run unit tests, it prints out the
location of the Firefox or XULRunner binary that it found, so you can check its
output to be sure.
[cfx Tool]: dev-guide/cfx-tool.html
Check Your Text Console
-----------------------
When errors are generated in the SDK's APIs and your code, they are logged to
the text console. This should be the same console or shell from which you ran
the `cfx` command.
Don't Leave Non-SDK Files Lying Around
------------------------------------------
Currently the SDK does not gracefully handle files and directories that it does
not expect to encounter. If there are empty directories or directories or files
that are not related to the SDK inside your `addon-sdk` directory or its
sub-directories, try removing them.
Search for Known Issues
-----------------------
Someone else might have experienced your problem, too. Other users often post
problems to the [project mailing list][jetpack-list]. You can also browse the
list of [known issues][bugzilla-known] or [search][bugzilla-search] for
specific keywords.
[bugzilla-known]: https://bugzilla.mozilla.org/buglist.cgi?order=Bug%20Number&resolution=---&resolution=DUPLICATE&query_format=advanced&product=Add-on%20SDK
[bugzilla-search]: https://bugzilla.mozilla.org/query.cgi?format=advanced&product=Add-on%20SDK
Contact the Project Team and User Group
---------------------------------------
SDK users and project team members discuss problems and proposals on the
[project mailing list][jetpack-list]. Someone else may have had the same
problem you do, so try searching the list.
You're welcome to post a question, too.
You can also chat with other SDK users in [#jetpack][#jetpack] on
[Mozilla's IRC network][IRC].
And if you'd like to [report a bug in the SDK][bugzilla-report], that's always
welcome! You will need to create an account with Bugzilla, Mozilla's bug
tracker.
[jetpack-list]: http://groups.google.com/group/mozilla-labs-jetpack/topics
[#jetpack]:http://mibbit.com/?channel=%23jetpack&server=irc.mozilla.org
[IRC]: http://irc.mozilla.org/
[bugzilla-report]: https://bugzilla.mozilla.org/enter_bug.cgi?product=Add-on%20SDK&component=General
Run the SDK's Unit Tests
------------------------
The SDK comes with a suite of tests which ensures that its APIs work correctly.
You can run it with the following command:
<pre>
cfx testall
</pre>
Some of the tests will open Firefox windows to check APIs related to the user
interface, so don't be alarmed. Please let the suite finish before resuming
your work.
When the suite is finished, your text console should contain output that looks
something like this:
<pre>
Testing cfx...
.............................................................
----------------------------------------------------------------------
Ran 61 tests in 4.388s
OK
Testing reading-data...
Using binary at '/Applications/Firefox.app/Contents/MacOS/firefox-bin'.
Using profile at '/var/folders/FL/FLC+17D+ERKgQe4K+HC9pE+++TI/-Tmp-/tmpu26K_5.mozrunner'.
.info: My ID is 6724fc1b-3ec4-40e2-8583-8061088b3185
..
3 of 3 tests passed.
OK
Total time: 4.036381 seconds
Program terminated successfully.
Testing all available packages: nsjetpack, test-harness, api-utils, development-mode.
Using binary at '/Applications/Firefox.app/Contents/MacOS/firefox-bin'.
Using profile at '/var/folders/FL/FLC+17D+ERKgQe4K+HC9pE+++TI/-Tmp-/tmp-dzeaA.mozrunner'.
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
.........................................................................
...............................................
3405 of 3405 tests passed.
OK
Total time: 43.105498 seconds
Program terminated successfully.
All tests were successful. Ship it!
</pre>
If you get lots of errors instead, that may be a sign that the SDK does not work
properly on your system. In that case, please file a bug or send a message to
the project mailing list. See the previous section for information on doing so.

Просмотреть файл

@ -0,0 +1,163 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Unit Testing #
<span class="aside">
To follow this tutorial you'll need to have
[installed the SDK](dev-guide/tutorials/installation.html),
learned the
[basics of `cfx`](dev-guide/tutorials/getting-started-with-cfx.html),
and followed the tutorial on
[writing reusable modules](dev-guide/tutorials/reusable-modules.html).
</span>
The SDK provides a framework to help creates and run unit tests for
your code. To demonstrate how it works we'll write some unit tests for
a simple [Base64](http://en.wikipedia.org/wiki/Base64) encoding module.
## A Simple Base64 Module ##
In a web page, you can perform Base64 encoding and decoding using the
`btoa()` and `atob()` functions. Unfortunately these functions are attached
to the `window` object: since this object is not available in your
main add-on code, `atob()` and `btoa()` aren't available either. Using the
low-level
[window-utils](modules/sdk/deprecated/window-utils.html) module you
can access `window`, enabling you to call these functions.
However, it's good practice to encapsulate the code that directly accesses
`window-utils` in its own module, and only export the `atob()`
and `btoa()` functions. So we'll create a Base64 module to do
exactly that.
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:
var window = require("sdk/window/utils").getMostRecentBrowserWindow();
exports.atob = function(a) {
return window.atob(a);
}
exports.btoa = function(b) {
return window.btoa(b);
}
This code exports two functions, which just call the corresponding
functions on the `window` object. To show the module in use, edit
the "main.js" file as follows:
var widgets = require("sdk/widget");
var base64 = require("./base64");
var widget = widgets.Widget({
id: "base64",
label: "Base64 encode",
contentURL: "http://www.mozilla.org/favicon.ico",
onClick: function() {
encoded = base64.btoa("hello");
console.log(encoded);
decoded = base64.atob(encoded);
console.log(decoded);
}
});
Now "main.js" imports the base64 module and calls its two exported
functions. If we run the add-on and click the widget, we should see
the following logging output:
<pre>
info: aGVsbG8=
info: hello
</pre>
## 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:
<pre><code>
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);
</code></pre>
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`](modules/sdk/test/assert.html) module and implements
the [CommonJS Unit Testing specification](http://wiki.commonjs.org/wiki/Unit_Testing/1.1).
* The first two functions call `atob()` and `btoa()` and use
[`assert.ok()`](modules/sdk/test/assert.html)
to check that the output is as expected.
* The second function tests the module's error-handling code by passing an
empty string into `atob()` and using
[`assert.throws()`](modules/sdk/test/assert.html)
to check that the expected exception is raised.
At this point your add-on ought to look like this:
<pre>
/base64
package.json
README.md
/doc
main.md
/lib
main.js
base64.js
/test
test-base64.js
</pre>
Now execute `cfx --verbose test` from the add-on's root directory.
You should see something like this:
<pre>
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.
</pre>
What happens here is that `cfx test`:
<span class="aside">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".</span>
* looks in the `test` directory of your
package
* loads any modules whose names start with the word `test-`
* calls each exported function whose name starts with "test", passing it
an [`assert`](modules/sdk/test/assert.html) object as its only argument.
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.

Просмотреть файл

@ -0,0 +1,18 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# High-Level APIs #
Modules in this section implement high-level APIs for
building add-ons:
* creating user interfaces
* interacting with the web
* interacting with the browser
Unless the documentation explicitly says otherwise, all these modules are
"supported": meaning that they are relatively stable, and that we'll avoid
making incompatible changes to them unless absolutely necessary.
<ul id="module-index"></ul>

Просмотреть файл

@ -0,0 +1,36 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
# Low-Level APIs #
Modules in this section implement low-level APIs. These
modules fall roughly into three categories:
* fundamental utilities such as
[collection](modules/sdk/util/collection.html) and
[url](modules/sdk/url.html). Many add-ons are likely to
want to use modules from this category.
* building blocks for higher level modules, such as
[events](modules/sdk/event/core.html),
[worker](modules/sdk/content/worker.html), and
[api-utils](modules/sdk/deprecated/api-utils.html). You're more
likely to use these if you are building your own modules that
implement new APIs, thus extending the SDK itself.
* privileged modules that expose powerful low-level capabilities
such as [window/utils](modules/sdk/window/utils.html) and
[xhr](modules/sdk/net/xhr.html). You can use these
modules in your add-on if you need to, but should be aware that
the cost of privileged access is the need to take more elaborate
security precautions. In many cases these modules have simpler,
more restricted analogs among the "High-Level APIs" (for
example, [windows](modules/sdk/windows.html) or
[request](modules/sdk/request.html)).
These modules are still in active development, and we expect to
make incompatible changes to them in future releases.
<ul id="module-index"></ul>

Просмотреть файл

@ -0,0 +1,32 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
With the Add-on SDK you can present information to the user,
such as a guide to using your add-on, in a browser tab.
You can supply the content in an HTML file in your add-on's
"data" directory.
***Note:*** This module has no effect on Fennec.
For pages like this, navigational elements such as the
[Awesome Bar](http://support.mozilla.org/en-US/kb/Location%20bar%20autocomplete),
[Search Bar](http://support.mozilla.org/en-US/kb/Search%20bar), or
[Bookmarks Toolbar](http://support.mozilla.org/en-US/kb/Bookmarks%20Toolbar)
are not usually relevant and distract from the content
you are presenting. The `addon-page` module provides a simple
way to have a page which excludes these elements.
To use the module import it using `require()`. After this,
the page loaded from "data/index.html" will not contain
navigational elements:
var addontab = require("sdk/addon-page");
var data = require("sdk/self").data;
require("sdk/tabs").open(data.url("index.html"));
<img src="static-files/media/screenshots/addon-page.png" alt="Example add-on page" class="image-center"/>
This only affects the page at "data/index.html":
all other pages are displayed normally.

Просмотреть файл

@ -0,0 +1,50 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
The module provides data encoding and decoding using Base64 algorithms.
##Example
var base64 = require("sdk/base64");
var encodedData = base64.encode("Hello, World");
var decodedData = base64.decode(encodedData);
##Unicode Strings
In order to `encode` and `decode` properly Unicode strings, the `charset`
parameter needs to be set to `"utf-8"`:
var base64 = require("sdk/base64");
var encodedData = base64.encode(unicodeString, "utf-8");
var decodedData = base64.decode(encodedData, "utf-8");
<api name="encode">
@function
Creates a base-64 encoded ASCII string from a string of binary data.
@param data {string}
The data to encode
@param [charset] {string}
The charset of the string to encode (optional).
The only accepted value is `"utf-8"`.
@returns {string}
The encoded string
</api>
<api name="decode">
@function
Decodes a string of data which has been encoded using base-64 encoding.
@param data {string}
The encoded data
@param [charset] {string}
The charset of the string to encode (optional).
The only accepted value is `"utf-8"`.
@returns {string}
The decoded string
</api>

Просмотреть файл

@ -0,0 +1,91 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!-- contributed by Dietrich Ayala [dietrich@mozilla.com] -->
The `clipboard` module allows callers to interact with the system clipboard,
setting and retrieving its contents.
You can optionally specify the type of the data to set and retrieve.
The following types are supported:
* `text` (plain text)
* `html` (a string of HTML)
* `image` (a base-64 encoded png)
If no data type is provided, then the module will detect it for you.
Currently `image`'s type doesn't support transparency on Windows.
Examples
--------
Set and get the contents of the clipboard.
var clipboard = require("sdk/clipboard");
clipboard.set("Lorem ipsum dolor sit amet");
var contents = clipboard.get();
Set the clipboard contents to some HTML.
var clipboard = require("sdk/clipboard");
clipboard.set("<blink>Lorem ipsum dolor sit amet</blink>", "html");
If the clipboard contains HTML content, open it in a new tab.
var clipboard = require("sdk/clipboard");
if (clipboard.currentFlavors.indexOf("html") != -1)
require("sdk/tabs").open("data:text/html;charset=utf-8," + clipboard.get("html"));
Set the clipboard contents to an image.
var clipboard = require("sdk/clipboard");
clipboard.set("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYA" +
"AABzenr0AAAASUlEQVRYhe3O0QkAIAwD0eyqe3Q993AQ3cBSUKpygfsNTy" +
"N5ugbQpK0BAADgP0BRDWXWlwEAAAAAgPsA3rzDaAAAAHgPcGrpgAnzQ2FG" +
"bWRR9AAAAABJRU5ErkJggg%3D%3D");
If the clipboard contains an image, open it in a new tab.
var clipboard = require("sdk/clipboard");
if (clipboard.currentFlavors.indexOf("image") != -1)
require("sdk/tabs").open(clipboard.get());
As noted before, data type can be easily omitted for images.
If the intention is set the clipboard to a data URL as string and not as image,
it can be easily done specifying a different flavor, like `text`.
var clipboard = require("sdk/clipboard");
clipboard.set("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYA" +
"AABzenr0AAAASUlEQVRYhe3O0QkAIAwD0eyqe3Q993AQ3cBSUKpygfsNTy" +
"N5ugbQpK0BAADgP0BRDWXWlwEAAAAAgPsA3rzDaAAAAHgPcGrpgAnzQ2FG" +
"bWRR9AAAAABJRU5ErkJggg%3D%3D", "text");
<api name="set">
@function
Replace the contents of the user's clipboard with the provided data.
@param data {string}
The data to put on the clipboard.
@param [datatype] {string}
The type of the data (optional).
</api>
<api name="get">
@function
Get the contents of the user's clipboard.
@param [datatype] {string}
Retrieve the clipboard contents only if matching this type (optional).
The function will return null if the contents of the clipboard do not match
the supplied type.
</api>
<api name="currentFlavors">
@property {array}
Data on the clipboard is sometimes available in multiple types. For example,
HTML data might be available as both a string of HTML (the `html` type)
and a string of plain text (the `text` type). This function returns an array
of all types in which the data currently on the clipboard is available.
</api>

Просмотреть файл

@ -0,0 +1,7 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
SDK add-ons can
[log debug messages using the global `console` object](dev-guide/tutorials/logging.html),
and the `plain-text-console` module implements this object.

Просмотреть файл

@ -0,0 +1,66 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!-- contributed by Atul Varma [atul@mozilla.com] -->
<!-- edited by Noelle Murata [fiveinchpixie@gmail.com] -->
The `traceback` module contains functionality similar to
Python's [traceback](http://docs.python.org/library/traceback.html) module.
## JSON Traceback Objects ##
Tracebacks are stored in JSON format. The stack is represented as an
array in which the most recent stack frame is the last element; each
element thus represents a stack frame and has the following keys:
<table>
<tr>
<td><code>filename</code></td>
<td>The name of the file that the stack frame takes place in.</td>
</tr>
<tr>
<td><code>lineNo</code></td>
<td>The line number is being executed at the stack frame.</td>
</tr>
<tr>
<td><code>funcName</code></td>
<td>The name of the function being executed at the stack frame, or
<code>null</code> if the function is anonymous or the stack frame is
being executed in a top-level script or module.</td>
</tr>
</table>
<api name="fromException">
@function
Attempts to extract the traceback from *`exception`*.
@returns {traceback}
JSON representation of the traceback or `null` if not found.
@param exception {exception}
exception where exception is an `nsIException`.
</api>
See [nsIException](https://developer.mozilla.org/en/NsIException) for more
information.
<api name="get">
@function
@returns {JSON}
Returns the JSON representation of the stack at the point that this
function is called.
</api>
<api name="format">
@function
Given a JSON representation of the stack or an exception instance,
returns a formatted plain text representation of it, similar to
Python's formatted stack tracebacks. If no argument is provided, the
stack at the point this function is called is used.
@param [tbOrException] {object}
</api>

Просмотреть файл

@ -0,0 +1,20 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!-- contributed by Irakli Gozalishvili [gozala@mozilla.com] -->
The `content` module re-exports three objects from
three other modules: [`Loader`](modules/sdk/content/loader.html),
[`Worker`](modules/sdk/content/worker.html), and
[`Symbiont`](modules/sdk/content/symbiont.html).
These objects are used in the internal implementations of SDK modules which use
[content scripts to interact with web content](dev-guide/guides/content-scripts/index.html),
such as the [`panel`](modules/sdk/panel.html) or [`page-mod`](modules/sdk/page-mod.html)
modules.
[Loader]:
[Worker]:modules/sdk/content/worker.html
[Symbiont]:modules/sdk/content/symbiont.html

Просмотреть файл

@ -0,0 +1,122 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!-- contributed by Irakli Gozalishvili [gozala@mozilla.com] -->
The `loader` module provides one of the building blocks for those modules
in the SDK that use
[content scripts to interact with web content](dev-guide/guides/content-scripts/index.html),
such as the [`panel`](modules/sdk/panel.html) and [`page-mod`](modules/sdk/page-mod.html)
modules.
The module exports a constructor for the `Loader` object, which is composed
from the [EventEmitter](modules/sdk/deprecated/events.html) trait, so it
inherits `on()`, `once()`, and `removeListener()` functions that
enable its users to listen to events.
`Loader` adds code to initialize and validate a set of properties for
managing content scripts:
* `contentURL`
* `contentScript`
* `contentScriptFile`
* `contentScriptWhen`
* `contentScriptOptions`
* `allow`
When certain of these properties are set, the `Loader` emits a
`propertyChange` event, enabling its users to take the appropriate action.
The `Loader` is used by modules that use content scripts but don't
themselves load content, such as [`page-mod`](modules/sdk/page-mod.html).
Modules that load their own content, such as
[`panel`](modules/sdk/panel.html), [`page-worker`](modules/sdk/page-worker.html), and
[`widget`](modules/sdk/widget.html), use the
[`symbiont`](modules/sdk/content/symbiont.html) module instead.
`Symbiont` inherits from `Loader` but contains its own frame into which
it loads content supplied as the `contentURL` option.
**Example:**
The following code creates a wrapper on a hidden frame that reloads a web page
in the frame every time the `contentURL` property is changed:
var hiddenFrames = require("sdk/frame/hidden-frame");
var { Loader } = require("sdk/content/content");
var PageLoader = Loader.compose({
constructor: function PageLoader(options) {
options = options || {};
if (options.contentURL)
this.contentURL = options.contentURL;
this.on('propertyChange', this._onChange = this._onChange.bind(this));
let self = this;
hiddenFrames.add(hiddenFrames.HiddenFrame({
onReady: function onReady() {
let frame = self._frame = this.element;
self._emit('propertyChange', { contentURL: self.contentURL });
}
}));
},
_onChange: function _onChange(e) {
if ('contentURL' in e)
this._frame.setAttribute('src', this._contentURL);
}
});
<api name="Loader">
@class
<api name="contentScriptFile">
@property {array}
The local file URLs of content scripts to load. Content scripts specified by
this property are loaded *before* those specified by the `contentScript`
property.
</api>
<api name="contentScript">
@property {array}
The texts of content scripts to load. Content scripts specified by this
property are loaded *after* those specified by the `contentScriptFile` property.
</api>
<api name="contentScriptWhen">
@property {string}
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 page 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](https://developer.mozilla.org/en/Gecko-Specific_DOM_Events)
event
* "end": load content scripts once all the content (DOM, JS, CSS,
images) for the page has been loaded, at the time the
[window.onload event](https://developer.mozilla.org/en/DOM/window.onload)
fires
</api>
<api name="contentScriptOptions">
@property {object}
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.
</api>
<api name="contentURL">
@property {string}
The URL of the content loaded.
</api>
<api name="allow">
@property {object}
Permissions for the content, with the following keys:
@prop script {boolean}
Whether or not to execute script in the content. Defaults to true.
</api>
</api>

Просмотреть файл

@ -0,0 +1,149 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!-- contributed by Myk Melez [myk@mozilla.org] -->
<!-- contributed by Irakli Gozalishvili [gozala@mozilla.com] -->
The `symbiont` module exports the `Symbiont` trait, which is used in
the internal implementation of SDK modules, such as
[`panel`](modules/sdk/panel.html) and
[`page-worker`](modules/sdk/page-mod.html), that can load web
content and attach
[content scripts](dev-guide/guides/content-scripts/index.html) to it.
A `Symbiont` loads the specified `contentURL` and content scripts into
a frame, and sets up an asynchronous channel between the content
scripts and the add-on code, enabling them to exchange messages using the
[`port`](dev-guide/guides/content-scripts/using-port.html) or
[`postMessage`](dev-guide/guides/content-scripts/using-postmessage.html)
APIs. You map optionally pass a frame into the `Symbiont`'s constructor:
if you don't, then a new hidden frame will be created to host the content.
This trait is composed from the
[`Loader`](modules/sdk/content/loader.html) and
[`Worker`](modules/sdk/content/worker.html) traits. It inherits
functions to load and configure content scripts from the `Loader`,
and functions to exchange messages between content scripts and the
main add-on code from the `Worker`.
var { Symbiont } = require('sdk/content/content');
var Thing = Symbiont.resolve({ constructor: '_init' }).compose({
constructor: function Thing(options) {
// `getMyFrame` returns the host application frame in which
// the page is loaded.
this._frame = getMyFrame();
this._init(options)
}
});
See the [panel][] module for a real-world example of usage of this module.
[panel]:modules/sdk/panel.html
<api name="Symbiont">
@class
Symbiont is composed from the [Worker][] trait, therefore instances
of Symbiont and their descendants expose all the public properties
exposed by [Worker][] along with additional public properties that
are listed below:
[Worker]:modules/sdk/content/worker.html
<api name="Symbiont">
@constructor
Creates a content symbiont.
@param options {object}
Options for the constructor. Includes all the keys that
the [Worker](modules/sdk/content/worker.html)
constructor accepts and a few more:
@prop [frame] {object}
The host application frame in which the page is loaded.
If frame is not provided hidden one will be created.
@prop [contentScriptWhen="end"] {string}
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 page 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](https://developer.mozilla.org/en/Gecko-Specific_DOM_Events)
event
* "end": load content scripts once all the content (DOM, JS, CSS,
images) for the page has been loaded, at the time the
[window.onload event](https://developer.mozilla.org/en/DOM/window.onload)
fires
This property is optional and defaults to "end".
@prop [contentScriptOptions] {object}
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.
@prop [allow] {object}
Permissions for the content, with the following keys:
@prop [script] {boolean}
Whether or not to execute script in the content. Defaults to true.
Optional.
Optional.
</api>
<api name="contentScriptFile">
@property {array}
The local file URLs of content scripts to load. Content scripts specified by
this property are loaded *before* those specified by the `contentScript`
property.
</api>
<api name="contentScript">
@property {array}
The texts of content scripts to load. Content scripts specified by this
property are loaded *after* those specified by the `contentScriptFile` property.
</api>
<api name="contentScriptWhen">
@property {string}
When to load the content scripts. This may have one of the following
values:
* "start": load content scripts immediately after the document
element for the page 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](https://developer.mozilla.org/en/Gecko-Specific_DOM_Events)
event
* "end": load content scripts once all the content (DOM, JS, CSS,
images) for the page has been loaded, at the time the
[window.onload event](https://developer.mozilla.org/en/DOM/window.onload)
fires
</api>
<api name="contentScriptOptions">
@property {object}
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.
</api>
<api name="contentURL">
@property {string}
The URL of the content loaded.
</api>
<api name="allow">
@property {object}
Permissions for the content, with a single boolean key called `script` which
defaults to true and indicates whether or not to execute scripts in the
content.
</api>
</api>

Просмотреть файл

@ -0,0 +1,135 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!-- contributed by Irakli Gozalishvili [gozala@mozilla.com] -->
This module is used in the internal implementation of SDK modules
which use
[content scripts to interact with web content](dev-guide/guides/content-scripts/index.html),
such as the [`panel`](modules/sdk/panel.html) or [`page-mod`](modules/sdk/page-mod.html)
modules.
It exports the `Worker` trait, which enables content
scripts and the add-on code to exchange messages using the
[`port`](dev-guide/guides/content-scripts/using-port.html) or
[`postMessage`](dev-guide/guides/content-scripts/using-postmessage.html)
APIs.
The `Worker` is similar to the [web worker][] interface defined by the W3C.
But unlike "web workers," these workers run in the
same process as web content and browser chrome, so code within workers can
block the UI.
[web worker]:http://www.w3.org/TR/workers/#worker
<api name="Worker">
@class
Worker is composed from the [EventEmitter][] trait, therefore instances
of Worker and their descendants expose all the public properties
exposed by [EventEmitter][] along with additional public properties that
are listed below.
**Example**
var workers = require("sdk/content/worker");
let worker = workers.Worker({
window: require("sdk/window/utils").getMostRecentBrowserWindow(),
contentScript:
"self.port.on('hello', function(name) { " +
" self.port.emit('response', window.location.href); " +
"});"
});
worker.port.emit("hello", { name: "worker"});
worker.port.on("response", function (location) {
console.log(location);
});
[EventEmitter]:modules/sdk/deprecated/events.html
<api name="Worker">
@constructor
Creates a content worker.
@param options {object}
Options for the constructor, with the following keys:
@prop window {object}
The content window to create JavaScript sandbox for communication with.
@prop [contentScriptFile] {string,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.
@prop [contentScript] {string,array}
The texts of content scripts to load. Content scripts specified by this
option are loaded *after* those specified by the `contentScriptFile` option.
Optional.
@prop [onMessage] {function}
Functions that will registered as a listener to a 'message' events.
@prop [onError] {function}
Functions that will registered as a listener to an 'error' events.
</api>
<api name="port">
@property {EventEmitter}
[EventEmitter](modules/sdk/deprecated/events.html) object that allows you to:
* send customized messages to the worker using the `port.emit` function
* receive events from the worker using the `port.on` function
</api>
<api name="postMessage">
@method
Asynchronously emits `"message"` events in the enclosed worker, where content
script was loaded.
@param data {number,string,JSON}
The data to send. Must be stringifiable to JSON.
</api>
<api name="destroy">
@method
Destroy the worker by removing the content script from the page and removing
all registered listeners. A `detach` event is fired just before removal.
</api>
<api name="url">
@property {string}
The URL of the content.
</api>
<api name="tab">
@property {object}
If this worker is attached to a content document, returns the related
[tab](modules/sdk/tabs.html).
</api>
<api name="message">
@event
This event allows the content worker to receive messages from its associated
content scripts. Calling the `self.postMessage()` function from a content
script will asynchronously emit the `message` event on the corresponding
worker.
@argument {value}
The event listener is passed the message, which must be a
<a href = "dev-guide/guides/content-scripts/using-port.html#json_serializable">JSON-serializable value</a>.
</api>
<api name="error">
@event
This event allows the content worker to react to an uncaught runtime script
error that occurs in one of the content scripts.
@argument {Error}
The event listener is passed a single argument which is an
[Error](https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error)
object.
</api>
<api name="detach">
@event
This event is emitted when the document associated with this worker is unloaded
or the worker's `destroy()` method is called.
</api>
</api>

Просмотреть файл

@ -0,0 +1,726 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!-- contributed by Drew Willcoxon [adw@mozilla.com] -->
<!-- edited by Noelle Murata [fiveinchpixie@gmail.com] -->
The `context-menu` module lets you add items to Firefox's page context menu.
Introduction
------------
The `context-menu` API provides a simple, declarative way to add items to the
page's context menu. You can add items that perform an action when clicked,
submenus, and menu separators.
Instead of manually adding items when particular contexts occur and then
removing them when those contexts go away, you *bind* items to contexts, and the
adding and removing is automatically handled for you. Items are bound to
contexts in much the same way that event listeners are bound to events. When
the user invokes the context menu, all of the items bound to the current context
are automatically added to the menu. If no items are bound, none are added.
Likewise, any items that were previously in the menu but are not bound to the
current context are automatically removed from the menu. You never need to
manually remove your items from the menu unless you want them to never appear
again.
For example, if your add-on needs to add a context menu item whenever the
user visits a certain page, don't create the item when that page loads, and
don't remove it when the page unloads. Rather, create your item only once and
supply a context that matches the target URL.
Context menu items are displayed in the order created or in the case of sub
menus the order added to the sub menu. Menu items for each add-on will be
grouped together automatically. If the total number of menu items in the main
context menu from all add-ons exceeds a certain number (normally 10 but
configurable with the `extensions.addon-sdk.context-menu.overflowThreshold`
preference) all of the menu items will instead appear in an overflow menu to
avoid making the context menu too large.
Specifying Contexts
-------------------
As its name implies, the context menu should be reserved for the occurrence of
specific contexts. Contexts can be related to page content or the page itself,
but they should never be external to the page.
For example, a good use of the menu would be to show an "Edit Image" item when
the user right-clicks an image in the page. A bad use would be to show a
submenu that listed all the user's tabs, since tabs aren't related to the page
or the node the user clicked to open the menu.
### The Page Context
First of all, you may not need to specify a context at all. When a top-level
item does not specify a context, the page context applies. An item that is in a
submenu is visible unless you specify a context.
The *page context* occurs when the user invokes the context menu on a
non-interactive portion of the page. Try right-clicking a blank spot in this
page, or on text. Make sure that no text is selected. The menu that appears
should contain the items "Back", "Forward", "Reload", "Stop", and so on. This
is the page context.
The page context is appropriate when your item acts on the page as a whole. It
does not occur when the user invokes the context menu on a link, image, or other
non-text node, or while a selection exists.
### Declarative Contexts
You can specify some simple, declarative contexts when you create a menu item by
setting the `context` property of the options object passed to its constructor,
like this:
var cm = require("sdk/context-menu");
cm.Item({
label: "My Menu Item",
context: cm.URLContext("*.mozilla.org")
});
These contexts may be specified by calling the following constructors. Each is
exported by the `context-menu` module.
<table>
<tr>
<th>Constructor</th>
<th>Description</th>
</tr>
<tr>
<td><code>
PageContext()
</code></td>
<td>
The page context.
</td>
</tr>
<tr>
<td><code>
SelectionContext()
</code></td>
<td>
This context occurs when the menu is invoked on a page in which the user
has made a selection.
</td>
</tr>
<tr>
<td><code>
SelectorContext(selector)
</code></td>
<td>
This context occurs when the menu is invoked on a node that either matches
<code>selector</code>, a CSS selector, or has an ancestor that matches.
<code>selector</code> may include multiple selectors separated by commas,
e.g., <code>"a[href], img"</code>.
</td>
</tr>
<tr>
<td><code>
URLContext(matchPattern)
</code></td>
<td>
This context occurs when the menu is invoked on pages with particular
URLs. <code>matchPattern</code> is a match pattern string or an array of
match pattern strings. When <code>matchPattern</code> is an array, the
context occurs when the menu is invoked on a page whose URL matches any of
the patterns. These are the same match pattern strings that you use with
the <a href="modules/sdk/page-mod.html"><code>page-mod</code></a>
<code>include</code> property.
<a href="modules/sdk/page-mod/match-pattern.html">Read more about patterns</a>.
</td>
</tr>
<tr>
<td>
array
</td>
<td>
An array of any of the other types. This context occurs when all contexts
in the array occur.
</td>
</tr>
</table>
Menu items also have a `context` property that can be used to add and remove
declarative contexts after construction. For example:
var context = require("sdk/context-menu").SelectorContext("img");
myMenuItem.context.add(context);
myMenuItem.context.remove(context);
When a menu item is bound to more than one context, it appears in the menu when
all of those contexts occur.
### In Content Scripts
The declarative contexts are handy but not very powerful. For instance, you
might want your menu item to appear for any page that has at least one image,
but declarative contexts won't help you there.
When you need more control control over the context in which your menu items are
shown, you can use content scripts. Like other APIs in the SDK, the
`context-menu` API uses
[content scripts](dev-guide/guides/content-scripts/index.html) to let your
add-on interact with pages in the browser. Each menu item you create in the
top-level context menu can have a content script.
A special event named `"context"` is emitted in your content scripts whenever
the context menu is about to be shown. If you register a listener function for
this event and it returns true, the menu item associated with the listener's
content script is shown in the menu.
For example, this item appears whenever the context menu is invoked on a page
that contains at least one image:
require("sdk/context-menu").Item({
label: "This Page Has Images",
contentScript: 'self.on("context", function (node) {' +
' return !!document.querySelector("img");' +
'});'
});
Note that the listener function has a parameter called `node`. This is the node
in the page that the user context-clicked to invoke the menu. You can use it to
determine whether your item should be shown.
You can both specify declarative contexts and listen for contexts in a content
script. In that case, the declarative contexts are evaluated first. If they
are not current, then your context listener is never called.
This example takes advantage of that fact. The listener can be assured that
`node` will always be an image:
var cm = require("sdk/context-menu");
cm.Item({
label: "A Mozilla Image",
context: cm.SelectorContext("img"),
contentScript: 'self.on("context", function (node) {' +
' return /mozilla/.test(node.src);' +
'});'
});
Your item is shown only when all declarative contexts are current and your
context listener returns true.
The content script is executed for every page that a context menu is shown for.
It will be executed the first time it is needed (i.e. when the context menu is
first shown and all of the declarative contexts for your item are current) and
then remains active until you destroy your context menu item or the page is
unloaded.
Handling Menu Item Clicks
-------------------------
In addition to using content scripts to listen for the `"context"` event as
described above, you can use content scripts to handle item clicks. When the
user clicks your menu item, an event named `"click"` is emitted in the item's
content script.
Therefore, to handle an item click, listen for the `"click"` event in that
item's content script like so:
require("sdk/context-menu").Item({
label: "My Item",
contentScript: 'self.on("click", function (node, data) {' +
' console.log("Item clicked!");' +
'});'
});
Note that the listener function has parameters called `node` and `data`. `node`
is the node that the user context-clicked to invoke the menu. You can use it
when performing some action. `data` is the `data` property of the menu item
that was clicked. Note that when you have a hierarchy of menu items the click
event will be sent to the content script of the item clicked and all ancestors
so be sure to verify that the `data` value passed matches the item you expect.
You can use this to simplify click handling by providing just a single click
listener on a `Menu` that reacts to clicks for any child items.:
var cm = require("sdk/context-menu");
cm.Menu({
label: "My Menu",
contentScript: 'self.on("click", function (node, data) {' +
' console.log("You clicked " + data);' +
'});',
items: [
cm.Item({ label: "Item 1", data: "item1" }),
cm.Item({ label: "Item 2", data: "item2" }),
cm.Item({ label: "Item 3", data: "item3" })
]
});
Often you will need to collect some kind of information in the click listener
and perform an action unrelated to content. To communicate to the menu item
associated with the content script, the content script can call the
`postMessage` function attached to the global `self` object, passing it some
JSON-able data. The menu item's `"message"` event listener will be called with
that data.
var cm = require("sdk/context-menu");
cm.Item({
label: "Edit Image",
context: cm.SelectorContext("img"),
contentScript: 'self.on("click", function (node, data) {' +
' self.postMessage(node.src);' +
'});',
onMessage: function (imgSrc) {
openImageEditor(imgSrc);
}
});
Updating a Menu Item's Label
----------------------------
Each menu item must be created with a label, but you can change its label later
using a couple of methods.
The simplest method is to set the menu item's `label` property. This example
updates the item's label based on the number of times it's been clicked:
var numClicks = 0;
var myItem = require("sdk/context-menu").Item({
label: "Click Me: " + numClicks,
contentScript: 'self.on("click", self.postMessage);',
onMessage: function () {
numClicks++;
this.label = "Click Me: " + numClicks;
// Setting myItem.label is equivalent.
}
});
Sometimes you might want to update the label based on the context. For
instance, if your item performs a search with the user's selected text, it would
be nice to display the text in the item to provide feedback to the user. In
these cases you can use the second method. Recall that your content scripts can
listen for the `"context"` event and if your listeners return true, the items
associated with the content scripts are shown in the menu. In addition to
returning true, your `"context"` listeners can also return strings. When a
`"context"` listener returns a string, it becomes the item's new label.
This item implements the aforementioned search example:
var cm = require("sdk/context-menu");
cm.Item({
label: "Search Google",
context: cm.SelectionContext(),
contentScript: 'self.on("context", function () {' +
' var text = window.getSelection().toString();' +
' if (text.length > 20)' +
' text = text.substr(0, 20) + "...";' +
' return "Search Google for " + text;' +
'});'
});
The `"context"` listener gets the window's current selection, truncating it if
it's too long, and includes it in the returned string. When the item is shown,
its label will be "Search Google for `text`", where `text` is the truncated
selection.
More Examples
-------------
For conciseness, these examples create their content scripts as strings and use
the `contentScript` property. In your own add-on, 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](dev-guide/guides/content-scripts/index.html)
for more information.
<div class="warning">
<p>Unless your content script is extremely simple and consists only of a
static string, don't use <code>contentScript</code>: if you do, you may
have problems getting your add-on approved on AMO.</p>
<p>Instead, keep the script in a separate file and load it using
<code>contentScriptFile</code>. This makes your code easier to maintain,
secure, debug and review.</p>
</div>
Show an "Edit Page Source" item when the user right-clicks a non-interactive
part of the page:
require("sdk/context-menu").Item({
label: "Edit Page Source",
contentScript: 'self.on("click", function (node, data) {' +
' self.postMessage(document.URL);' +
'});',
onMessage: function (pageURL) {
editSource(pageURL);
}
});
Show an "Edit Image" item when the menu is invoked on an image:
var cm = require("sdk/context-menu");
cm.Item({
label: "Edit Image",
context: cm.SelectorContext("img"),
contentScript: 'self.on("click", function (node, data) {' +
' self.postMessage(node.src);' +
'});',
onMessage: function (imgSrc) {
openImageEditor(imgSrc);
}
});
Show an "Edit Mozilla Image" item when the menu is invoked on an image in a
mozilla.org or mozilla.com page:
var cm = require("sdk/context-menu");
cm.Item({
label: "Edit Mozilla Image",
context: [
cm.URLContext(["*.mozilla.org", "*.mozilla.com"]),
cm.SelectorContext("img")
],
contentScript: 'self.on("click", function (node, data) {' +
' self.postMessage(node.src);' +
'});',
onMessage: function (imgSrc) {
openImageEditor(imgSrc);
}
});
Show an "Edit Page Images" item when the page contains at least one image:
var cm = require("sdk/context-menu");
cm.Item({
label: "Edit Page Images",
// This ensures the item only appears during the page context.
context: cm.PageContext(),
contentScript: 'self.on("context", function (node) {' +
' var pageHasImgs = !!document.querySelector("img");' +
' return pageHasImgs;' +
'});' +
'self.on("click", function (node, data) {' +
' var imgs = document.querySelectorAll("img");' +
' var imgSrcs = [];' +
' for (var i = 0 ; i < imgs.length; i++)' +
' imgSrcs.push(imgs[i].src);' +
' self.postMessage(imgSrcs);' +
'});',
onMessage: function (imgSrcs) {
openImageEditor(imgSrcs);
}
});
Show a "Search With" menu when the user right-clicks an anchor that searches
Google or Wikipedia with the text contained in the anchor:
var cm = require("sdk/context-menu");
var googleItem = cm.Item({
label: "Google",
data: "http://www.google.com/search?q="
});
var wikipediaItem = cm.Item({
label: "Wikipedia",
data: "http://en.wikipedia.org/wiki/Special:Search?search="
});
var searchMenu = cm.Menu({
label: "Search With",
context: cm.SelectorContext("a[href]"),
contentScript: 'self.on("click", function (node, data) {' +
' var searchURL = data + node.textContent;' +
' window.location.href = searchURL;' +
'});',
items: [googleItem, wikipediaItem]
});
<api name="Item">
@class
A labeled menu item that can perform an action when clicked.
<api name="Item">
@constructor
Creates a labeled menu item that can perform an action when clicked.
@param options {object}
An object with the following keys:
@prop label {string}
The item's label. It must either be a string or an object that implements
`toString()`.
@prop [image] {string}
The item's icon, a string URL. The URL can be remote, a reference to an
image in the add-on's `data` directory, or a data URI.
@prop [data] {string}
An optional arbitrary value to associate with the item. It must be either a
string or an object that implements `toString()`. It will be passed to
click listeners.
@prop [context] {value}
If the item is contained in the top-level context menu, this declaratively
specifies the context under which the item will appear; see Specifying
Contexts above.
@prop [contentScript] {string,array}
If the item is contained in the top-level context menu, this is the content
script or an array of content scripts that the item can use to interact with
the page.
@prop [contentScriptFile] {string,array}
If the item is contained in the top-level context menu, this is the local
file URL of the content script or an array of such URLs that the item can
use to interact with the page.
@prop [onMessage] {function}
If the item is contained in the top-level context menu, this function will
be called when the content script calls `self.postMessage`. It will be
passed the data that was passed to `postMessage`.
</api>
<api name="label">
@property {string}
The menu item's label. You can set this after creating the item to update its
label later.
</api>
<api name="image">
@property {string}
The item's icon, a string URL. The URL can be remote, a reference to an image
in the add-on's `data` directory, or a data URI. You can set this after
creating the item to update its image later. To remove the item's image, set
it to `null`.
</api>
<api name="data">
@property {string}
An optional arbitrary value to associate with the item. It must be either a
string or an object that implements `toString()`. It will be passed to
click listeners. You can set this after creating the item to update its data
later.
</api>
<api name="context">
@property {list}
A list of declarative contexts for which the menu item will appear in the
context menu. Contexts can be added by calling `context.add()` and removed by
called `context.remove()`.
</api>
<api name="parentMenu">
@property {Menu}
The item's parent `Menu`, or `null` if the item is contained in the top-level
context menu. This property is read-only. To add the item to a new menu,
call that menu's `addItem()` method.
</api>
<api name="contentScript">
@property {string,array}
The content script or the array of content scripts associated with the menu
item during creation.
</api>
<api name="contentScriptFile">
@property {string,array}
The URL of a content script or the array of such URLs associated with the menu
item during creation.
</api>
<api name="destroy">
@method
Permanently removes the item from its parent menu and frees its resources.
The item must not be used afterward. If you need to remove the item from its
parent menu but use it afterward, call `removeItem()` on the parent menu
instead.
</api>
<api name="message">
@event
If you listen to this event you can receive message events from content
scripts associated with this menu item. When a content script posts a
message using `self.postMessage()`, the message is delivered to the add-on
code in the menu item's `message` event.
@argument {value}
Listeners are passed a single argument which is the message posted
from the content script. The message can be any
<a href = "dev-guide/guides/content-scripts/using-port.html#json_serializable">JSON-serializable value</a>.
</api>
</api>
<api name="Menu">
@class
A labeled menu item that expands into a submenu.
<api name="Menu">
@constructor
Creates a labeled menu item that expands into a submenu.
@param options {object}
An object with the following keys:
@prop label {string}
The item's label. It must either be a string or an object that implements
`toString()`.
@prop items {array}
An array of menu items that the menu will contain. Each must be an `Item`,
`Menu`, or `Separator`.
@prop [image] {string}
The menu's icon, a string URL. The URL can be remote, a reference to an
image in the add-on's `data` directory, or a data URI.
@prop [context] {value}
If the menu is contained in the top-level context menu, this declaratively
specifies the context under which the menu will appear; see Specifying
Contexts above.
@prop [contentScript] {string,array}
If the menu is contained in the top-level context menu, this is the content
script or an array of content scripts that the menu can use to interact with
the page.
@prop [contentScriptFile] {string,array}
If the menu is contained in the top-level context menu, this is the local
file URL of the content script or an array of such URLs that the menu can
use to interact with the page.
@prop [onMessage] {function}
If the menu is contained in the top-level context menu, this function will
be called when the content script calls `self.postMessage`. It will be
passed the data that was passed to `postMessage`.
</api>
<api name="label">
@property {string}
The menu's label. You can set this after creating the menu to update its
label later.
</api>
<api name="items">
@property {array}
An array containing the items in the menu. The array is read-only, meaning
that modifications to it will not affect the menu. However, setting this
property to a new array will replace all the items currently in the menu with
the items in the new array.
</api>
<api name="image">
@property {string}
The menu's icon, a string URL. The URL can be remote, a reference to an image
in the add-on's `data` directory, or a data URI. You can set this after
creating the menu to update its image later. To remove the menu's image, set
it to `null`.
</api>
<api name="context">
@property {list}
A list of declarative contexts for which the menu will appear in the context
menu. Contexts can be added by calling `context.add()` and removed by called
`context.remove()`.
</api>
<api name="parentMenu">
@property {Menu}
The menu's parent `Menu`, or `null` if the menu is contained in the top-level
context menu. This property is read-only. To add the menu to a new menu,
call that menu's `addItem()` method.
</api>
<api name="contentScript">
@property {string,array}
The content script or the array of content scripts associated with the menu
during creation.
</api>
<api name="contentScriptFile">
@property {string,array}
The URL of a content script or the array of such URLs associated with the menu
during creation.
</api>
<api name="addItem">
@method
Appends a menu item to the end of the menu. If the item is already contained
in another menu or in the top-level context menu, it's automatically removed
first. If the item is already contained in this menu it will just be moved
to the end of the menu.
@param item {Item,Menu,Separator}
The `Item`, `Menu`, or `Separator` to add to the menu.
</api>
<api name="removeItem">
@method
Removes the given menu item from the menu. If the menu does not contain the
item, this method does nothing.
@param item {Item,Menu,Separator}
The menu item to remove from the menu.
</api>
<api name="destroy">
@method
Permanently removes the menu from its parent menu and frees its resources.
The menu must not be used afterward. If you need to remove the menu from its
parent menu but use it afterward, call `removeItem()` on the parent menu
instead.
</api>
<api name="message">
@event
If you listen to this event you can receive message events from content
scripts associated with this menu item. When a content script posts a
message using `self.postMessage()`, the message is delivered to the add-on
code in the menu item's `message` event.
@argument {value}
Listeners are passed a single argument which is the message posted
from the content script. The message can be any
<a href = "dev-guide/guides/content-scripts/using-port.html#json_serializable">JSON-serializable value</a>.
</api>
</api>
<api name="Separator">
@class
A menu separator. Separators can be contained only in `Menu`s, not in the
top-level context menu.
<api name="Separator">
@constructor
Creates a menu separator.
</api>
<api name="parentMenu">
@property {Menu}
The separator's parent `Menu`. This property is read-only. To add the
separator to a new menu, call that menu's `addItem()` method.
</api>
<api name="destroy">
@method
Permanently removes the separator from its parent menu and frees its
resources. The separator must not be used afterward. If you need to remove
the separator from its parent menu but use it afterward, call `removeItem()`
on the parent menu instead.
</api>
</api>
<api name="PageContext">
@class
<api name="PageContext">
@constructor
Creates a page context. See Specifying Contexts above.
</api>
</api>
<api name="SelectionContext">
@class
<api name="SelectionContext">
@constructor
Creates a context that occurs when a page contains a selection. See
Specifying Contexts above.
</api>
</api>
<api name="SelectorContext">
@class
<api name="SelectorContext">
@constructor
Creates a context that matches a given CSS selector. See Specifying Contexts
above.
@param selector {string}
A CSS selector.
</api>
</api>
<api name="URLContext">
@class
<api name="URLContext">
@constructor
Creates a context that matches pages with particular URLs. See Specifying
Contexts above.
@param matchPattern {string,array}
A [match pattern](modules/sdk/page-mod/match-pattern.html) string, regexp or an
array of match pattern strings or regexps.
</api>
</api>

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше