From f6ab6622aab00fe7c2f4c3dc41f786ebbe0f0d73 Mon Sep 17 00:00:00 2001 From: Rogan Creswick Date: Fri, 30 Mar 2012 17:07:02 -0700 Subject: initial revision --- .../python-lib/cuddlefish/__init__.py | 766 +++++++++++++++++++++ .../cuddlefish/app-extension/application.ini | 11 + .../cuddlefish/app-extension/bootstrap.js | 154 +++++ .../cuddlefish/app-extension/components/harness.js | 660 ++++++++++++++++++ .../cuddlefish/app-extension/install.rdf | 31 + tools/addon-sdk-1.3/python-lib/cuddlefish/bunch.py | 30 + .../python-lib/cuddlefish/docs/__init__.py | 0 .../python-lib/cuddlefish/docs/apiparser.py | 388 +++++++++++ .../python-lib/cuddlefish/docs/apirenderer.py | 299 ++++++++ .../python-lib/cuddlefish/docs/generate.py | 230 +++++++ .../python-lib/cuddlefish/docs/renderapi.readme.md | 206 ++++++ .../python-lib/cuddlefish/docs/webdocs.py | 195 ++++++ .../python-lib/cuddlefish/manifest.py | 725 +++++++++++++++++++ .../cuddlefish/mobile-killer/bootstrap.js | 71 ++ .../cuddlefish/mobile-killer/install.rdf | 26 + .../python-lib/cuddlefish/packaging.py | 395 +++++++++++ .../python-lib/cuddlefish/preflight.py | 73 ++ tools/addon-sdk-1.3/python-lib/cuddlefish/prefs.py | 111 +++ tools/addon-sdk-1.3/python-lib/cuddlefish/rdf.py | 172 +++++ .../addon-sdk-1.3/python-lib/cuddlefish/runner.py | 627 +++++++++++++++++ .../python-lib/cuddlefish/templates.py | 79 +++ .../python-lib/cuddlefish/tests/__init__.py | 61 ++ .../packages/explicit-icon/explicit-icon.png | 0 .../packages/explicit-icon/explicit-icon64.png | 0 .../packages/explicit-icon/lib/main.js | 0 .../packages/explicit-icon/package.json | 5 + .../packages/implicit-icon/icon.png | 0 .../packages/implicit-icon/icon64.png | 0 .../packages/implicit-icon/lib/main.js | 0 .../packages/implicit-icon/package.json | 3 + .../bug-588119-files/packages/no-icon/lib/main.js | 0 .../bug-588119-files/packages/no-icon/package.json | 3 + .../packages/bar/lib/bar-loader.js | 0 .../bug-588661-files/packages/bar/package.json | 3 + .../packages/foo/lib/foo-loader.js | 0 .../bug-588661-files/packages/foo/package.json | 4 + .../tests/bug-611495-files/jspath-one/docs/main.md | 1 + .../tests/bug-611495-files/jspath-one/lib/main.js | 4 + .../tests/bug-611495-files/jspath-one/package.json | 5 + .../packages/commonjs-naming/doc/foo.md | 1 + .../packages/commonjs-naming/lib/foo-loader.js | 1 + .../packages/commonjs-naming/package.json | 3 + .../packages/commonjs-naming/test/test-foo.js | 3 + .../packages/original-naming/docs/foo.md | 1 + .../packages/original-naming/lib/foo-loader.js | 1 + .../packages/original-naming/package.json | 3 + .../packages/original-naming/tests/test-foo.js | 3 + .../packages/default-lib/doc/foo.md | 1 + .../packages/default-lib/lib/foo.js | 1 + .../packages/default-lib/lib/loader.js | 1 + .../packages/default-lib/package.json | 3 + .../packages/default-lib/test/test-foo.js | 3 + .../packages/default-root/doc/foo.md | 1 + .../bug-652227-files/packages/default-root/foo.js | 1 + .../packages/default-root/loader.js | 1 + .../packages/default-root/package.json | 3 + .../packages/default-root/test/test-foo.js | 3 + .../packages/explicit-dir-lib/alt-lib/foo.js | 1 + .../packages/explicit-dir-lib/alt-lib/loader.js | 1 + .../packages/explicit-dir-lib/doc/foo.md | 1 + .../packages/explicit-dir-lib/package.json | 4 + .../packages/explicit-dir-lib/test/test-foo.js | 3 + .../packages/explicit-lib/alt2-lib/foo.js | 1 + .../packages/explicit-lib/alt2-lib/loader.js | 1 + .../packages/explicit-lib/doc/foo.md | 1 + .../packages/explicit-lib/package.json | 4 + .../packages/explicit-lib/test/test-foo.js | 3 + .../packages/extra-options/docs/main.md | 1 + .../packages/extra-options/lib/main.js | 4 + .../packages/extra-options/package.json | 6 + .../packages/foo/lib/bar-e10s-adapter.js | 7 + .../e10s-adapter-files/packages/foo/lib/bar.js | 1 + .../e10s-adapter-files/packages/foo/lib/foo.js | 1 + .../e10s-adapter-files/packages/foo/package.json | 1 + .../cuddlefish/tests/linker-files/five/lib/main.js | 1 + .../tests/linker-files/five/package.json | 3 + .../linker-files/four-deps/four-a/lib/misc.js | 1 + .../linker-files/four-deps/four-a/package.json | 4 + .../linker-files/four-deps/four-a/topfiles/main.js | 1 + .../cuddlefish/tests/linker-files/four/lib/main.js | 1 + .../tests/linker-files/four/package.json | 3 + .../cuddlefish/tests/linker-files/one/lib/main.js | 5 + .../tests/linker-files/one/lib/subdir/three.js | 2 + .../cuddlefish/tests/linker-files/one/lib/two.js | 4 + .../cuddlefish/tests/linker-files/one/package.json | 3 + .../tests/linker-files/seven/data/text.data | 1 + .../tests/linker-files/seven/lib/main.js | 2 + .../tests/linker-files/seven/lib/unused.js | 1 + .../tests/linker-files/seven/package.json | 4 + .../tests/linker-files/six/lib/unused.js | 1 + .../cuddlefish/tests/linker-files/six/package.json | 3 + .../tests/linker-files/six/unreachable.js | 1 + .../linker-files/three-deps/three-a/data/msg.txt | 1 + .../three-deps/three-a/data/subdir/submsg.txt | 1 + .../linker-files/three-deps/three-a/lib/main.js | 4 + .../three-deps/three-a/lib/subdir/subfile.js | 1 + .../linker-files/three-deps/three-a/lib/unused.js | 1 + .../linker-files/three-deps/three-a/package.json | 3 + .../linker-files/three-deps/three-b/lib/main.js | 1 + .../linker-files/three-deps/three-b/package.json | 3 + .../linker-files/three-deps/three-c/lib/main.js | 1 + .../linker-files/three-deps/three-c/lib/sub/foo.js | 2 + .../linker-files/three-deps/three-c/package.json | 3 + .../tests/linker-files/three/lib/main.js | 4 + .../tests/linker-files/three/package.json | 3 + .../static-files/doc/dev-guide-source/no_h1.md | 3 + .../static-files/doc/dev-guide-source/welcome.md | 3 + .../static-files/doc/static-files/another.html | 1 + .../tests/static-files/doc/static-files/base.html | 136 ++++ .../tests/static-files/doc/static-files/index.html | 23 + .../tests/static-files/docs/APIreference.html | 465 +++++++++++++ .../tests/static-files/docs/APIsample.md | 158 +++++ .../packages/aardvark/doc/aardvark-feeder.md | 8 + .../static-files/packages/aardvark/doc/main.md | 0 .../static-files/packages/aardvark/lib/ignore_me | 3 + .../static-files/packages/aardvark/lib/main.js | 4 + .../aardvark/lib/surprise.js/ignore_me_too | 2 + .../static-files/packages/aardvark/package.json | 7 + .../packages/anteater_files/lib/main.js | 4 + .../packages/anteater_files/package.json | 8 + .../static-files/packages/api-utils/lib/loader.js | 3 + .../static-files/packages/api-utils/package.json | 5 + .../packages/barbeque/lib/bar-module.js | 3 + .../static-files/packages/barbeque/package.json | 4 + .../static-files/packages/minimal/docs/main.md | 1 + .../static-files/packages/minimal/lib/main.js | 4 + .../static-files/packages/minimal/package.json | 4 + .../xpi-template/components/harness.js | 4 + .../python-lib/cuddlefish/tests/test_apiparser.py | 524 ++++++++++++++ .../cuddlefish/tests/test_apirenderer.py | 28 + .../python-lib/cuddlefish/tests/test_generate.py | 76 ++ .../python-lib/cuddlefish/tests/test_init.py | 85 +++ .../python-lib/cuddlefish/tests/test_linker.py | 200 ++++++ .../python-lib/cuddlefish/tests/test_manifest.py | 256 +++++++ .../python-lib/cuddlefish/tests/test_packaging.py | 105 +++ .../python-lib/cuddlefish/tests/test_preflight.py | 143 ++++ .../python-lib/cuddlefish/tests/test_rdf.py | 15 + .../python-lib/cuddlefish/tests/test_runner.py | 24 + .../python-lib/cuddlefish/tests/test_util.py | 18 + .../python-lib/cuddlefish/tests/test_version.py | 32 + .../python-lib/cuddlefish/tests/test_webdocs.py | 92 +++ .../python-lib/cuddlefish/tests/test_xpi.py | 253 +++++++ tools/addon-sdk-1.3/python-lib/cuddlefish/util.py | 19 + .../addon-sdk-1.3/python-lib/cuddlefish/version.py | 6 + .../python-lib/cuddlefish/version_comparator.py | 202 ++++++ tools/addon-sdk-1.3/python-lib/cuddlefish/xpi.py | 105 +++ 146 files changed, 8518 insertions(+) create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/__init__.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/app-extension/application.ini create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/app-extension/bootstrap.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/app-extension/components/harness.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/app-extension/install.rdf create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/bunch.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/docs/__init__.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/docs/apiparser.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/docs/apirenderer.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/docs/generate.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/docs/renderapi.readme.md create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/docs/webdocs.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/manifest.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/mobile-killer/bootstrap.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/mobile-killer/install.rdf create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/packaging.py create mode 100755 tools/addon-sdk-1.3/python-lib/cuddlefish/preflight.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/prefs.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/rdf.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/runner.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/templates.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/__init__.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/explicit-icon.png create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/explicit-icon64.png create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/lib/main.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/icon.png create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/icon64.png create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/lib/main.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/no-icon/lib/main.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/no-icon/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588661-files/packages/bar/lib/bar-loader.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588661-files/packages/bar/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588661-files/packages/foo/lib/foo-loader.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588661-files/packages/foo/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/docs/main.md create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/lib/main.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/doc/foo.md create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/lib/foo-loader.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/test/test-foo.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/docs/foo.md create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/lib/foo-loader.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/tests/test-foo.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/doc/foo.md create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/lib/foo.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/lib/loader.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/test/test-foo.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/doc/foo.md create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/foo.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/loader.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/test/test-foo.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/alt-lib/foo.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/alt-lib/loader.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/doc/foo.md create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/test/test-foo.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/alt2-lib/foo.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/alt2-lib/loader.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/doc/foo.md create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/test/test-foo.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/docs/main.md create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/lib/main.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/bar-e10s-adapter.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/bar.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/foo.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/five/lib/main.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/five/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/lib/misc.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/topfiles/main.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/four/lib/main.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/four/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/one/lib/main.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/one/lib/subdir/three.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/one/lib/two.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/one/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/seven/data/text.data create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/seven/lib/main.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/seven/lib/unused.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/seven/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/six/lib/unused.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/six/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/six/unreachable.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/data/msg.txt create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/data/subdir/submsg.txt create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/main.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/subdir/subfile.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/unused.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-b/lib/main.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-b/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/lib/main.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/lib/sub/foo.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three/lib/main.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/doc/dev-guide-source/no_h1.md create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/doc/dev-guide-source/welcome.md create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/doc/static-files/another.html create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/doc/static-files/base.html create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/doc/static-files/index.html create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/docs/APIreference.html create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/docs/APIsample.md create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/aardvark/doc/aardvark-feeder.md create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/aardvark/doc/main.md create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/ignore_me create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/main.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/surprise.js/ignore_me_too create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/aardvark/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/anteater_files/lib/main.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/anteater_files/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/api-utils/lib/loader.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/api-utils/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/barbeque/lib/bar-module.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/barbeque/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/minimal/docs/main.md create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/minimal/lib/main.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/minimal/package.json create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/xpi-template/components/harness.js create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_apiparser.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_apirenderer.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_generate.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_init.py create mode 100755 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_linker.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_manifest.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_packaging.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_preflight.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_rdf.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_runner.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_util.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_version.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_webdocs.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_xpi.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/util.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/version.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/version_comparator.py create mode 100644 tools/addon-sdk-1.3/python-lib/cuddlefish/xpi.py (limited to 'tools/addon-sdk-1.3/python-lib/cuddlefish') diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/__init__.py b/tools/addon-sdk-1.3/python-lib/cuddlefish/__init__.py new file mode 100644 index 0000000..a576721 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/__init__.py @@ -0,0 +1,766 @@ +import sys +import os +import optparse +import webbrowser + +from copy import copy +import simplejson as json +from cuddlefish import packaging +from cuddlefish.bunch import Bunch +from cuddlefish.version import get_version + +MOZRUNNER_BIN_NOT_FOUND = 'Mozrunner could not locate your binary' +MOZRUNNER_BIN_NOT_FOUND_HELP = """ +I can't find the application binary in any of its default locations +on your system. Please specify one using the -b/--binary option. +""" + +UPDATE_RDF_FILENAME = "%s.update.rdf" +XPI_FILENAME = "%s.xpi" + +usage = """ +%prog [options] command [command-specific options] + +Supported Commands: + docs - view web-based documentation + init - create a sample addon in an empty directory + test - run tests + run - run program + xpi - generate an xpi + +Internal Commands: + sdocs - export static documentation + testcfx - test the cfx tool + testex - test all example code + testpkgs - test all installed packages + testall - test whole environment + +Experimental and internal commands and options are not supported and may be +changed or removed in the future. +""" + +global_options = [ + (("-v", "--verbose",), dict(dest="verbose", + help="enable lots of output", + action="store_true", + default=False)), + ] + +parser_groups = ( + ("Supported Command-Specific Options", [ + (("", "--update-url",), dict(dest="update_url", + help="update URL in install.rdf", + metavar=None, + default=None, + cmds=['xpi'])), + (("", "--update-link",), dict(dest="update_link", + help="generate update.rdf", + metavar=None, + default=None, + cmds=['xpi'])), + (("-p", "--profiledir",), dict(dest="profiledir", + help=("profile directory to pass to " + "app"), + metavar=None, + default=None, + cmds=['test', 'run', 'testex', + 'testpkgs', 'testall'])), + (("-b", "--binary",), dict(dest="binary", + help="path to app binary", + metavar=None, + default=None, + cmds=['test', 'run', 'testex', 'testpkgs', + 'testall'])), + (("", "--binary-args",), dict(dest="cmdargs", + help=("additional arguments passed to the " + "binary"), + metavar=None, + default=None, + cmds=['run', 'test'])), + (("", "--dependencies",), dict(dest="dep_tests", + help="include tests for all deps", + action="store_true", + default=False, + cmds=['test', 'testex', 'testpkgs', + 'testall'])), + (("", "--times",), dict(dest="iterations", + type="int", + help="number of times to run tests", + default=1, + cmds=['test', 'testex', 'testpkgs', + 'testall'])), + (("-f", "--filter",), dict(dest="filter", + help=("only run tests whose filenames " + "match FILENAME and optionally " + "match TESTNAME, both regexps"), + metavar="FILENAME[:TESTNAME]", + default=None, + cmds=['test', 'testex', 'testpkgs', + 'testall'])), + (("-g", "--use-config",), dict(dest="config", + help="use named config from local.json", + metavar=None, + default="default", + cmds=['test', 'run', 'xpi', 'testex', + 'testpkgs', 'testall'])), + (("", "--templatedir",), dict(dest="templatedir", + help="XULRunner app/ext. template", + metavar=None, + default=None, + cmds=['run', 'xpi'])), + (("", "--package-path",), dict(dest="packagepath", action="append", + help="extra directories for package search", + metavar=None, + default=[], + cmds=['run', 'xpi', 'test'])), + (("", "--extra-packages",), dict(dest="extra_packages", + help=("extra packages to include, " + "comma-separated. Default is " + "'addon-kit'."), + metavar=None, + default="addon-kit", + cmds=['run', 'xpi', 'test', 'testex', + 'testpkgs', 'testall', + 'testcfx'])), + (("", "--pkgdir",), dict(dest="pkgdir", + help=("package dir containing " + "package.json; default is " + "current directory"), + metavar=None, + default=None, + cmds=['run', 'xpi', 'test'])), + (("", "--static-args",), dict(dest="static_args", + help="extra harness options as JSON", + type="json", + metavar=None, + default="{}", + cmds=['run', 'xpi'])), + ] + ), + + ("Experimental Command-Specific Options", [ + (("-a", "--app",), dict(dest="app", + help=("app to run: firefox (default), fennec, " + "fennec-on-device, xulrunner or " + "thunderbird"), + metavar=None, + default="firefox", + cmds=['test', 'run', 'testex', 'testpkgs', + 'testall'])), + (("", "--no-run",), dict(dest="no_run", + help=("Instead of launching the " + "application, just show the command " + "for doing so. Use this to launch " + "the application in a debugger like " + "gdb."), + action="store_true", + default=False, + cmds=['run', 'test'])), + (("", "--no-strip-xpi",), dict(dest="no_strip_xpi", + help="retain unused modules in XPI", + action="store_true", + default=False, + cmds=['xpi'])), + (("", "--force-mobile",), dict(dest="enable_mobile", + help="Force compatibility with Firefox Mobile", + action="store_true", + default=False, + cmds=['run', 'test', 'xpi', 'testall'])), + (("", "--mobile-app",), dict(dest="mobile_app_name", + help=("Name of your Android application to " + "use. Possible values: 'firefox', " + "'firefox_beta', 'firefox_nightly'."), + metavar=None, + default=None, + cmds=['run', 'test', 'testall'])), + (("", "--harness-option",), dict(dest="extra_harness_option_args", + help=("Extra properties added to " + "harness-options.json"), + action="append", + metavar="KEY=VALUE", + default=[], + cmds=['xpi'])), + ] + ), + + ("Internal Command-Specific Options", [ + (("", "--addons",), dict(dest="addons", + help=("paths of addons to install, " + "comma-separated"), + metavar=None, + default=None, + cmds=['test', 'run', 'testex', 'testpkgs', + 'testall'])), + (("", "--baseurl",), dict(dest="baseurl", + help=("root of static docs tree: " + "for example: 'http://me.com/the_docs/'"), + metavar=None, + default='', + cmds=['sdocs'])), + (("", "--test-runner-pkg",), dict(dest="test_runner_pkg", + help=("name of package " + "containing test runner " + "program (default is " + "test-harness)"), + default="test-harness", + cmds=['test', 'testex', 'testpkgs', + 'testall'])), + # --keydir was removed in 1.0b5, but we keep it around in the options + # parser to make life easier for frontends like FlightDeck which + # might still pass it. It can go away once the frontends are updated. + (("", "--keydir",), dict(dest="keydir", + help=("obsolete, ignored"), + metavar=None, + default=None, + cmds=['test', 'run', 'xpi', 'testex', + 'testpkgs', 'testall'])), + (("", "--e10s",), dict(dest="enable_e10s", + help="enable out-of-process Jetpacks", + action="store_true", + default=False, + cmds=['test', 'run', 'testex', 'testpkgs'])), + (("", "--logfile",), dict(dest="logfile", + help="log console output to file", + metavar=None, + default=None, + cmds=['run', 'test', 'testex', 'testpkgs'])), + # TODO: This should default to true once our memory debugging + # issues are resolved; see bug 592774. + (("", "--profile-memory",), dict(dest="profileMemory", + help=("profile memory usage " + "(default is false)"), + type="int", + action="store", + default=0, + cmds=['test', 'testex', 'testpkgs', + 'testall'])), + ] + ), + ) + +def find_parent_package(cur_dir): + tail = True + while tail: + if os.path.exists(os.path.join(cur_dir, 'package.json')): + return cur_dir + cur_dir, tail = os.path.split(cur_dir) + return None + +def check_json(option, opt, value): + # We return the parsed JSON here; see bug 610816 for background on why. + try: + return json.loads(value) + except ValueError: + raise optparse.OptionValueError("Option %s must be JSON." % opt) + +class CfxOption(optparse.Option): + TYPES = optparse.Option.TYPES + ('json',) + TYPE_CHECKER = copy(optparse.Option.TYPE_CHECKER) + TYPE_CHECKER['json'] = check_json + +def parse_args(arguments, global_options, usage, parser_groups, defaults=None): + parser = optparse.OptionParser(usage=usage.strip(), option_class=CfxOption) + + def name_cmp(a, b): + # a[0] = name sequence + # a[0][0] = short name (possibly empty string) + # a[0][1] = long name + names = [] + for seq in (a, b): + names.append(seq[0][0][1:] if seq[0][0] else seq[0][1][2:]) + return cmp(*names) + + global_options.sort(name_cmp) + for names, opts in global_options: + parser.add_option(*names, **opts) + + for group_name, options in parser_groups: + group = optparse.OptionGroup(parser, group_name) + options.sort(name_cmp) + for names, opts in options: + if 'cmds' in opts: + cmds = opts['cmds'] + del opts['cmds'] + cmds.sort() + if not 'help' in opts: + opts['help'] = "" + opts['help'] += " (%s)" % ", ".join(cmds) + group.add_option(*names, **opts) + parser.add_option_group(group) + + if defaults: + parser.set_defaults(**defaults) + + (options, args) = parser.parse_args(args=arguments) + + if not args: + parser.print_help() + parser.exit() + + return (options, args) + +# all tests emit progress messages to stderr, not stdout. (the mozrunner +# console output goes to stderr and is hard to change, and +# unittest.TextTestRunner prefers stderr, so we send everything else there +# too, to keep all the messages in order) + +def test_all(env_root, defaults): + fail = False + + print >>sys.stderr, "Testing cfx..." + sys.stderr.flush() + result = test_cfx(env_root, defaults['verbose']) + if result.failures or result.errors: + fail = True + + print >>sys.stderr, "Testing all examples..." + sys.stderr.flush() + + try: + test_all_examples(env_root, defaults) + except SystemExit, e: + fail = (e.code != 0) or fail + + print >>sys.stderr, "Testing all packages..." + sys.stderr.flush() + try: + test_all_packages(env_root, defaults) + except SystemExit, e: + fail = (e.code != 0) or fail + + if fail: + print >>sys.stderr, "Some tests were unsuccessful." + sys.exit(1) + print >>sys.stderr, "All tests were successful. Ship it!" + sys.exit(0) + +def test_cfx(env_root, verbose): + import cuddlefish.tests + + # tests write to stderr. flush everything before and after to avoid + # confusion later. + sys.stdout.flush(); sys.stderr.flush() + olddir = os.getcwd() + os.chdir(env_root) + retval = cuddlefish.tests.run(verbose) + os.chdir(olddir) + sys.stdout.flush(); sys.stderr.flush() + return retval + +def test_all_examples(env_root, defaults): + examples_dir = os.path.join(env_root, "examples") + examples = [dirname for dirname in os.listdir(examples_dir) + if os.path.isdir(os.path.join(examples_dir, dirname))] + examples.sort() + fail = False + for dirname in examples: + print >>sys.stderr, "Testing %s..." % dirname + sys.stderr.flush() + try: + run(arguments=["test", + "--pkgdir", + os.path.join(examples_dir, dirname)], + defaults=defaults, + env_root=env_root) + except SystemExit, e: + fail = (e.code != 0) or fail + + if fail: + sys.exit(-1) + +def test_all_packages(env_root, defaults): + deps = [] + target_cfg = Bunch(name = "testpkgs", dependencies = deps, version="fake") + pkg_cfg = packaging.build_config(env_root, target_cfg) + for name in pkg_cfg.packages: + if name != "testpkgs": + deps.append(name) + print >>sys.stderr, "Testing all available packages: %s." % (", ".join(deps)) + sys.stderr.flush() + run(arguments=["test", "--dependencies"], + target_cfg=target_cfg, + pkg_cfg=pkg_cfg, + defaults=defaults) + +def get_config_args(name, env_root): + local_json = os.path.join(env_root, "local.json") + if not (os.path.exists(local_json) and + os.path.isfile(local_json)): + if name == "default": + return [] + else: + print >>sys.stderr, "File does not exist: %s" % local_json + sys.exit(1) + local_json = packaging.load_json_file(local_json) + if 'configs' not in local_json: + print >>sys.stderr, "'configs' key not found in local.json." + sys.exit(1) + if name not in local_json.configs: + if name == "default": + return [] + else: + print >>sys.stderr, "No config found for '%s'." % name + sys.exit(1) + config = local_json.configs[name] + if type(config) != list: + print >>sys.stderr, "Config for '%s' must be a list of strings." % name + sys.exit(1) + return config + +def initializer(env_root, args, out=sys.stdout, err=sys.stderr): + from templates import MAIN_JS, PACKAGE_JSON, README_DOC, MAIN_JS_DOC, TEST_MAIN_JS + path = os.getcwd() + addon = os.path.basename(path) + # if more than one argument + if len(args) > 1: + print >>err, 'Too many arguments.' + return 1 + # avoid clobbering existing files, but we tolerate things like .git + existing = [fn for fn in os.listdir(path) if not fn.startswith(".")] + if existing: + print >>err, 'This command must be run in an empty directory.' + return 1 + for d in ['lib','data','test','doc']: + os.mkdir(os.path.join(path,d)) + print >>out, '*', d, 'directory created' + open('README.md','w').write(README_DOC % {'name':addon}) + print >>out, '* README.md written' + open('package.json','w').write(PACKAGE_JSON % {'name':addon.lower(), + 'fullName':addon }) + print >>out, '* package.json written' + open(os.path.join(path,'test','test-main.js'),'w').write(TEST_MAIN_JS) + print >>out, '* test/test-main.js written' + open(os.path.join(path,'lib','main.js'),'w').write(MAIN_JS) + print >>out, '* lib/main.js written' + open(os.path.join(path,'doc','main.md'),'w').write(MAIN_JS_DOC) + print >>out, '* doc/main.md written' + print >>out, '\nYour sample add-on is now ready.' + print >>out, 'Do "cfx test" to test it and "cfx run" to try it. Have fun!' + return 0 + +def run(arguments=sys.argv[1:], target_cfg=None, pkg_cfg=None, + defaults=None, env_root=os.environ.get('CUDDLEFISH_ROOT'), + stdout=sys.stdout): + parser_kwargs = dict(arguments=arguments, + global_options=global_options, + parser_groups=parser_groups, + usage=usage, + defaults=defaults) + + (options, args) = parse_args(**parser_kwargs) + + config_args = get_config_args(options.config, env_root); + + # reparse configs with arguments from local.json + if config_args: + parser_kwargs['arguments'] += config_args + (options, args) = parse_args(**parser_kwargs) + + command = args[0] + + if command == "init": + initializer(env_root, args) + return + if command == "testpkgs": + test_all_packages(env_root, defaults=options.__dict__) + return + elif command == "testex": + test_all_examples(env_root, defaults=options.__dict__) + return + elif command == "testall": + test_all(env_root, defaults=options.__dict__) + return + elif command == "testcfx": + test_cfx(env_root, options.verbose) + return + elif command == "docs": + from cuddlefish.docs import generate + if len(args) > 1: + docs_home = generate.generate_docs(env_root, filename=args[1]) + else: + docs_home = generate.generate_docs(env_root) + webbrowser.open(docs_home) + return + elif command == "sdocs": + from cuddlefish.docs import generate + + # TODO: Allow user to change this filename via cmd line. + filename = generate.generate_static_docs(env_root, base_url=options.baseurl) + print >>stdout, "Wrote %s." % filename + return + + target_cfg_json = None + if not target_cfg: + if not options.pkgdir: + options.pkgdir = find_parent_package(os.getcwd()) + if not options.pkgdir: + print >>sys.stderr, ("cannot find 'package.json' in the" + " current directory or any parent.") + sys.exit(1) + else: + options.pkgdir = os.path.abspath(options.pkgdir) + if not os.path.exists(os.path.join(options.pkgdir, 'package.json')): + print >>sys.stderr, ("cannot find 'package.json' in" + " %s." % options.pkgdir) + sys.exit(1) + + target_cfg_json = os.path.join(options.pkgdir, 'package.json') + target_cfg = packaging.get_config_in_dir(options.pkgdir) + + # At this point, we're either building an XPI or running Jetpack code in + # a Mozilla application (which includes running tests). + + use_main = False + inherited_options = ['verbose', 'enable_e10s'] + enforce_timeouts = False + + if command == "xpi": + use_main = True + elif command == "test": + if 'tests' not in target_cfg: + target_cfg['tests'] = [] + inherited_options.extend(['iterations', 'filter', 'profileMemory']) + enforce_timeouts = True + elif command == "run": + use_main = True + else: + print >>sys.stderr, "Unknown command: %s" % command + print >>sys.stderr, "Try using '--help' for assistance." + sys.exit(1) + + if use_main and 'main' not in target_cfg: + # If the user supplies a template dir, then the main + # program may be contained in the template. + if not options.templatedir: + print >>sys.stderr, "package.json does not have a 'main' entry." + sys.exit(1) + + if not pkg_cfg: + pkg_cfg = packaging.build_config(env_root, target_cfg, options.packagepath) + + target = target_cfg.name + + # the harness_guid is used for an XPCOM class ID. We use the + # JetpackID for the add-on ID and the XPCOM contract ID. + if "harnessClassID" in target_cfg: + # For the sake of non-bootstrapped extensions, we allow to specify the + # classID of harness' XPCOM component in package.json. This makes it + # possible to register the component using a static chrome.manifest file + harness_guid = target_cfg["harnessClassID"] + else: + import uuid + harness_guid = str(uuid.uuid4()) + + # TODO: Consider keeping a cache of dynamic UUIDs, based + # on absolute filesystem pathname, in the root directory + # or something. + if command in ('xpi', 'run'): + from cuddlefish.preflight import preflight_config + if target_cfg_json: + config_was_ok, modified = preflight_config(target_cfg, + target_cfg_json) + if not config_was_ok: + if modified: + # we need to re-read package.json . The safest approach + # is to re-run the "cfx xpi"/"cfx run" command. + print >>sys.stderr, ("package.json modified: please re-run" + " 'cfx %s'" % command) + else: + print >>sys.stderr, ("package.json needs modification:" + " please update it and then re-run" + " 'cfx %s'" % command) + sys.exit(1) + # if we make it this far, we have a JID + else: + assert command == "test" + + if "id" in target_cfg: + jid = target_cfg["id"] + else: + jid = harness_guid + if not ("@" in jid or jid.startswith("{")): + jid = jid + "@jetpack" + unique_prefix = '%s-' % jid # used for resource: URLs + bundle_id = jid + + # the resource: URL's prefix is treated too much like a DNS hostname + unique_prefix = unique_prefix.lower() + unique_prefix = unique_prefix.replace("@", "-at-") + unique_prefix = unique_prefix.replace(".", "-dot-") + + targets = [target] + if command == "test": + targets.append(options.test_runner_pkg) + + extra_packages = [] + if options.extra_packages: + extra_packages = options.extra_packages.split(",") + if extra_packages: + targets.extend(extra_packages) + target_cfg.extra_dependencies = extra_packages + + deps = packaging.get_deps_for_targets(pkg_cfg, targets) + + from cuddlefish.manifest import build_manifest, ModuleNotFoundError + uri_prefix = "resource://%s" % unique_prefix + # Figure out what loader files should be scanned. This is normally + # computed inside packaging.generate_build_for_target(), by the first + # dependent package that defines a "loader" property in its package.json. + # This property is interpreted as a filename relative to the top of that + # file, and stored as a URI in build.loader . generate_build_for_target() + # cannot be called yet (it needs the list of used_deps that + # build_manifest() computes, but build_manifest() needs the list of + # loader files that it computes). We could duplicate or factor out this + # build.loader logic, but that would be messy, so instead we hard-code + # the choice of loader for manifest-generation purposes. In practice, + # this means that alternative loaders probably won't work with + # --strip-xpi. + assert packaging.DEFAULT_LOADER == "api-utils" + assert pkg_cfg.packages["api-utils"].loader == "lib/cuddlefish.js" + cuddlefish_js_path = os.path.join(pkg_cfg.packages["api-utils"].root_dir, + "lib", "cuddlefish.js") + loader_modules = [("api-utils", "lib", "cuddlefish", cuddlefish_js_path)] + scan_tests = command == "test" + try: + manifest = build_manifest(target_cfg, pkg_cfg, deps, uri_prefix, scan_tests, + loader_modules) + except ModuleNotFoundError, e: + print str(e) + sys.exit(1) + used_deps = manifest.get_used_packages() + if command == "test": + # The test runner doesn't appear to link against any actual packages, + # because it loads everything at runtime (invisible to the linker). + # If we believe that, we won't set up URI mappings for anything, and + # tests won't be able to run. + used_deps = deps + for xp in extra_packages: + if xp not in used_deps: + used_deps.append(xp) + + build = packaging.generate_build_for_target( + pkg_cfg, target, used_deps, + prefix=unique_prefix, # used to create resource: URLs + include_dep_tests=options.dep_tests + ) + + if 'resources' in build: + resources = build.resources + for name in resources: + resources[name] = os.path.abspath(resources[name]) + + harness_contract_id = ('@mozilla.org/harness-service;1?id=%s' % jid) + harness_options = { + 'bootstrap': { + 'contractID': harness_contract_id, + 'classID': '{%s}' % harness_guid + }, + 'jetpackID': jid, + 'bundleID': bundle_id, + 'uriPrefix': uri_prefix, + 'staticArgs': options.static_args, + 'name': target, + } + + harness_options.update(build) + + if command == "test": + # This should be contained in the test runner package. + harness_options['main'] = 'test-harness/run-tests' + else: + harness_options['main'] = target_cfg.get('main') + + for option in inherited_options: + harness_options[option] = getattr(options, option) + + harness_options['metadata'] = packaging.get_metadata(pkg_cfg, used_deps) + + sdk_version = get_version(env_root) + harness_options['sdkVersion'] = sdk_version + + packaging.call_plugins(pkg_cfg, used_deps) + + retval = 0 + + if options.templatedir: + app_extension_dir = os.path.abspath(options.templatedir) + else: + mydir = os.path.dirname(os.path.abspath(__file__)) + app_extension_dir = os.path.join(mydir, "app-extension") + + harness_options['manifest'] = manifest.get_harness_options_manifest(uri_prefix) + + from cuddlefish.rdf import gen_manifest, RDFUpdate + + manifest_rdf = gen_manifest(template_root_dir=app_extension_dir, + target_cfg=target_cfg, + bundle_id=bundle_id, + update_url=options.update_url, + bootstrap=True, + enable_mobile=options.enable_mobile) + + if command == "xpi" and options.update_link: + rdf_name = UPDATE_RDF_FILENAME % target_cfg.name + print >>stdout, "Exporting update description to %s." % rdf_name + update = RDFUpdate() + update.add(manifest_rdf, options.update_link) + open(rdf_name, "w").write(str(update)) + + # ask the manifest what files were used, so we can construct an XPI + # without the rest. This will include the loader (and everything it + # uses) because of the "loader_modules" starting points we passed to + # build_manifest earlier + used_files = None + if command == "xpi": + used_files = set(manifest.get_used_files()) + + if options.no_strip_xpi: + used_files = None # disables the filter, includes all files + + if command == 'xpi': + from cuddlefish.xpi import build_xpi + extra_harness_options = {} + for kv in options.extra_harness_option_args: + key,value = kv.split("=", 1) + extra_harness_options[key] = value + xpi_path = XPI_FILENAME % target_cfg.name + print >>stdout, "Exporting extension to %s." % xpi_path + build_xpi(template_root_dir=app_extension_dir, + manifest=manifest_rdf, + xpi_path=xpi_path, + harness_options=harness_options, + limit_to=used_files, + extra_harness_options=extra_harness_options) + else: + from cuddlefish.runner import run_app + + if options.profiledir: + options.profiledir = os.path.expanduser(options.profiledir) + options.profiledir = os.path.abspath(options.profiledir) + + if options.addons is not None: + options.addons = options.addons.split(",") + + try: + retval = run_app(harness_root_dir=app_extension_dir, + manifest_rdf=manifest_rdf, + harness_options=harness_options, + app_type=options.app, + binary=options.binary, + profiledir=options.profiledir, + verbose=options.verbose, + enforce_timeouts=enforce_timeouts, + logfile=options.logfile, + addons=options.addons, + args=options.cmdargs, + norun=options.no_run, + used_files=used_files, + enable_mobile=options.enable_mobile, + mobile_app_name=options.mobile_app_name) + except Exception, e: + if str(e).startswith(MOZRUNNER_BIN_NOT_FOUND): + print >>sys.stderr, MOZRUNNER_BIN_NOT_FOUND_HELP.strip() + retval = -1 + else: + raise + sys.exit(retval) diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/app-extension/application.ini b/tools/addon-sdk-1.3/python-lib/cuddlefish/app-extension/application.ini new file mode 100644 index 0000000..6cec69a --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/app-extension/application.ini @@ -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.* diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/app-extension/bootstrap.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/app-extension/bootstrap.js new file mode 100644 index 0000000..2d72ec2 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/app-extension/bootstrap.js @@ -0,0 +1,154 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Weave. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dan Mills + * Atul Varma + * Drew Willcoxon + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +"use strict"; + +// For more information on the context in which this script is executed, see: +// https://developer.mozilla.org/en/Extensions/Bootstrapped_extensions + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; +const Cr = Components.results; + +// Object containing information about the XPCOM harness service +// that manages our addon. + +var gHarness; + +var ios = Cc['@mozilla.org/network/io-service;1'] + .getService(Ci.nsIIOService); + +var manager = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); + +// Dynamically evaluate and initialize the XPCOM component in +// components/harness.js, which bootstraps our addon. (We want to keep +// components/harness.js around so that versions of Gecko that don't +// support rebootless addons can still work.) + +function setupHarness(installPath, loadReason) { + var harnessJs = installPath.clone(); + harnessJs.append("components"); + harnessJs.append("harness.js"); + var path = ios.newFileURI(harnessJs).spec; + var harness = {}; + var loader = Cc["@mozilla.org/moz/jssubscript-loader;1"] + .getService(Ci.mozIJSSubScriptLoader); + loader.loadSubScript(path, harness); + + var HarnessService = harness.buildHarnessService(installPath); + var factory = HarnessService.prototype._xpcom_factory; + var proto = HarnessService.prototype; + + // We want to keep this factory around for the lifetime of + // the addon so legacy code with access to Components can + // access the addon if needed. + manager.registerFactory(proto.classID, + proto.classDescription, + proto.contractID, + factory); + + var harnessService = factory.createInstance(null, Ci.nsISupports); + harnessService = harnessService.wrappedJSObject; + + gHarness = { + service: harnessService, + classID: proto.classID, + contractID: proto.contractID, + factory: factory + }; + + if (loadReason == "startup") + // Simulate a startup event; the harness service will take care of + // waiting until the app is ready for the extension's code to run. + harnessService.observe(null, "profile-after-change", null); + else + harnessService.load(loadReason); +} + +function reasonToString(reason) { + // If you change these names, change them in harness.js's lifeCycleObserver192 + // too. + switch (reason) { + case ADDON_INSTALL: + return "install"; + case ADDON_UNINSTALL: + return "uninstall"; + case ADDON_ENABLE: + return "enable"; + case ADDON_DISABLE: + return "disable"; + case ADDON_UPGRADE: + return "upgrade"; + case ADDON_DOWNGRADE: + return "downgrade"; + // The startup and shutdown strings are also used outside of + // lifeCycleObserver192. + case APP_STARTUP: + return "startup"; + case APP_SHUTDOWN: + return "shutdown"; + } + return undefined; +} + +function install(data, reason) { + // We shouldn't start up here; startup() will always be called when + // an extension should load, and install() sometimes gets called when + // an extension has been installed but is disabled. +} + +function startup(data, reason) { + if (!gHarness) + setupHarness(data.installPath, reasonToString(reason)); +} + +function shutdown(data, reason) { + if (gHarness) { + var harness = gHarness; + gHarness = undefined; + harness.service.unload(reasonToString(reason)); + manager.unregisterFactory(harness.classID, harness.factory); + } +} + +function uninstall(data, reason) { + // We shouldn't shutdown here; shutdown() will always be called when + // an extension should shutdown, and uninstall() sometimes gets + // called when startup() has never been called before it. +} diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/app-extension/components/harness.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/app-extension/components/harness.js new file mode 100644 index 0000000..23030e9 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/app-extension/components/harness.js @@ -0,0 +1,660 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Weave. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dan Mills + * Atul Varma + * Drew Willcoxon + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +// This file contains an XPCOM component which "bootstraps" a Jetpack +// program. +// +// The main entry point, `NSGetModule()`, is data-driven, and obtains +// a lot of its configuration information from a JSON file +// called `harness-options.json` in the root directory of the extension +// or application it's a part of. +// +// `NSGetModule()` then uses this configuration information to +// dynamically create an XPCOM component called a "Harness Service", +// which is responsible for setting up and shutting down the Jetpack +// program's CommonJS environment. It's also the main mechanism through +// which other parts of the application can communicate with the Jetpack +// program. +// +// If we're on Gecko 1.9.3, which supports rebootless extensions, the +// bootstrap.js file actually evaluates this file and calls parts of +// it automatically. +// +// It should be noted that a lot of what's done by the Harness Service is +// very similar to what's normally done by a `chrome.manifest` file: the +// difference here is that everything the Harness Service does is +// undoable during the lifetime of the application. This is the +// foundation of what makes it possible for Jetpack-based extensions +// to be installed and uninstalled without needing to reboot the +// application being extended. + +"use strict"; + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; + +Cu.import("resource://gre/modules/XPCOMUtils.jsm"); + +const obSvc = Cc["@mozilla.org/observer-service;1"] + .getService(Ci.nsIObserverService); + +const ioSvc = Cc["@mozilla.org/network/io-service;1"] + .getService(Ci.nsIIOService); + +const FIREFOX_ID = "{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"; +const THUNDERBIRD_ID = "{3550f703-e582-4d05-9a08-453d09bdfdc6}"; +const FENNEC_ID = "{a23983c0-fd0e-11dc-95ff-0800200c9a66}"; + +// This function builds and returns a Harness Service XPCOM component. +// +// Parameters: +// +// rootFileSpec - nsILocalFile corresponding to root of extension +// (required). +// +// dump - function to output string to console (required). +// +// logError - function to log an exception (required). +// +// onQuit - function called when the app quits (required). +// +// options - JSON configuration information passed in from the +// environment (required). + +function buildHarnessService(rootFileSpec, dump, logError, + onQuit, options) { + if (arguments.length == 1) { + ({dump, logError, onQuit, options}) = getDefaults(rootFileSpec); + } + + // The loader for securable modules, typically a Cuddlefish loader. + var loader; + + // Singleton Harness Service. + var harnessService; + + // Whether we've initialized or not yet. + var isStarted; + + // Whether we've been asked to quit or not yet. + var isQuitting; + + // The Jetpack program's main module. + var program; + + var ioService = Cc["@mozilla.org/network/io-service;1"] + .getService(Ci.nsIIOService); + var resProt = ioService.getProtocolHandler("resource") + .QueryInterface(Ci.nsIResProtocolHandler); + + function quit(status) { + if (status === undefined) + status = "OK"; + if (status != "OK" && status != "FAIL") { + dump("Warning: quit() expected 'OK' or 'FAIL' as an " + + "argument, but got '" + status + "' instead."); + status = "FAIL"; + } + + if (isQuitting) + return; + + isQuitting = true; + + if (harnessService) + harnessService.unload(); + + onQuit(status); + } + + function logErrorAndBail(e) { + logError(e); + quit("FAIL"); + } + + function ensureIsDir(dir) { + if (!(dir.exists() && dir.isDirectory)) + throw new Error("directory not found: " + dir.path); + } + + function getDir(path) { + var dir = Cc['@mozilla.org/file/local;1'] + .createInstance(Ci.nsILocalFile); + dir.initWithPath(path); + ensureIsDir(dir); + return dir; + } + + function buildLoader() { + // TODO: This variable doesn't seem to be used, we should + // be able to remove it. + var compMgr = Components.manager; + compMgr = compMgr.QueryInterface(Ci.nsIComponentRegistrar); + + for (let name in options.resources) { + var path = options.resources[name]; + var dir; + if (typeof(path) == "string") + dir = getDir(path); + else { + dir = rootFileSpec.clone(); + path.forEach(function(part) { dir.append(part); }); + ensureIsDir(dir); + } + var dirUri = ioService.newFileURI(dir); + resProt.setSubstitution(name, dirUri); + } + + var jsm = {}; + Cu.import(options.loader, jsm); + var packaging = new Packaging(); + var loader = new jsm.Loader({rootPaths: options.rootPaths.slice(), + print: dump, + packaging: packaging, + metadata: options.metadata, + uriPrefix: options.uriPrefix, + name: options.name, + globals: { packaging: packaging } + }); + packaging.__setLoader(loader); + return loader; + } + + // This will be exposed as the 'packaging' global to all + // modules loaded within our loader. + + function Packaging() { + this.__packages = options.manifest; + } + + Packaging.prototype = { + __setLoader: function setLoader(loader) { + this.__loader = loader; + }, + + get root() { + return rootFileSpec.clone(); + }, + + get harnessService() { + return harnessService; + }, + + get buildHarnessService() { + return buildHarnessService; + }, + + get options() { + return options; + }, + + enableE10s: options.enable_e10s, + + jetpackID: options.jetpackID, + uriPrefix: options.uriPrefix, + + bundleID: options.bundleID, + + getModuleInfo: function getModuleInfo(path) { + return this.__packages[path]; + }, + + createLoader: function createLoader() { + return buildLoader(); + } + }; + + // Singleton XPCOM component that is responsible for instantiating + // a Cuddlefish loader and running the main program, if any. + + function HarnessService() { + this.wrappedJSObject = this; + } + + HarnessService.prototype = { + get classDescription() { + // This needs to be unique, lest we regress bug 554489. + return "Harness Service for " + options.bootstrap.contractID; + }, + + get contractID() { return options.bootstrap.contractID; }, + + get classID() { return Components.ID(options.bootstrap.classID); }, + + _xpcom_categories: [{ category: "profile-after-change" }], + + _xpcom_factory: { + get singleton() { + return harnessService; + }, + + createInstance: function(outer, iid) { + if (outer) + throw Cr.NS_ERROR_NO_AGGREGATION; + if (!harnessService) + harnessService = new HarnessService(); + return harnessService.QueryInterface(iid); + } + }, + + QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, + Ci.nsISupportsWeakReference]), + + get loader() { + if (!loader) + loader = buildLoader(); + return loader; + }, + + get options() { + return options; + }, + + load: function Harness_load(reason) { + if (isStarted) + return; + + isStarted = true; + obSvc.addObserver(this, "quit-application-granted", true); + if (options.main) { + try { + + if (reason) + options.loadReason = reason; + program = this.loader.require(options.main); + if ('main' in program) + program.main(options, {quit: quit, print: dump}); + + // Send application readiness notification + const APP_READY_TOPIC = options.jetpackID + "_APPLICATION_READY"; + obSvc.notifyObservers(null, APP_READY_TOPIC, null); + + } catch (e) { + this.loader.console.exception(e); + quit("FAIL"); + } + } + }, + + unload: function Harness_unload(reason) { + if (!isStarted) + return; + + isStarted = false; + harnessService = null; + + obSvc.removeObserver(this, "quit-application-granted"); + + lifeCycleObserver192.unload(); + + // Notify the program of unload. + if (program) { + if (typeof(program.onUnload) === "function") { + try { + program.onUnload(reason); + } + catch (err) { + if (loader) + loader.console.exception(err); + } + } + program = null; + } + + // Notify the loader of unload. + if (loader) { + loader.unload(reason); + loader = null; + } + + for (let name in options.resources) + resProt.setSubstitution(name, null); + }, + + observe: function Harness_observe(subject, topic, data) { + try { + switch (topic) { + case "profile-after-change": + var appInfo = Cc["@mozilla.org/xre/app-info;1"] + .getService(Ci.nsIXULAppInfo); + switch (appInfo.ID) { + case THUNDERBIRD_ID: + case FENNEC_ID: + obSvc.addObserver(this, "xul-window-visible", true); + break; + case FIREFOX_ID: + obSvc.addObserver(this, "sessionstore-windows-restored", true); + break; + default: + obSvc.addObserver(this, "final-ui-startup", true); + break; + } + lifeCycleObserver192.init(options.bundleID, logError); + break; + case "final-ui-startup": // XULRunner + case "sessionstore-windows-restored": // Firefox + case "xul-window-visible": // Thunderbird, Fennec + obSvc.removeObserver(this, topic); + this.load(lifeCycleObserver192.loadReason || "startup"); + break; + case "quit-application-granted": + this.unload(lifeCycleObserver192.unloadReason || "shutdown"); + quit("OK"); + break; + } + } catch (e) { + logErrorAndBail(e); + } + } + }; + + var factory = HarnessService.prototype._xpcom_factory; + if (!factory.wrappedJSObject) + factory.wrappedJSObject = factory; + + return HarnessService; +} + +// This is an error logger of last resort; if we're here, then +// we weren't able to initialize Cuddlefish and display a nice +// traceback through it. + +function defaultLogError(e, print) { + if (!print) + print = dump; + + var level = "error"; + print(e + " (" + e.fileName + ":" + e.lineNumber + ")\n", level); + if (e.stack) + print("stack:\n" + e.stack + "\n", level); +} + +// Builds an onQuit() function that writes a result file if necessary +// and does some other extra things to enhance developer ergonomics. + +function buildDevQuit(options, dump) { + // Absolute path to a file that we put our result code in. Ordinarily + // we'd just exit the process with a zero or nonzero return code, but + // there doesn't appear to be a way to do this in XULRunner. + var resultFile = options.resultFile; + + // Whether we've written resultFile or not. + var fileWritten = false; + + function attemptQuit() { + var appStartup = Cc['@mozilla.org/toolkit/app-startup;1']. + getService(Ci.nsIAppStartup); + appStartup.quit(Ci.nsIAppStartup.eAttemptQuit); + } + + return function onQuit(result) { + dump(result + "\n"); + + function writeResult() { + if (!fileWritten) + try { + var file = Cc["@mozilla.org/file/local;1"] + .createInstance(Ci.nsILocalFile); + file.initWithPath(resultFile); + + var foStream = Cc["@mozilla.org/network/file-output-stream;1"] + .createInstance(Ci.nsIFileOutputStream); + foStream.init(file, -1, -1, 0); + foStream.write(result, result.length); + foStream.close(); + fileWritten = true; + } catch (e) { + dump(e + "\n"); + } + } + + writeResult(); + attemptQuit(); + }; +} + +function buildForsakenConsoleDump(dump) { + var buffer = ""; + var cService = Cc['@mozilla.org/consoleservice;1'].getService() + .QueryInterface(Ci.nsIConsoleService); + + function stringify(arg) { + try { + return String(arg); + } + catch(ex) { + return ""; + } + } + + return function forsakenConsoleDump(msg, level) { + msg = stringify(msg); + if (msg.indexOf('\n') >= 0) { + var str = buffer + msg; + if (level === "error") { + var err = Cc["@mozilla.org/scripterror;1"] + .createInstance(Ci.nsIScriptError); + str = str.replace(/^error: /, ""); + err.init(str, null, null, 0, 0, 0, "Add-on SDK"); + cService.logMessage(err); + } + else + cService.logStringMessage(str); + buffer = ""; + } else { + buffer += msg; + } + }; +} + +function getDefaults(rootFileSpec) { + // Default options to pass back. + var options; + + try { + var environ = Cc["@mozilla.org/process/environment;1"] + .getService(Ci.nsIEnvironment); + + var jsonData; + var optionsFile = rootFileSpec.clone(); + optionsFile.append('harness-options.json'); + if (optionsFile.exists()) { + var fiStream = Cc['@mozilla.org/network/file-input-stream;1'] + .createInstance(Ci.nsIFileInputStream); + var siStream = Cc['@mozilla.org/scriptableinputstream;1'] + .createInstance(Ci.nsIScriptableInputStream); + fiStream.init(optionsFile, 1, 0, false); + siStream.init(fiStream); + var data = new String(); + data += siStream.read(-1); + siStream.close(); + fiStream.close(); + jsonData = data; + } + else { + throw new Error("harness-options.json file must exist."); + } + + options = JSON.parse(jsonData); + } catch (e) { + defaultLogError(e); + throw e; + } + + var onQuit = function() {}; + var doDump = buildForsakenConsoleDump(dump); + + if ('resultFile' in options) + onQuit = buildDevQuit(options, print); + + var logFile; + var logStream; + + if ('logFile' in options) { + logFile = Cc["@mozilla.org/file/local;1"] + .createInstance(Ci.nsILocalFile); + logFile.initWithPath(options.logFile); + + logStream = Cc["@mozilla.org/network/file-output-stream;1"] + .createInstance(Ci.nsIFileOutputStream); + logStream.init(logFile, 26 /* PR_WRONLY | PR_APPEND | PR_CREATE_FILE */, + -1 , 0); + } + + function print(msg, level) { + doDump(msg, level); + if (logStream && typeof(msg) == "string") { + logStream.write(msg, msg.length); + logStream.flush(); + } + } + + function logError(e) { + defaultLogError(e, print); + } + + return {options: options, onQuit: onQuit, dump: print, + logError: logError}; +} + +// Gecko 2, entry point for non-bootstrapped extensions (which register this +// component via chrome.manifest.) +// FIXME: no install/uninstall notifications on 2.0 for non-bootstrapped addons +function NSGetFactory(cid) { + try { + if (!NSGetFactory.fn) { + var rootFileSpec = __LOCATION__.parent.parent; + var HarnessService = buildHarnessService(rootFileSpec); + NSGetFactory.fn = XPCOMUtils.generateNSGetFactory([HarnessService]); + } + } catch(e) { + Components.utils.reportError(e); + dump(e); + throw e; + } + return NSGetFactory.fn(cid); +} + +// Everything below is only used on Gecko 1.9.2 or below. + +function NSGetModule(compMgr, fileSpec) { + var rootFileSpec = fileSpec.parent.parent; + var HarnessService = buildHarnessService(rootFileSpec); + return XPCOMUtils.generateModule([HarnessService]); +} + +// Program life-cycle events originate in bootstrap.js on 1.9.3. But 1.9.2 +// doesn't use bootstrap.js, so we need to do a little extra work there to +// determine the reasons for app startup and shutdown. That's what this +// singleton is for. On 1.9.3 all methods are no-ops. +var lifeCycleObserver192 = { + get loadReason() { + if (this._inited) { + // If you change these names, change them in bootstrap.js too. + if (this._addonIsNew) + return "install"; + return "startup"; + } + return undefined; + }, + + get unloadReason() { + if (this._inited) { + // If you change these names, change them in bootstrap.js too. + switch (this._emState) { + case "item-uninstalled": + return "uninstall"; + case "item-disabled": + return "disable"; + } + return "shutdown"; + } + return undefined; + }, + + // This must be called first to initialize the singleton. It must be called + // on profile-after-change. + init: function lifeCycleObserver192_init(bundleID, logError) { + // This component is present in 1.9.2 but not 2.0. + if ("@mozilla.org/extensions/manager;1" in Cc && !this._inited) { + obSvc.addObserver(this, "em-action-requested", true); + this._bundleID = bundleID; + this._logError = logError; + this._inited = true; + + try { + // This throws if the pref doesn't exist, which is the case when no + // new add-ons were installed. + var addonIdStr = Cc["@mozilla.org/preferences-service;1"]. + getService(Ci.nsIPrefBranch). + getCharPref("extensions.newAddons"); + } + catch (err) {} + if (addonIdStr) { + var addonIds = addonIdStr.split(","); + this._addonIsNew = addonIds.indexOf(this._bundleID) >= 0; + } + } + }, + + unload: function lifeCycleObserver192_unload() { + if (this._inited && !this._unloaded) { + obSvc.removeObserver(this, "em-action-requested"); + delete this._logError; + this._unloaded = true; + } + }, + + observe: function lifeCycleObserver192_observe(subj, topic, data) { + try { + if (topic === "em-action-requested") { + if (subj instanceof Ci.nsIUpdateItem && subj.id === this._bundleID) + this._emState = data; + } + } + catch (err) { + this._logError(err); + } + }, + + QueryInterface: XPCOMUtils.generateQI([ + Ci.nsIObserver, + Ci.nsISupportsWeakReference, + ]) +}; diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/app-extension/install.rdf b/tools/addon-sdk-1.3/python-lib/cuddlefish/app-extension/install.rdf new file mode 100644 index 0000000..e76c25d --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/app-extension/install.rdf @@ -0,0 +1,31 @@ + + + + + xulapp@toolness.com + 1.0 + 2 + true + true + + + + + {ec8030f7-c20a-464f-9b0e-13a3a9e97384} + 8.0 + 9.* + + + + + Test App + Harness for tests. + Mozilla Corporation + + + + + + + diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/bunch.py b/tools/addon-sdk-1.3/python-lib/cuddlefish/bunch.py new file mode 100644 index 0000000..7262cbc --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/bunch.py @@ -0,0 +1,30 @@ +# Taken from Paver's paver.options module. + +class Bunch(dict): + """A dictionary that provides attribute-style access.""" + + def __repr__(self): + keys = self.keys() + keys.sort() + args = ', '.join(['%s=%r' % (key, self[key]) for key in keys]) + return '%s(%s)' % (self.__class__.__name__, args) + + def __getitem__(self, key): + item = dict.__getitem__(self, key) + if callable(item): + return item() + return item + + def __getattr__(self, name): + try: + return self[name] + except KeyError: + raise AttributeError(name) + + __setattr__ = dict.__setitem__ + + def __delattr__(self, name): + try: + del self[name] + except KeyError: + raise AttributeError(name) diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/docs/__init__.py b/tools/addon-sdk-1.3/python-lib/cuddlefish/docs/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/docs/apiparser.py b/tools/addon-sdk-1.3/python-lib/cuddlefish/docs/apiparser.py new file mode 100644 index 0000000..05a24fc --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/docs/apiparser.py @@ -0,0 +1,388 @@ +import sys, re, textwrap + +VERSION = 4 + +class ParseError(Exception): + # args[1] is the line number that caused the problem + def __init__(self, why, lineno): + self.why = why + self.lineno = lineno + def __str__(self): + return ("ParseError: the JS API docs were unparseable on line %d: %s" % + (self.lineno, self.why)) + +class Accumulator: + def __init__(self, holder, firstline): + self.holder = holder + self.firstline = firstline + self.otherlines = [] + def addline(self, line): + self.otherlines.append(line) + def finish(self): + # take a list of strings like: + # "initial stuff" (this is in firstline) + # " more stuff" (this is in lines[0]) + # " yet more stuff" + # " indented block" + # " indented block" + # " nonindented stuff" (lines[-1]) + # + # calculate the indentation level by looking at all but the first + # line, and removing the whitespace they all have in common. Then + # join the results with newlines and return a single string. + pieces = [] + if self.firstline: + pieces.append(self.firstline) + if self.otherlines: + pieces.append(textwrap.dedent("\n".join(self.otherlines))) + self.holder["description"] = "\n".join(pieces) + + +class APIParser: + def parse(self, lines, lineno): + api = {"line_number": lineno + 1} +# assign the name from the first line, of the form "" + title_line = lines[lineno].rstrip("\n") + api["name"] = self._parse_title_line(title_line, lineno + 1) + lineno += 1 +# finished with the first line, assigned the name + working_set = self._initialize_working_set() + props = [] + currentPropHolder = api +# fetch the next line, of the form "@tag [name] {datatype} description" +# and parse it into tag, info, description + tag, info, firstline = self._parseTypeLine(lines[lineno], lineno + 1) + api["type"] = tag +# if this API element is a property then datatype must be set + if tag == 'property': + api['datatype'] = info['datatype'] + # info is ignored + currentAccumulator = Accumulator(api, firstline) + lineno += 1 + while (lineno) < len(lines): + line = lines[lineno].rstrip("\n") + # accumulate any multiline descriptive text belonging to + # the preceding "@" section + if self._is_description_line(line): + currentAccumulator.addline(line) + else: + currentAccumulator.finish() + if line.startswith(" element + tag, info, desc = self._parseTypeLine(line, lineno + 1) + currentAccumulator = Accumulator(info, desc) + if tag == "prop": + # build up props[] + props.append(info) + elif tag == "returns": + # close off the @prop list + if props and currentPropHolder: + currentPropHolder["props"] = props + props = [] + api["returns"] = info + currentPropHolder = info + elif tag == "param": + # close off the @prop list + if props and currentPropHolder: + currentPropHolder["props"] = props + props = [] + working_set["params"].append(info) + currentPropHolder = info + elif tag == "argument": + # close off the @prop list + if props and currentPropHolder: + currentPropHolder["props"] = props + props = [] + working_set["arguments"].append(info) + currentPropHolder = info + else: + raise ParseError("unknown '@' section header %s in \ + '%s'" % (tag, line), lineno + 1) + lineno += 1 + raise ParseError("closing tag not found for ", lineno + 1) + + def _parse_title_line(self, title_line, lineno): + if "name" not in title_line: + raise ParseError("Opening tag must have a name attribute.", + lineno) + m = re.search("name=['\"]{0,1}([-\w\.]*?)['\"]", title_line) + if not m: + raise ParseError("No value for name attribute found in " + "opening tag.", lineno) + return m.group(1) + + def _is_description_line(self, line): + return not ( (line.lstrip().startswith("@")) or + (line.lstrip().startswith(" 0: + signature += params[0]["name"] + for param in params[1:]: + signature += ", " + param["name"] + signature += ")" + api_element["signature"] = signature + + def _assemble_api_element(self, api_element, working_set): + # if any of this working set's lists are non-empty, + # add it to the current api element + if (api_element["type"] == "constructor") or \ + (api_element["type"] == "function") or \ + (api_element["type"] == "method"): + self._assemble_signature(api_element, working_set["params"]) + if len(working_set["params"]) > 0: + api_element["params"] = working_set["params"] + if len(working_set["properties"]) > 0: + api_element["properties"] = working_set["properties"] + if len(working_set["constructors"]) > 0: + api_element["constructors"] = working_set["constructors"] + if len(working_set["methods"]) > 0: + api_element["methods"] = working_set["methods"] + if len(working_set["events"]) > 0: + api_element["events"] = working_set["events"] + if len(working_set["arguments"]) > 0: + api_element["arguments"] = working_set["arguments"] + + def _validate_info(self, tag, info, line, lineno): + if tag == 'property': + if not 'datatype' in info: + raise ParseError("No type found for @property.", lineno) + elif tag == "param": + if info.get("required", False) and "default" in info: + raise ParseError( + "required parameters should not have defaults: '%s'" + % line, lineno) + elif tag == "prop": + if "datatype" not in info: + raise ParseError("@prop lines must include {type}: '%s'" % + line, lineno) + if "name" not in info: + raise ParseError("@prop lines must provide a name: '%s'" % + line, lineno) + + def _parseTypeLine(self, line, lineno): + # handle these things: + # @method + # @returns description + # @returns {string} description + # @param NAME {type} description + # @param NAME + # @prop NAME {type} description + # @prop NAME + # returns: + # tag: type of api element + # info: linenumber, required, default, name, datatype + # description + + info = {"line_number": lineno} + line = line.rstrip("\n") + pieces = line.split() + + if not pieces: + raise ParseError("line is too short: '%s'" % line, lineno) + if not pieces[0].startswith("@"): + raise ParseError("type line should start with @: '%s'" % line, + lineno) + tag = pieces[0][1:] + skip = 1 + + expect_name = tag in ("param", "prop") + + if len(pieces) == 1: + description = "" + else: + if pieces[1].startswith("{"): + # NAME is missing, pieces[1] is TYPE + pass + else: + if expect_name: + info["required"] = not pieces[1].startswith("[") + name = pieces[1].strip("[ ]") + if "=" in name: + name, info["default"] = name.split("=") + info["name"] = name + skip += 1 + + if len(pieces) > skip and pieces[skip].startswith("{"): + info["datatype"] = pieces[skip].strip("{ }") + skip += 1 + + # we've got the metadata, now extract the description + pieces = line.split(None, skip) + if len(pieces) > skip: + description = pieces[skip] + else: + description = "" + self._validate_info(tag, info, line, lineno) + return tag, info, description + +def parse_hunks(text): + # return a list of tuples. Each is one of: + # ("raw", string) : non-API blocks + # ("api-json", dict) : API blocks + yield ("version", VERSION) + lines = text.splitlines(True) + line_number = 0 + markdown_string = "" + while line_number < len(lines): + line = lines[line_number] + if line.startswith(" 0: + yield ("markdown", markdown_string) + markdown_string = "" + api, line_number = APIParser().parse(lines, line_number) + # this business with 'leftover' is a horrible thing to do, + # and exists only to collect the \n after the closing /api tag. + # It's not needed probably, except to help keep compatibility + # with the previous behaviour + leftover = lines[line_number].lstrip("") + if len(leftover) > 0: + markdown_string += leftover + line_number = line_number + 1 + yield ("api-json", api) + else: + markdown_string += line + line_number = line_number + 1 + if len(markdown_string) > 0: + yield ("markdown", markdown_string) + +class TestRenderer: + # render docs for test purposes + + def getm(self, d, key): + return d.get(key, "") + + def join_lines(self, text): + return " ".join([line.strip() for line in text.split("\n")]) + + def render_prop(self, p): + s = "props[%s]: " % self.getm(p, "name") + pieces = [] + for k in ("type", "description", "required", "default"): + if k in p: + pieces.append("%s=%s" % (k, self.join_lines(str(p[k])))) + return s + ", ".join(pieces) + + def render_param(self, p): + pieces = [] + for k in ("name", "type", "description", "required", "default"): + if k in p: + pieces.append("%s=%s" % (k, self.join_lines(str(p[k])))) + yield ", ".join(pieces) + for prop in p.get("props", []): + yield " " + self.render_prop(prop) + + def render_method(self, method): + yield "name= %s" % self.getm(method, "name") + yield "type= %s" % self.getm(method, "type") + yield "description= %s" % self.getm(method, "description") + signature = method.get("signature") + if signature: + yield "signature= %s" % self.getm(method, "signature") + params = method.get("params", []) + if params: + yield "parameters:" + for p in params: + for pline in self.render_param(p): + yield " " + pline + r = method.get("returns", None) + if r: + yield "returns:" + if "type" in r: + yield " type= %s" % r["type"] + if "description" in r: + yield " description= %s" % self.join_lines(r["description"]) + props = r.get("props", []) + for p in props: + yield " " + self.render_prop(p) + + def format_api(self, api): + for mline in self.render_method(api): + yield mline + constructors = api.get("constructors", []) + if constructors: + yield "constructors:" + for m in constructors: + for mline in self.render_method(m): + yield " " + mline + methods = api.get("methods", []) + if methods: + yield "methods:" + for m in methods: + for mline in self.render_method(m): + yield " " + mline + properties = api.get("properties", []) + if properties: + yield "properties:" + for p in properties: + yield " " + self.render_prop(p) + + def render_docs(self, docs_json, outf=sys.stdout): + + for (t,data) in docs_json: + if t == "api-json": + for line in self.format_api(data): + line = line.rstrip("\n") + outf.write("API: " + line + "\n") + else: + for line in str(data).split("\n"): + outf.write("MD :" + line + "\n") + +def hunks_to_dict(docs_json): + exports = {} + for (t,data) in docs_json: + if t != "api-json": + continue + if data["name"]: + exports[data["name"]] = data + return exports + +if __name__ == "__main__": + json = False + if sys.argv[1] == "--json": + json = True + del sys.argv[1] + docs_text = open(sys.argv[1]).read() + docs_parsed = list(parse_hunks(docs_text)) + if json: + import simplejson + print simplejson.dumps(docs_parsed, indent=2) + else: + TestRenderer().render_docs(docs_parsed) diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/docs/apirenderer.py b/tools/addon-sdk-1.3/python-lib/cuddlefish/docs/apirenderer.py new file mode 100644 index 0000000..2488aa9 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/docs/apirenderer.py @@ -0,0 +1,299 @@ +import sys, os +import markdown +import apiparser +import time + +# list of all the 'class' and 'id' attributes assigned to +#
and tags by the renderer. +API_REFERENCE = 'api_reference' +MODULE_API_DOCS_CLASS = 'module_api_docs' +MODULE_API_DOCS_ID = '_module_api_docs' +API_HEADER = 'api_header' +API_NAME = 'api_name' +API_COMPONENT_GROUP = 'api_component_group' +API_COMPONENT = 'api_component' +DATATYPE = 'datatype' +RETURNS = 'returns' +PARAMETER_SET = 'parameter_set' +MODULE_DESCRIPTION = 'module_description' + +HTML_HEADER = ''' +\n +\n +\n + \n + \n + \n + \n + Add-on SDK Documentation\n + \n +\n + \n +\n +\n +\n''' + +HTML_FOOTER = ''' +\n +\n +\n''' + +def indent(text_in): + text_out = '' + lines = text_in.splitlines(True) + indentation_level = 0 + indentation_depth = 2 + for line in lines: + if (line.startswith('' in line: + indentation_level += 1 + else: + if (line.startswith('
')): + indentation_level -= 1 + text_out += ((' ' * indentation_depth) * indentation_level) + line + return text_out + +def tag_wrap_id(text, classname, id, tag = 'div'): + return ''.join(['\n<'+ tag + ' id="', id, '" class="', \ + classname, '">\n', text + '\n\n']) + +def tag_wrap(text, classname, tag = 'div', inline = False): + if inline: + return ''.join(['\n<' + tag + ' class="', classname, '">', \ + text, '\n']) + else: + return ''.join(['\n<' + tag + ' class="', classname, '">', \ + text, '\n\n']) + +def tag_wrap_inline(text, classname, tag = 'div'): + return ''.join(['\n<' + tag + ' class="', classname, '">', \ + text, '\n']) + +def span_wrap(text, classname): + return ''.join(['', \ + text, '']) + +class API_Renderer(object): + def __init__(self, json, tag): + self.name = json.get('name', None) + self.tag = tag + self.description = json.get('description', '') + self.json = json + + def render_name(self): + raise Exception('not implemented in this class') + + def render_description(self): + return markdown.markdown(self.description) + + def render_subcomponents(self): + raise Exception('not implemented in this class') + + def get_tag(self): + return self.tag + +class Class_Doc(API_Renderer): + def __init__(self, json, tag): + API_Renderer.__init__(self, json, tag) + + def render_name(self): + return self.name + + def render_subcomponents(self): + return render_object_contents(self.json, 'h5', 'h6') + +class Event_Doc(API_Renderer): + def __init__(self, json, tag): + API_Renderer.__init__(self, json, tag) + self.arguments_json = json.get('arguments', None) + + def render_name(self): + return self.name + + def render_subcomponents(self): + if not self.arguments_json: + return '' + text = ''.join([render_comp(Argument_Doc(argument_json, 'div')) \ + for argument_json in self.arguments_json]) + return tag_wrap(text, PARAMETER_SET) + +class Argument_Doc(API_Renderer): + def __init__(self, json, tag): + API_Renderer.__init__(self, json, tag) + self.datatype = json.get('datatype', None) + + def render_name(self): + return span_wrap(self.datatype, DATATYPE) + + def render_subcomponents(self): + return '' + +class Function_Doc(API_Renderer): + def __init__(self, json, tag): + API_Renderer.__init__(self, json, tag) + self.signature = json['signature'] + self.returns = json.get('returns', None) + self.parameters_json = json.get('params', None) + + def render_name(self): + return self.signature + + def render_subcomponents(self): + return self._render_parameters() + self._render_returns() + + def _render_parameters(self): + if not self.parameters_json: + return '' + text = ''.join([render_comp(Parameter_Doc(parameter_json, 'div')) \ + for parameter_json in self.parameters_json]) + return tag_wrap(text, PARAMETER_SET) + + def _render_returns(self): + if not self.returns: + return '' + text = 'Returns: ' + span_wrap(self.returns['datatype'], DATATYPE) + text += markdown.markdown(self.returns['description']) + return tag_wrap(text, RETURNS) + +class Property_Doc(API_Renderer): + def __init__(self, json, tag): + API_Renderer.__init__(self, json, tag) + self.datatype = json.get('datatype', None) + self.required = json.get('required', True) + self.default = json.get('default', False) + + def render_name(self): + rendered = self.name + if self.default: + rendered = rendered + " = " + self.default + if self.datatype: + rendered = rendered + ' : ' + span_wrap(self.datatype, DATATYPE) + if not self.required: + rendered = '[ ' + rendered + ' ]' + return rendered + + def render_subcomponents(self): + return render_object_contents(self.json) + +class Parameter_Doc(Property_Doc): + def __init__(self, json, tag): + Property_Doc.__init__(self, json, tag) + self.properties_json = json.get('props', None) + + def render_subcomponents(self): + if not self.properties_json: + return '' + text = ''.join([render_comp(Property_Doc(property_json, 'div')) \ + for property_json in self.properties_json]) + return text + +def render_object_contents(json, tag = 'div', comp_tag = 'div'): + ctors = json.get('constructors', None) + text = render_comp_group(ctors, 'Constructors', Function_Doc, tag, comp_tag) + methods = json.get('methods', None) + text += render_comp_group(methods, 'Methods', Function_Doc, tag, comp_tag) + properties = json.get('properties', None) + text += render_comp_group(properties, 'Properties', Property_Doc, tag, comp_tag) + events = json.get('events', None) + text += render_comp_group(events, 'Events', Event_Doc, tag, comp_tag) + return text + +def render_comp(component): + # a component is wrapped inside a single div marked 'API_COMPONENT' + # containing: + # 1) the component name, marked 'API_NAME' + text = tag_wrap(component.render_name(), API_NAME, component.get_tag(), True) + # 2) the component description + text += component.render_description() + # 3) the component contents + text += component.render_subcomponents() + return tag_wrap(text, API_COMPONENT) + +def render_comp_group(group, group_name, ctor, tag = 'div', comp_tag = 'div'): + if not group: + return '' + # component group is a list of components in a single div called + # 'API_COMPONENT_GROUP' containing: + # 1) a title for the group marked with 'API_HEADER' + text = tag_wrap(group_name, API_HEADER, tag, True) + # 2) each component + text += ''.join([render_comp(ctor(api, comp_tag)) for api in group]) + return tag_wrap(text, API_COMPONENT_GROUP) + +def render_descriptions(descriptions_md): + text = ''.join([description_md for description_md in descriptions_md]) + return tag_wrap(markdown.markdown(text), MODULE_DESCRIPTION) + +def render_api_reference(api_docs): + if (len(api_docs) == 0): + return '' + # at the top level api reference is in a single div marked 'API_REFERENCE', + # containing: + # 1) a title 'API Reference' marked with 'API_HEADER' + text = tag_wrap('API Reference', API_HEADER, 'h2', True) + # 2) a component group called 'Classes' containing any class elements + classes = [api for api in api_docs if api['type'] == 'class'] + text += render_comp_group(classes, 'Classes', Class_Doc, 'h3', 'h4') + # 3) a component group called 'Functions' containing any global functions + functions = [api for api in api_docs if api['type'] == 'function'] + text += render_comp_group(functions, 'Functions', Function_Doc, 'h3', 'h4') + # 4) a component group called 'Properties' containing any global properties + properties = [api for api in api_docs if api['type'] == 'property'] + text += render_comp_group(properties, 'Properties', Property_Doc, 'h3', 'h4') + # 5) a component group called 'Events' containing any global events + events = [api for api in api_docs if api['type'] == 'event'] + text += render_comp_group(events, 'Events', Event_Doc, 'h3', 'h4') + return tag_wrap(text, API_REFERENCE) + +# take the JSON output of apiparser +# return the HTML DIV containing the rendered component +def json_to_div(json, markdown_filename): + module_name, ext = os.path.splitext(os.path.basename(markdown_filename)) + descriptions = [hunk[1] for hunk in json if hunk[0]=='markdown'] + api_docs = [hunk[1] for hunk in json if hunk[0]=='api-json'] + text = "

" + module_name + "

" + text += render_descriptions(descriptions) + text += render_api_reference(api_docs) + text = tag_wrap_id(text, MODULE_API_DOCS_CLASS, \ + module_name + MODULE_API_DOCS_ID) + return text.encode('utf8') + +# take the JSON output of apiparser +# return standalone HTML containing the rendered component +def json_to_html(json, markdown_filename): + return indent(HTML_HEADER + \ + json_to_div(json, markdown_filename) + HTML_FOOTER) + +# take the name of a Markdown file +# return the HTML DIV containing the rendered component +def md_to_div(markdown_filename): + markdown_contents = open(markdown_filename).read().decode('utf8') + json = list(apiparser.parse_hunks(markdown_contents)) + return json_to_div(json, markdown_filename) + +# take the name of a Markdown file +# return standalone HTML containing the rendered component +def md_to_html(markdown_filename): + return indent(HTML_HEADER + md_to_div(markdown_filename) + HTML_FOOTER) + +if __name__ == '__main__': + if (len(sys.argv) == 0): + print 'Supply the name of a docs file to parse' + else: + print md_to_html(sys.argv[1]) diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/docs/generate.py b/tools/addon-sdk-1.3/python-lib/cuddlefish/docs/generate.py new file mode 100644 index 0000000..1c0fbf9 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/docs/generate.py @@ -0,0 +1,230 @@ +import os +import sys +import shutil +import hashlib +import tarfile +import StringIO + +from cuddlefish import packaging +from cuddlefish import Bunch +from cuddlefish.docs import apiparser +from cuddlefish.docs import apirenderer +from cuddlefish.docs import webdocs +import simplejson as json + +DOCS_DIR = "doc" +DIGEST = "status.md5" +TGZ_FILENAME = "addon-sdk-docs.tgz" + +def clean_generated_docs(docs_dir): + status_file = os.path.join(docs_dir, "status.md5") + if os.path.exists(status_file): + os.remove(status_file) + index_file = os.path.join(docs_dir, "index.html") + if os.path.exists(index_file): + os.remove(index_file) + dev_guide_dir = os.path.join(docs_dir, "dev-guide") + if os.path.exists(dev_guide_dir): + shutil.rmtree(dev_guide_dir) + api_doc_dir = os.path.join(docs_dir, "packages") + if os.path.exists(api_doc_dir): + shutil.rmtree(api_doc_dir) + +def generate_static_docs(env_root, base_url=None): + docs_dir = os.path.join(env_root, DOCS_DIR) + clean_generated_docs(docs_dir) + generate_docs(env_root, base_url=base_url, stdout=StringIO.StringIO()) + tgz = tarfile.open(TGZ_FILENAME, 'w:gz') + tgz.add(docs_dir, DOCS_DIR) + tgz.close() + return TGZ_FILENAME + +def generate_docs(env_root, base_url=None, filename=None, stdout=sys.stdout): + docs_dir = os.path.join(env_root, DOCS_DIR) + base_url = calculate_base_url(base_url, docs_dir) + # if we were given a filename, just generate the named file + # and return its URL + if filename: + return generate_named_file(env_root, base_url, filename) + # if the generated docs don't exist, generate everything + if not os.path.exists(os.path.join(docs_dir, "index.html")): + print >>stdout, "Generating documentation..." + generate_docs_from_scratch(env_root, base_url, docs_dir) + current_status = calculate_current_status(env_root) + open(os.path.join(env_root, DOCS_DIR, DIGEST), "w").write(current_status) + else: + current_status = calculate_current_status(env_root) + previous_status_file = os.path.join(env_root, DOCS_DIR, DIGEST) + docs_are_up_to_date = False + if os.path.exists(previous_status_file): + docs_are_up_to_date = current_status == open(previous_status_file, "r").read() + # if the docs are not up to date, generate everything + if not docs_are_up_to_date: + print >>stdout, "Regenerating documentation..." + generate_docs_from_scratch(env_root, base_url, docs_dir) + open(os.path.join(env_root, DOCS_DIR, DIGEST), "w").write(current_status) + return base_url + "index.html" + +def calculate_base_url(base_url, docs_dir): + if base_url == None: + base_url_path = docs_dir + # this is to ensure the path starts with "/" + # whether or not it's on Windows + # there might be a better way + if not docs_dir.startswith("/"): + base_url_path = "/" + base_url_path + base_url_path_pieces = base_url_path.split(os.sep) + base_url = "file://" + "/".join(base_url_path_pieces) + "/" + return base_url + +def generate_named_file(env_root, base_url, filename): + docs_dir = os.path.join(env_root, DOCS_DIR) + web_docs = webdocs.WebDocs(env_root, base_url) + + # next, generate api doc or guide doc + abs_path = os.path.abspath(filename) + if abs_path.startswith(os.path.join(env_root, 'packages')): + return generate_api_doc(env_root, abs_path, web_docs) + elif abs_path.startswith(os.path.join(env_root, DOCS_DIR, 'dev-guide-source')): + return generate_guide_doc(env_root, abs_path, web_docs) + else: + raise ValueError("Not a valid path to a documentation file") + +# this function builds a hash of the name and last modification date of: +# * every file in "packages" which ends in ".md" +# * every file in "static-files" which does not start with "." +def calculate_current_status(env_root): + current_status = hashlib.md5() + package_src_dir = os.path.join(env_root, "packages") + for (dirpath, dirnames, filenames) in os.walk(package_src_dir): + for filename in filenames: + if filename.endswith(".md"): + current_status.update(filename) + current_status.update(str(os.path.getmtime(os.path.join(dirpath, filename)))) + guide_src_dir = os.path.join(env_root, DOCS_DIR, "dev-guide-source") + for (dirpath, dirnames, filenames) in os.walk(guide_src_dir): + for filename in filenames: + if filename.endswith(".md"): + current_status.update(filename) + current_status.update(str(os.path.getmtime(os.path.join(dirpath, filename)))) + base_html_file = os.path.join(env_root, DOCS_DIR, "static-files", "base.html") + current_status.update(base_html_file) + current_status.update(str(os.path.getmtime(os.path.join(dirpath, base_html_file)))) + return current_status.digest() + +def generate_docs_from_scratch(env_root, base_url, docs_dir): + web_docs = webdocs.WebDocs(env_root, base_url) + clean_generated_docs(docs_dir) + + # py2.5 doesn't have ignore=, so we delete tempfiles afterwards. If we + # required >=py2.6, we could use ignore=shutil.ignore_patterns("*~") + for (dirpath, dirnames, filenames) in os.walk(docs_dir): + for n in filenames: + if n.endswith("~"): + os.unlink(os.path.join(dirpath, n)) + + # generate api docs from all packages + os.mkdir(os.path.join(docs_dir, "packages")) + # create the index file and save that + pkg_cfg = packaging.build_pkg_cfg(env_root) + index = json.dumps(packaging.build_pkg_index(pkg_cfg)) + index_path = os.path.join(docs_dir, "packages", 'index.json') + open(index_path, 'w').write(index) + + # for each package, generate its docs + for pkg_name, pkg in pkg_cfg['packages'].items(): + src_dir = pkg.root_dir + package_dirname = os.path.basename(src_dir) + dest_dir = os.path.join(docs_dir, "packages", package_dirname) + os.mkdir(dest_dir) + + src_readme = os.path.join(src_dir, "README.md") + if os.path.exists(src_readme): + shutil.copyfile(src_readme, + os.path.join(dest_dir, "README.md")) + + # create the package page + package_filename = os.path.join(dest_dir, pkg_name + ".html") + if not os.path.exists(package_filename): + package_doc_html = web_docs.create_package_page(pkg_name) + open(package_filename, "w").write(package_doc_html) + + # generate all the API docs + docs_src_dir = os.path.join(src_dir, "doc") + if os.path.isdir(os.path.join(src_dir, "docs")): + docs_src_dir = os.path.join(src_dir, "docs") + generate_file_tree(env_root, docs_src_dir, web_docs, generate_api_doc) + + # generate all the guide docs + dev_guide_src = os.path.join(env_root, DOCS_DIR, "dev-guide-source") + generate_file_tree(env_root, dev_guide_src, web_docs, generate_guide_doc) + + # make /md/dev-guide/welcome.html the top level index file + shutil.copy(os.path.join(env_root, DOCS_DIR, 'dev-guide', 'welcome.html'), \ + os.path.join(docs_dir, 'index.html')) + +def generate_file_tree(env_root, src_dir, web_docs, generate_file): + for (dirpath, dirnames, filenames) in os.walk(src_dir): + assert dirpath.startswith(src_dir) # what is this for?? + for filename in filenames: + if filename.endswith("~"): + continue + src_path = os.path.join(dirpath, filename) + generate_file(env_root, src_path, web_docs) + +def generate_api_doc(env_root, src_dir, web_docs): + if src_dir.endswith(".md"): + dest_dir, filename = get_api_doc_dest_path(env_root, src_dir) + if not os.path.exists(dest_dir): + os.makedirs(dest_dir) + + # parse and JSONify the API docs + docs_md = open(src_dir, 'r').read() + docs_parsed = list(apiparser.parse_hunks(docs_md)) + docs_json = json.dumps(docs_parsed) + dest_path_json = os.path.join(dest_dir, filename) + ".json" + replace_file(dest_path_json, docs_json) + + # write the HTML div files + docs_div = apirenderer.json_to_div(docs_parsed, src_dir) + dest_path_div = os.path.join(dest_dir, filename) + ".div" + replace_file(dest_path_div, docs_div) + + # write the standalone HTML files + docs_html = web_docs.create_module_page(src_dir) + dest_path_html = os.path.join(dest_dir, filename) + ".html" + replace_file(dest_path_html, docs_html) + + return dest_path_html + +def generate_guide_doc(env_root, src_dir, web_docs): + if src_dir.endswith(".md"): + dest_dir, filename = get_guide_doc_dest_path(env_root, src_dir) + if not os.path.exists(dest_dir): + os.makedirs(dest_dir) + # write the standalone HTML files + docs_html = web_docs.create_guide_page(src_dir) + dest_path_html = os.path.join(dest_dir, filename) + ".html" + replace_file(dest_path_html, docs_html) + return dest_path_html + +def replace_file(dest_path, file_contents): + if os.path.exists(dest_path): + os.remove(dest_path) + open(dest_path, "w").write(file_contents) + +# Given the full path to an API source file, and the root, +# return a tuple of: +# 1) the full path to the corresponding HTML file, without the filename +# 2) the filename without the extension +def get_guide_doc_dest_path(env_root, src_dir): + src_dir_relative = src_dir[len(os.path.join(env_root, DOCS_DIR, "dev-guide-source")) + 1:] + return os.path.split(os.path.join(env_root, DOCS_DIR, "dev-guide", src_dir_relative)[:-3]) + +# Given the full path to a dev guide source file, and the root, +# return a tuple of: +# 1) the full path to the corresponding HTML file, without the filename +# 2) the filename without the extension +def get_api_doc_dest_path(env_root, src_dir): + src_dir_relative = src_dir[len(env_root) + 1:] + return os.path.split(os.path.join(env_root, DOCS_DIR, src_dir_relative)[:-3]) \ No newline at end of file diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/docs/renderapi.readme.md b/tools/addon-sdk-1.3/python-lib/cuddlefish/docs/renderapi.readme.md new file mode 100644 index 0000000..7086fe1 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/docs/renderapi.readme.md @@ -0,0 +1,206 @@ + +This document describes the structure of the HTML generated by the renderapi.py +tool, both for use in the API docs shown by "cfx docs" and as exported by +"cfx sdocs". The particular HTML id and class attributes embedded in the files, +as well as their organization, represent the interface between the tool and any +front-end code wanting to style the docs in some particular way. + +renderapi generates two sorts of files: + +- a file called ".div": this is the contents of the parsed +Markdown file rendered inside a well-defined DIV tag + +- a file called ".html": this is the DIV from above inserted into +a simple HTML template that references a sample CSS file which styles the +contents of the DIV. This CSS file is the same as the one used by the SDK +itself. + +DIV tags +-------- +The following class and id attributes are used in the DIV: + +renderapi uses a number of class attributes and a single id attribute in the DIV: + +id attribute "_module_api_docs" +class attribute "api_reference" +class attribute "module_api_docs" +class attribute "api_header" +class attribute "api_name" +class attribute "api_component_group" +class attribute "api_component" +class attribute "datatype" +class attribute "returns" +class attribute "parameter_set" +class attribute "module_description" + +DIV structure +------------- +The top level DIV is marked with the id attribute and the "module_api_docs" class +attribute: + +
+ //module doc contents +
+ + +Inside this: + +- the first item is an

heading containing the name of the module: + +- all "markdown" hunks (that is, all descriptive text not occurring +inside tags) are rendered inside a DIV marked with the +"module-description" class attribute + +- all content is rendered, enclosed in a single tag marked +with the "api_reference" class attribute: + +
+
+ //descriptions +
+
+ //api reference +
+
+ +If there is no content, then the "api-reference" section is absent. + +### API Reference structure ### + +The first item in API reference is an

heading title marked with the +"api_header" attribute. This might have the text content "API Reference" +(but you should not rely on that): + +
+ +

API Reference

+ + //api contents + +
+ +After the title come one or more component groups. + +#### Component Group #### + +A component group is marked with the "api_component_group" attribute. The +component group is a collection of some sort of component: for example, a group +of classes, a group of functions, or a group of events. + +Each component group starts off with a header marked with the +"api_header" attribute and is followed by one or more sections marked with the +"api_component" attribute. +At the top level (that is, when they are directly under the "API Reference" +heading), the "api_header" items are

headings, otherwise they are divs. + +
+ +

API Reference

+ +
+ +

Classes

+ +
+ // the first class +
+ +
+ // another class +
+ +
+ +
+ //some different components + +

Functions

+ +
+ the first function +
+ +
+ another function +
+ +
+ +
+ +#### Component #### + +API components represent actual objects in the API like classes, functions, +properties and events. + +Each component starts with a section marked with the +"api_name" tag, which includes the name of the component in the API: for +example "postMessage(message)". + +Components at the top level (i.e., directly under h3 headings) are

+headings, otherwise they are divs. + +After the name, the component's contents are listed. Different sorts of +components may have different sorts of contents: for example, a function might +have parameters. If the component is composite then it may contain its own +component group. For example, a class may contain methods and properties, +which might be grouped together. + +
+ +

Panel

+ +
+ +
+ Methods +
+ +
+ show() +
+ +
+ +
+ +Other attributes +----------------------------- + +### Datatype ### +All primitive data types, like "string" and "number", are marked with the +"datatype" class attribute: + +
+ +
+ label : string +
+ +

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

+ +
+ +### Returns ### + +Functions mark return values with the "returns" class attribute. + +
+ +
+ get() +
+ + Make a `GET` request. + +
+ Returns: Request +
+ +
+ +### Parameter_set ### + +Functions that take parameters mark them with the parameter_set class +attribute. diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/docs/webdocs.py b/tools/addon-sdk-1.3/python-lib/cuddlefish/docs/webdocs.py new file mode 100644 index 0000000..3528ac3 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/docs/webdocs.py @@ -0,0 +1,195 @@ +import sys, os, re, errno +import markdown +import simplejson as json + +from cuddlefish import packaging +from cuddlefish import Bunch +from cuddlefish.docs import apiparser +from cuddlefish.docs import apirenderer + +INDEX_PAGE = '/doc/static-files/base.html' +BASE_URL_INSERTION_POINT = '\n' + return result + +def is_high_level(package_json): + return not is_low_level(package_json) + +def is_low_level(package_json): + return 'jetpack-low-level' in package_json.get('keywords', []) + +def insert_after(target, insertion_point_id, text_to_insert): + insertion_point = target.find(insertion_point_id) + len(insertion_point_id) + return target[:insertion_point] + text_to_insert + target[insertion_point:] + +class WebDocs(object): + def __init__(self, root, base_url = '/'): + self.root = root + self.pkg_cfg = packaging.build_pkg_cfg(root) + self.packages_json = packaging.build_pkg_index(self.pkg_cfg) + self.base_page = self._create_base_page(root, base_url) + + def create_guide_page(self, path): + path, ext = os.path.splitext(path) + md_path = path + '.md' + md_content = unicode(open(md_path, 'r').read(), 'utf8') + guide_content = markdown.markdown(md_content) + return self._create_page(guide_content) + + def create_module_page(self, path): + path, ext = os.path.splitext(path) + md_path = path + '.md' + module_content = apirenderer.md_to_div(md_path) + return self._create_page(module_content) + + def create_package_page(self, package_name): + package_content = self._create_package_detail(package_name) + return self._create_page(package_content) + + def _create_page(self, page_content): + page = self._insert_title(self.base_page, page_content) + page = insert_after(page, CONTENT_ID, page_content) + return page.encode('utf8') + + def _create_module_list(self, package_json): + package_name = package_json['name'] + libs = package_json['files'][1]['lib'][1] + doc_path = package_json.get('doc', None) + if not doc_path: + return '' + modules = get_documented_modules(package_name, libs, doc_path) + modules.sort() + module_items = '' + relative_doc_path = doc_path[len(self.root) + 1:] + relative_doc_URL = "/".join(relative_doc_path.split(os.sep)) + for module in modules: + module_link = tag_wrap('/'.join(module), 'a', \ + {'href': relative_doc_URL + '/' + '/'.join(module) + '.html'}) + module_items += tag_wrap(module_link, 'li', {'class':'module'}) + return tag_wrap(module_items, 'ul', {'class':'modules'}) + + def _create_package_summaries(self, packages_json, include): + packages = '' + for package_name in packages_json.keys(): + package_json = packages_json[package_name] + if not include(package_json): + continue + package_path = self.pkg_cfg["packages"][package_name]["root_dir"] + package_directory = package_path[len(self.root) + 1:] + package_directory = "/".join(package_directory.split(os.sep)) + package_link = tag_wrap(package_name, 'a', {'href': \ + package_directory + "/" \ + + package_name + '.html'}) + text = tag_wrap(package_link, 'h4') + text += self._create_module_list(package_json) + packages += tag_wrap(text, 'div', {'class':'package-summary', \ + 'style':'display: block;'}) + return packages + + def _create_base_page(self, root, base_url): + base_page = unicode(open(root + INDEX_PAGE, 'r').read(), 'utf8') + base_tag = 'href="' + base_url + '"' + base_page = insert_after(base_page, BASE_URL_INSERTION_POINT, base_tag) + high_level_summaries = \ + self._create_package_summaries(self.packages_json, is_high_level) + base_page = insert_after(base_page, \ + HIGH_LEVEL_PACKAGE_SUMMARIES, high_level_summaries) + low_level_summaries = \ + self._create_package_summaries(self.packages_json, is_low_level) + base_page = insert_after(base_page, \ + LOW_LEVEL_PACKAGE_SUMMARIES, low_level_summaries) + return base_page + + def _create_package_detail_row(self, field_value, \ + field_descriptor, field_name): + meta = tag_wrap(tag_wrap(field_descriptor, 'span', \ + {'class':'meta-header'}), 'td') + value = tag_wrap(tag_wrap(field_value, 'span', \ + {'class':field_name}), 'td') + return tag_wrap(meta + value, 'tr') + + def _create_package_detail_table(self, package_json): + table_contents = '' + if package_json.get('author', None): + table_contents += self._create_package_detail_row(\ + package_json['author'], 'Author', 'author') + if package_json.get('version', None): + table_contents += self._create_package_detail_row(\ + package_json['version'], 'Version', 'version') + if package_json.get('license', None): + table_contents += self._create_package_detail_row(\ + package_json['license'], 'License', 'license') + if package_json.get('dependencies', None): + table_contents += self._create_package_detail_row(\ + ', '.join(package_json['dependencies']), \ + 'Dependencies', 'dependencies') + table_contents += self._create_package_detail_row(\ + self._create_module_list(package_json), 'Modules', 'modules') + return tag_wrap(tag_wrap(table_contents, 'tbody'), 'table', \ + {'class':'meta-table'}) + + def _create_package_detail(self, package_name): + package_json = self.packages_json.get(package_name, None) + if not package_json: + raise IOError(errno.ENOENT, 'Package not found') + # pieces of the package detail: 1) title, 2) table, 3) description + package_title = tag_wrap(package_name, 'h1') + table = self._create_package_detail_table(package_json) + description = '' + if package_json.get('readme', None): + description += tag_wrap(tag_wrap(\ + markdown.markdown(\ + package_json['readme']), 'p'), 'div', {'class':'docs'}) + return tag_wrap(package_title + table + description, 'div', \ + {'class':'package-detail'}) + + def _insert_title(self, target, content): + match = re.search('

.*

', content) + if match: + title = match.group(0)[len('

'):-len('

')] + ' - ' + \ + DEFAULT_TITLE + else: + title = DEFAULT_TITLE + target = insert_after(target, TITLE_ID, title) + return target diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/manifest.py b/tools/addon-sdk-1.3/python-lib/cuddlefish/manifest.py new file mode 100644 index 0000000..03ca0aa --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/manifest.py @@ -0,0 +1,725 @@ + +import os, sys, re, hashlib +import simplejson as json +SEP = os.path.sep +from cuddlefish.util import filter_filenames, filter_dirnames + +def js_zipname(packagename, modulename): + return "%s-lib/%s.js" % (packagename, modulename) +def docs_zipname(packagename, modulename): + return "%s-docs/%s.md" % (packagename, modulename) +def datamap_zipname(packagename): + return "%s-data.json" % packagename +def datafile_zipname(packagename, datapath): + return "%s-data/%s" % (packagename, datapath) + +def to_json(o): + return json.dumps(o, indent=1).encode("utf-8")+"\n" + +class ModuleNotFoundError(Exception): + def __init__(self, requirement_type, requirement_name, + used_by, line_number, looked_in): + Exception.__init__(self) + self.requirement_type = requirement_type # "require" or "define" + self.requirement_name = requirement_name # string, what they require()d + self.used_by = used_by # string, full path to module which did require() + self.line_number = line_number # int, 1-indexed line number of first require() + self.looked_in = looked_in # list of full paths to potential .js files + def __str__(self): + what = "%s(%s)" % (self.requirement_type, self.requirement_name) + where = self.used_by + if self.line_number is not None: + where = "%s:%d" % (self.used_by, self.line_number) + searched = "Looked for it in:\n %s\n" % "\n ".join(self.looked_in) + return ("ModuleNotFoundError: unable to satisfy: %s from\n" + " %s:\n" % (what, where)) + searched + +class BadModuleIdentifier(Exception): + pass +class BadSection(Exception): + pass +class UnreachablePrefixError(Exception): + pass + +class ManifestEntry: + def __init__(self): + self.docs_filename = None + self.docs_hash = None + self.requirements = {} + self.datamap = None + + def get_uri(self, prefix): + uri = "%s%s-%s/%s" % \ + (prefix, self.packageName, self.sectionName, self.moduleName) + if not uri.endswith(".js"): + uri += ".js" + return uri + + def get_entry_for_manifest(self, prefix): + entry = { "packageName": self.packageName, + "sectionName": self.sectionName, + "moduleName": self.moduleName, + "jsSHA256": self.js_hash, + "docsSHA256": self.docs_hash, + "requirements": {}, + } + for req in self.requirements: + if self.requirements[req]: + them = self.requirements[req] # this is another ManifestEntry + them_uri = them.get_uri(prefix) + entry["requirements"][req] = {"uri": them_uri} + else: + # something magic. The manifest entry indicates that they're + # allowed to require() it + entry["requirements"][req] = {} + if self.datamap: + entry["requirements"]["self"] = { + "mapSHA256": self.datamap.data_manifest_hash, + "mapName": self.packageName+"-data", + "dataURIPrefix": "%s%s-data/" % (prefix, self.packageName), + } + return entry + + def add_js(self, js_filename): + self.js_filename = js_filename + self.js_hash = hash_file(js_filename) + def add_docs(self, docs_filename): + self.docs_filename = docs_filename + self.docs_hash = hash_file(docs_filename) + def add_requirement(self, reqname, reqdata): + self.requirements[reqname] = reqdata + def add_data(self, datamap): + self.datamap = datamap + + def get_js_zipname(self): + return js_zipname(self.packagename, self.modulename) + def get_docs_zipname(self): + if self.docs_hash: + return docs_zipname(self.packagename, self.modulename) + return None + # self.js_filename + # self.docs_filename + + +def hash_file(fn): + return hashlib.sha256(open(fn,"rb").read()).hexdigest() + +def get_datafiles(datadir): + # yields pathnames relative to DATADIR, ignoring some files + for dirpath, dirnames, filenames in os.walk(datadir): + filenames = list(filter_filenames(filenames)) + # this tells os.walk to prune the search + dirnames[:] = filter_dirnames(dirnames) + for filename in filenames: + fullname = os.path.join(dirpath, filename) + assert fullname.startswith(datadir+SEP), "%s%s not in %s" % (datadir, SEP, fullname) + yield fullname[len(datadir+SEP):] + + +class DataMap: + # one per package + def __init__(self, pkg, uri_prefix): + self.pkg = pkg + self.name = pkg.name + self.files_to_copy = [] + datamap = {} + datadir = os.path.join(pkg.root_dir, "data") + for dataname in get_datafiles(datadir): + absname = os.path.join(datadir, dataname) + zipname = datafile_zipname(pkg.name, dataname) + datamap[dataname] = hash_file(absname) + self.files_to_copy.append( (zipname, absname) ) + self.data_manifest = to_json(datamap) + self.data_manifest_hash = hashlib.sha256(self.data_manifest).hexdigest() + self.data_manifest_zipname = datamap_zipname(pkg.name) + self.data_uri_prefix = "%s%s-data/" % (uri_prefix, self.name) + +class BadChromeMarkerError(Exception): + pass + +class ModuleInfo: + def __init__(self, package, section, name, js, docs): + self.package = package + self.section = section + self.name = name + self.js = js + self.docs = docs + + def __hash__(self): + return hash( (self.package.name, self.section, self.name, + self.js, self.docs) ) + def __eq__(self, them): + if them.__class__ is not self.__class__: + return False + if ((them.package.name, them.section, them.name, them.js, them.docs) != + (self.package.name, self.section, self.name, self.js, self.docs) ): + return False + return True + + def __repr__(self): + return "ModuleInfo [%s %s %s] (%s, %s)" % (self.package.name, + self.section, + self.name, + self.js, self.docs) + +class ManifestBuilder: + def __init__(self, target_cfg, pkg_cfg, deps, uri_prefix, extra_modules, + stderr=sys.stderr): + self.manifest = {} # maps (package,section,module) to ManifestEntry + self.target_cfg = target_cfg # the entry point + self.pkg_cfg = pkg_cfg # all known packages + self.deps = deps # list of package names to search + self.used_packagenames = set() + self.stderr = stderr + self.uri_prefix = uri_prefix + self.extra_modules = extra_modules + self.modules = {} # maps ModuleInfo to URI in self.manifest + self.datamaps = {} # maps package name to DataMap instance + self.files = [] # maps manifest index to (absfn,absfn) js/docs pair + + def build(self, scan_tests): + # process the top module, which recurses to process everything it + # reaches + if "main" in self.target_cfg: + self.top_uri = self.process_module(self.find_top(self.target_cfg)) + if scan_tests: + mi = self._find_module_in_package("test-harness", "lib", "run-tests", []) + self.process_module(mi) + # also scan all test files in all packages that we use. By making + # a copy of self.used_packagenames first, we refrain from + # processing tests in packages that our own tests depend upon. If + # we're running tests for package A, and either modules in A or + # tests in A depend upon modules from package B, we *don't* want + # to run tests for package B. + dirnames = self.target_cfg["tests"] + if isinstance(dirnames, basestring): + dirnames = [dirnames] + dirnames = [os.path.join(self.target_cfg.root_dir, d) + for d in dirnames] + for d in dirnames: + for tname in os.listdir(d): + if tname.startswith("test-") and tname.endswith(".js"): + #re.search(r'^test-.*\.js$', tname): + tmi = ModuleInfo(self.target_cfg, "tests", tname[:-3], + os.path.join(d, tname), None) + self.process_module(tmi) + + # include files used by the loader + for em in self.extra_modules: + (pkgname, section, modname, js) = em + mi = ModuleInfo(self.pkg_cfg.packages[pkgname], section, modname, + js, None) + self.process_module(mi) + + + def get_module_entries(self): + return frozenset(self.manifest.values()) + def get_data_entries(self): + return frozenset(self.datamaps.values()) + + def get_used_packages(self): + used = set() + for index in self.manifest: + (package, section, module) = index + used.add(package) + return sorted(used) + + def get_used_files(self): + # returns all .js files that we reference, plus data/ files. You will + # need to add the loader, off-manifest files that it needs, and + # generated metadata. + for me in self.get_module_entries(): + yield me.js_filename + if me.datamap: + for (zipname, absname) in me.datamap.files_to_copy: + yield absname + + def get_harness_options_manifest(self, uri_prefix): + manifest = {} + for me in self.get_module_entries(): + uri = me.get_uri(uri_prefix) + manifest[uri] = me.get_entry_for_manifest(uri_prefix) + return manifest + + def get_manifest_entry(self, package, section, module): + index = (package, section, module) + if index not in self.manifest: + m = self.manifest[index] = ManifestEntry() + m.packageName = package + m.sectionName = section + m.moduleName = module + self.used_packagenames.add(package) + return self.manifest[index] + + def uri_name_from_path(self, pkg, fn): + # given a filename like .../pkg1/lib/bar/foo.js, and a package + # specification (with a .root_dir like ".../pkg1" and a .lib list of + # paths where .lib[0] is like "lib"), return the appropriate NAME + # that can be put into a URI like resource://JID-pkg1-lib/NAME . This + # will throw an exception if the file is outside of the lib/ + # directory, since that means we can't construct a URI that points to + # it. + # + # This should be a lot easier, and shouldn't fail when the file is in + # the root of the package. Both should become possible when the XPI + # is rearranged and our URI scheme is simplified. + fn = os.path.abspath(fn) + pkglib = pkg.lib[0] + libdir = os.path.abspath(os.path.join(pkg.root_dir, pkglib)) + # AARGH, section and name! we need to reverse-engineer a + # ModuleInfo instance that will produce a URI (in the form + # PREFIX/PKGNAME-SECTION/JS) that will map to the existing file. + # Until we fix URI generation to get rid of "sections", this is + # limited to files in the same .directories.lib as the rest of + # the package uses. So if the package's main files are in lib/, + # but the main.js is in the package root, there is no URI we can + # construct that will point to it, and we must fail. + # + # This will become much easier (and the failure case removed) + # when we get rid of sections and change the URIs to look like + # (PREFIX/PKGNAME/PATH-TO-JS). + + # AARGH 2, allowing .lib to be a list is really getting in the + # way. That needs to go away eventually too. + if not fn.startswith(libdir): + raise UnreachablePrefixError("Sorry, but the 'main' file (%s) in package %s is outside that package's 'lib' directory (%s), so I cannot construct a URI to reach it." + % (fn, pkg.name, pkglib)) + name = fn[len(libdir):].lstrip(SEP)[:-len(".js")] + return name + + + def parse_main(self, root_dir, main, check_lib_dir=None): + # 'main' can be like one of the following: + # a: ./lib/main.js b: ./lib/main c: lib/main + # we require it to be a path to the file, though, and ignore the + # .directories stuff. So just "main" is insufficient if you really + # want something in a "lib/" subdirectory. + if main.endswith(".js"): + main = main[:-len(".js")] + if main.startswith("./"): + main = main[len("./"):] + # package.json must always use "/", but on windows we'll replace that + # with "\" before using it as an actual filename + main = os.sep.join(main.split("/")) + paths = [os.path.join(root_dir, main+".js")] + if check_lib_dir is not None: + paths.append(os.path.join(root_dir, check_lib_dir, main+".js")) + return paths + + def find_top_js(self, target_cfg): + for libdir in target_cfg.lib: + for n in self.parse_main(target_cfg.root_dir, target_cfg.main, + libdir): + if os.path.exists(n): + return n + raise KeyError("unable to find main module '%s.js' in top-level package" % target_cfg.main) + + def find_top(self, target_cfg): + top_js = self.find_top_js(target_cfg) + n = os.path.join(target_cfg.root_dir, "README.md") + if os.path.exists(n): + top_docs = n + else: + top_docs = None + name = self.uri_name_from_path(target_cfg, top_js) + return ModuleInfo(target_cfg, "lib", name, top_js, top_docs) + + def process_module(self, mi): + pkg = mi.package + #print "ENTERING", pkg.name, mi.name + # mi.name must be fully-qualified + assert (not mi.name.startswith("./") and + not mi.name.startswith("../")) + # create and claim the manifest row first + me = self.get_manifest_entry(pkg.name, mi.section, mi.name) + + me.add_js(mi.js) + if mi.docs: + me.add_docs(mi.docs) + + js_lines = open(mi.js,"r").readlines() + requires, problems, locations = scan_module(mi.js,js_lines,self.stderr) + if mi.section == "tests": + requires["chrome"] = {} + if problems: + # the relevant instructions have already been written to stderr + raise BadChromeMarkerError() + + # We update our requirements on the way out of the depth-first + # traversal of the module graph + + for reqname in sorted(requires.keys()): + if reqname in ("chrome", "loader", "manifest"): + me.add_requirement(reqname, None) + elif reqname == "self": + # this might reference bundled data, so: + # 1: hash that data, add the hash as a dependency + # 2: arrange for the data to be copied into the XPI later + if pkg.name not in self.datamaps: + self.datamaps[pkg.name] = DataMap(pkg, self.uri_prefix) + dm = self.datamaps[pkg.name] + me.add_data(dm) # 'self' is implicit + else: + # when two modules require() the same name, do they get a + # shared instance? This is a deep question. For now say yes. + + # find_req_for() returns an entry to put in our + # 'requirements' dict, and will recursively process + # everything transitively required from here. It will also + # populate the self.modules[] cache. Note that we must + # tolerate cycles in the reference graph. + looked_in = [] # populated by subroutines + them_me = self.find_req_for(mi, reqname, looked_in) + if them_me is None: + if mi.section == "tests": + # tolerate missing modules in tests, because + # test-securable-module.js, and the modules/red.js + # that it imports, both do that intentionally + continue + lineno = locations.get(reqname) # None means define() + if lineno is None: + reqtype = "define" + else: + reqtype = "require" + err = ModuleNotFoundError(reqtype, reqname, + mi.js, lineno, looked_in) + raise err + else: + me.add_requirement(reqname, them_me) + + return me + #print "LEAVING", pkg.name, mi.name + + def find_req_for(self, from_module, reqname, looked_in): + # handle a single require(reqname) statement from from_module . + # Return a uri that exists in self.manifest + # Populate looked_in with places we looked. + def BAD(msg): + return BadModuleIdentifier(msg + " in require(%s) from %s" % + (reqname, from_module)) + + if not reqname: + raise BAD("no actual modulename") + + # Allow things in tests/*.js to require both test code and real code. + # But things in lib/*.js can only require real code. + if from_module.section == "tests": + lookfor_sections = ["tests", "lib"] + elif from_module.section == "lib": + lookfor_sections = ["lib"] + else: + raise BadSection(from_module.section) + modulename = from_module.name + + #print " %s require(%s))" % (from_module, reqname) + bits = reqname.split("/") + + if reqname.startswith("./") or reqname.startswith("../"): + # 1: they want something relative to themselves, always from + # their own package + them = modulename.split("/")[:-1] + while bits[0] in (".", ".."): + if not bits: + raise BAD("no actual modulename") + if bits[0] == "..": + if not them: + raise BAD("too many ..") + them.pop() + bits.pop(0) + bits = them+bits + lookfor_pkg = from_module.package.name + lookfor_mod = "/".join(bits) + return self._get_module_from_package(lookfor_pkg, + lookfor_sections, lookfor_mod, + looked_in) + + # non-relative import. Might be a short name (requiring a search + # through "library" packages), or a fully-qualified one. + + if "/" in reqname: + # 2: PKG/MOD: find PKG, look inside for MOD + lookfor_pkg = bits[0] + lookfor_mod = "/".join(bits[1:]) + mi = self._get_module_from_package(lookfor_pkg, + lookfor_sections, lookfor_mod, + looked_in) + if mi: # caution, 0==None + return mi + else: + # 3: try finding PKG, if found, use its main.js entry point + lookfor_pkg = reqname + mi = self._get_entrypoint_from_package(lookfor_pkg, looked_in) + if mi: + return mi + + # 4: search packages for MOD or MODPARENT/MODCHILD. We always search + # their own package first, then the list of packages defined by their + # .dependencies list + from_pkg = from_module.package.name + return self._search_packages_for_module(from_pkg, + lookfor_sections, reqname, + looked_in) + + def _handle_module(self, mi): + if not mi: + return None + + # we tolerate cycles in the reference graph, which means we need to + # populate the self.modules cache before recursing into + # process_module() . We must also check the cache first, so recursion + # can terminate. + if mi in self.modules: + return self.modules[mi] + + # this creates the entry + new_entry = self.get_manifest_entry(mi.package.name, mi.section, mi.name) + # and populates the cache + self.modules[mi] = new_entry + self.process_module(mi) + return new_entry + + def _get_module_from_package(self, pkgname, sections, modname, looked_in): + if pkgname not in self.pkg_cfg.packages: + return None + mi = self._find_module_in_package(pkgname, sections, modname, + looked_in) + return self._handle_module(mi) + + def _get_entrypoint_from_package(self, pkgname, looked_in): + if pkgname not in self.pkg_cfg.packages: + return None + pkg = self.pkg_cfg.packages[pkgname] + main = pkg.get("main", None) + if not main: + return None + for js in self.parse_main(pkg.root_dir, main): + looked_in.append(js) + if os.path.exists(js): + section = "lib" + name = self.uri_name_from_path(pkg, js) + docs = None + mi = ModuleInfo(pkg, section, name, js, docs) + return self._handle_module(mi) + return None + + def _search_packages_for_module(self, from_pkg, sections, reqname, + looked_in): + searchpath = [] # list of package names + searchpath.append(from_pkg) # search self first + us = self.pkg_cfg.packages[from_pkg] + if 'dependencies' in us: + # only look in dependencies + searchpath.extend(us['dependencies']) + else: + # they didn't declare any dependencies (or they declared an empty + # list, but we'll treat that as not declaring one, because it's + # easier), so look in all deps, sorted alphabetically, so + # addon-kit comes first. Note that self.deps includes all + # packages found by traversing the ".dependencies" lists in each + # package.json, starting from the main addon package, plus + # everything added by --extra-packages + searchpath.extend(sorted(self.deps)) + for pkgname in searchpath: + mi = self._find_module_in_package(pkgname, sections, reqname, + looked_in) + if mi: + return self._handle_module(mi) + return None + + def _find_module_in_package(self, pkgname, sections, name, looked_in): + # require("a/b/c") should look at ...\a\b\c.js on windows + filename = os.sep.join(name.split("/")) + # normalize filename, make sure that we do not add .js if it already has + # it. + if not filename.endswith(".js"): + filename += ".js" + basename = filename[:-3] + + pkg = self.pkg_cfg.packages[pkgname] + if isinstance(sections, basestring): + sections = [sections] + for section in sections: + for sdir in pkg.get(section, []): + js = os.path.join(pkg.root_dir, sdir, filename) + looked_in.append(js) + if os.path.exists(js): + docs = None + maybe_docs = os.path.join(pkg.root_dir, "docs", + basename+".md") + if section == "lib" and os.path.exists(maybe_docs): + docs = maybe_docs + return ModuleInfo(pkg, section, name, js, docs) + return None + +def build_manifest(target_cfg, pkg_cfg, deps, uri_prefix, scan_tests, + extra_modules=[]): + """ + Perform recursive dependency analysis starting from entry_point, + building up a manifest of modules that need to be included in the XPI. + Each entry will map require() names to the URL of the module that will + be used to satisfy that dependency. The manifest will be used by the + runtime's require() code. + + This returns a ManifestBuilder object, with two public methods. The + first, get_module_entries(), returns a set of ManifestEntry objects, each + of which can be asked for the following: + + * its contribution to the harness-options.json '.manifest' + * the local disk name + * the name in the XPI at which it should be placed + + The second is get_data_entries(), which returns a set of DataEntry + objects, each of which has: + + * local disk name + * name in the XPI + + note: we don't build the XPI here, but our manifest is passed to the + code which does, so it knows what to copy into the XPI. + """ + + mxt = ManifestBuilder(target_cfg, pkg_cfg, deps, uri_prefix, extra_modules) + mxt.build(scan_tests) + return mxt + + + +COMMENT_PREFIXES = ["//", "/*", "*", "dump("] + +REQUIRE_RE = r"(?>stderr, """ +The following lines from file %(fn)s: +%(lines)s +use 'Components' to access chrome authority. To do so, you need to add a +line somewhat like the following: + + const {%(needs)s} = require("chrome"); + +Then you can use 'Components' as well as any shortcuts to its properties +that you import from the 'chrome' module ('Cc', 'Ci', 'Cm', 'Cr', and +'Cu' for the 'classes', 'interfaces', 'manager', 'results', and 'utils' +properties, respectively). + +(Note: once bug 636145 is fixed, to access 'Components' directly you'll +need to retrieve it from the 'chrome' module by adding it to the list of +symbols you import from the module. To avoid having to make this change +in the future, replace all occurrences of 'Components' in your code with +the equivalent shortcuts now.) +""" % { "fn": fn, "needs": ",".join(sorted(old_chrome)), + "lines": "\n".join([" %3d: %s" % (lineno,line) + for (lineno, line) in old_chrome_lines]), + } + problems = True + return problems + +def scan_module(fn, lines, stderr=sys.stderr): + filename = os.path.basename(fn) + requires, locations = scan_requirements_with_grep(fn, lines) + if filename == "cuddlefish.js" or filename == "securable-module.js": + # these are the loader: don't scan for chrome + problems = False + elif "chrome" in requires: + # if they declare require("chrome"), we tolerate the use of + # Components (see bug 663541 for rationale) + problems = False + else: + problems = scan_for_bad_chrome(fn, lines, stderr) + return requires, problems, locations + + + +if __name__ == '__main__': + for fn in sys.argv[1:]: + requires, problems, locations = scan_module(fn, open(fn).readlines()) + print + print "---", fn + if problems: + print "PROBLEMS" + sys.exit(1) + print "requires: %s" % (",".join(sorted(requires.keys()))) + print "locations: %s" % locations + diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/mobile-killer/bootstrap.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/mobile-killer/bootstrap.js new file mode 100644 index 0000000..9dfd993 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/mobile-killer/bootstrap.js @@ -0,0 +1,71 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Weave. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2008 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Alexandre Poirot + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +"use strict"; + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cu = Components.utils; +const Cr = Components.results; + +Components.utils.import("resource://gre/modules/Services.jsm"); + +function startup(data, reason) { + let Watcher = { + window: null, + onOpenWindow: function(window) { + window = window.docShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow); + window.addEventListener("keydown", this, true); + }, + onWindowTitleChange: function () {}, + handleEvent: function(event) { + // This event is dispatched via: abd shell input keycode 19 + // KEYCODE_DPAD_UP=19, UP can't be fired by virtual keyboard, + // so it should be safe to take this event as a kill signal. + if (event.keyCode == 38 && event.which == 38) { + Cu.reportError("Mobile killer triggered!"); + let appStartup = Cc['@mozilla.org/toolkit/app-startup;1']. + getService(Ci.nsIAppStartup); + appStartup.quit(Ci.nsIAppStartup.eForceQuit); + } + } + }; + Services.wm.addListener(Watcher); + Cu.reportError("Mobile killer ready to kill firefox."); +} + +function install() {} +function shutdown() {} diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/mobile-killer/install.rdf b/tools/addon-sdk-1.3/python-lib/cuddlefish/mobile-killer/install.rdf new file mode 100644 index 0000000..9ade0a9 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/mobile-killer/install.rdf @@ -0,0 +1,26 @@ + + + + + mobile-killer@mozilla.com + 1.0 + 2 + true + + + + + {a23983c0-fd0e-11dc-95ff-0800200c9a66} + 4.0b7 + * + + + + + Mobile killer + Allow cfx to automatically kill firefox. + Mozilla Corporation + + + diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/packaging.py b/tools/addon-sdk-1.3/python-lib/cuddlefish/packaging.py new file mode 100644 index 0000000..3dfc2ae --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/packaging.py @@ -0,0 +1,395 @@ +import os +import sys +import re +import copy + +import simplejson as json +from cuddlefish.bunch import Bunch + +MANIFEST_NAME = 'package.json' + +DEFAULT_LOADER = 'api-utils' + +DEFAULT_PROGRAM_MODULE = 'main' + +DEFAULT_ICON = 'icon.png' +DEFAULT_ICON64 = 'icon64.png' + +METADATA_PROPS = ['name', 'description', 'keywords', 'author', 'version', + 'contributors', 'license', 'homepage', 'icon', 'icon64', + 'main', 'directories'] + +RESOURCE_BAD_PACKAGE_NAME_RE = re.compile(r'[\s\.]') + +RESOURCE_HOSTNAME_RE = re.compile(r'^[a-z0-9_\-]+$') + +class Error(Exception): + pass + +class MalformedPackageError(Error): + pass + +class MalformedJsonFileError(Error): + pass + +class DuplicatePackageError(Error): + pass + +class PackageNotFoundError(Error): + def __init__(self, missing_package, reason): + self.missing_package = missing_package + self.reason = reason + def __str__(self): + return "%s (%s)" % (self.missing_package, self.reason) + +class BadChromeMarkerError(Error): + pass + +def validate_resource_hostname(name): + """ + Validates the given hostname for a resource: URI. + + For more information, see: + + https://bugzilla.mozilla.org/show_bug.cgi?id=566812#c13 + + Examples: + + >>> validate_resource_hostname('blarg') + + >>> validate_resource_hostname('bl arg') + Traceback (most recent call last): + ... + ValueError: package names cannot contain spaces or periods: bl arg + + >>> validate_resource_hostname('BLARG') + Traceback (most recent call last): + ... + ValueError: package names need to be lowercase: BLARG + + >>> validate_resource_hostname('foo@bar') + Traceback (most recent call last): + ... + ValueError: invalid resource hostname: foo@bar + """ + + # See https://bugzilla.mozilla.org/show_bug.cgi?id=568131 for details. + if not name.islower(): + raise ValueError('package names need to be lowercase: %s' % name) + + # See https://bugzilla.mozilla.org/show_bug.cgi?id=597837 for details. + if RESOURCE_BAD_PACKAGE_NAME_RE.search(name): + raise ValueError('package names cannot contain spaces or periods: %s' % name) + + if not RESOURCE_HOSTNAME_RE.match(name): + raise ValueError('invalid resource hostname: %s' % name) + +def find_packages_with_module(pkg_cfg, name): + # TODO: Make this support more than just top-level modules. + filename = "%s.js" % name + packages = [] + for cfg in pkg_cfg.packages.itervalues(): + if 'lib' in cfg: + matches = [dirname for dirname in resolve_dirs(cfg, cfg.lib) + if os.path.exists(os.path.join(dirname, filename))] + if matches: + packages.append(cfg.name) + return packages + +def resolve_dirs(pkg_cfg, dirnames): + for dirname in dirnames: + yield resolve_dir(pkg_cfg, dirname) + +def resolve_dir(pkg_cfg, dirname): + return os.path.join(pkg_cfg.root_dir, dirname) + +def get_metadata(pkg_cfg, deps): + metadata = Bunch() + for pkg_name in deps: + cfg = pkg_cfg.packages[pkg_name] + metadata[pkg_name] = Bunch() + for prop in METADATA_PROPS: + if cfg.get(prop): + metadata[pkg_name][prop] = cfg[prop] + return metadata + +def set_section_dir(base_json, name, base_path, dirnames, allow_root=False): + resolved = compute_section_dir(base_json, base_path, dirnames, allow_root) + if resolved: + base_json[name] = os.path.abspath(resolved) + +def compute_section_dir(base_json, base_path, dirnames, allow_root): + # PACKAGE_JSON.lib is highest priority + # then PACKAGE_JSON.directories.lib + # then lib/ (if it exists) + # then . (but only if allow_root=True) + for dirname in dirnames: + if base_json.get(dirname): + return os.path.join(base_path, base_json[dirname]) + if "directories" in base_json: + for dirname in dirnames: + if dirname in base_json.directories: + return os.path.join(base_path, base_json.directories[dirname]) + for dirname in dirnames: + if os.path.isdir(os.path.join(base_path, dirname)): + return os.path.join(base_path, dirname) + if allow_root: + return os.path.abspath(base_path) + return None + +def normalize_string_or_array(base_json, key): + if base_json.get(key): + if isinstance(base_json[key], basestring): + base_json[key] = [base_json[key]] + +def load_json_file(path): + data = open(path, 'r').read() + try: + return Bunch(json.loads(data)) + except ValueError, e: + raise MalformedJsonFileError('%s when reading "%s"' % (str(e), + path)) + +def get_config_in_dir(path): + package_json = os.path.join(path, MANIFEST_NAME) + if not (os.path.exists(package_json) and + os.path.isfile(package_json)): + raise MalformedPackageError('%s not found in "%s"' % (MANIFEST_NAME, + path)) + base_json = load_json_file(package_json) + + if 'name' not in base_json: + base_json.name = os.path.basename(path) + + # later processing steps will expect to see the following keys in the + # base_json that we return: + # + # name: name of the package + # lib: list of directories with .js files + # test: list of directories with test-*.js files + # doc: list of directories with documentation .md files + # data: list of directories with bundled arbitrary data files + # packages: ? + + if (not base_json.get('tests') and + os.path.isdir(os.path.join(path, 'test'))): + base_json['tests'] = 'test' + + set_section_dir(base_json, 'lib', path, ['lib'], True) + set_section_dir(base_json, 'tests', path, ['test', 'tests'], False) + set_section_dir(base_json, 'doc', path, ['doc', 'docs']) + set_section_dir(base_json, 'data', path, ['data']) + set_section_dir(base_json, 'packages', path, ['packages']) + + if (not base_json.get('icon') and + os.path.isfile(os.path.join(path, DEFAULT_ICON))): + base_json['icon'] = DEFAULT_ICON + + if (not base_json.get('icon64') and + os.path.isfile(os.path.join(path, DEFAULT_ICON64))): + base_json['icon64'] = DEFAULT_ICON64 + + for key in ['lib', 'tests', 'dependencies', 'packages']: + # TODO: lib/tests can be an array?? consider interaction with + # compute_section_dir above + normalize_string_or_array(base_json, key) + + if 'main' not in base_json and 'lib' in base_json: + for dirname in base_json['lib']: + program = os.path.join(path, dirname, + '%s.js' % DEFAULT_PROGRAM_MODULE) + if os.path.exists(program): + base_json['main'] = DEFAULT_PROGRAM_MODULE + break + + base_json.root_dir = path + + return base_json + +def _is_same_file(a, b): + if hasattr(os.path, 'samefile'): + return os.path.samefile(a, b) + return a == b + +def build_config(root_dir, target_cfg, packagepath=[]): + dirs_to_scan = [] + + def add_packages_from_config(pkgconfig): + if 'packages' in pkgconfig: + for package_dir in resolve_dirs(pkgconfig, pkgconfig.packages): + dirs_to_scan.append(package_dir) + + add_packages_from_config(target_cfg) + + packages_dir = os.path.join(root_dir, 'packages') + if os.path.exists(packages_dir) and os.path.isdir(packages_dir): + dirs_to_scan.append(packages_dir) + dirs_to_scan.extend(packagepath) + + packages = Bunch({target_cfg.name: target_cfg}) + + while dirs_to_scan: + packages_dir = dirs_to_scan.pop() + if os.path.exists(os.path.join(packages_dir, "package.json")): + package_paths = [packages_dir] + else: + package_paths = [os.path.join(packages_dir, dirname) + for dirname in os.listdir(packages_dir) + if not dirname.startswith('.')] + package_paths = [dirname for dirname in package_paths + if os.path.isdir(dirname)] + + for path in package_paths: + pkgconfig = get_config_in_dir(path) + if pkgconfig.name in packages: + otherpkg = packages[pkgconfig.name] + if not _is_same_file(otherpkg.root_dir, path): + raise DuplicatePackageError(path, otherpkg.root_dir) + else: + packages[pkgconfig.name] = pkgconfig + add_packages_from_config(pkgconfig) + + return Bunch(packages=packages) + +def get_deps_for_targets(pkg_cfg, targets): + visited = [] + deps_left = [[dep, None] for dep in list(targets)] + + while deps_left: + [dep, required_by] = deps_left.pop() + if dep not in visited: + visited.append(dep) + if dep not in pkg_cfg.packages: + required_reason = ("required by '%s'" % (required_by)) \ + if required_by is not None \ + else "specified as target" + raise PackageNotFoundError(dep, required_reason) + dep_cfg = pkg_cfg.packages[dep] + deps_left.extend([[i, dep] for i in dep_cfg.get('dependencies', [])]) + deps_left.extend([[i, dep] for i in dep_cfg.get('extra_dependencies', [])]) + + return visited + +def generate_build_for_target(pkg_cfg, target, deps, prefix='', + include_tests=True, + include_dep_tests=False, + default_loader=DEFAULT_LOADER): + validate_resource_hostname(prefix) + + build = Bunch(resources=Bunch(), + resourcePackages=Bunch(), + packageData=Bunch(), + rootPaths=[], + ) + + def add_section_to_build(cfg, section, is_code=False, + is_data=False): + if section in cfg: + dirnames = cfg[section] + if isinstance(dirnames, basestring): + # This is just for internal consistency within this + # function, it has nothing to do w/ a non-canonical + # configuration dict. + dirnames = [dirnames] + for dirname in resolve_dirs(cfg, dirnames): + lib_base = os.path.basename(dirname) + name = "-".join([prefix + cfg.name, section]) + validate_resource_hostname(name) + if name in build.resources: + raise KeyError('resource already defined', name) + build.resourcePackages[name] = cfg.name + build.resources[name] = dirname + resource_url = 'resource://%s/' % name + + if is_code: + build.rootPaths.insert(0, resource_url) + + if is_data: + build.packageData[cfg.name] = resource_url + + def add_dep_to_build(dep): + dep_cfg = pkg_cfg.packages[dep] + add_section_to_build(dep_cfg, "lib", is_code=True) + add_section_to_build(dep_cfg, "data", is_data=True) + if include_tests and include_dep_tests: + add_section_to_build(dep_cfg, "tests", is_code=True) + if ("loader" in dep_cfg) and ("loader" not in build): + build.loader = "resource://%s-%s" % (prefix + dep, + dep_cfg.loader) + + target_cfg = pkg_cfg.packages[target] + if include_tests and not include_dep_tests: + add_section_to_build(target_cfg, "tests", is_code=True) + + for dep in deps: + add_dep_to_build(dep) + + if 'loader' not in build: + add_dep_to_build(DEFAULT_LOADER) + + if 'icon' in target_cfg: + build['icon'] = os.path.join(target_cfg.root_dir, target_cfg.icon) + del target_cfg['icon'] + + if 'icon64' in target_cfg: + build['icon64'] = os.path.join(target_cfg.root_dir, target_cfg.icon64) + del target_cfg['icon64'] + + return build + +def _get_files_in_dir(path): + data = {} + files = os.listdir(path) + for filename in files: + fullpath = os.path.join(path, filename) + if os.path.isdir(fullpath): + data[filename] = _get_files_in_dir(fullpath) + else: + try: + info = os.stat(fullpath) + data[filename] = ("file", dict(size=info.st_size)) + except OSError: + pass + return ("directory", data) + +def build_pkg_index(pkg_cfg): + pkg_cfg = copy.deepcopy(pkg_cfg) + for pkg in pkg_cfg.packages: + root_dir = pkg_cfg.packages[pkg].root_dir + files = _get_files_in_dir(root_dir) + pkg_cfg.packages[pkg].files = files + try: + readme = open(root_dir + '/README.md').read() + pkg_cfg.packages[pkg].readme = readme + except IOError: + pass + del pkg_cfg.packages[pkg].root_dir + return pkg_cfg.packages + +def build_pkg_cfg(root): + pkg_cfg = build_config(root, Bunch(name='dummy')) + del pkg_cfg.packages['dummy'] + return pkg_cfg + +def call_plugins(pkg_cfg, deps): + for dep in deps: + dep_cfg = pkg_cfg.packages[dep] + dirnames = dep_cfg.get('python-lib', []) + for dirname in resolve_dirs(dep_cfg, dirnames): + sys.path.append(dirname) + module_names = dep_cfg.get('python-plugins', []) + for module_name in module_names: + module = __import__(module_name) + module.init(root_dir=dep_cfg.root_dir) + +def call_cmdline_tool(env_root, pkg_name): + pkg_cfg = build_config(env_root, Bunch(name='dummy')) + if pkg_name not in pkg_cfg.packages: + print "This tool requires the '%s' package." % pkg_name + sys.exit(1) + cfg = pkg_cfg.packages[pkg_name] + for dirname in resolve_dirs(cfg, cfg['python-lib']): + sys.path.append(dirname) + module_name = cfg.get('python-cmdline-tool') + module = __import__(module_name) + module.run() diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/preflight.py b/tools/addon-sdk-1.3/python-lib/cuddlefish/preflight.py new file mode 100755 index 0000000..141a3e8 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/preflight.py @@ -0,0 +1,73 @@ +import os, sys +import base64 +import simplejson as json + +def create_jid(): + """Return 'jid1-XYZ', where 'XYZ' is a randomly-generated string. (in the + previous jid0- series, the string securely identified a specific public + key). To get a suitable add-on ID, append '@jetpack' to this string. + """ + # per https://developer.mozilla.org/en/Install_Manifests#id all XPI id + # values must either be in the form of a 128-bit GUID (crazy braces + # and all) or in the form of an email address (crazy @ and all). + # Firefox will refuse to install an add-on with an id that doesn't + # match one of these forms. The actual regexp is at: + # http://mxr.mozilla.org/mozilla-central/source/toolkit/mozapps/extensions/XPIProvider.jsm#130 + # So the JID needs an @-suffix, and the only legal punctuation is + # "-._". So we start with a base64 encoding, and replace the + # punctuation (+/) with letters (AB), losing a few bits of integrity. + + # even better: windows has a maximum path length limitation of 256 + # characters: + # http://msdn.microsoft.com/en-us/library/aa365247%28VS.85%29.aspx + # (unless all paths are prefixed with "\\?\", I kid you not). The + # typical install will put add-on code in a directory like: + # C:\Documents and Settings\\Application Data\Mozilla\Firefox\Profiles\232353483.default\extensions\$JID\... + # (which is 108 chars long without the $JID). + # Then the unpacked XPI contains packaged resources like: + # resources/$JID-api-utils-lib/main.js (35 chars plus the $JID) + # + # We create a random 80 bit string, base64 encode that (with + # AB instead of +/ to be path-safe), then bundle it into + # "jid1-XYZ@jetpack". This gives us 27 characters. The resulting + # main.js will have a path length of 211 characters, leaving us 45 + # characters of margin. + # + # 80 bits is enough to generate one billion JIDs and still maintain lower + # than a one-in-a-million chance of accidental collision. (1e9 JIDs is 30 + # bits, square for the "birthday-paradox" to get 60 bits, add 20 bits for + # the one-in-a-million margin to get 80 bits) + + # if length were no issue, we'd prefer to use this: + h = os.urandom(80/8) + s = base64.b64encode(h, "AB").strip("=") + jid = "jid1-" + s + return jid + +def preflight_config(target_cfg, filename, stderr=sys.stderr): + modified = False + config = json.load(open(filename, 'r')) + + if "id" not in config: + print >>stderr, ("No 'id' in package.json: creating a new ID for you.") + jid = create_jid() + config["id"] = jid + modified = True + + if modified: + i = 0 + backup = filename + ".backup" + while os.path.exists(backup): + if i > 1000: + raise ValueError("I'm having problems finding a good name" + " for the backup file. Please move %s out" + " of the way and try again." + % (filename + ".backup")) + backup = filename + ".backup-%d" % i + i += 1 + os.rename(filename, backup) + new_json = json.dumps(config, indent=4) + open(filename, 'w').write(new_json+"\n") + return False, True + + return True, False diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/prefs.py b/tools/addon-sdk-1.3/python-lib/cuddlefish/prefs.py new file mode 100644 index 0000000..76b3a19 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/prefs.py @@ -0,0 +1,111 @@ +DEFAULT_COMMON_PREFS = { + # allow debug output via dump to be printed to the system console + # (setting it here just in case, even though PlainTextConsole also + # sets this preference) + 'browser.dom.window.dump.enabled': True, + # warn about possibly incorrect code + 'javascript.options.strict': True, + 'javascript.options.showInConsole': True +} + +DEFAULT_FENNEC_PREFS = { + 'javascript.options.showInConsole': True, + 'browser.console.showInPanel': True, + 'browser.firstrun.show.uidiscovery': False +} + +# When launching a temporary new Firefox profile, use these preferences. +DEFAULT_FIREFOX_PREFS = { + 'extensions.checkCompatibility.nightly' : False, + 'browser.startup.homepage' : 'about:blank', + 'startup.homepage_welcome_url' : 'about:blank', + 'devtools.errorconsole.enabled' : True, + + # Disable extension updates and notifications. + 'extensions.update.enabled' : False, + 'extensions.update.notifyUser' : False, + + # From: + # http://hg.mozilla.org/mozilla-central/file/1dd81c324ac7/build/automation.py.in#l372 + # Only load extensions from the application and user profile. + # AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_APPLICATION + 'extensions.enabledScopes' : 5, + # Disable metadata caching for installed add-ons by default + 'extensions.getAddons.cache.enabled' : False, + # Disable intalling any distribution add-ons + 'extensions.installDistroAddons' : False, + # Allow installing extensions dropped into the profile folder + 'extensions.autoDisableScopes' : 10, + 'extensions.testpilot.runStudies' : False, + + # From: + # http://hg.mozilla.org/mozilla-central/file/1dd81c324ac7/build/automation.py.in#l388 + # Make url-classifier updates so rare that they won't affect tests. + 'urlclassifier.updateinterval' : 172800, + # Point the url-classifier to a nonexistent local URL for fast failures. + 'browser.safebrowsing.provider.0.gethashURL' : 'http://localhost/safebrowsing-dummy/gethash', + 'browser.safebrowsing.provider.0.keyURL' : 'http://localhost/safebrowsing-dummy/newkey', + 'browser.safebrowsing.provider.0.updateURL' : 'http://localhost/safebrowsing-dummy/update', + # Point update checks to a nonexistent local URL for fast failures. + 'extensions.update.url' : 'http://localhost/extensions-dummy/updateURL', + 'extensions.blocklist.url' : 'http://localhost/extensions-dummy/blocklistURL', + # Make sure opening about:addons won't hit the network. + 'extensions.webservice.discoverURL' : 'http://localhost/extensions-dummy/discoveryURL' + } + +# When launching a temporary new Thunderbird profile, use these preferences. +# Note that these were taken from: +# http://mxr.mozilla.org/comm-central/source/mail/test/mozmill/runtest.py +DEFAULT_THUNDERBIRD_PREFS = { + # say no to slow script warnings + 'dom.max_chrome_script_run_time': 200, + 'dom.max_script_run_time': 0, + # disable extension stuffs + 'extensions.update.enabled' : False, + 'extensions.update.notifyUser' : False, + # do not ask about being the default mail client + 'mail.shell.checkDefaultClient': False, + # disable non-gloda indexing daemons + 'mail.winsearch.enable': False, + 'mail.winsearch.firstRunDone': True, + 'mail.spotlight.enable': False, + 'mail.spotlight.firstRunDone': True, + # disable address books for undisclosed reasons + 'ldap_2.servers.osx.position': 0, + 'ldap_2.servers.oe.position': 0, + # disable the first use junk dialog + 'mailnews.ui.junk.firstuse': False, + # other unknown voodoo + # -- dummied up local accounts to stop the account wizard + 'mail.account.account1.server' : "server1", + 'mail.account.account2.identities' : "id1", + 'mail.account.account2.server' : "server2", + 'mail.accountmanager.accounts' : "account1,account2", + 'mail.accountmanager.defaultaccount' : "account2", + 'mail.accountmanager.localfoldersserver' : "server1", + 'mail.identity.id1.fullName' : "Tinderbox", + 'mail.identity.id1.smtpServer' : "smtp1", + 'mail.identity.id1.useremail' : "tinderbox@invalid.com", + 'mail.identity.id1.valid' : True, + 'mail.root.none-rel' : "[ProfD]Mail", + 'mail.root.pop3-rel' : "[ProfD]Mail", + 'mail.server.server1.directory-rel' : "[ProfD]Mail/Local Folders", + 'mail.server.server1.hostname' : "Local Folders", + 'mail.server.server1.name' : "Local Folders", + 'mail.server.server1.type' : "none", + 'mail.server.server1.userName' : "nobody", + 'mail.server.server2.check_new_mail' : False, + 'mail.server.server2.directory-rel' : "[ProfD]Mail/tinderbox", + 'mail.server.server2.download_on_biff' : True, + 'mail.server.server2.hostname' : "tinderbox", + 'mail.server.server2.login_at_startup' : False, + 'mail.server.server2.name' : "tinderbox@invalid.com", + 'mail.server.server2.type' : "pop3", + 'mail.server.server2.userName' : "tinderbox", + 'mail.smtp.defaultserver' : "smtp1", + 'mail.smtpserver.smtp1.hostname' : "tinderbox", + 'mail.smtpserver.smtp1.username' : "tinderbox", + 'mail.smtpservers' : "smtp1", + 'mail.startup.enabledMailCheckOnce' : True, + 'mailnews.start_page_override.mstone' : "ignore", + } diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/rdf.py b/tools/addon-sdk-1.3/python-lib/cuddlefish/rdf.py new file mode 100644 index 0000000..9f64fc0 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/rdf.py @@ -0,0 +1,172 @@ +import os +import xml.dom.minidom +import StringIO + +RDF_NS = "http://www.w3.org/1999/02/22-rdf-syntax-ns#" +EM_NS = "http://www.mozilla.org/2004/em-rdf#" + +class RDF(object): + def __str__(self): + # real files have an .encoding attribute and use it when you + # write() unicode into them: they read()/write() unicode and + # put encoded bytes in the backend file. StringIO objects + # read()/write() unicode and put unicode in the backing store, + # so we must encode the output of getvalue() to get a + # bytestring. (cStringIO objects are weirder: they effectively + # have .encoding hardwired to "ascii" and put only bytes in + # the backing store, so we can't use them here). + # + # The encoding= argument to dom.writexml() merely sets the XML header's + # encoding= attribute. It still writes unencoded unicode to the output file, + # so we have to encode it for real afterwards. + # + # Also see: https://bugzilla.mozilla.org/show_bug.cgi?id=567660 + + buf = StringIO.StringIO() + self.dom.writexml(buf, encoding="utf-8") + return buf.getvalue().encode('utf-8') + +class RDFUpdate(RDF): + def __init__(self): + impl = xml.dom.minidom.getDOMImplementation() + self.dom = impl.createDocument(RDF_NS, "RDF", None) + self.dom.documentElement.setAttribute("xmlns", RDF_NS) + self.dom.documentElement.setAttribute("xmlns:em", EM_NS) + + def _make_node(self, name, value, parent): + elem = self.dom.createElement(name) + elem.appendChild(self.dom.createTextNode(value)) + parent.appendChild(elem) + return elem + + def add(self, manifest, update_link): + desc = self.dom.createElement("Description") + desc.setAttribute( + "about", + "urn:mozilla:extension:%s" % manifest.get("em:id") + ) + self.dom.documentElement.appendChild(desc) + + updates = self.dom.createElement("em:updates") + desc.appendChild(updates) + + seq = self.dom.createElement("Seq") + updates.appendChild(seq) + + li = self.dom.createElement("li") + seq.appendChild(li) + + li_desc = self.dom.createElement("Description") + li.appendChild(li_desc) + + self._make_node("em:version", manifest.get("em:version"), + li_desc) + + apps = manifest.dom.documentElement.getElementsByTagName( + "em:targetApplication" + ) + + for app in apps: + target_app = self.dom.createElement("em:targetApplication") + li_desc.appendChild(target_app) + + ta_desc = self.dom.createElement("Description") + target_app.appendChild(ta_desc) + + for name in ["em:id", "em:minVersion", "em:maxVersion"]: + elem = app.getElementsByTagName(name)[0] + self._make_node(name, elem.firstChild.nodeValue, ta_desc) + + self._make_node("em:updateLink", update_link, ta_desc) + +class RDFManifest(RDF): + def __init__(self, path): + self.dom = xml.dom.minidom.parse(path) + + def set(self, property, value): + elements = self.dom.documentElement.getElementsByTagName(property) + if not elements: + raise ValueError("Element with value not found: %s" % property) + if not elements[0].firstChild: + elements[0].appendChild(self.dom.createTextNode(value)) + else: + elements[0].firstChild.nodeValue = value + + def get(self, property, default=None): + elements = self.dom.documentElement.getElementsByTagName(property) + if not elements: + return default + return elements[0].firstChild.nodeValue + + def remove(self, property): + elements = self.dom.documentElement.getElementsByTagName(property) + if not elements: + return True + else: + for i in elements: + i.parentNode.removeChild(i); + + return True; + +def gen_manifest(template_root_dir, target_cfg, bundle_id, + update_url=None, bootstrap=True, enable_mobile=False): + install_rdf = os.path.join(template_root_dir, "install.rdf") + manifest = RDFManifest(install_rdf) + + manifest.set("em:id", bundle_id) + manifest.set("em:version", + target_cfg.get('version', '1.0')) + manifest.set("em:name", + target_cfg.get('fullName', target_cfg['name'])) + manifest.set("em:description", + target_cfg.get("description", "")) + manifest.set("em:creator", + target_cfg.get("author", "")) + manifest.set("em:bootstrap", str(bootstrap).lower()) + manifest.set("em:unpack", "true") + if update_url: + manifest.set("em:updateURL", update_url) + else: + manifest.remove("em:updateURL") + + if enable_mobile: + dom = manifest.dom + target_app = dom.createElement("em:targetApplication") + dom.documentElement.getElementsByTagName("Description")[0].appendChild(target_app) + + ta_desc = dom.createElement("Description") + target_app.appendChild(ta_desc) + + elem = dom.createElement("em:id") + elem.appendChild(dom.createTextNode("{a23983c0-fd0e-11dc-95ff-0800200c9a66}")) + ta_desc.appendChild(elem) + + elem = dom.createElement("em:minVersion") + elem.appendChild(dom.createTextNode("4.0b7")) + ta_desc.appendChild(elem) + + elem = dom.createElement("em:maxVersion") + elem.appendChild(dom.createTextNode("9.0a1")) + ta_desc.appendChild(elem) + + if target_cfg.get("homepage"): + manifest.set("em:homepageURL", target_cfg.get("homepage")) + else: + manifest.remove("em:homepageURL") + + return manifest + +if __name__ == "__main__": + print "Running smoke test." + root = os.path.join(os.path.dirname(__file__), 'app-extension') + manifest = gen_manifest(root, {'name': 'test extension'}, + 'fakeid', 'http://foo.com/update.rdf') + update = RDFUpdate() + update.add(manifest, "https://foo.com/foo.xpi") + exercise_str = str(manifest) + str(update) + for tagname in ["em:targetApplication", "em:version", "em:id"]: + if not len(update.dom.getElementsByTagName(tagname)): + raise Exception("tag does not exist: %s" % tagname) + if not update.dom.getElementsByTagName(tagname)[0].firstChild: + raise Exception("tag has no children: %s" % tagname) + print "Success!" diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/runner.py b/tools/addon-sdk-1.3/python-lib/cuddlefish/runner.py new file mode 100644 index 0000000..72e6585 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/runner.py @@ -0,0 +1,627 @@ +import os +import sys +import time +import tempfile +import atexit +import shutil +import shlex +import subprocess +import re + +import simplejson as json +import mozrunner +from cuddlefish.prefs import DEFAULT_COMMON_PREFS +from cuddlefish.prefs import DEFAULT_FIREFOX_PREFS +from cuddlefish.prefs import DEFAULT_THUNDERBIRD_PREFS +from cuddlefish.prefs import DEFAULT_FENNEC_PREFS + +# Maximum time we'll wait for tests to finish, in seconds. +# The purpose of this timeout is to recover from infinite loops. It should be +# longer than the amount of time any test run takes, including those on slow +# machines running slow (debug) versions of Firefox. +RUN_TIMEOUT = 30 * 60 # 30 minutes + +# Maximum time we'll wait for tests to emit output, in seconds. +# The purpose of this timeout is to recover from hangs. It should be longer +# than the amount of time any test takes to report results. +OUTPUT_TIMEOUT = 60 # one minute + +def follow_file(filename): + """ + Generator that yields the latest unread content from the given + file, or None if no new content is available. + + For example: + + >>> f = open('temp.txt', 'w') + >>> f.write('hello') + >>> f.flush() + >>> tail = follow_file('temp.txt') + >>> tail.next() + 'hello' + >>> tail.next() is None + True + >>> f.write('there') + >>> f.flush() + >>> tail.next() + 'there' + >>> f.close() + >>> os.remove('temp.txt') + """ + + last_pos = 0 + last_size = 0 + while True: + newstuff = None + if os.path.exists(filename): + size = os.stat(filename).st_size + if size > last_size: + last_size = size + f = open(filename, 'r') + f.seek(last_pos) + newstuff = f.read() + last_pos = f.tell() + f.close() + yield newstuff + +# subprocess.check_output only appeared in python2.7, so this code is taken +# from python source code for compatibility with py2.5/2.6 +class CalledProcessError(Exception): + def __init__(self, returncode, cmd, output=None): + self.returncode = returncode + self.cmd = cmd + self.output = output + def __str__(self): + return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode) + +def check_output(*popenargs, **kwargs): + if 'stdout' in kwargs: + raise ValueError('stdout argument not allowed, it will be overridden.') + process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs) + output, unused_err = process.communicate() + retcode = process.poll() + if retcode: + cmd = kwargs.get("args") + if cmd is None: + cmd = popenargs[0] + raise CalledProcessError(retcode, cmd, output=output) + return output + + +class FennecProfile(mozrunner.Profile): + preferences = {} + names = ['fennec'] + +class FennecRunner(mozrunner.Runner): + profile_class = FennecProfile + + names = ['fennec'] + + __DARWIN_PATH = '/Applications/Fennec.app/Contents/MacOS/fennec' + + def __init__(self, binary=None, **kwargs): + if sys.platform == 'darwin' and binary and binary.endswith('.app'): + # Assume it's a Fennec app dir. + binary = os.path.join(binary, 'Contents/MacOS/fennec') + + self.__real_binary = binary + + mozrunner.Runner.__init__(self, **kwargs) + + def find_binary(self): + if not self.__real_binary: + if sys.platform == 'darwin': + if os.path.exists(self.__DARWIN_PATH): + return self.__DARWIN_PATH + self.__real_binary = mozrunner.Runner.find_binary(self) + return self.__real_binary + + +class RemoteFennecRunner(mozrunner.Runner): + profile_class = FennecProfile + + names = ['fennec'] + + _REMOTE_PATH = '/mnt/sdcard/jetpack-profile' + _INTENT_PREFIX = 'org.mozilla.' + + _adb_path = None + + def __init__(self, binary=None, **kwargs): + # Check that we have a binary set + if not binary: + raise ValueError("You have to define `--binary` option set to the " + "path to your ADB executable.") + # Ensure that binary refer to a valid ADB executable + output = subprocess.Popen([binary], stdout=subprocess.PIPE, + stderr=subprocess.PIPE).communicate() + output = "".join(output) + if not ("Android Debug Bridge" in output): + raise ValueError("`--binary` option should be the path to your " + "ADB executable.") + self.binary = binary + + mobile_app_name = kwargs['cmdargs'][0] + self.profile = kwargs['profile'] + self._adb_path = binary + + # This pref has to be set to `false` otherwise, we do not receive + # output of adb commands! + subprocess.call([self._adb_path, "shell", + "setprop log.redirect-stdio false"]) + + # Android apps are launched by their "intent" name, + # Automatically detect already installed firefox by using `pm` program + # or use name given as cfx `--mobile-app` argument. + intents = self.getIntentNames() + if not intents: + raise ValueError("Unable to found any Firefox " + "application on your device.") + elif mobile_app_name: + if not mobile_app_name in intents: + raise ValueError("Unable to found Firefox application " + "with intent name '%s'", mobile_app_name) + self._intent_name = self._INTENT_PREFIX + mobile_app_name + else: + if "firefox" in intents: + self._intent_name = self._INTENT_PREFIX + "firefox" + elif "firefox_beta" in intents: + self._intent_name = self._INTENT_PREFIX + "firefox_beta" + elif "firefox_nightly" in intents: + self._intent_name = self._INTENT_PREFIX + "firefox_nightly" + else: + self._intent_name = self._INTENT_PREFIX + intents[0] + + print "Launching mobile application with intent name " + self._intent_name + + # First try to kill firefox if it is already running + pid = self.getProcessPID(self._intent_name) + if pid != None: + # Send a key "up" signal to mobile-killer addon + # in order to kill running firefox instance + print "Killing running Firefox instance ..." + subprocess.call([self._adb_path, "shell", "input keyevent 19"]) + subprocess.Popen(self.command, stdout=subprocess.PIPE).wait() + time.sleep(2) + + print "Pushing the addon to your device" + + # Create a clean empty profile on the sd card + subprocess.call([self._adb_path, "shell", "rm -r " + self._REMOTE_PATH]) + subprocess.call([self._adb_path, "shell", "mkdir " + self._REMOTE_PATH]) + + # Push the profile folder created by mozrunner to the device + # (we can't simply use `adb push` as it doesn't copy empty folders) + localDir = self.profile.profile + remoteDir = self._REMOTE_PATH + for root, dirs, files in os.walk(localDir, followlinks='true'): + relRoot = os.path.relpath(root, localDir) + # Note about os.path usage below: + # Local files may be using Windows `\` separators but + # remote are always `/`, so we need to convert local ones to `/` + for file in files: + localFile = os.path.join(root, file) + remoteFile = remoteDir.replace("/", os.sep) + if relRoot != ".": + remoteFile = os.path.join(remoteFile, relRoot) + remoteFile = os.path.join(remoteFile, file) + remoteFile = "/".join(remoteFile.split(os.sep)) + subprocess.Popen([self._adb_path, "push", localFile, remoteFile], + stderr=subprocess.PIPE).wait() + for dir in dirs: + targetDir = remoteDir.replace("/", os.sep) + if relRoot != ".": + targetDir = os.path.join(targetDir, relRoot) + targetDir = os.path.join(targetDir, dir) + targetDir = "/".join(targetDir.split(os.sep)) + # `-p` option is not supported on all devices! + subprocess.call([self._adb_path, "shell", "mkdir " + targetDir]) + + @property + def command(self): + """Returns the command list to run.""" + return [self._adb_path, + "shell", + "am start " + + "-a android.activity.MAIN " + + "-n " + self._intent_name + "/" + self._intent_name + ".App " + + "--es args \"-profile " + self._REMOTE_PATH + "\"" + ] + + def start(self): + subprocess.call(self.command) + + def getProcessPID(self, processName): + p = subprocess.Popen([self._adb_path, "shell", "ps"], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + line = p.stdout.readline() + while line: + columns = line.split() + pid = columns[1] + name = columns[-1] + line = p.stdout.readline() + if processName in name: + return pid + return None + + def getIntentNames(self): + p = subprocess.Popen([self._adb_path, "shell", "pm list packages"], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + names = [] + for line in p.stdout.readlines(): + line = re.sub("(^package:)|\s", "", line) + if self._INTENT_PREFIX in line: + names.append(line.replace(self._INTENT_PREFIX, "")) + return names + + +class XulrunnerAppProfile(mozrunner.Profile): + preferences = {} + names = [] + +class XulrunnerAppRunner(mozrunner.Runner): + """ + Runner for any XULRunner app. Can use a Firefox binary in XULRunner + mode to execute the app, or can use XULRunner itself. Expects the + app's application.ini to be passed in as one of the items in + 'cmdargs' in the constructor. + + This class relies a lot on the particulars of mozrunner.Runner's + implementation, and does some unfortunate acrobatics to get around + some of the class' limitations/assumptions. + """ + + profile_class = XulrunnerAppProfile + + # This is a default, and will be overridden in the instance if + # Firefox is used in XULRunner mode. + names = ['xulrunner'] + + # Default location of XULRunner on OS X. + __DARWIN_PATH = "/Library/Frameworks/XUL.framework/xulrunner-bin" + __LINUX_PATH = "/usr/bin/xulrunner" + + # What our application.ini's path looks like if it's part of + # an "installed" XULRunner app on OS X. + __DARWIN_APP_INI_SUFFIX = '.app/Contents/Resources/application.ini' + + def __init__(self, binary=None, **kwargs): + if sys.platform == 'darwin' and binary and binary.endswith('.app'): + # Assume it's a Firefox app dir. + binary = os.path.join(binary, 'Contents/MacOS/firefox-bin') + + self.__app_ini = None + self.__real_binary = binary + + mozrunner.Runner.__init__(self, **kwargs) + + # See if we're using a genuine xulrunner-bin from the XULRunner SDK, + # or if we're being asked to use Firefox in XULRunner mode. + self.__is_xulrunner_sdk = 'xulrunner' in self.binary + + if sys.platform == 'linux2' and not self.env.get('LD_LIBRARY_PATH'): + self.env['LD_LIBRARY_PATH'] = os.path.dirname(self.binary) + + newargs = [] + for item in self.cmdargs: + if 'application.ini' in item: + self.__app_ini = item + else: + newargs.append(item) + self.cmdargs = newargs + + if not self.__app_ini: + raise ValueError('application.ini not found in cmdargs') + if not os.path.exists(self.__app_ini): + raise ValueError("file does not exist: '%s'" % self.__app_ini) + + if (sys.platform == 'darwin' and + self.binary == self.__DARWIN_PATH and + self.__app_ini.endswith(self.__DARWIN_APP_INI_SUFFIX)): + # If the application.ini is in an app bundle, then + # it could be inside an "installed" XULRunner app. + # If this is the case, use the app's actual + # binary instead of the XUL framework's, so we get + # a proper app icon, etc. + new_binary = '/'.join(self.__app_ini.split('/')[:-2] + + ['MacOS', 'xulrunner']) + if os.path.exists(new_binary): + self.binary = new_binary + + @property + def command(self): + """Returns the command list to run.""" + + if self.__is_xulrunner_sdk: + return [self.binary, self.__app_ini, '-profile', + self.profile.profile] + else: + return [self.binary, '-app', self.__app_ini, '-profile', + self.profile.profile] + + def __find_xulrunner_binary(self): + if sys.platform == 'darwin': + if os.path.exists(self.__DARWIN_PATH): + return self.__DARWIN_PATH + if sys.platform == 'linux2': + if os.path.exists(self.__LINUX_PATH): + return self.__LINUX_PATH + return None + + def find_binary(self): + # This gets called by the superclass constructor. It will + # always get called, even if a binary was passed into the + # constructor, because we want to have full control over + # what the exact setting of self.binary is. + + if not self.__real_binary: + self.__real_binary = self.__find_xulrunner_binary() + if not self.__real_binary: + dummy_profile = {} + runner = mozrunner.FirefoxRunner(profile=dummy_profile) + self.__real_binary = runner.find_binary() + self.names = runner.names + return self.__real_binary + +def run_app(harness_root_dir, manifest_rdf, harness_options, + app_type, binary=None, profiledir=None, verbose=False, + enforce_timeouts=False, + logfile=None, addons=None, args=None, norun=None, + used_files=None, enable_mobile=False, + mobile_app_name=None): + if binary: + binary = os.path.expanduser(binary) + + if addons is None: + addons = [] + else: + addons = list(addons) + + cmdargs = [] + preferences = dict(DEFAULT_COMMON_PREFS) + + if app_type == "fennec-on-device": + profile_class = FennecProfile + preferences.update(DEFAULT_FENNEC_PREFS) + runner_class = RemoteFennecRunner + # We pass the intent name through command arguments + cmdargs.append(mobile_app_name) + elif enable_mobile or app_type == "fennec": + profile_class = FennecProfile + preferences.update(DEFAULT_FENNEC_PREFS) + runner_class = FennecRunner + elif app_type == "xulrunner": + profile_class = XulrunnerAppProfile + runner_class = XulrunnerAppRunner + cmdargs.append(os.path.join(harness_root_dir, 'application.ini')) + elif app_type == "firefox": + profile_class = mozrunner.FirefoxProfile + preferences.update(DEFAULT_FIREFOX_PREFS) + runner_class = mozrunner.FirefoxRunner + elif app_type == "thunderbird": + profile_class = mozrunner.ThunderbirdProfile + preferences.update(DEFAULT_THUNDERBIRD_PREFS) + runner_class = mozrunner.ThunderbirdRunner + else: + raise ValueError("Unknown app: %s" % app_type) + if sys.platform == 'darwin' and app_type != 'xulrunner': + cmdargs.append('-foreground') + + if args: + cmdargs.extend(shlex.split(args)) + + # TODO: handle logs on remote device + if app_type != "fennec-on-device": + # tempfile.gettempdir() was constant, preventing two simultaneous "cfx + # run"/"cfx test" on the same host. On unix it points at /tmp (which is + # world-writeable), enabling a symlink attack (e.g. imagine some bad guy + # does 'ln -s ~/.ssh/id_rsa /tmp/harness_result'). NamedTemporaryFile + # gives us a unique filename that fixes both problems. We leave the + # (0-byte) file in place until the browser-side code starts writing to + # it, otherwise the symlink attack becomes possible again. + fileno,resultfile = tempfile.mkstemp(prefix="harness-result-") + os.close(fileno) + harness_options['resultFile'] = resultfile + + def maybe_remove_logfile(): + if os.path.exists(logfile): + os.remove(logfile) + + logfile_tail = None + + # We always buffer output through a logfile for two reasons: + # 1. On Windows, it's the only way to print console output to stdout/err. + # 2. It enables us to keep track of the last time output was emitted, + # so we can raise an exception if the test runner hangs. + if not logfile: + fileno,logfile = tempfile.mkstemp(prefix="harness-log-") + os.close(fileno) + logfile_tail = follow_file(logfile) + atexit.register(maybe_remove_logfile) + + logfile = os.path.abspath(os.path.expanduser(logfile)) + maybe_remove_logfile() + harness_options['logFile'] = logfile + + env = {} + env.update(os.environ) + env['MOZ_NO_REMOTE'] = '1' + env['XPCOM_DEBUG_BREAK'] = 'warn' + env['NS_TRACE_MALLOC_DISABLE_STACKS'] = '1' + if norun: + cmdargs.append("-no-remote") + + # Create the addon XPI so mozrunner will copy it to the profile it creates. + # We delete it below after getting mozrunner to create the profile. + from cuddlefish.xpi import build_xpi + xpi_path = tempfile.mktemp(suffix='cfx-tmp.xpi') + build_xpi(template_root_dir=harness_root_dir, + manifest=manifest_rdf, + xpi_path=xpi_path, + harness_options=harness_options, + limit_to=used_files) + addons.append(xpi_path) + + starttime = last_output_time = time.time() + + # Redirect runner output to a file so we can catch output not generated + # by us. + # In theory, we could do this using simple redirection on all platforms + # other than Windows, but this way we only have a single codepath to + # maintain. + fileno,outfile = tempfile.mkstemp(prefix="harness-stdout-") + os.close(fileno) + outfile_tail = follow_file(outfile) + def maybe_remove_outfile(): + if os.path.exists(outfile): + os.remove(outfile) + atexit.register(maybe_remove_outfile) + outf = open(outfile, "w") + popen_kwargs = { 'stdout': outf, 'stderr': outf} + + profile = None + + if app_type == "fennec-on-device": + # Install a special addon when we run firefox on mobile device + # in order to be able to kill it + mydir = os.path.dirname(os.path.abspath(__file__)) + killer_dir = os.path.join(mydir, "mobile-killer") + addons.append(killer_dir) + + # the XPI file is copied into the profile here + profile = profile_class(addons=addons, + profile=profiledir, + preferences=preferences) + + # Delete the temporary xpi file + os.remove(xpi_path) + + runner = runner_class(profile=profile, + binary=binary, + env=env, + cmdargs=cmdargs, + kp_kwargs=popen_kwargs) + + sys.stdout.flush(); sys.stderr.flush() + + if app_type == "fennec-on-device": + # in case we run it on a mobile device, we only have to launch it + runner.start() + profile.cleanup() + time.sleep(1) + print >>sys.stderr, "Remote application launched successfully." + return 0 + + print >>sys.stderr, "Using binary at '%s'." % runner.binary + + # Ensure cfx is being used with Firefox 4.0+. + # TODO: instead of dying when Firefox is < 4, warn when Firefox is outside + # the minVersion/maxVersion boundaries. + version_output = check_output(runner.command + ["-v"]) + # Note: this regex doesn't handle all valid versions in the Toolkit Version + # Format , just the + # common subset that we expect Mozilla apps to use. + mo = re.search(r"Mozilla (Firefox|Iceweasel|Fennec) ((\d+)\.\S*)", + version_output) + if not mo: + # cfx may be used with Thunderbird, SeaMonkey or an exotic Firefox + # version. + print """ + WARNING: cannot determine Firefox version; please ensure you are running + a Mozilla application equivalent to Firefox 4.0 or greater. + """ + elif mo.group(1) == "Fennec": + # For now, only allow running on Mobile with --force-mobile argument + if not enable_mobile: + print """ + WARNING: Firefox Mobile support is still experimental. + If you would like to run an addon on this platform, use --force-mobile flag: + + cfx --force-mobile""" + return + else: + version = mo.group(3) + if int(version) < 4: + print """ + cfx requires Firefox 4 or greater and is unable to find a compatible + binary. Please install a newer version of Firefox or provide the path to + your existing compatible version with the --binary flag: + + cfx --binary=PATH_TO_FIREFOX_BINARY""" + return + + # Set the appropriate extensions.checkCompatibility preference to false, + # so the tests run even if the SDK is not marked as compatible with the + # version of Firefox on which they are running, and we don't have to + # ensure we update the maxVersion before the version of Firefox changes + # every six weeks. + # + # The regex we use here is effectively the same as BRANCH_REGEX from + # /toolkit/mozapps/extensions/content/extensions.js, which toolkit apps + # use to determine whether or not to load an incompatible addon. + # + br = re.search(r"^([^\.]+\.[0-9]+[a-z]*).*", mo.group(2), re.I) + if br: + prefname = 'extensions.checkCompatibility.' + br.group(1) + profile.preferences[prefname] = False + # Calling profile.set_preferences here duplicates the list of prefs + # in prefs.js, since the profile calls self.set_preferences in its + # constructor, but that is ok, because it doesn't change the set of + # preferences that are ultimately registered in Firefox. + profile.set_preferences(profile.preferences) + + print >>sys.stderr, "Using profile at '%s'." % profile.profile + sys.stderr.flush() + + if norun: + print "To launch the application, enter the following command:" + print " ".join(runner.command) + " " + (" ".join(runner.cmdargs)) + return 0 + + runner.start() + + done = False + result = None + try: + while not done: + time.sleep(0.05) + for tail in (logfile_tail, outfile_tail): + if tail: + new_chars = tail.next() + if new_chars: + last_output_time = time.time() + sys.stderr.write(new_chars) + sys.stderr.flush() + if os.path.exists(resultfile): + result = open(resultfile).read() + if result: + if result in ['OK', 'FAIL']: + done = True + else: + sys.stderr.write("Hrm, resultfile (%s) contained something weird (%d bytes)\n" % (resultfile, len(result))) + sys.stderr.write("'"+result+"'\n") + if enforce_timeouts: + if time.time() - last_output_time > OUTPUT_TIMEOUT: + raise Exception("Test output exceeded timeout (%ds)." % + OUTPUT_TIMEOUT) + if time.time() - starttime > RUN_TIMEOUT: + raise Exception("Test run exceeded timeout (%ds)." % + RUN_TIMEOUT) + except: + runner.stop() + raise + else: + runner.wait(10) + finally: + outf.close() + if profile: + profile.cleanup() + + print >>sys.stderr, "Total time: %f seconds" % (time.time() - starttime) + + if result == 'OK': + print >>sys.stderr, "Program terminated successfully." + return 0 + else: + print >>sys.stderr, "Program terminated unsuccessfully." + return -1 diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/templates.py b/tools/addon-sdk-1.3/python-lib/cuddlefish/templates.py new file mode 100644 index 0000000..577bcc5 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/templates.py @@ -0,0 +1,79 @@ +#Template used by main.js +MAIN_JS = '''\ +const widgets = require("widget"); +const tabs = require("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/"); + } +}); + +console.log("The add-on is running."); +''' + +#Template used by test-main.js +TEST_MAIN_JS = '''\ +const main = require("main"); + +exports.test_test_run = function(test) { + test.pass("Unit test running!"); +}; + +exports.test_id = function(test) { + test.assert(require("self").id.length > 0); +}; + +exports.test_url = function(test) { + require("request").Request({ + url: "http://www.mozilla.org/", + onComplete: function(response) { + test.assertEqual(response.statusText, "OK"); + test.done(); + } + }).get(); + test.waitUntilDone(20000); +}; + +exports.test_open_tab = function(test) { + const tabs = require("tabs"); + tabs.open({ + url: "http://www.mozilla.org/", + onReady: function(tab) { + test.assertEqual(tab.url, "http://www.mozilla.org/"); + test.done(); + } + }); + test.waitUntilDone(20000); +}; +''' + +#Template used by main.md +MAIN_JS_DOC = '''\ +The main module is a program that creates a widget. When a user clicks on +the widget, the program loads the mozilla.org website in a new tab. +''' + +#Template used by README.md +README_DOC = '''\ +This is the %(name)s add-on. It contains: + +* A program (lib/main.js). +* A few tests. +* Some meager documentation. +''' + +#Template used by package.json +PACKAGE_JSON = '''\ +{ + "name": "%(name)s", + "fullName": "%(fullName)s", + "description": "a basic add-on", + "author": "", + "license": "MPL 1.1/GPL 2.0/LGPL 2.1", + "version": "0.1" +} +''' diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/__init__.py b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/__init__.py new file mode 100644 index 0000000..c0b34dc --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/__init__.py @@ -0,0 +1,61 @@ +import os +import unittest +import doctest +import glob + +env_root = os.environ['CUDDLEFISH_ROOT'] + +def get_tests(): + import cuddlefish + import cuddlefish.tests + + tests = [] + packages = [cuddlefish, cuddlefish.tests] + for package in packages: + path = os.path.abspath(package.__path__[0]) + pynames = glob.glob(os.path.join(path, '*.py')) + for filename in pynames: + basename = os.path.basename(filename) + module_name = os.path.splitext(basename)[0] + full_name = "%s.%s" % (package.__name__, module_name) + module = __import__(full_name, fromlist=[package.__name__]) + + loader = unittest.TestLoader() + suite = loader.loadTestsFromModule(module) + for test in suite: + tests.append(test) + + finder = doctest.DocTestFinder() + doctests = finder.find(module) + for test in doctests: + if len(test.examples) > 0: + tests.append(doctest.DocTestCase(test)) + + md_dir = os.path.join(env_root, 'dev-guide') + doctest_opts = (doctest.NORMALIZE_WHITESPACE | + doctest.REPORT_UDIFF) + for dirpath, dirnames, filenames in os.walk(md_dir): + for filename in filenames: + if filename.endswith('.md'): + absname = os.path.join(dirpath, filename) + tests.append(doctest.DocFileTest( + absname, + module_relative=False, + optionflags=doctest_opts + )) + + return tests + +def run(verbose=False): + if verbose: + verbosity = 2 + else: + verbosity = 1 + + tests = get_tests() + suite = unittest.TestSuite(tests) + runner = unittest.TextTestRunner(verbosity=verbosity) + return runner.run(suite) + +if __name__ == '__main__': + run() diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/explicit-icon.png b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/explicit-icon.png new file mode 100644 index 0000000..e69de29 diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/explicit-icon64.png b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/explicit-icon64.png new file mode 100644 index 0000000..e69de29 diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/lib/main.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/lib/main.js new file mode 100644 index 0000000..e69de29 diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/package.json b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/package.json new file mode 100644 index 0000000..8d56d74 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/package.json @@ -0,0 +1,5 @@ +{ + "loader": "lib/main.js", + "icon": "explicit-icon.png", + "icon64": "explicit-icon64.png" +} diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/icon.png b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/icon.png new file mode 100644 index 0000000..e69de29 diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/icon64.png b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/icon64.png new file mode 100644 index 0000000..e69de29 diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/lib/main.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/lib/main.js new file mode 100644 index 0000000..e69de29 diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/package.json b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/package.json new file mode 100644 index 0000000..3f0e241 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/package.json @@ -0,0 +1,3 @@ +{ + "loader": "lib/main.js" +} diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/no-icon/lib/main.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/no-icon/lib/main.js new file mode 100644 index 0000000..e69de29 diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/no-icon/package.json b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/no-icon/package.json new file mode 100644 index 0000000..3f0e241 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588119-files/packages/no-icon/package.json @@ -0,0 +1,3 @@ +{ + "loader": "lib/main.js" +} diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588661-files/packages/bar/lib/bar-loader.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588661-files/packages/bar/lib/bar-loader.js new file mode 100644 index 0000000..e69de29 diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588661-files/packages/bar/package.json b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588661-files/packages/bar/package.json new file mode 100644 index 0000000..e83a9d4 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588661-files/packages/bar/package.json @@ -0,0 +1,3 @@ +{ + "loader": "lib/bar-loader.js" +} diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588661-files/packages/foo/lib/foo-loader.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588661-files/packages/foo/lib/foo-loader.js new file mode 100644 index 0000000..e69de29 diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588661-files/packages/foo/package.json b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588661-files/packages/foo/package.json new file mode 100644 index 0000000..4648df6 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-588661-files/packages/foo/package.json @@ -0,0 +1,4 @@ +{ + "loader": "lib/foo-loader.js", + "dependencies": ["bar"] +} diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/docs/main.md b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/docs/main.md new file mode 100644 index 0000000..916389e --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/docs/main.md @@ -0,0 +1 @@ +minimal docs diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/lib/main.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/lib/main.js new file mode 100644 index 0000000..27af5dd --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/lib/main.js @@ -0,0 +1,4 @@ +exports.main = function(options, callbacks) { + console.log("minimal"); + callbacks.quit(); +}; diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/package.json b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/package.json new file mode 100644 index 0000000..45d409a --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/package.json @@ -0,0 +1,5 @@ +{ + "name": "jspath-one", + "author": "Jon Smith", + "description": "A package w/ a main module; can be built into an extension." +} diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/doc/foo.md b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/doc/foo.md new file mode 100644 index 0000000..2b38e6a --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/doc/foo.md @@ -0,0 +1 @@ +I am documentation for foo. diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/lib/foo-loader.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/lib/foo-loader.js new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/lib/foo-loader.js @@ -0,0 +1 @@ + diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/package.json b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/package.json new file mode 100644 index 0000000..ccc61b2 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/package.json @@ -0,0 +1,3 @@ +{ + "loader": "lib/foo-loader.js" +} diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/test/test-foo.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/test/test-foo.js new file mode 100644 index 0000000..3045af3 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/test/test-foo.js @@ -0,0 +1,3 @@ +exports.testThing = function(test) { + test.assertEqual(2, 1 + 1); +}; diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/docs/foo.md b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/docs/foo.md new file mode 100644 index 0000000..2b38e6a --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/docs/foo.md @@ -0,0 +1 @@ +I am documentation for foo. diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/lib/foo-loader.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/lib/foo-loader.js new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/lib/foo-loader.js @@ -0,0 +1 @@ + diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/package.json b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/package.json new file mode 100644 index 0000000..ccc61b2 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/package.json @@ -0,0 +1,3 @@ +{ + "loader": "lib/foo-loader.js" +} diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/tests/test-foo.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/tests/test-foo.js new file mode 100644 index 0000000..3045af3 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/tests/test-foo.js @@ -0,0 +1,3 @@ +exports.testThing = function(test) { + test.assertEqual(2, 1 + 1); +}; diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/doc/foo.md b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/doc/foo.md new file mode 100644 index 0000000..2b38e6a --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/doc/foo.md @@ -0,0 +1 @@ +I am documentation for foo. diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/lib/foo.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/lib/foo.js new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/lib/foo.js @@ -0,0 +1 @@ + diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/lib/loader.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/lib/loader.js new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/lib/loader.js @@ -0,0 +1 @@ + diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/package.json b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/package.json new file mode 100644 index 0000000..c2d22aa --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/package.json @@ -0,0 +1,3 @@ +{ + "loader": "lib/loader.js" +} diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/test/test-foo.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/test/test-foo.js new file mode 100644 index 0000000..3045af3 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/test/test-foo.js @@ -0,0 +1,3 @@ +exports.testThing = function(test) { + test.assertEqual(2, 1 + 1); +}; diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/doc/foo.md b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/doc/foo.md new file mode 100644 index 0000000..2b38e6a --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/doc/foo.md @@ -0,0 +1 @@ +I am documentation for foo. diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/foo.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/foo.js new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/foo.js @@ -0,0 +1 @@ + diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/loader.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/loader.js new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/loader.js @@ -0,0 +1 @@ + diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/package.json b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/package.json new file mode 100644 index 0000000..100249f --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/package.json @@ -0,0 +1,3 @@ +{ + "loader": "loader.js" +} diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/test/test-foo.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/test/test-foo.js new file mode 100644 index 0000000..3045af3 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/test/test-foo.js @@ -0,0 +1,3 @@ +exports.testThing = function(test) { + test.assertEqual(2, 1 + 1); +}; diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/alt-lib/foo.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/alt-lib/foo.js new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/alt-lib/foo.js @@ -0,0 +1 @@ + diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/alt-lib/loader.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/alt-lib/loader.js new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/alt-lib/loader.js @@ -0,0 +1 @@ + diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/doc/foo.md b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/doc/foo.md new file mode 100644 index 0000000..2b38e6a --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/doc/foo.md @@ -0,0 +1 @@ +I am documentation for foo. diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/package.json b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/package.json new file mode 100644 index 0000000..f79250e --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/package.json @@ -0,0 +1,4 @@ +{ + "directories": { "lib": "./alt-lib" }, + "loader": "alt-lib/loader.js" +} diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/test/test-foo.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/test/test-foo.js new file mode 100644 index 0000000..3045af3 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/test/test-foo.js @@ -0,0 +1,3 @@ +exports.testThing = function(test) { + test.assertEqual(2, 1 + 1); +}; diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/alt2-lib/foo.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/alt2-lib/foo.js new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/alt2-lib/foo.js @@ -0,0 +1 @@ + diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/alt2-lib/loader.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/alt2-lib/loader.js new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/alt2-lib/loader.js @@ -0,0 +1 @@ + diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/doc/foo.md b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/doc/foo.md new file mode 100644 index 0000000..2b38e6a --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/doc/foo.md @@ -0,0 +1 @@ +I am documentation for foo. diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/package.json b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/package.json new file mode 100644 index 0000000..b017baa --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/package.json @@ -0,0 +1,4 @@ +{ + "lib": "./alt2-lib", + "loader": "alt2-lib/loader.js" +} diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/test/test-foo.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/test/test-foo.js new file mode 100644 index 0000000..3045af3 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/test/test-foo.js @@ -0,0 +1,3 @@ +exports.testThing = function(test) { + test.assertEqual(2, 1 + 1); +}; diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/docs/main.md b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/docs/main.md new file mode 100644 index 0000000..916389e --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/docs/main.md @@ -0,0 +1 @@ +minimal docs diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/lib/main.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/lib/main.js new file mode 100644 index 0000000..27af5dd --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/lib/main.js @@ -0,0 +1,4 @@ +exports.main = function(options, callbacks) { + console.log("minimal"); + callbacks.quit(); +}; diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/package.json b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/package.json new file mode 100644 index 0000000..de868f7 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/package.json @@ -0,0 +1,6 @@ +{ + "name": "extra-options", + "author": "Jon Smith", + "description": "A package w/ a main module; can be built into an extension.", + "loader": "lib/main.js" +} diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/bar-e10s-adapter.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/bar-e10s-adapter.js new file mode 100644 index 0000000..24095f2 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/bar-e10s-adapter.js @@ -0,0 +1,7 @@ +if (this.sendMessage) { +} else { + require('bar'); + + exports.register = function(process) { + }; +} diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/bar.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/bar.js new file mode 100644 index 0000000..460045b --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/bar.js @@ -0,0 +1 @@ +require('chrome'); diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/foo.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/foo.js new file mode 100644 index 0000000..2bb1e10 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/foo.js @@ -0,0 +1 @@ +require('bar'); diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/package.json b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/package.json new file mode 100644 index 0000000..0967ef4 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/package.json @@ -0,0 +1 @@ +{} diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/five/lib/main.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/five/lib/main.js new file mode 100644 index 0000000..1bae788 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/five/lib/main.js @@ -0,0 +1 @@ +exports.main = "'main' mainly reigns in main(.js)"; diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/five/package.json b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/five/package.json new file mode 100644 index 0000000..98e4b85 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/five/package.json @@ -0,0 +1,3 @@ +{ "name": "five", + "main": "./lib/main" +} diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/lib/misc.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/lib/misc.js new file mode 100644 index 0000000..950c7b0 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/lib/misc.js @@ -0,0 +1 @@ +exports.main = 42; diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/package.json b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/package.json new file mode 100644 index 0000000..3010fae --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/package.json @@ -0,0 +1,4 @@ +{ "name": "four-a", + "directories": {"lib": "lib"}, + "main": "./topfiles/main.js" +} diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/topfiles/main.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/topfiles/main.js new file mode 100644 index 0000000..950c7b0 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/topfiles/main.js @@ -0,0 +1 @@ +exports.main = 42; diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/four/lib/main.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/four/lib/main.js new file mode 100644 index 0000000..d5c9345 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/four/lib/main.js @@ -0,0 +1 @@ +var a = require("four-a"); diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/four/package.json b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/four/package.json new file mode 100644 index 0000000..53180b9 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/four/package.json @@ -0,0 +1,3 @@ +{ "name": "four", + "main": "main" +} diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/one/lib/main.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/one/lib/main.js new file mode 100644 index 0000000..85f37e3 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/one/lib/main.js @@ -0,0 +1,5 @@ +var panel = require("panel"); +var two = require("two.js"); +var a = require("./two"); +var b = require("addon-kit/tabs.js"); +var c = require("./subdir/three"); diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/one/lib/subdir/three.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/one/lib/subdir/three.js new file mode 100644 index 0000000..c24fdee --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/one/lib/subdir/three.js @@ -0,0 +1,2 @@ +exports.foo = 1; +var main = require("../main"); diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/one/lib/two.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/one/lib/two.js new file mode 100644 index 0000000..ddc754f --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/one/lib/two.js @@ -0,0 +1,4 @@ + +// this ought to find our sibling, not packages/development-mode/lib/main.js +var main = require("main"); +exports.foo = 1; diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/one/package.json b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/one/package.json new file mode 100644 index 0000000..0420771 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/one/package.json @@ -0,0 +1,3 @@ +{ "name": "one", + "main": "main" +} diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/seven/data/text.data b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/seven/data/text.data new file mode 100644 index 0000000..1269488 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/seven/data/text.data @@ -0,0 +1 @@ +data diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/seven/lib/main.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/seven/lib/main.js new file mode 100644 index 0000000..7e651fb --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/seven/lib/main.js @@ -0,0 +1,2 @@ +var self = require("self"); // trigger inclusion of data +exports.main = function () { console.log("main"); }; diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/seven/lib/unused.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/seven/lib/unused.js new file mode 100644 index 0000000..d529639 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/seven/lib/unused.js @@ -0,0 +1 @@ +exports.unused = "just pretend I'm not here"; diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/seven/package.json b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/seven/package.json new file mode 100644 index 0000000..ef14839 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/seven/package.json @@ -0,0 +1,4 @@ +{ + "name": "seven", + "id": "jid1" +} diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/six/lib/unused.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/six/lib/unused.js new file mode 100644 index 0000000..0fa8f43 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/six/lib/unused.js @@ -0,0 +1 @@ +exports.unused = "I am."; diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/six/package.json b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/six/package.json new file mode 100644 index 0000000..906b249 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/six/package.json @@ -0,0 +1,3 @@ +{ "name": "six", + "main": "./unreachable" +} diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/six/unreachable.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/six/unreachable.js new file mode 100644 index 0000000..a208943 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/six/unreachable.js @@ -0,0 +1 @@ +exports.main = "I am outside lib/ and cannot be reached, yet"; diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/data/msg.txt b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/data/msg.txt new file mode 100644 index 0000000..3b18e51 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/data/msg.txt @@ -0,0 +1 @@ +hello world diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/data/subdir/submsg.txt b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/data/subdir/submsg.txt new file mode 100644 index 0000000..d2cfe80 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/data/subdir/submsg.txt @@ -0,0 +1 @@ +hello subdir diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/main.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/main.js new file mode 100644 index 0000000..ed76814 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/main.js @@ -0,0 +1,4 @@ +exports.main = 42; +require("./subdir/subfile"); +require("self"); // trigger inclusion of our data/ directory + diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/subdir/subfile.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/subdir/subfile.js new file mode 100644 index 0000000..66bff8b --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/subdir/subfile.js @@ -0,0 +1 @@ +exports.main = "I should be included in a subdir"; diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/unused.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/unused.js new file mode 100644 index 0000000..e70fe71 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/unused.js @@ -0,0 +1 @@ +exports.main = "unused, linker should not include me in the XPI"; diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/package.json b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/package.json new file mode 100644 index 0000000..6b796fc --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/package.json @@ -0,0 +1,3 @@ +{ "name": "three-a", + "main": "./lib/main.js" +} diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-b/lib/main.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-b/lib/main.js new file mode 100644 index 0000000..950c7b0 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-b/lib/main.js @@ -0,0 +1 @@ +exports.main = 42; diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-b/package.json b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-b/package.json new file mode 100644 index 0000000..c0ff5ee --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-b/package.json @@ -0,0 +1,3 @@ +{ "name": "three-b", + "main": "./lib/main" +} diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/lib/main.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/lib/main.js new file mode 100644 index 0000000..950c7b0 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/lib/main.js @@ -0,0 +1 @@ +exports.main = 42; diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/lib/sub/foo.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/lib/sub/foo.js new file mode 100644 index 0000000..9084831 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/lib/sub/foo.js @@ -0,0 +1,2 @@ +exports.foo = "you found me down here"; + diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/package.json b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/package.json new file mode 100644 index 0000000..169c914 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/package.json @@ -0,0 +1,3 @@ +{ "name": "three-c", + "main": "lib/main" +} diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three/lib/main.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three/lib/main.js new file mode 100644 index 0000000..b4fe4f5 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three/lib/main.js @@ -0,0 +1,4 @@ +var a = require("three-a"); +var b = require("three-b"); +var c = require("three-c"); +var c3 = require("three-c/sub/foo"); diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three/package.json b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three/package.json new file mode 100644 index 0000000..cbfbc5b --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/linker-files/three/package.json @@ -0,0 +1,3 @@ +{ "name": "three", + "main": "main" +} diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/doc/dev-guide-source/no_h1.md b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/doc/dev-guide-source/no_h1.md new file mode 100644 index 0000000..51f3108 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/doc/dev-guide-source/no_h1.md @@ -0,0 +1,3 @@ +## A heading ## + +*Some words!* diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/doc/dev-guide-source/welcome.md b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/doc/dev-guide-source/welcome.md new file mode 100644 index 0000000..573822a --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/doc/dev-guide-source/welcome.md @@ -0,0 +1,3 @@ +# An Imposing Title # + +*Some words!* diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/doc/static-files/another.html b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/doc/static-files/another.html new file mode 100644 index 0000000..cdcb284 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/doc/static-files/another.html @@ -0,0 +1 @@ +another file \ No newline at end of file diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/doc/static-files/base.html b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/doc/static-files/base.html new file mode 100644 index 0000000..8b896cd --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/doc/static-files/base.html @@ -0,0 +1,136 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/doc/static-files/index.html b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/doc/static-files/index.html new file mode 100644 index 0000000..b84eeec --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/doc/static-files/index.html @@ -0,0 +1,23 @@ + + + + + + + + + + +
+ +
+ +
+ +
+ +
+ + + + diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/docs/APIreference.html b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/docs/APIreference.html new file mode 100644 index 0000000..a33a4f9 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/docs/APIreference.html @@ -0,0 +1,465 @@ + + + + + + + + + + + + + + + + Add-on SDK Documentation + + + + + + + + + + + + + +
+

APIsample

+

Title

+

Some text here

+

This text appears between the API blocks.

+

Wooo, more text.

+

Some more text here, at the end of the file.

+
+ +
+

API Reference

+ +
+

Classes

+ +
+

empty-class

+

This class contains nothing.

+
+ +
+

only-one-ctor

+

This class contains only one constructor.

+
+
Constructors
+ +
+
one-constructor(options)
+ +
+
+
[ options ]
+

An object-bag of goodies.

+
+ +
+ +
+ +
+ +
+ +
+

two-ctors

+

This class contains two constructors.

+
+
Constructors
+ +
+
one-constructor(options)
+

The first constructor.

+
+
+
[ options ]
+

An object-bag of goodies.

+
+ +
+ +
+ +
+
another-constructor(options)
+

The second constructor.

+
+
+
[ options ]
+

An object-bag of goodies.

+
+ +
+ +
+ +
+ +
+ +
+

ctor-and-method

+

This class contains one constructor and one method.

+
+
Constructors
+ +
+
one-constructor(options)
+

The first constructor.

+
+
+
[ options ]
+

An object-bag of goodies.

+
+ +
+ +
+ +
+ +
+
Methods
+ +
+
a-method(options)
+

Does things.

+
+
+
[ options ]
+

An argument.

+
+ +
+ +
+ +
+ +
+ +
+

ctor-method-prop-event

+

This class contains one constructor, one method, one property and an event.

+
+
Constructors
+ +
+
one-constructor(options)
+

The first constructor.

+
+
+
[ options ]
+

An object-bag of goodies.

+
+ +
+ +
+ +
+ +
+
Methods
+ +
+
a-method(options)
+

Does things.

+
+
+
[ options ]
+

An argument.

+
+ +
+ +
+ +
+ +
+
Properties
+ +
+
a-property : bool
+

Represents stuff.

+
+ +
+ +
+
Events
+ +
+
message
+

Event emitted when the content script sends a message to the add-on.

+
+
+
JSON
+

The message itself as a JSON-serialized object.

+
+ +
+ +
+ +
+ +
+ +
+ +
+

Functions

+ +
+

test(argOne, argTwo, argThree, options)

+

This is a function which does nothing in particular.

+
+
+
argOne : string
+

This is the first argument.

+
+ +
+
[ argTwo : bool ]
+

This is the second argument.

+
+ +
+
[ argThree = default : uri ]
+

This is the third and final argument. And this is + a test of the ability to do multiple lines of + text.

+
+ +
+
[ options ]
+

Options Bag

+
+
[ style : string ]
+

Some style information.

+
+ +
+
[ secondToLastOption = True : bool ]
+

The last property.

+
+ +
+
[ lastOption : uri ]
+

And this time we have + A multiline description + Written as haiku

+
+ +
+ +
+ +
Returns: object +
+ +
+ +
+

append(options)

+

This is a list of options to specify modifications to your slideBar instance.

+
+
+
options
+

Pass in all of your options here.

+
+
[ icon : uri ]
+

The HREF of an icon to show as the method of accessing your features slideBar

+
+ +
+
[ html : string/xml ]
+

The content of the feature, either as an HTML string, + or an E4X document fragment.

+
+ +
+
[ url : uri ]
+

The url to load into the content area of the feature

+
+ +
+
[ width : int ]
+

Width of the content area and the selected slide size

+
+ +
+
[ persist : bool ]
+

Default slide behavior when being selected as follows: + If true: blah; If false: double blah.

+
+ +
+
[ autoReload : bool ]
+

Automatically reload content on select

+
+ +
+
[ onClick : function ]
+

Callback when the icon is clicked

+
+ +
+
[ onSelect : function ]
+

Callback when the feature is selected

+
+ +
+
[ onReady : function ]
+

Callback when featured is loaded

+
+ +
+ +
+ +
+ +
+

cool-func.dot(howMuch, double, options, onemore, options2)

+ +
+
+
howMuch : string
+

How much cool it is.

+
+ +
+
[ double = true : bool ]
+

In case you just really need to double it.

+
+ +
+
[ options ]
+

An object-bag of goodies.

+
+
callback : function
+

The callback

+
+ +
+
[ random : bool ]
+

Do something random?

+
+ +
+ +
+
[ onemore : bool ]
+

One more paramater

+
+ +
+
[ options2 ]
+

This is a full description of something + that really sucks. Because I now have a multiline + description of this thingy.

+
+
monkey : string
+

You heard me right

+
+ +
+
[ freak = true : bool ]
+

Yes, you are a freak.

+
+ +
+ +
+ +
Returns: string

A value telling you just how cool you are. + A boa-constructor! + This description can go on for a while, and can even contain + some realy fancy things. Like code, or even + ~~~~{.javascript} + // Some code! + ~~~~

+
+ +
+ +
+

random()

+

A function that returns a random integer between 0 and 10.

+
Returns: int

The random number.

+
+ +
+ +
+ +
+

Events

+ +
+

open

+

A module-level event called open.

+
+
+
bool
+

Yes, it's open.

+
+ +
+ +
+ +
+ +
+ +
+ + + + + + + diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/docs/APIsample.md b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/docs/APIsample.md new file mode 100644 index 0000000..24271ec --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/docs/APIsample.md @@ -0,0 +1,158 @@ +# Title # + +Some text here + + +@function +This is a function which does nothing in particular. +@returns {object} + @prop firststring {string} First string + @prop firsturl {url} First URL +@param argOne {string} This is the first argument. +@param [argTwo] {bool} This is the second argument. +@param [argThree=default] {uri} + This is the third and final argument. And this is + a test of the ability to do multiple lines of + text. +@param [options] Options Bag + @prop [style] {string} Some style information. + @prop [secondToLastOption=True] {bool} The last property. + @prop [lastOption] {uri} + And this time we have + A multiline description + Written as haiku + + +This text appears between the API blocks. + + +@function +This is a list of options to specify modifications to your slideBar instance. +@param options + Pass in all of your options here. + @prop [icon] {uri} The HREF of an icon to show as the method of accessing your features slideBar + @prop [html] {string/xml} + The content of the feature, either as an HTML string, + or an E4X document fragment. + @prop [url] {uri} The url to load into the content area of the feature + @prop [width] {int} Width of the content area and the selected slide size + @prop [persist] {bool} + Default slide behavior when being selected as follows: + If true: blah; If false: double blah. + @prop [autoReload] {bool} Automatically reload content on select + @prop [onClick] {function} Callback when the icon is clicked + @prop [onSelect] {function} Callback when the feature is selected + @prop [onReady] {function} Callback when featured is loaded + + +Wooo, more text. + + +@function +@returns {string} A value telling you just how cool you are. +A boa-constructor! +This description can go on for a while, and can even contain +some **realy** fancy things. Like `code`, or even +~~~~{.javascript} +// Some code! +~~~~ +@param howMuch {string} How much cool it is. +@param [double=true] {bool} + In case you just really need to double it. +@param [options] An object-bag of goodies. + @prop callback {function} The callback + @prop [random] {bool} Do something random? +@param [onemore] {bool} One more paramater +@param [options2] + This is a full description of something + that really sucks. Because I now have a multiline + description of this thingy. + @prop monkey {string} You heard me right + @prop [freak=true] {bool} + Yes, you are a freak. + + + +@function +A function that returns a random integer between 0 and 10. +@returns {int} The random number. + + + +@class +This class contains nothing. + + + +@class +This class contains only one constructor. + +@constructor +@param [options] An object-bag of goodies. + + + + +@class +This class contains two constructors. + +@constructor +The first constructor. +@param [options] An object-bag of goodies. + + +@constructor +The second constructor. +@param [options] An object-bag of goodies. + + + + +@class +This class contains one constructor and one method. + +@constructor +The first constructor. +@param [options] An object-bag of goodies. + + +@method +Does things. +@param [options] An argument. + + + + +@class +This class contains one constructor, one method, one property and an event. + +@constructor +The first constructor. +@param [options] An object-bag of goodies. + + +@method +Does things. +@param [options] An argument. + + +@property {bool} +Represents stuff. + + +@event +Event emitted when the content script sends a message to the add-on. +@argument {JSON} +The message itself as a JSON-serialized object. + + + + +@event +A module-level event called open. +@argument {bool} +Yes, it's open. + + +Some more text here, at the end of the file. + diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/aardvark/doc/aardvark-feeder.md b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/aardvark/doc/aardvark-feeder.md new file mode 100644 index 0000000..9f61e86 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/aardvark/doc/aardvark-feeder.md @@ -0,0 +1,8 @@ +The `aardvark-feeder` module simplifies feeding aardvarks. + + +@function + Feed the aardvark. +@param food {string} + The food. Aardvarks will eat anything. + diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/aardvark/doc/main.md b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/aardvark/doc/main.md new file mode 100644 index 0000000..e69de29 diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/ignore_me b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/ignore_me new file mode 100644 index 0000000..014242c --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/ignore_me @@ -0,0 +1,3 @@ +The docs processor should tolerate (by ignoring) random non-.js files in lib +directories, such as those left around by editors, version-control systems, +or OS metadata like .DS_Store . This file exercises that tolerance. diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/main.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/main.js new file mode 100644 index 0000000..0591fe0 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/main.js @@ -0,0 +1,4 @@ +exports.main = function(options, callbacks) { + console.log("1 + 1 =", require("bar-module").add(1, 1)); + callbacks.quit(); +}; diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/surprise.js/ignore_me_too b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/surprise.js/ignore_me_too new file mode 100644 index 0000000..066f9b5 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/surprise.js/ignore_me_too @@ -0,0 +1,2 @@ +The docs processor should also ignore directories named *.js, and their +contents. diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/aardvark/package.json b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/aardvark/package.json new file mode 100644 index 0000000..07eb9b9 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/aardvark/package.json @@ -0,0 +1,7 @@ +{ + "author": "Jon Smith", + "description": "A package w/ a main module; can be built into an extension.", + "keywords": ["potato"], + "version": "1.0", + "dependencies": ["api-utils", "barbeque"] +} diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/anteater_files/lib/main.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/anteater_files/lib/main.js new file mode 100644 index 0000000..0591fe0 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/anteater_files/lib/main.js @@ -0,0 +1,4 @@ +exports.main = function(options, callbacks) { + console.log("1 + 1 =", require("bar-module").add(1, 1)); + callbacks.quit(); +}; diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/anteater_files/package.json b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/anteater_files/package.json new file mode 100644 index 0000000..0e2b552 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/anteater_files/package.json @@ -0,0 +1,8 @@ +{ + "name": "anteater", + "author": "Jon Smith", + "description": "A package w/ a main module; can be built into an extension.", + "keywords": ["potato"], + "version": "1.0", + "dependencies": ["api-utils", "barbeque"] +} diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/api-utils/lib/loader.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/api-utils/lib/loader.js new file mode 100644 index 0000000..d115349 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/api-utils/lib/loader.js @@ -0,0 +1,3 @@ +// This module will be imported by the XPCOM harness/boostrapper +// via Components.utils.import() and is responsible for creating a +// CommonJS module loader. diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/api-utils/package.json b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/api-utils/package.json new file mode 100644 index 0000000..64eb065 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/api-utils/package.json @@ -0,0 +1,5 @@ +{ + "description": "A foundational package that provides a CommonJS module loader implementation.", + "keywords": ["potato", "jetpack-low-level"], + "loader": "lib/loader.js" +} diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/barbeque/lib/bar-module.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/barbeque/lib/bar-module.js new file mode 100644 index 0000000..44a9a43 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/barbeque/lib/bar-module.js @@ -0,0 +1,3 @@ +exports.add = function add(a, b) { + return a + b; +}; diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/barbeque/package.json b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/barbeque/package.json new file mode 100644 index 0000000..62e3c12 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/barbeque/package.json @@ -0,0 +1,4 @@ +{ + "keywords": ["potato", "jetpack-low-level"], + "description": "A package used by 'aardvark' as a library." +} diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/minimal/docs/main.md b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/minimal/docs/main.md new file mode 100644 index 0000000..916389e --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/minimal/docs/main.md @@ -0,0 +1 @@ +minimal docs diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/minimal/lib/main.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/minimal/lib/main.js new file mode 100644 index 0000000..27af5dd --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/minimal/lib/main.js @@ -0,0 +1,4 @@ +exports.main = function(options, callbacks) { + console.log("minimal"); + callbacks.quit(); +}; diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/minimal/package.json b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/minimal/package.json new file mode 100644 index 0000000..530f3c2 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/packages/minimal/package.json @@ -0,0 +1,4 @@ +{ + "author": "Jon Smith", + "description": "A package w/ a main module; can be built into an extension." +} diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/xpi-template/components/harness.js b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/xpi-template/components/harness.js new file mode 100644 index 0000000..5e2d20a --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/static-files/xpi-template/components/harness.js @@ -0,0 +1,4 @@ +// This file contains XPCOM code that bootstraps an SDK-based add-on +// by loading its harness-options.json, registering all its resource +// directories, executing its loader, and then executing its program's +// main() function. diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_apiparser.py b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_apiparser.py new file mode 100644 index 0000000..1e95f4c --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_apiparser.py @@ -0,0 +1,524 @@ + +import os +import unittest +from cuddlefish.docs.apiparser import parse_hunks, ParseError + +tests_path = os.path.abspath(os.path.dirname(__file__)) +static_files_path = os.path.join(tests_path, "static-files") + +class ParserTests(unittest.TestCase): + def pathname(self, filename): + return os.path.join(static_files_path, "docs", filename) + + def parse_text(self, text): + return list(parse_hunks(text)) + + def parse(self, pathname): + return self.parse_text(open(pathname).read()) + + def test_parser(self): + parsed = self.parse(self.pathname("APIsample.md")) + #for i,h in enumerate(parsed): + # print i, h + self.assertEqual(parsed[0], + ("version", 4)) + self.assertEqual(parsed[1], + ("markdown", "# Title #\n\nSome text here\n\n")) + self.assertEqual(parsed[2][0], "api-json") + p_test = parsed[2][1] + self.assertEqual(p_test["name"], "test") + self.assertEqual(p_test["type"], "function") + self.assertEqual(p_test["signature"], "test(argOne, argTwo, \ +argThree, options)") + self.assertEqual(p_test["description"], + "This is a function which does nothing in \ +particular.") + r = p_test["returns"] + self.assertEqual(r["datatype"], "object") + self.assertEqual(r["description"], "") + self.assertEqual(len(r["props"]), 2) + self.assertEqual(r["props"][0]["datatype"], "string") + self.assertEqual(r["props"][0]["description"], "First string") + self.assertEqual(r["props"][1]["datatype"], "url") + self.assertEqual(r["props"][1]["description"], "First URL") + + self.assertEqual(p_test["params"][0], + {"name": "argOne", + "required": True, + "datatype": "string", + "description": "This is the first argument.", + "line_number": 11, + }) + + self.assertEqual(p_test["params"][1], + {"name": "argTwo", + "required": False, + "datatype": "bool", + "description": "This is the second argument.", + "line_number": 12, + }) + + self.assertEqual(p_test["params"][2], + {"name": "argThree", + "required": False, + "default": "default", + "datatype": "uri", + "line_number": 13, + "description": """\ +This is the third and final argument. And this is +a test of the ability to do multiple lines of +text.""", + }) + p3 = p_test["params"][3] + self.assertEqual(p3["name"], "options") + self.assertEqual(p3["required"], False) + self.failIf("type" in p3) + self.assertEqual(p3["description"], "Options Bag") + self.assertEqual(p3["props"][0], + {"name": "style", + "required": False, + "datatype": "string", + "description": "Some style information.", + "line_number": 18, + }) + self.assertEqual(p3["props"][1], + {"name": "secondToLastOption", + "required": False, + "default": "True", + "datatype": "bool", + "description": "The last property.", + "line_number": 19, + }) + self.assertEqual(p3["props"][2]["name"], "lastOption") + self.assertEqual(p3["props"][2]["required"], False) + self.assertEqual(p3["props"][2]["datatype"], "uri") + self.assertEqual(p3["props"][2]["description"], """\ +And this time we have +A multiline description +Written as haiku""") + + self.assertEqual(parsed[3][0], "markdown") + self.assertEqual(parsed[3][1], "\n\nThis text appears between the \ +API blocks.\n\n") + + self.assertEqual(parsed[4][0], "api-json") + p_test = parsed[4][1] + + expected = {'line_number': 28, + 'name': 'append', + 'params': [{'props':[{'line_number': 33, + 'required': False, + 'datatype': 'uri', + 'name': 'icon', + 'description': 'The HREF of an icon to show as the \ +method of accessing your features slideBar'}, + {'line_number': 34, + 'required': False, + 'datatype': 'string/xml', + 'name': 'html', + 'description': 'The content of the feature, either \ +as an HTML string,\nor an E4X document fragment.'}, + {'line_number': 37, + 'required': False, + 'datatype': 'uri', + 'name': 'url', + 'description': 'The url to load into the content area \ +of the feature'}, + {'line_number': 38, + 'required': False, + 'datatype': 'int', + 'name': 'width', + 'description': 'Width of the content area and the \ +selected slide size'}, + {'line_number': 39, + 'required': False, + 'datatype': 'bool', + 'name': 'persist', + 'description': 'Default slide behavior when being \ +selected as follows:\nIf true: blah; If false: double blah.'}, + {'line_number': 42, + 'required': False, + 'datatype': 'bool', + 'name': 'autoReload', + 'description': 'Automatically reload content on \ +select'}, + {'line_number': 43, + 'required': False, + 'datatype': 'function', + 'name': 'onClick', + 'description': 'Callback when the icon is \ +clicked'}, + {'line_number': 44, + 'required': False, + 'datatype': 'function', + 'name': 'onSelect', + 'description': 'Callback when the feature is selected'}, + {'line_number': 45, + 'required': False, + 'datatype': 'function', + 'name': 'onReady', + 'description': + 'Callback when featured is loaded'}], + 'line_number': 31, + 'required': True, + 'name': 'options', + 'description': 'Pass in all of your options here.'}], + 'signature': 'append(options)', + 'type': 'function', + 'description': 'This is a list of options to specify modifications to your \ +slideBar instance.'} + self.assertEqual(p_test, expected) + + self.assertEqual(parsed[6][0], "api-json") + p_test = parsed[6][1] + self.assertEqual(p_test["name"], "cool-func.dot") + self.assertEqual(p_test["signature"], "cool-func.dot(howMuch, double, \ +options, onemore, options2)") + self.assertEqual(p_test["returns"]["description"], + """\ +A value telling you just how cool you are. +A boa-constructor! +This description can go on for a while, and can even contain +some **realy** fancy things. Like `code`, or even +~~~~{.javascript} +// Some code! +~~~~""") + self.assertEqual(p_test["params"][2]["props"][0], + {"name": "callback", + "required": True, + "datatype": "function", + "line_number": 63, + "description": "The callback", + }) + self.assertEqual(p_test["params"][2]["props"][1], + {"name": "random", + "required": False, + "datatype": "bool", + "line_number": 64, + "description": "Do something random?", + }) + + p_test = parsed[8][1] + self.assertEqual(p_test["signature"],"random()") + + # tests for classes + #1) empty class + p_test = parsed[10][1] + self.assertEqual(len(p_test), 4) + self.assertEqual(p_test["name"], "empty-class") + self.assertEqual(p_test["description"], "This class contains nothing.") + self.assertEqual(p_test["type"], "class") + # 2) class with just one ctor + p_test = parsed[12][1] + self.assertEqual(len(p_test), 5) + self.assertEqual(p_test["name"], "only-one-ctor") + self.assertEqual(p_test["description"], "This class contains only \ +one constructor.") + self.assertEqual(p_test["type"], "class") + constructors = p_test["constructors"] + self.assertEqual(len(constructors), 1) + self._test_class_constructor(constructors[0], "one-constructor") + # 3) class with 2 ctors + p_test = parsed[14][1] + self.assertEqual(len(p_test), 5) + self.assertEqual(p_test["name"], "two-ctors") + self.assertEqual(p_test["description"], "This class contains two \ +constructors.") + self.assertEqual(p_test["type"], "class") + constructors = p_test["constructors"] + self.assertEqual(len(constructors), 2) + self._test_class_constructor(constructors[0], "one-constructor") + self._test_class_constructor(constructors[1], "another-constructor") + # 4) class with ctor + method + p_test = parsed[16][1] + self.assertEqual(len(p_test), 6) + self.assertEqual(p_test["name"], "ctor-and-method") + self.assertEqual(p_test["description"], "This class contains one \ +constructor and one method.") + self.assertEqual(p_test["type"], "class") + constructors = p_test["constructors"] + self.assertEqual(len(constructors), 1) + self._test_class_constructor(constructors[0], "one-constructor") + methods = p_test["methods"] + self.assertEqual(len(methods), 1) + self._test_class_method(methods[0]) + # 5) class with ctor + method + property + p_test = parsed[18][1] + self.assertEqual(len(p_test), 8) + self.assertEqual(p_test["name"], "ctor-method-prop-event") + self.assertEqual(p_test["description"], "This class contains one \ +constructor, one method, one property and an event.") + self.assertEqual(p_test["type"], "class") + constructors = p_test["constructors"] + self.assertEqual(len(constructors), 1) + self._test_class_constructor(constructors[0], "one-constructor") + methods = p_test["methods"] + self.assertEqual(len(methods), 1) + self._test_class_method(methods[0]) + properties = p_test["properties"] + self.assertEqual(len(properties), 1) + self._test_class_property(properties[0]) + events = p_test["events"] + self.assertEqual(len(events), 1) + self._test_class_event(events[0]) + + self.assertEqual(parsed[-1][0], "markdown") + self.assertEqual(parsed[-1][1], "\n\nSome more text here, \ +at the end of the file.\n\n") + + def _test_class_constructor(self, constructor, name): + self.assertEqual(constructor["type"], "constructor") + self.assertEqual(constructor["name"], name) + params = constructor["params"] + self.assertEqual(len(params), 1) + self.assertEqual(params[0]["name"], "options") + self.assertEqual(params[0]["description"], "An object-bag of goodies.") + + def _test_class_method(self, method): + self.assertEqual(method["type"], "method") + self.assertEqual(method["name"], "a-method") + self.assertEqual(method["description"], "Does things.") + params = method["params"] + self.assertEqual(len(params), 1) + self.assertEqual(params[0]["name"], "options") + self.assertEqual(params[0]["description"], "An argument.") + + def _test_class_property(self, prop): + self.assertEqual(prop["type"], "property") + self.assertEqual(prop["name"], "a-property") + self.assertEqual(prop["description"], "Represents stuff.") + self.assertEqual(prop["datatype"], "bool") + + def _test_class_event(self, event): + self.assertEqual(event["type"], "event") + self.assertEqual(event["name"], "message") + self.assertEqual(event["description"], "Event emitted when the \ +content script sends a message to the add-on.") + arguments = event["arguments"] + self.assertEqual(len(arguments), 1) + argument = arguments[0] + self.assertEqual(argument["datatype"], "JSON") + self.assertEqual(argument["description"], "The message itself as a \ +JSON-serialized object.") + + def test_missing_return_propname(self): + md = '''\ + +@method +This is a function which does nothing in particular. +@returns {object} + @prop {string} First string, but the property name is missing + @prop {url} First URL, same problem +@param argOne {string} This is the first argument. + +''' + self.assertRaises(ParseError, self.parse_text, md) + + def test_missing_return_proptype(self): + md = '''\ + +@method +This is a function which does nothing in particular. +@returns {object} + @prop untyped It is an error to omit the type of a return property. +@param argOne {string} This is the first argument. +@param [argTwo=True] {bool} This is the second argument. + +''' + self.assertRaises(ParseError, self.parse_text, md) + + def test_return_propnames(self): + md = '''\ + +@method +This is a function which does nothing in particular. +@returns {object} + @prop firststring {string} First string. + @prop [firsturl] {url} First URL, not always provided. +@param argOne {string} This is the first argument. +@param [argTwo=True] {bool} This is the second argument. + +''' + parsed = self.parse_text(md) + r = parsed[1][1]["returns"] + self.assertEqual(r["props"][0]["name"], "firststring") + self.assertEqual(r["props"][0], + {"name": "firststring", + "datatype": "string", + "description": "First string.", + "required": True, + "line_number": 5, # 1-indexed + }) + self.assertEqual(r["props"][1], + {"name": "firsturl", + "datatype": "url", + "description": "First URL, not always provided.", + "required": False, + "line_number": 6, + }) + + def test_return_description_1(self): + md = '''\ + +@method +This is a function which does nothing in particular. +@returns {object} A one-line description. + @prop firststring {string} First string. + @prop [firsturl] {url} First URL, not always provided. +@param argOne {string} This is the first argument. +@param [argTwo=True] {bool} This is the second argument. + +''' + parsed = self.parse_text(md) + r = parsed[1][1]["returns"] + self.assertEqual(r["description"], "A one-line description.") + + def test_return_description_2(self): + md = '''\ + +@method +This is a function which does nothing in particular. +@returns {object} A six-line description + which is consistently indented by two spaces + except for this line + and preserves the following empty line + + from which a two-space indentation will be removed. + @prop firststring {string} First string. + @prop [firsturl] {url} First URL, not always provided. +@param argOne {string} This is the first argument. +@param [argTwo=True] {bool} This is the second argument. + +''' + parsed = self.parse_text(md) + r = parsed[1][1]["returns"] + self.assertEqual(r["description"], + "A six-line description\n" + "which is consistently indented by two spaces\n" + " except for this line\n" + "and preserves the following empty line\n" + "\n" + "from which a two-space indentation will be removed.") + + def test_return_description_3(self): + md = '''\ + +@method +This is a function which does nothing in particular. +@returns A one-line untyped description. +@param argOne {string} This is the first argument. +@param [argTwo=True] {bool} This is the second argument. + +''' + parsed = self.parse_text(md) + r = parsed[1][1]["returns"] + self.assertEqual(r["description"], "A one-line untyped description.") + + # if the return value was supposed to be an array, the correct syntax + # would not have any @prop tags: + # @returns {array} + # Array consists of two elements, a string and a url... + + def test_return_array(self): + md = '''\ + +@method +This is a function which returns an array. +@returns {array} + Array consists of two elements, a string and a url. +@param argOne {string} This is the first argument. +@param [argTwo=True] {bool} This is the second argument. + +''' + parsed = self.parse_text(md) + r = parsed[1][1]["returns"] + self.assertEqual(r["description"], + "Array consists of two elements, a string and a url.") + + def test_bad_default_on_required_parameter(self): + md = '''\ + +@method +This is a function which does nothing in particular. +@returns something +@param argOne=ILLEGAL {string} Mandatory parameters do not take defaults. +@param [argTwo=Chicago] {string} This is the second argument. + +''' + self.assertRaises(ParseError, self.parse_text, md) + + def test_missing_apitype(self): + md = '''\ + +Sorry, you must have a @method or something before the description. +Putting it after the description is not good enough +@method +@returns something + +''' + self.assertRaises(ParseError, self.parse_text, md) + + def test_missing_param_propname(self): + md = '''\ + +@method +This is a function which does nothing in particular. +@param p1 {object} This is a parameter. + @prop {string} Oops, props must have a name. + +''' + self.assertRaises(ParseError, self.parse_text, md) + + def test_missing_param_proptype(self): + md = '''\ + +@method +This is a function which does nothing in particular. +@param p1 {object} This is a parameter. + @prop name Oops, props must have a type. + +''' + self.assertRaises(ParseError, self.parse_text, md) + + def test_property(self): + md = '''\ + +@property {foo} +An object property named test of type foo. + +''' + parsed = self.parse_text(md) + self.assertEqual(parsed[1][0], 'api-json') + actual_api_json_obj = parsed[1][1] + expected_api_json_obj = { + 'line_number': 1, + 'datatype': 'foo', + 'type': 'property', + 'name': 'test', + 'description': "An object property named test of type foo." + } + self.assertEqual(actual_api_json_obj, expected_api_json_obj) + + def test_property_no_type(self): + md = '''\ + +@property +This property needs to specify a type! + +''' + self.assertRaises(ParseError, self.parse_text, md) + + def test_missing_api_closing_tag(self): + md = '''\ + +@class +This is a class with a missing closing tag. + +''' + self.assertRaises(ParseError, self.parse_text, md) + +if __name__ == "__main__": + unittest.main() diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_apirenderer.py b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_apirenderer.py new file mode 100644 index 0000000..43ee872 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_apirenderer.py @@ -0,0 +1,28 @@ + +import os +import unittest +from cuddlefish.docs.apiparser import parse_hunks, ParseError +from cuddlefish.docs.apirenderer import md_to_html + +tests_path = os.path.abspath(os.path.dirname(__file__)) +static_files_path = os.path.join(tests_path, "static-files") + +class ParserTests(unittest.TestCase): + def pathname(self, filename): + return os.path.join(static_files_path, "docs", filename) + + def render_markdown(self, pathname): + return md_to_html(pathname) + + def test_renderer(self): + test = self.render_markdown(self.pathname("APIsample.md")) + reference = open(self.pathname("APIreference.html")).read() + test_lines = test.splitlines(True) + reference_lines = reference.splitlines(True) + for x in range(len(test_lines)): + self.assertEqual(test_lines[x], reference_lines[x], + "line %d: expected '%s', got '%s'" + % (x+1, reference_lines[x], test_lines[x])) + +if __name__ == "__main__": + unittest.main() diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_generate.py b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_generate.py new file mode 100644 index 0000000..72a4487 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_generate.py @@ -0,0 +1,76 @@ +import os +import shutil +import unittest +import StringIO + +from cuddlefish.docs import generate +from cuddlefish.tests import env_root + +INITIAL_FILESET = [ ["static-files", "base.html"], \ + ["dev-guide", "welcome.html"], \ + ["packages", "aardvark", "aardvark.html"] ] + +EXTENDED_FILESET = [ ["static-files", "base.html"], \ + ["dev-guide", "extra.html"], \ + ["dev-guide", "welcome.html"], \ + ["packages", "aardvark", "aardvark.html"] ] + +EXTRAFILE = ["dev-guide", "extra.html"] + +class Generate_Docs_Tests(unittest.TestCase): + def test_generate_static_docs_does_not_smoke(self): + filename = 'testdocs.tgz' + if os.path.exists(filename): + os.remove(filename) + filename = generate.generate_static_docs(env_root) + self.assertTrue(os.path.exists(filename)) + os.remove(filename) + + def test_generate_docs_does_not_smoke(self): + test_root = os.path.join(env_root, "python-lib", "cuddlefish", "tests", "static-files") + docs_root = os.path.join(test_root, "doc") + generate.clean_generated_docs(docs_root) + new_digest = self.check_generate_regenerate_cycle(test_root, INITIAL_FILESET) + # touching an MD file under packages **does** cause a regenerate + os.utime(os.path.join(test_root, "packages", "aardvark", "doc", "main.md"), None) + new_digest = self.check_generate_regenerate_cycle(test_root, INITIAL_FILESET, new_digest) + # touching a non MD file under packages **does not** cause a regenerate + os.utime(os.path.join(test_root, "packages", "aardvark", "lib", "main.js"), None) + self.check_generate_is_skipped(test_root, INITIAL_FILESET, new_digest) + # touching a non MD file under static-files **does not** cause a regenerate + os.utime(os.path.join(docs_root, "static-files", "another.html"), None) + new_digest = self.check_generate_is_skipped(test_root, INITIAL_FILESET, new_digest) + # touching an MD file under dev-guide **does** cause a regenerate + os.utime(os.path.join(docs_root, "dev-guide-source", "welcome.md"), None) + new_digest = self.check_generate_regenerate_cycle(test_root, INITIAL_FILESET, new_digest) + # adding a file **does** cause a regenerate + open(os.path.join(docs_root, "dev-guide-source", "extra.md"), "w").write("some content") + new_digest = self.check_generate_regenerate_cycle(test_root, EXTENDED_FILESET, new_digest) + # deleting a file **does** cause a regenerate + os.remove(os.path.join(docs_root, "dev-guide-source", "extra.md")) + new_digest = self.check_generate_regenerate_cycle(test_root, INITIAL_FILESET, new_digest) + # remove the files + generate.clean_generated_docs(docs_root) + + def check_generate_is_skipped(self, test_root, files_to_expect, initial_digest): + generate.generate_docs(test_root, stdout=StringIO.StringIO()) + docs_root = os.path.join(test_root, "doc") + for file_to_expect in files_to_expect: + self.assertTrue(os.path.exists(os.path.join(docs_root, *file_to_expect))) + self.assertTrue(initial_digest == open(os.path.join(docs_root, "status.md5"), "r").read()) + + def check_generate_regenerate_cycle(self, test_root, files_to_expect, initial_digest = None): + # test that if we generate, files are getting generated + generate.generate_docs(test_root, stdout=StringIO.StringIO()) + docs_root = os.path.join(test_root, "doc") + for file_to_expect in files_to_expect: + self.assertTrue(os.path.exists(os.path.join(docs_root, *file_to_expect))) + if initial_digest: + self.assertTrue(initial_digest != open(os.path.join(docs_root, "status.md5"), "r").read()) + # and that if we regenerate, nothing changes... + new_digest = open(os.path.join(docs_root, "status.md5"), "r").read() + self.check_generate_is_skipped(test_root, files_to_expect, new_digest) + return new_digest + +if __name__ == '__main__': + unittest.main() diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_init.py b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_init.py new file mode 100644 index 0000000..f11ce2c --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_init.py @@ -0,0 +1,85 @@ +import os, unittest, shutil +from StringIO import StringIO +from cuddlefish import initializer +from cuddlefish.templates import MAIN_JS, TEST_MAIN_JS, PACKAGE_JSON + +class TestInit(unittest.TestCase): + + def run_init_in_subdir(self, dirname, f, *args, **kwargs): + top = os.path.abspath(os.getcwd()) + basedir = os.path.abspath(os.path.join(".test_tmp",self.id(),dirname)) + if os.path.isdir(basedir): + assert basedir.startswith(top) + shutil.rmtree(basedir) + os.makedirs(basedir) + try: + os.chdir(basedir) + return f(basedir, *args, **kwargs) + finally: + os.chdir(top) + + def do_test_init(self,basedir): + # Let's init the addon, no error admited + f = open(".ignoreme","w") + f.write("stuff") + f.close() + + out, err = StringIO(), StringIO() + init_run = initializer(None, ["init"], out, err) + out, err = out.getvalue(), err.getvalue() + self.assertEqual(init_run, 0) + self.assertTrue("* lib directory created" in out) + self.assertTrue("* data directory created" in out) + self.assertTrue("Have fun!" in out) + self.assertEqual(err,"") + self.assertTrue(len(os.listdir(basedir))>0) + main_js = os.path.join(basedir,"lib","main.js") + package_json = os.path.join(basedir,"package.json") + test_main_js = os.path.join(basedir,"test","test-main.js") + self.assertTrue(os.path.exists(main_js)) + self.assertTrue(os.path.exists(package_json)) + self.assertTrue(os.path.exists(test_main_js)) + self.assertEqual(open(main_js,"r").read(),MAIN_JS) + self.assertEqual(open(package_json,"r").read(), + PACKAGE_JSON % {"name":"tmp_addon_sample", + "fullName": "tmp_addon_SAMPLE" }) + self.assertEqual(open(test_main_js,"r").read(),TEST_MAIN_JS) + + # Let's check that the addon is initialized + out, err = StringIO(), StringIO() + init_run = initializer(None, ["init"], out, err) + out, err = out.getvalue(), err.getvalue() + self.failIfEqual(init_run,0) + self.assertTrue("This command must be run in an empty directory." in err) + + def test_initializer(self): + self.run_init_in_subdir("tmp_addon_SAMPLE",self.do_test_init) + + def do_test_args(self, basedir): + # check that running it with spurious arguments will fail + out,err = StringIO(), StringIO() + init_run = initializer(None, ["init", "ignored-dirname"], out, err) + out, err = out.getvalue(), err.getvalue() + self.failIfEqual(init_run, 0) + self.assertTrue("Too many arguments" in err) + + def test_args(self): + self.run_init_in_subdir("tmp_addon_sample", self.do_test_args) + + def _test_existing_files(self, basedir): + f = open("pay_attention_to_me","w") + f.write("stuff") + f.close() + out,err = StringIO(), StringIO() + rc = initializer(None, ["init"], out, err) + out, err = out.getvalue(), err.getvalue() + self.assertEqual(rc, 1) + self.failUnless("This command must be run in an empty directory" in err, + err) + self.failIf(os.path.exists("lib")) + + def test_existing_files(self): + self.run_init_in_subdir("existing_files", self._test_existing_files) + +if __name__ == "__main__": + unittest.main() diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_linker.py b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_linker.py new file mode 100755 index 0000000..b2e4f19 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_linker.py @@ -0,0 +1,200 @@ +import os.path +import shutil +import zipfile +from StringIO import StringIO +import unittest +import cuddlefish +from cuddlefish import packaging, manifest + +def up(path, generations=1): + for i in range(generations): + path = os.path.dirname(path) + return path + +ROOT = up(os.path.abspath(__file__), 4) +def get_linker_files_dir(name): + return os.path.join(up(os.path.abspath(__file__)), "linker-files", name) + +class Basic(unittest.TestCase): + def get_pkg(self, name): + d = get_linker_files_dir(name) + return packaging.get_config_in_dir(d) + + def test_deps(self): + target_cfg = self.get_pkg("one") + pkg_cfg = packaging.build_config(ROOT, target_cfg) + deps = packaging.get_deps_for_targets(pkg_cfg, ["one"]) + self.failUnlessEqual(deps, ["one"]) + deps = packaging.get_deps_for_targets(pkg_cfg, + [target_cfg.name, "addon-kit"]) + self.failUnlessEqual(deps, ["addon-kit", "api-utils", "one"]) + + def test_manifest(self): + target_cfg = self.get_pkg("one") + pkg_cfg = packaging.build_config(ROOT, target_cfg) + deps = packaging.get_deps_for_targets(pkg_cfg, + [target_cfg.name, "addon-kit"]) + self.failUnlessEqual(deps, ["addon-kit", "api-utils", "one"]) + # target_cfg.dependencies is not provided, so we'll search through + # all known packages (everything in 'deps'). + m = manifest.build_manifest(target_cfg, pkg_cfg, deps, + "P/", scan_tests=False) + m = m.get_harness_options_manifest("P/") + + def assertReqIs(modname, reqname, uri): + reqs = m["P/one-lib/%s.js" % modname]["requirements"] + self.failUnlessEqual(reqs[reqname]["uri"], uri) + assertReqIs("main", "panel", "P/addon-kit-lib/panel.js") + assertReqIs("main", "two.js", "P/one-lib/two.js") + assertReqIs("main", "./two", "P/one-lib/two.js") + assertReqIs("main", "addon-kit/tabs.js", "P/addon-kit-lib/tabs.js") + assertReqIs("main", "./subdir/three", "P/one-lib/subdir/three.js") + assertReqIs("two", "main", "P/one-lib/main.js") + assertReqIs("subdir/three", "../main", "P/one-lib/main.js") + + target_cfg.dependencies = [] + # now, because .dependencies *is* provided, we won't search 'deps', + # so we'll get a link error + self.assertRaises(manifest.ModuleNotFoundError, + manifest.build_manifest, + target_cfg, pkg_cfg, deps, "P/", scan_tests=False) + + def test_main_in_deps(self): + target_cfg = self.get_pkg("three") + package_path = [get_linker_files_dir("three-deps")] + pkg_cfg = packaging.build_config(ROOT, target_cfg, + packagepath=package_path) + deps = packaging.get_deps_for_targets(pkg_cfg, + [target_cfg.name, "addon-kit"]) + self.failUnlessEqual(deps, ["addon-kit", "api-utils", "three"]) + m = manifest.build_manifest(target_cfg, pkg_cfg, deps, + "P/", scan_tests=False) + m = m.get_harness_options_manifest("P/") + def assertReqIs(modname, reqname, uri): + reqs = m["P/three-lib/%s.js" % modname]["requirements"] + self.failUnlessEqual(reqs[reqname]["uri"], uri) + assertReqIs("main", "three-a", "P/three-a-lib/main.js") + assertReqIs("main", "three-b", "P/three-b-lib/main.js") + assertReqIs("main", "three-c", "P/three-c-lib/main.js") + + def test_relative_main_in_top(self): + target_cfg = self.get_pkg("five") + package_path = [] + pkg_cfg = packaging.build_config(ROOT, target_cfg, + packagepath=package_path) + deps = packaging.get_deps_for_targets(pkg_cfg, + [target_cfg.name, "addon-kit"]) + self.failUnlessEqual(deps, ["addon-kit", "api-utils", "five"]) + # all we care about is that this next call doesn't raise an exception + m = manifest.build_manifest(target_cfg, pkg_cfg, deps, + "P/", scan_tests=False) + m = m.get_harness_options_manifest("P/") + reqs = m["P/five-lib/main.js"]["requirements"] + self.failUnlessEqual(reqs, {}); + + def test_unreachable_relative_main_in_top(self): + target_cfg = self.get_pkg("six") + package_path = [] + pkg_cfg = packaging.build_config(ROOT, target_cfg, + packagepath=package_path) + deps = packaging.get_deps_for_targets(pkg_cfg, + [target_cfg.name, "addon-kit"]) + self.failUnlessEqual(deps, ["addon-kit", "api-utils", "six"]) + self.assertRaises(manifest.UnreachablePrefixError, + manifest.build_manifest, + target_cfg, pkg_cfg, deps, + "P/", scan_tests=False) + + def test_unreachable_in_deps(self): + target_cfg = self.get_pkg("four") + package_path = [get_linker_files_dir("four-deps")] + pkg_cfg = packaging.build_config(ROOT, target_cfg, + packagepath=package_path) + deps = packaging.get_deps_for_targets(pkg_cfg, + [target_cfg.name, "addon-kit"]) + self.failUnlessEqual(deps, ["addon-kit", "api-utils", "four"]) + self.assertRaises(manifest.UnreachablePrefixError, + manifest.build_manifest, + target_cfg, pkg_cfg, deps, + "P/", scan_tests=False) + +class Contents(unittest.TestCase): + + def run_in_subdir(self, dirname, f, *args, **kwargs): + top = os.path.abspath(os.getcwd()) + basedir = os.path.abspath(os.path.join(".test_tmp",self.id(),dirname)) + if os.path.isdir(basedir): + assert basedir.startswith(top) + shutil.rmtree(basedir) + os.makedirs(basedir) + try: + os.chdir(basedir) + return f(basedir, *args, **kwargs) + finally: + os.chdir(top) + + def assertIn(self, what, inside_what): + self.failUnless(what in inside_what, inside_what) + + def test_strip_default(self): + seven = get_linker_files_dir("seven") + # now run 'cfx xpi' in that directory, except put the generated .xpi + # elsewhere + def _test(basedir): + stdout = StringIO() + shutil.copytree(seven, "seven") + os.chdir("seven") + try: + # regrettably, run() always finishes with sys.exit() + cuddlefish.run(["xpi"], # --strip-xpi is now the default + stdout=stdout) + except SystemExit, e: + self.failUnlessEqual(e.args[0], 0) + zf = zipfile.ZipFile("seven.xpi", "r") + names = zf.namelist() + # the first problem found in bug 664840 was that cuddlefish.js + # (the loader) was stripped out on windows, due to a /-vs-\ bug + self.assertIn("resources/jid1-at-jetpack-api-utils-lib/cuddlefish.js", names) + self.assertIn("resources/jid1-at-jetpack-api-utils-lib/securable-module.js", names) + # the second problem found in bug 664840 was that an addon + # without an explicit tests/ directory would copy all files from + # the package into a bogus JID-PKGNAME-tests/ directory, so check + # for that + testfiles = [fn for fn in names if "jid1-at-jetpack-seven-tests" in fn] + self.failUnlessEqual([], testfiles) + # the third problem was that data files were being stripped from + # the XPI. Note that data/ is only supposed to be included if a + # module that actually gets used does a require("self") . + self.assertIn("resources/jid1-at-jetpack-seven-data/text.data", + names) + self.failIf("resources/jid1-at-jetpack-seven-lib/unused.js" + in names, names) + self.run_in_subdir("x", _test) + + def test_no_strip(self): + seven = get_linker_files_dir("seven") + def _test(basedir): + stdout = StringIO() + shutil.copytree(seven, "seven") + os.chdir("seven") + try: + # regrettably, run() always finishes with sys.exit() + cuddlefish.run(["xpi", "--no-strip-xpi"], + stdout=stdout) + except SystemExit, e: + self.failUnlessEqual(e.args[0], 0) + zf = zipfile.ZipFile("seven.xpi", "r") + names = zf.namelist() + self.assertIn("resources/jid1-at-jetpack-api-utils-lib/cuddlefish.js", names) + self.assertIn("resources/jid1-at-jetpack-api-utils-lib/securable-module.js", names) + testfiles = [fn for fn in names if "jid1-at-jetpack-seven-tests" in fn] + self.failUnlessEqual([], testfiles) + self.assertIn("resources/jid1-at-jetpack-seven-data/text.data", + names) + self.failUnless("resources/jid1-at-jetpack-seven-lib/unused.js" + in names, names) + self.run_in_subdir("x", _test) + + +if __name__ == '__main__': + unittest.main() diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_manifest.py b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_manifest.py new file mode 100644 index 0000000..c9324ee --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_manifest.py @@ -0,0 +1,256 @@ + +import unittest +from StringIO import StringIO +from cuddlefish.manifest import scan_module + +class Extra: + def failUnlessKeysAre(self, d, keys): + self.failUnlessEqual(sorted(d.keys()), sorted(keys)) + +class Require(unittest.TestCase, Extra): + def scan(self, text): + lines = StringIO(text).readlines() + requires, problems, locations = scan_module("fake.js", lines) + self.failUnlessEqual(problems, False) + return requires + + def scan_locations(self, text): + lines = StringIO(text).readlines() + requires, problems, locations = scan_module("fake.js", lines) + self.failUnlessEqual(problems, False) + return requires, locations + + def test_modules(self): + mod = """var foo = require('one');""" + requires = self.scan(mod) + self.failUnlessKeysAre(requires, ["one"]) + + mod = """var foo = require(\"one\");""" + requires = self.scan(mod) + self.failUnlessKeysAre(requires, ["one"]) + + mod = """var foo=require( 'one' ) ; """ + requires = self.scan(mod) + self.failUnlessKeysAre(requires, ["one"]) + + mod = """var foo = require('o'+'ne'); // tricky, denied""" + requires = self.scan(mod) + self.failUnlessKeysAre(requires, []) + + mod = """require('one').immediately.do().stuff();""" + requires, locations = self.scan_locations(mod) + self.failUnlessKeysAre(requires, ["one"]) + self.failUnlessEqual(locations, {"one": 1}) + + # these forms are commented out, and thus ignored + + mod = """// var foo = require('one');""" + requires = self.scan(mod) + self.failUnlessKeysAre(requires, []) + + mod = """/* var foo = require('one');""" + requires = self.scan(mod) + self.failUnlessKeysAre(requires, []) + + mod = """ * var foo = require('one');""" + requires = self.scan(mod) + self.failUnlessKeysAre(requires, []) + + mod = """ ' var foo = require('one');""" + requires = self.scan(mod) + self.failUnlessKeysAre(requires, ["one"]) + + mod = """ \" var foo = require('one');""" + requires = self.scan(mod) + self.failUnlessKeysAre(requires, ["one"]) + + # multiple requires + + mod = """const foo = require('one'); + const foo = require('two');""" + requires, locations = self.scan_locations(mod) + self.failUnlessKeysAre(requires, ["one", "two"]) + self.failUnlessEqual(locations["one"], 1) + self.failUnlessEqual(locations["two"], 2) + + mod = """const foo = require('repeated'); + const bar = require('repeated'); + const baz = require('repeated');""" + requires, locations = self.scan_locations(mod) + self.failUnlessKeysAre(requires, ["repeated"]) + self.failUnlessEqual(locations["repeated"], 1) # first occurrence + + mod = """const foo = require('one'); const foo = require('two');""" + requires = self.scan(mod) + self.failUnlessKeysAre(requires, ["one", "two"]) + + # define calls + + mod = """define('one', ['two', 'numbers/three'], function(t, th) {});""" + requires = self.scan(mod) + self.failUnlessKeysAre(requires, ["two", "numbers/three"]) + + mod = """define( + ['odd', + "numbers/four"], function() {});""" + requires = self.scan(mod) + self.failUnlessKeysAre(requires, ["odd", "numbers/four"]) + + mod = """define(function(require, exports, module) { + var a = require("some/module/a"), + b = require('b/v1'); + exports.a = a; + //This is a fakeout: require('bad'); + /* And another var bad = require('bad2'); */ + require('foo').goFoo(); + });""" + requires = self.scan(mod) + self.failUnlessKeysAre(requires, ["some/module/a", "b/v1", "foo"]) + + mod = """define ( + "foo", + ["bar"], function (bar) { + var me = require("me"); + } + )""" + requires = self.scan(mod) + self.failUnlessKeysAre(requires, ["bar", "me"]) + + mod = """define(['se' + 'ven', 'eight', nine], function () {});""" + requires = self.scan(mod) + self.failUnlessKeysAre(requires, ["eight"]) + + # async require calls + + mod = """require(['one'], function(one) {var o = require("one");});""" + requires = self.scan(mod) + self.failUnlessKeysAre(requires, ["one"]) + + mod = """require([ 'one' ], function(one) {var t = require("two");});""" + requires = self.scan(mod) + self.failUnlessKeysAre(requires, ["one", "two"]) + + mod = """require ( ['two', 'numbers/three'], function(t, th) {});""" + requires = self.scan(mod) + self.failUnlessKeysAre(requires, ["two", "numbers/three"]) + + mod = """require ( + ["bar", "fa" + 'ke' ], function (bar) { + var me = require("me"); + // require("bad").doBad(); + } + )""" + requires = self.scan(mod) + self.failUnlessKeysAre(requires, ["bar", "me"]) + +def scan2(text, fn="fake.js"): + stderr = StringIO() + lines = StringIO(text).readlines() + requires, problems, locations = scan_module(fn, lines, stderr) + stderr.seek(0) + return requires, problems, stderr.readlines() + +class Chrome(unittest.TestCase, Extra): + + def test_ignore_loader(self): + # we specifically ignore the two loader files + mod = """let {Cc,Ci} = require('chrome');""" + requires, problems, err = scan2(mod, "blah/cuddlefish.js") + self.failUnlessKeysAre(requires, ["chrome"]) + self.failUnlessEqual(problems, False) + self.failUnlessEqual(err, []) + + mod = """let {Cc,Ci} = require('chrome');""" + requires, problems, err = scan2(mod, "securable-module.js") + self.failUnlessKeysAre(requires, ["chrome"]) + self.failUnlessEqual(problems, False) + self.failUnlessEqual(err, []) + + def test_chrome(self): + mod = """let {Cc,Ci} = require('chrome');""" + requires, problems, err = scan2(mod) + self.failUnlessKeysAre(requires, ["chrome"]) + self.failUnlessEqual(problems, False) + self.failUnlessEqual(err, []) + + mod = """var foo = require('foo'); + let {Cc,Ci} = require('chrome');""" + requires, problems, err = scan2(mod) + self.failUnlessKeysAre(requires, ["foo", "chrome"]) + self.failUnlessEqual(problems, False) + self.failUnlessEqual(err, []) + + mod = """let c = require('chrome');""" + requires, problems, err = scan2(mod) + self.failUnlessKeysAre(requires, ["chrome"]) + self.failUnlessEqual(problems, False) + self.failUnlessEqual(err, []) + + mod = """var foo = require('foo'); + let c = require('chrome');""" + requires, problems, err = scan2(mod) + self.failUnlessKeysAre(requires, ["foo", "chrome"]) + self.failUnlessEqual(problems, False) + self.failUnlessEqual(err, []) + + def test_chrome_components(self): + # Bug 663541: tolerate "Components" if you're marked with + # require("chrome"), to avoid requiring module authors to rewrite a + # lot of code. Once bug 636145 is fixed, such code will break. To fix + # it, add {Components}=require("chrome"), but that won't work until + # after 636145 is fixed. + mod = """require("chrome"); + var ios = Components.classes['@mozilla.org/network/io-service;1'];""" + requires, problems, err = scan2(mod) + self.failUnlessKeysAre(requires, ["chrome"]) + self.failUnlessEqual((problems, err), (False, [])) + + def test_not_chrome(self): + # from bug 596595 + mod = r'soughtLines: new RegExp("^\\s*(\\[[0-9 .]*\\])?\\s*\\(\\((EE|WW)\\)|.* [Cc]hipsets?: \\)|\\s*Backtrace")' + requires, problems, err = scan2(mod) + self.failUnlessKeysAre(requires, []) + self.failUnlessEqual((problems,err), (False, [])) + + def test_not_chrome2(self): + # from bug 655788 + mod = r"var foo = 'some stuff Cr';" + requires, problems, err = scan2(mod) + self.failUnlessKeysAre(requires, []) + self.failUnlessEqual((problems,err), (False, [])) + +class BadChrome(unittest.TestCase, Extra): + def test_bad_alias(self): + # using Components.* gets you an error, with a message that teaches + # you the correct approach. + mod = """let Cc = Components.classes; + let Cu = Components.utils; + """ + requires, problems, err = scan2(mod) + self.failUnlessKeysAre(requires, []) + self.failUnlessEqual(problems, True) + self.failUnlessEqual(err[1], "The following lines from file fake.js:\n") + self.failUnlessEqual(err[2], " 1: let Cc = Components.classes;\n") + self.failUnlessEqual(err[3], " 2: let Cu = Components.utils;\n") + self.failUnlessEqual(err[4], "use 'Components' to access chrome authority. To do so, you need to add a\n") + self.failUnlessEqual(err[5], "line somewhat like the following:\n") + self.failUnlessEqual(err[7], ' const {Cc,Cu} = require("chrome");\n') + self.failUnlessEqual(err[9], "Then you can use 'Components' as well as any shortcuts to its properties\n") + + def test_bad_misc(self): + # If it looks like you're using something that doesn't have an alias, + # the warning also suggests a better way. + mod = """if (Components.isSuccessCode(foo)) + """ + requires, problems, err = scan2(mod) + self.failUnlessKeysAre(requires, []) + self.failUnlessEqual(problems, True) + self.failUnlessEqual(err[1], "The following lines from file fake.js:\n") + self.failUnlessEqual(err[2], " 1: if (Components.isSuccessCode(foo))\n") + self.failUnlessEqual(err[3], "use 'Components' to access chrome authority. To do so, you need to add a\n") + self.failUnlessEqual(err[4], "line somewhat like the following:\n") + self.failUnlessEqual(err[6], ' const {components} = require("chrome");\n') + self.failUnlessEqual(err[8], "Then you can use 'Components' as well as any shortcuts to its properties\n") + +if __name__ == '__main__': + unittest.main() diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_packaging.py b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_packaging.py new file mode 100644 index 0000000..aa21131 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_packaging.py @@ -0,0 +1,105 @@ +import os +import unittest + +from cuddlefish import packaging +from cuddlefish.bunch import Bunch + +tests_path = os.path.abspath(os.path.dirname(__file__)) +static_files_path = os.path.join(tests_path, 'static-files') + +def get_configs(pkg_name, dirname='static-files'): + root_path = os.path.join(tests_path, dirname) + pkg_path = os.path.join(root_path, 'packages', pkg_name) + if not (os.path.exists(pkg_path) and os.path.isdir(pkg_path)): + raise Exception('path does not exist: %s' % pkg_path) + target_cfg = packaging.get_config_in_dir(pkg_path) + pkg_cfg = packaging.build_config(root_path, target_cfg) + deps = packaging.get_deps_for_targets(pkg_cfg, [pkg_name]) + build = packaging.generate_build_for_target( + pkg_cfg=pkg_cfg, + target=pkg_name, + deps=deps, + prefix='guid-' + ) + return Bunch(target_cfg=target_cfg, pkg_cfg=pkg_cfg, build=build) + +class PackagingTests(unittest.TestCase): + def test_bug_588661(self): + configs = get_configs('foo', 'bug-588661-files') + self.assertEqual(configs.build.loader, + 'resource://guid-foo-lib/foo-loader.js') + + def test_bug_614712(self): + configs = get_configs('commonjs-naming', 'bug-614712-files') + packages = configs.pkg_cfg.packages + base = os.path.join(tests_path, 'bug-614712-files', 'packages') + self.assertEqual(packages['original-naming'].tests, + [os.path.join(base, 'original-naming', 'tests')]) + self.assertEqual(packages['commonjs-naming'].tests, + [os.path.join(base, 'commonjs-naming', 'test')]) + + def test_basic(self): + configs = get_configs('aardvark') + packages = configs.pkg_cfg.packages + + self.assertTrue('api-utils' in packages) + self.assertTrue('aardvark' in packages) + self.assertTrue('api-utils' in packages.aardvark.dependencies) + self.assertEqual(packages['api-utils'].loader, 'lib/loader.js') + self.assertTrue(packages.aardvark.main == 'main') + self.assertTrue(packages.aardvark.version == "1.0") + +class PackagePath(unittest.TestCase): + def test_packagepath(self): + root_path = os.path.join(tests_path, 'static-files') + pkg_path = os.path.join(root_path, 'packages', 'minimal') + target_cfg = packaging.get_config_in_dir(pkg_path) + pkg_cfg = packaging.build_config(root_path, target_cfg) + base_packages = set(pkg_cfg.packages.keys()) + ppath = [os.path.join(tests_path, 'bug-611495-files')] + pkg_cfg2 = packaging.build_config(root_path, target_cfg, packagepath=ppath) + all_packages = set(pkg_cfg2.packages.keys()) + self.assertEqual(sorted(["jspath-one"]), + sorted(all_packages - base_packages)) + +class Directories(unittest.TestCase): + # for bug 652227 + packages_path = os.path.join(tests_path, "bug-652227-files", "packages") + def get_config(self, pkg_name): + pkg_path = os.path.join(tests_path, "bug-652227-files", "packages", + pkg_name) + return packaging.get_config_in_dir(pkg_path) + + def test_explicit_lib(self): + # package.json provides .lib + p = self.get_config('explicit-lib') + self.assertEqual(os.path.abspath(p.lib[0]), + os.path.abspath(os.path.join(self.packages_path, + "explicit-lib", + "alt2-lib"))) + + def test_directories_lib(self): + # package.json provides .directories.lib + p = self.get_config('explicit-dir-lib') + self.assertEqual(os.path.abspath(p.lib[0]), + os.path.abspath(os.path.join(self.packages_path, + "explicit-dir-lib", + "alt-lib"))) + + def test_lib(self): + # package.json is empty, but lib/ exists + p = self.get_config("default-lib") + self.assertEqual(os.path.abspath(p.lib[0]), + os.path.abspath(os.path.join(self.packages_path, + "default-lib", + "lib"))) + + def test_root(self): + # package.json is empty, no lib/, so files are in root + p = self.get_config('default-root') + self.assertEqual(os.path.abspath(p.lib[0]), + os.path.abspath(os.path.join(self.packages_path, + "default-root"))) + +if __name__ == "__main__": + unittest.main() diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_preflight.py b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_preflight.py new file mode 100644 index 0000000..a71afbc --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_preflight.py @@ -0,0 +1,143 @@ + +import os, shutil +import simplejson as json +import unittest +import hashlib +import base64 +from cuddlefish import preflight +from StringIO import StringIO + +class Util(unittest.TestCase): + def get_basedir(self): + return os.path.join(".test_tmp", self.id()) + def make_basedir(self): + basedir = self.get_basedir() + if os.path.isdir(basedir): + here = os.path.abspath(os.getcwd()) + assert os.path.abspath(basedir).startswith(here) # safety + shutil.rmtree(basedir) + os.makedirs(basedir) + return basedir + + def test_base62(self): + for i in range(1000): + h = hashlib.sha1(str(i)).digest() + s1 = base64.b64encode(h, "AB").strip("=") + s2 = base64.b64encode(h).strip("=").replace("+","A").replace("/","B") + self.failUnlessEqual(s1, s2) + + def write(self, config): + basedir = self.get_basedir() + fn = os.path.join(basedir, "package.json") + open(fn,"w").write(config) + def read(self): + basedir = self.get_basedir() + fn = os.path.join(basedir, "package.json") + return open(fn,"r").read() + + def get_cfg(self): + cfg = json.loads(self.read()) + if "name" not in cfg: + # the cfx parser always provides a name, even if package.json + # doesn't contain one + cfg["name"] = "pretend name" + return cfg + + def parse(self, keydata): + fields = {} + fieldnames = [] + for line in keydata.split("\n"): + if line.strip(): + k,v = line.split(":", 1) + k = k.strip() ; v = v.strip() + fields[k] = v + fieldnames.append(k) + return fields, fieldnames + + def test_preflight(self): + basedir = self.make_basedir() + fn = os.path.join(basedir, "package.json") + + # empty config is not ok: need id (name is automatically supplied) + config_orig = "{}" + self.write(config_orig) + out = StringIO() + cfg = self.get_cfg() + config_was_ok, modified = preflight.preflight_config(cfg, fn, + stderr=out) + self.failUnlessEqual(config_was_ok, False) + self.failUnlessEqual(modified, True) + backup_fn = os.path.join(basedir, "package.json.backup") + config_backup = open(backup_fn,"r").read() + self.failUnlessEqual(config_backup, config_orig) + config = json.loads(self.read()) + self.failIf("name" in config) + self.failUnless("id" in config) + self.failUnless(config["id"].startswith("jid1-"), config["id"]) + self.failUnlessEqual(out.getvalue().strip(), + "No 'id' in package.json: creating a new ID for you.") + os.unlink(backup_fn) + + # just a name? we add the id + config_orig = '{"name": "my-awesome-package"}' + self.write(config_orig) + out = StringIO() + cfg = self.get_cfg() + config_was_ok, modified = preflight.preflight_config(cfg, fn, + stderr=out) + self.failUnlessEqual(config_was_ok, False) + self.failUnlessEqual(modified, True) + backup_fn = os.path.join(basedir, "package.json.backup") + config_backup = open(backup_fn,"r").read() + self.failUnlessEqual(config_backup, config_orig) + config = json.loads(self.read()) + self.failUnlessEqual(config["name"], "my-awesome-package") + self.failUnless("id" in config) + self.failUnless(config["id"].startswith("jid1-"), config["id"]) + jid = str(config["id"]) + self.failUnlessEqual(out.getvalue().strip(), + "No 'id' in package.json: creating a new ID for you.") + os.unlink(backup_fn) + + # name and valid id? great! ship it! + config2 = '{"name": "my-awesome-package", "id": "%s"}' % jid + self.write(config2) + out = StringIO() + cfg = self.get_cfg() + config_was_ok, modified = preflight.preflight_config(cfg, fn, + stderr=out) + self.failUnlessEqual(config_was_ok, True) + self.failUnlessEqual(modified, False) + config2a = self.read() + self.failUnlessEqual(config2a, config2) + self.failUnlessEqual(out.getvalue().strip(), "") + + # name and anonymous ID? without asking to see its papers, ship it + config3 = '{"name": "my-old-skool-package", "id": "anonid0-deadbeef"}' + self.write(config3) + out = StringIO() + cfg = self.get_cfg() + config_was_ok, modified = preflight.preflight_config(cfg, fn, + stderr=out) + self.failUnlessEqual(config_was_ok, True) + self.failUnlessEqual(modified, False) + config3a = self.read() + self.failUnlessEqual(config3a, config3) + self.failUnlessEqual(out.getvalue().strip(), "") + + # name and old-style ID? with nostalgic trepidation, ship it + config4 = '{"name": "my-old-skool-package", "id": "foo@bar.baz"}' + self.write(config4) + out = StringIO() + cfg = self.get_cfg() + config_was_ok, modified = preflight.preflight_config(cfg, fn, + stderr=out) + self.failUnlessEqual(config_was_ok, True) + self.failUnlessEqual(modified, False) + config4a = self.read() + self.failUnlessEqual(config4a, config4) + self.failUnlessEqual(out.getvalue().strip(), "") + + +if __name__ == '__main__': + unittest.main() diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_rdf.py b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_rdf.py new file mode 100644 index 0000000..4585143 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_rdf.py @@ -0,0 +1,15 @@ +import unittest +import xml.dom.minidom + +from cuddlefish import rdf + + +class RDFTests(unittest.TestCase): + def testBug567660(self): + obj = rdf.RDF() + data = u'\u2026'.encode('utf-8') + x = '%s' % data + obj.dom = xml.dom.minidom.parseString(x) + self.assertEqual(obj.dom.documentElement.firstChild.nodeValue, + u'\u2026') + self.assertEqual(str(obj).replace("\n",""), x.replace("\n","")) diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_runner.py b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_runner.py new file mode 100644 index 0000000..15ec8d2 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_runner.py @@ -0,0 +1,24 @@ +import sys + +from cuddlefish import runner + +def xulrunner_app_runner_doctests(): + """ + >>> runner.XulrunnerAppRunner(binary='foo') + Traceback (most recent call last): + ... + Exception: Binary path does not exist foo + + >>> runner.XulrunnerAppRunner(binary=sys.executable) + Traceback (most recent call last): + ... + ValueError: application.ini not found in cmdargs + + >>> runner.XulrunnerAppRunner(binary=sys.executable, + ... cmdargs=['application.ini']) + Traceback (most recent call last): + ... + ValueError: file does not exist: 'application.ini' + """ + + pass diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_util.py b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_util.py new file mode 100644 index 0000000..8f5e697 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_util.py @@ -0,0 +1,18 @@ + +import unittest +from cuddlefish.manifest import filter_filenames, filter_dirnames + +class Filter(unittest.TestCase): + def test_filter_filenames(self): + names = ["foo", "bar.js", "image.png", + ".hidden", "foo~", ".foo.swp", "bar.js.swp"] + self.failUnlessEqual(sorted(filter_filenames(names)), + sorted(["foo", "bar.js", "image.png"])) + + def test_filter_dirnames(self): + names = ["subdir", "data", ".git", ".hg", ".svn", "defaults"] + self.failUnlessEqual(sorted(filter_dirnames(names)), + sorted(["subdir", "data", "defaults"])) + +if __name__ == '__main__': + unittest.main() diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_version.py b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_version.py new file mode 100644 index 0000000..2fb32f3 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_version.py @@ -0,0 +1,32 @@ +import os +import unittest +import shutil + +from cuddlefish.version import get_version + +class Version(unittest.TestCase): + def get_basedir(self): + return os.path.join(".test_tmp", self.id()) + def make_basedir(self): + basedir = self.get_basedir() + if os.path.isdir(basedir): + here = os.path.abspath(os.getcwd()) + assert os.path.abspath(basedir).startswith(here) # safety + shutil.rmtree(basedir) + os.makedirs(basedir) + return basedir + + def test_current_version(self): + # the SDK should be able to determine its own version. We don't care + # what it is, merely that it can be computed. + env_root = os.environ.get('CUDDLEFISH_ROOT') + version = get_version(env_root) + self.failUnless(isinstance(version, str), (version, type(version))) + self.failUnless(len(version) > 0, version) + def test_read(self): + basedir = self.make_basedir() + f = open(os.path.join(basedir, ".version"), "w") + f.write("versioniffic\n") + f.close() + sdk_version = get_version(basedir) + self.failUnlessEqual(sdk_version, "versioniffic") diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_webdocs.py b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_webdocs.py new file mode 100644 index 0000000..c8d66d4 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_webdocs.py @@ -0,0 +1,92 @@ +import os, re +import unittest + +from cuddlefish.docs import webdocs +from cuddlefish.tests import env_root + +class WebDocTests(unittest.TestCase): + def test_create_package_doc(self): + root = os.path.join(os.getcwd() + \ + '/python-lib/cuddlefish/tests/static-files') + web_docs = webdocs.WebDocs(root) + aarvark_package = web_docs.create_package_page('aardvark') + self._test_common_contents(aarvark_package) + self.assertTrue('

aardvark

'\ + in aarvark_package) + self.assertTrue(\ + 'Author'\ + in aarvark_package) + self.assertTrue(\ + 'Jon Smith'\ + in aarvark_package) + self.assertTrue(\ + 'aardvark - Add-on SDK Documentation'\ + in aarvark_package) + + def test_create_guide1_doc(self): + root = os.path.join(os.getcwd() + \ + '/python-lib/cuddlefish/tests/static-files') + web_docs = webdocs.WebDocs(root) + guide = web_docs.create_guide_page(os.path.join(\ + root + '/doc/dev-guide-source/welcome.blah')) + self._test_common_contents(guide) + self.assertTrue(\ + 'An Imposing Title - Add-on SDK Documentation'\ + in guide) + self.assertTrue('

Some words!

'\ + in guide) + + def test_create_guide2_doc(self): + root = os.path.join(os.getcwd() + \ + '/python-lib/cuddlefish/tests/static-files') + web_docs = webdocs.WebDocs(root) + guide = web_docs.create_guide_page(os.path.join(\ + root + '/doc/dev-guide-source/no_h1.blah')) + self._test_common_contents(guide) + self.assertTrue('Add-on SDK Documentation'\ + in guide) + self.assertTrue('

A heading

'\ + in guide) + + def test_create_module_doc(self): + root = os.path.join(os.getcwd() + \ + '/python-lib/cuddlefish/tests/static-files') + web_docs = webdocs.WebDocs(root) + module = web_docs.create_module_page(os.path.join(\ + root + '/packages/aardvark/doc/aardvark-feeder.blah')) + self._test_common_contents(module) + self.assertTrue(\ + 'aardvark-feeder - Add-on SDK Documentation'\ + in module) + self.assertTrue(\ + '

aardvark-feeder

'\ + in module) + self.assertTrue(\ + '
'\ + in module) + self.assertTrue(\ + '

The aardvark-feeder module simplifies feeding aardvarks.

'\ + in module) + self.assertTrue(\ + '

API Reference

'\ + in module) + self.assertTrue(\ + '

Functions

'\ + in module) + self.assertTrue(\ + '

feed(food)

'\ + in module) + self.assertTrue( + '

Feed the aardvark.

'\ + in module) + + def _test_common_contents(self, doc): + self.assertTrue(\ + 'main' in doc) + +if __name__ == "__main__": + unittest.main() diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_xpi.py b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_xpi.py new file mode 100644 index 0000000..d824392 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/tests/test_xpi.py @@ -0,0 +1,253 @@ +import os +import unittest +import zipfile +import pprint +import shutil + +import simplejson as json +from cuddlefish import xpi, packaging, manifest +from cuddlefish.tests import test_packaging +from test_linker import up + +xpi_template_path = os.path.join(test_packaging.static_files_path, + 'xpi-template') + +fake_manifest = '' + +class Bug588119Tests(unittest.TestCase): + def makexpi(self, pkg_name): + self.xpiname = "%s.xpi" % pkg_name + create_xpi(self.xpiname, pkg_name, 'bug-588119-files') + self.xpi = zipfile.ZipFile(self.xpiname, 'r') + options = self.xpi.read('harness-options.json') + self.xpi_harness_options = json.loads(options) + + def setUp(self): + self.xpiname = None + self.xpi = None + + def tearDown(self): + if self.xpi: + self.xpi.close() + if self.xpiname and os.path.exists(self.xpiname): + os.remove(self.xpiname) + + def testPackageWithImplicitIcon(self): + self.makexpi('implicit-icon') + assert 'icon.png' in self.xpi.namelist() + + def testPackageWithImplicitIcon64(self): + self.makexpi('implicit-icon') + assert 'icon64.png' in self.xpi.namelist() + + def testPackageWithExplicitIcon(self): + self.makexpi('explicit-icon') + assert 'icon.png' in self.xpi.namelist() + + def testPackageWithExplicitIcon64(self): + self.makexpi('explicit-icon') + assert 'icon64.png' in self.xpi.namelist() + + def testPackageWithNoIcon(self): + self.makexpi('no-icon') + assert 'icon.png' not in self.xpi.namelist() + + def testIconPathNotInHarnessOptions(self): + self.makexpi('implicit-icon') + assert 'icon' not in self.xpi_harness_options + + def testIcon64PathNotInHarnessOptions(self): + self.makexpi('implicit-icon') + assert 'icon64' not in self.xpi_harness_options + +class ExtraHarnessOptions(unittest.TestCase): + def setUp(self): + self.xpiname = None + self.xpi = None + + def tearDown(self): + if self.xpi: + self.xpi.close() + if self.xpiname and os.path.exists(self.xpiname): + os.remove(self.xpiname) + + def testOptions(self): + pkg_name = "extra-options" + self.xpiname = "%s.xpi" % pkg_name + create_xpi(self.xpiname, pkg_name, "bug-669274-files", + extra_harness_options={"builderVersion": "futuristic"}) + self.xpi = zipfile.ZipFile(self.xpiname, 'r') + options = self.xpi.read('harness-options.json') + hopts = json.loads(options) + self.failUnless("builderVersion" in hopts) + self.failUnlessEqual(hopts["builderVersion"], "futuristic") + + def testBadOptionName(self): + pkg_name = "extra-options" + self.xpiname = "%s.xpi" % pkg_name + self.failUnlessRaises(xpi.HarnessOptionAlreadyDefinedError, + create_xpi, + self.xpiname, pkg_name, "bug-669274-files", + extra_harness_options={"main": "already in use"}) + +class SmallXPI(unittest.TestCase): + def setUp(self): + self.root = up(os.path.abspath(__file__), 4) + def get_linker_files_dir(self, name): + return os.path.join(up(os.path.abspath(__file__)), "linker-files", name) + def get_pkg(self, name): + d = self.get_linker_files_dir(name) + return packaging.get_config_in_dir(d) + + def get_basedir(self): + return os.path.join(".test_tmp", self.id()) + def make_basedir(self): + basedir = self.get_basedir() + if os.path.isdir(basedir): + here = os.path.abspath(os.getcwd()) + assert os.path.abspath(basedir).startswith(here) # safety + shutil.rmtree(basedir) + os.makedirs(basedir) + return basedir + + def test_contents(self): + target_cfg = self.get_pkg("three") + package_path = [self.get_linker_files_dir("three-deps")] + pkg_cfg = packaging.build_config(self.root, target_cfg, + packagepath=package_path) + deps = packaging.get_deps_for_targets(pkg_cfg, + [target_cfg.name, "addon-kit"]) + m = manifest.build_manifest(target_cfg, pkg_cfg, deps, + "P/", scan_tests=False) + used_files = list(m.get_used_files()) + here = up(os.path.abspath(__file__)) + def absify(*parts): + fn = os.path.join(here, "linker-files", *parts) + return os.path.abspath(fn) + expected = [absify(*parts) for parts in + [("three", "lib", "main.js"), + ("three-deps", "three-a", "lib", "main.js"), + ("three-deps", "three-a", "lib", "subdir", "subfile.js"), + ("three-deps", "three-a", "data", "msg.txt"), + ("three-deps", "three-a", "data", "subdir", "submsg.txt"), + ("three-deps", "three-b", "lib", "main.js"), + ("three-deps", "three-c", "lib", "main.js"), + ("three-deps", "three-c", "lib", "sub", "foo.js"), + ]] + missing = set(expected) - set(used_files) + extra = set(used_files) - set(expected) + self.failUnlessEqual((list(missing), list(extra)), ([], [])) + used_deps = m.get_used_packages() + + build = packaging.generate_build_for_target(pkg_cfg, target_cfg.name, + used_deps, + prefix="p-", + include_tests=False) + options = {'main': target_cfg.main} + options.update(build) + basedir = self.make_basedir() + xpi_name = os.path.join(basedir, "contents.xpi") + xpi.build_xpi(template_root_dir=xpi_template_path, + manifest=fake_manifest, + xpi_path=xpi_name, + harness_options=options, + limit_to=used_files) + x = zipfile.ZipFile(xpi_name, "r") + names = x.namelist() + expected = ["components/", + "components/harness.js", + # the real template also has 'bootstrap.js', but the fake + # one in tests/static-files/xpi-template doesn't + "harness-options.json", + "install.rdf", + "resources/", + "resources/p-api-utils-data/", + "resources/p-api-utils-lib/", + "resources/p-three-lib/", + "resources/p-three-lib/main.js", + "resources/p-three-a-data/", + "resources/p-three-a-data/msg.txt", + "resources/p-three-a-data/subdir/", + "resources/p-three-a-data/subdir/submsg.txt", + "resources/p-three-a-lib/", + "resources/p-three-a-lib/main.js", + "resources/p-three-a-lib/subdir/", + "resources/p-three-a-lib/subdir/subfile.js", + "resources/p-three-b-lib/", + "resources/p-three-b-lib/main.js", + "resources/p-three-c-lib/", + "resources/p-three-c-lib/main.js", + "resources/p-three-c-lib/sub/", + "resources/p-three-c-lib/sub/foo.js", + # notably absent: p-three-a-lib/unused.js + ] + # showing deltas makes failures easier to investigate + missing = set(expected) - set(names) + extra = set(names) - set(expected) + self.failUnlessEqual((list(missing), list(extra)), ([], [])) + self.failUnlessEqual(sorted(names), sorted(expected)) + + + +def document_dir(name): + if name in ['packages', 'xpi-template']: + dirname = os.path.join(test_packaging.static_files_path, name) + document_dir_files(dirname) + elif name == 'xpi-output': + create_xpi('test-xpi.xpi') + document_zip_file('test-xpi.xpi') + os.remove('test-xpi.xpi') + else: + raise Exception('unknown dir: %s' % name) + +def normpath(path): + """ + Make a platform-specific relative path use '/' as a separator. + """ + + return path.replace(os.path.sep, '/') + +def document_zip_file(path): + zip = zipfile.ZipFile(path, 'r') + for name in sorted(zip.namelist()): + contents = zip.read(name) + lines = contents.splitlines() + if len(lines) == 1 and name.endswith('.json') and len(lines[0]) > 75: + # Ideally we would json-decode this, but it results + # in an annoying 'u' before every string literal, + # since json decoding makes all strings unicode. + contents = eval(contents) + contents = pprint.pformat(contents) + lines = contents.splitlines() + contents = "\n ".join(lines) + print "%s:\n %s" % (normpath(name), contents) + zip.close() + +def document_dir_files(path): + filename_contents_tuples = [] + for dirpath, dirnames, filenames in os.walk(path): + relpath = dirpath[len(path)+1:] + for filename in filenames: + abspath = os.path.join(dirpath, filename) + contents = open(abspath, 'r').read() + contents = "\n ".join(contents.splitlines()) + relfilename = os.path.join(relpath, filename) + filename_contents_tuples.append((normpath(relfilename), contents)) + filename_contents_tuples.sort() + for filename, contents in filename_contents_tuples: + print "%s:" % filename + print " %s" % contents + +def create_xpi(xpiname, pkg_name='aardvark', dirname='static-files', + extra_harness_options={}): + configs = test_packaging.get_configs(pkg_name, dirname) + options = {'main': configs.target_cfg.main} + options.update(configs.build) + xpi.build_xpi(template_root_dir=xpi_template_path, + manifest=fake_manifest, + xpi_path=xpiname, + harness_options=options, + extra_harness_options=extra_harness_options) + +if __name__ == '__main__': + unittest.main() diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/util.py b/tools/addon-sdk-1.3/python-lib/cuddlefish/util.py new file mode 100644 index 0000000..b3d1ec1 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/util.py @@ -0,0 +1,19 @@ + +IGNORED_FILE_PREFIXES = ["."] +IGNORED_FILE_SUFFIXES = ["~", ".swp"] +IGNORED_DIRS = [".git", ".svn", ".hg"] + +def filter_filenames(filenames, ignored_files=[".hgignore"]): + for filename in filenames: + if filename in ignored_files: + continue + if any([filename.startswith(suffix) + for suffix in IGNORED_FILE_PREFIXES]): + continue + if any([filename.endswith(suffix) + for suffix in IGNORED_FILE_SUFFIXES]): + continue + yield filename + +def filter_dirnames(dirnames): + return [dirname for dirname in dirnames if dirname not in IGNORED_DIRS] diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/version.py b/tools/addon-sdk-1.3/python-lib/cuddlefish/version.py new file mode 100644 index 0000000..f2a91a7 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/version.py @@ -0,0 +1,6 @@ +import os + +def get_version(env_root): + f = open(os.path.join(env_root, ".version"), "r") + sdk_version = f.read().strip() + return sdk_version diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/version_comparator.py b/tools/addon-sdk-1.3/python-lib/cuddlefish/version_comparator.py new file mode 100644 index 0000000..b89bd6c --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/version_comparator.py @@ -0,0 +1,202 @@ +''' + This is a really crummy, slow Python implementation of the Mozilla + platform's nsIVersionComparator interface: + + https://developer.mozilla.org/En/NsIVersionComparator + + For more information, also see: + + http://mxr.mozilla.org/mozilla/source/xpcom/glue/nsVersionComparator.cpp +''' + +import re +import sys + +class VersionPart(object): + ''' + Examples: + + >>> VersionPart('1') + (1, None, 0, None) + + >>> VersionPart('1pre') + (1, 'pre', 0, None) + + >>> VersionPart('1pre10') + (1, 'pre', 10, None) + + >>> VersionPart('1pre10a') + (1, 'pre', 10, 'a') + + >>> VersionPart('1+') + (2, 'pre', 0, None) + + >>> VersionPart('*').numA == sys.maxint + True + + >>> VersionPart('1') < VersionPart('2') + True + + >>> VersionPart('2') > VersionPart('1') + True + + >>> VersionPart('1') == VersionPart('1') + True + + >>> VersionPart('1pre') > VersionPart('1') + False + + >>> VersionPart('1') < VersionPart('1pre') + False + + >>> VersionPart('1pre1') < VersionPart('1pre2') + True + + >>> VersionPart('1pre10b') > VersionPart('1pre10a') + True + + >>> VersionPart('1pre10b') == VersionPart('1pre10b') + True + + >>> VersionPart('1pre10a') < VersionPart('1pre10b') + True + + >>> VersionPart('1') > VersionPart('') + True + ''' + + _int_part = re.compile('[+-]?(\d*)(.*)') + _num_chars = '0123456789+-' + + def __init__(self, part): + self.numA = 0 + self.strB = None + self.numC = 0 + self.extraD = None + + if not part: + return + + if part == '*': + self.numA = sys.maxint + else: + match = self._int_part.match(part) + self.numA = int(match.group(1)) + self.strB = match.group(2) or None + if self.strB == '+': + self.strB = 'pre' + self.numA += 1 + elif self.strB: + i = 0 + num_found = -1 + for char in self.strB: + if char in self._num_chars: + num_found = i + break + i += 1 + if num_found != -1: + match = self._int_part.match(self.strB[num_found:]) + self.numC = int(match.group(1)) + self.extraD = match.group(2) or None + self.strB = self.strB[:num_found] + + def _strcmp(self, str1, str2): + # Any string is *before* no string. + if str1 is None: + if str2 is None: + return 0 + else: + return 1 + + if str2 is None: + return -1 + + return cmp(str1, str2) + + def __cmp__(self, other): + r = cmp(self.numA, other.numA) + if r: + return r + + r = self._strcmp(self.strB, other.strB) + if r: + return r + + r = cmp(self.numC, other.numC) + if r: + return r + + return self._strcmp(self.extraD, other.extraD) + + def __repr__(self): + return repr((self.numA, self.strB, self.numC, self.extraD)) + +def compare(a, b): + ''' + Examples: + + >>> compare('1', '2') + -1 + + >>> compare('1', '1') + 0 + + >>> compare('2', '1') + 1 + + >>> compare('1.0pre1', '1.0pre2') + -1 + + >>> compare('1.0pre2', '1.0') + -1 + + >>> compare('1.0', '1.0.0') + 0 + + >>> compare('1.0.0', '1.0.0.0') + 0 + + >>> compare('1.0.0.0', '1.1pre') + -1 + + >>> compare('1.1pre', '1.1pre0') + 0 + + >>> compare('1.1pre0', '1.0+') + 0 + + >>> compare('1.0+', '1.1pre1a') + -1 + + >>> compare('1.1pre1a', '1.1pre1') + -1 + + >>> compare('1.1pre1', '1.1pre10a') + -1 + + >>> compare('1.1pre10a', '1.1pre10') + -1 + + >>> compare('1.1pre10a', '1.*') + -1 + ''' + + a_parts = a.split('.') + b_parts = b.split('.') + + if len(a_parts) < len(b_parts): + a_parts.extend([''] * (len(b_parts) - len(a_parts))) + else: + b_parts.extend([''] * (len(a_parts) - len(b_parts))) + + for a_part, b_part in zip(a_parts, b_parts): + r = cmp(VersionPart(a_part), VersionPart(b_part)) + if r: + return r + + return 0 + +if __name__ == '__main__': + import doctest + + doctest.testmod(verbose=True) diff --git a/tools/addon-sdk-1.3/python-lib/cuddlefish/xpi.py b/tools/addon-sdk-1.3/python-lib/cuddlefish/xpi.py new file mode 100644 index 0000000..2708106 --- /dev/null +++ b/tools/addon-sdk-1.3/python-lib/cuddlefish/xpi.py @@ -0,0 +1,105 @@ +import os +import zipfile +import simplejson as json +from cuddlefish.util import filter_filenames, filter_dirnames + +class HarnessOptionAlreadyDefinedError(Exception): + """You cannot use --harness-option on keys that already exist in + harness-options.json""" + +ZIPSEP = "/" # always use "/" in zipfiles + +def make_zipfile_path(localroot, localpath): + return ZIPSEP.join(localpath[len(localroot)+1:].split(os.sep)) + +def mkzipdir(zf, path): + dirinfo = zipfile.ZipInfo(path) + dirinfo.external_attr = int("040755", 8) << 16L + zf.writestr(dirinfo, "") + +def build_xpi(template_root_dir, manifest, xpi_path, + harness_options, limit_to=None, extra_harness_options={}): + zf = zipfile.ZipFile(xpi_path, "w", zipfile.ZIP_DEFLATED) + + open('.install.rdf', 'w').write(str(manifest)) + zf.write('.install.rdf', 'install.rdf') + os.remove('.install.rdf') + + if 'icon' in harness_options: + zf.write(str(harness_options['icon']), 'icon.png') + del harness_options['icon'] + + if 'icon64' in harness_options: + zf.write(str(harness_options['icon64']), 'icon64.png') + del harness_options['icon64'] + + IGNORED_FILES = [".hgignore", ".DS_Store", "install.rdf", + "application.ini", xpi_path] + + files_to_copy = {} # maps zipfile path to local-disk abspath + dirs_to_create = set() # zipfile paths, no trailing slash + + for dirpath, dirnames, filenames in os.walk(template_root_dir): + filenames = list(filter_filenames(filenames, IGNORED_FILES)) + dirnames[:] = filter_dirnames(dirnames) + for dirname in dirnames: + arcpath = make_zipfile_path(template_root_dir, + os.path.join(dirpath, dirname)) + dirs_to_create.add(arcpath) + for filename in filenames: + abspath = os.path.join(dirpath, filename) + arcpath = make_zipfile_path(template_root_dir, abspath) + files_to_copy[arcpath] = abspath + + new_resources = {} + for resource in harness_options['resources']: + new_resources[resource] = ['resources', resource] + base_arcpath = ZIPSEP.join(['resources', resource]) + # Always write the top directory, even if it contains no files, since + # the harness will try to access it. + dirs_to_create.add(base_arcpath) + abs_dirname = harness_options['resources'][resource] + # cp -r stuff from abs_dirname/ into ZIP/resources/RESOURCEBASE/ + for dirpath, dirnames, filenames in os.walk(abs_dirname): + goodfiles = list(filter_filenames(filenames, IGNORED_FILES)) + dirnames[:] = filter_dirnames(dirnames) + for filename in goodfiles: + abspath = os.path.join(dirpath, filename) + if limit_to is not None and abspath not in limit_to: + continue # strip unused files + arcpath = ZIPSEP.join( + ['resources', + resource, + make_zipfile_path(abs_dirname, + os.path.join(dirpath, filename)), + ]) + files_to_copy[str(arcpath)] = str(abspath) + harness_options['resources'] = new_resources + + # now figure out which directories we need: all retained files parents + for arcpath in files_to_copy: + bits = arcpath.split("/") + for i in range(1,len(bits)): + parentpath = ZIPSEP.join(bits[0:i]) + dirs_to_create.add(parentpath) + + # create zipfile in alphabetical order, with each directory before its + # files + for name in sorted(dirs_to_create.union(set(files_to_copy))): + if name in dirs_to_create: + mkzipdir(zf, name+"/") + if name in files_to_copy: + zf.write(files_to_copy[name], name) + + harness_options = harness_options.copy() + for key,value in extra_harness_options.items(): + if key in harness_options: + msg = "Can't use --harness-option for existing key '%s'" % key + raise HarnessOptionAlreadyDefinedError(msg) + harness_options[key] = value + open('.options.json', 'w').write(json.dumps(harness_options, indent=1, + sort_keys=True)) + zf.write('.options.json', 'harness-options.json') + os.remove('.options.json') + + zf.close() -- cgit v1.2.3