From 6e728ed97991800a31bf37c908d56e2fbfcf6691 Mon Sep 17 00:00:00 2001 From: ndragomirov Date: Mon, 26 May 2014 05:40:29 -0700 Subject: [PATCH 01/26] update oauth; add some firefox functionality --- Gruntfile.js | 52 +- addon-sdk-1.16/LICENSE | 30 + addon-sdk-1.16/README | 41 + addon-sdk-1.16/app-extension/application.ini | 11 + addon-sdk-1.16/app-extension/bootstrap.js | 339 ++ addon-sdk-1.16/app-extension/install.rdf | 33 + addon-sdk-1.16/bin/activate | 84 + addon-sdk-1.16/bin/activate.bat | 134 + addon-sdk-1.16/bin/activate.fish | 66 + addon-sdk-1.16/bin/activate.ps1 | 99 + addon-sdk-1.16/bin/cfx | 33 + addon-sdk-1.16/bin/cfx.bat | 6 + addon-sdk-1.16/bin/deactivate.bat | 23 + .../buildbot-run-cfx-helper | 14 + .../bin/integration-scripts/integration-check | 364 ++ addon-sdk-1.16/examples/annotator/README.md | 46 + .../annotator/data/annotation/annotation.html | 31 + .../annotator/data/annotation/annotation.js | 11 + .../data/editor/annotation-editor.html | 39 + .../data/editor/annotation-editor.js | 23 + .../annotator/data/jquery-1.4.2.min.js | 154 + .../annotator/data/list/annotation-list.css | 40 + .../annotator/data/list/annotation-list.html | 26 + .../annotator/data/list/annotation-list.js | 31 + .../examples/annotator/data/matcher.js | 50 + .../examples/annotator/data/selector.js | 73 + .../annotator/data/widget/pencil-off.png | Bin 0 -> 1740 bytes .../annotator/data/widget/pencil-on.png | Bin 0 -> 2054 bytes .../examples/annotator/data/widget/widget.js | 17 + addon-sdk-1.16/examples/annotator/lib/main.js | 266 + .../examples/annotator/package.json | 9 + .../examples/annotator/tests/test-main.js | 7 + .../examples/library-detector/README.md | 13 + .../library-detector/data/icons/closure.ico | Bin 0 -> 1150 bytes .../library-detector/data/icons/jquery.ico | Bin 0 -> 3638 bytes .../library-detector/data/icons/jquery_ui.ico | Bin 0 -> 1150 bytes .../library-detector/data/icons/modernizr.ico | Bin 0 -> 1150 bytes .../library-detector/data/icons/mootools.png | Bin 0 -> 386 bytes .../library-detector/data/icons/yui.ico | Bin 0 -> 6598 bytes .../library-detector/data/library-detector.js | 97 + .../examples/library-detector/data/panel.html | 16 + .../library-detector/data/widget.html | 50 + .../examples/library-detector/lib/main.js | 67 + .../examples/library-detector/package.json | 9 + .../library-detector/test/test-main.js | 7 + .../examples/reading-data/data/mom.png | Bin 0 -> 4778 bytes .../examples/reading-data/data/sample.html | 7 + .../examples/reading-data/lib/main.js | 44 + .../examples/reading-data/package.json | 9 + .../examples/reading-data/tests/test-main.js | 24 + .../examples/reddit-panel/README.md | 14 + .../reddit-panel/data/jquery-1.4.4.min.js | 167 + .../examples/reddit-panel/data/panel.js | 35 + .../examples/reddit-panel/lib/main.js | 31 + .../examples/reddit-panel/package.json | 9 + .../examples/reddit-panel/tests/test-main.js | 21 + .../examples/toolbar-api/data/favicon.ico | Bin 0 -> 15086 bytes .../examples/toolbar-api/data/index.html | 21 + .../examples/toolbar-api/lib/main.js | 48 + .../examples/toolbar-api/package.json | 9 + addon-sdk-1.16/lib/diffpatcher/.travis.yml | 5 + addon-sdk-1.16/lib/diffpatcher/History.md | 14 + addon-sdk-1.16/lib/diffpatcher/License.md | 18 + addon-sdk-1.16/lib/diffpatcher/Readme.md | 70 + addon-sdk-1.16/lib/diffpatcher/diff.js | 45 + addon-sdk-1.16/lib/diffpatcher/index.js | 5 + addon-sdk-1.16/lib/diffpatcher/package.json | 54 + addon-sdk-1.16/lib/diffpatcher/patch.js | 21 + addon-sdk-1.16/lib/diffpatcher/rebase.js | 36 + addon-sdk-1.16/lib/diffpatcher/test/common.js | 3 + addon-sdk-1.16/lib/diffpatcher/test/diff.js | 59 + addon-sdk-1.16/lib/diffpatcher/test/index.js | 14 + addon-sdk-1.16/lib/diffpatcher/test/patch.js | 83 + addon-sdk-1.16/lib/diffpatcher/test/tap.js | 3 + addon-sdk-1.16/lib/main.js/main.js | 20 + addon-sdk-1.16/lib/method/.travis.yml | 5 + addon-sdk-1.16/lib/method/History.md | 55 + addon-sdk-1.16/lib/method/License.md | 18 + addon-sdk-1.16/lib/method/Readme.md | 117 + addon-sdk-1.16/lib/method/core.js | 225 + addon-sdk-1.16/lib/method/package.json | 41 + addon-sdk-1.16/lib/method/test/browser.js | 20 + addon-sdk-1.16/lib/method/test/common.js | 272 + addon-sdk-1.16/lib/node/os.js | 76 + addon-sdk-1.16/lib/sdk/addon-page.js | 73 + addon-sdk-1.16/lib/sdk/addon/events.js | 54 + addon-sdk-1.16/lib/sdk/addon/host.js | 12 + addon-sdk-1.16/lib/sdk/addon/installer.js | 117 + addon-sdk-1.16/lib/sdk/addon/runner.js | 179 + addon-sdk-1.16/lib/sdk/addon/window.js | 68 + addon-sdk-1.16/lib/sdk/base64.js | 40 + addon-sdk-1.16/lib/sdk/browser/events.js | 20 + addon-sdk-1.16/lib/sdk/clipboard.js | 335 ++ addon-sdk-1.16/lib/sdk/console/plain-text.js | 79 + addon-sdk-1.16/lib/sdk/console/traceback.js | 89 + .../lib/sdk/content/content-worker.js | 351 ++ addon-sdk-1.16/lib/sdk/content/content.js | 13 + addon-sdk-1.16/lib/sdk/content/events.js | 57 + addon-sdk-1.16/lib/sdk/content/loader.js | 207 + addon-sdk-1.16/lib/sdk/content/mod.js | 58 + addon-sdk-1.16/lib/sdk/content/sandbox.js | 404 ++ addon-sdk-1.16/lib/sdk/content/thumbnail.js | 46 + addon-sdk-1.16/lib/sdk/content/utils.js | 82 + addon-sdk-1.16/lib/sdk/content/worker.js | 282 + addon-sdk-1.16/lib/sdk/context-menu.js | 1202 ++++ addon-sdk-1.16/lib/sdk/core/disposable.js | 74 + addon-sdk-1.16/lib/sdk/core/heritage.js | 183 + addon-sdk-1.16/lib/sdk/core/namespace.js | 43 + addon-sdk-1.16/lib/sdk/core/promise.js | 290 + .../lib/sdk/deprecated/api-utils.js | 193 + addon-sdk-1.16/lib/sdk/deprecated/cortex.js | 112 + addon-sdk-1.16/lib/sdk/deprecated/errors.js | 64 + addon-sdk-1.16/lib/sdk/deprecated/events.js | 182 + .../lib/sdk/deprecated/events/assembler.js | 52 + .../lib/sdk/deprecated/light-traits.js | 599 ++ addon-sdk-1.16/lib/sdk/deprecated/list.js | 126 + addon-sdk-1.16/lib/sdk/deprecated/memory.js | 129 + addon-sdk-1.16/lib/sdk/deprecated/symbiont.js | 229 + .../lib/sdk/deprecated/traits-worker.js | 660 +++ addon-sdk-1.16/lib/sdk/deprecated/traits.js | 187 + .../lib/sdk/deprecated/traits/core.js | 322 + .../lib/sdk/deprecated/unit-test-finder.js | 89 + .../lib/sdk/deprecated/unit-test.js | 489 ++ .../lib/sdk/deprecated/window-utils.js | 227 + addon-sdk-1.16/lib/sdk/dom/events.js | 139 + addon-sdk-1.16/lib/sdk/dom/events/keys.js | 63 + addon-sdk-1.16/lib/sdk/event/chrome.js | 54 + addon-sdk-1.16/lib/sdk/event/core.js | 172 + addon-sdk-1.16/lib/sdk/event/dom.js | 26 + addon-sdk-1.16/lib/sdk/event/target.js | 76 + addon-sdk-1.16/lib/sdk/event/utils.js | 275 + addon-sdk-1.16/lib/sdk/frame/hidden-frame.js | 116 + addon-sdk-1.16/lib/sdk/frame/utils.js | 100 + addon-sdk-1.16/lib/sdk/fs/path.js | 500 ++ addon-sdk-1.16/lib/sdk/hotkeys.js | 40 + addon-sdk-1.16/lib/sdk/indexed-db.js | 58 + addon-sdk-1.16/lib/sdk/input/browser.js | 73 + .../lib/sdk/input/customizable-ui.js | 40 + addon-sdk-1.16/lib/sdk/input/frame.js | 85 + addon-sdk-1.16/lib/sdk/input/system.js | 109 + addon-sdk-1.16/lib/sdk/input/window.js | 80 + addon-sdk-1.16/lib/sdk/io/buffer.js | 346 ++ addon-sdk-1.16/lib/sdk/io/byte-streams.js | 104 + addon-sdk-1.16/lib/sdk/io/data.js | 82 + addon-sdk-1.16/lib/sdk/io/file.js | 196 + addon-sdk-1.16/lib/sdk/io/fs.js | 940 +++ addon-sdk-1.16/lib/sdk/io/stream.js | 436 ++ addon-sdk-1.16/lib/sdk/io/text-streams.js | 242 + addon-sdk-1.16/lib/sdk/keyboard/hotkeys.js | 110 + addon-sdk-1.16/lib/sdk/keyboard/observer.js | 57 + addon-sdk-1.16/lib/sdk/keyboard/utils.js | 189 + addon-sdk-1.16/lib/sdk/l10n.js | 83 + addon-sdk-1.16/lib/sdk/l10n/core.js | 35 + addon-sdk-1.16/lib/sdk/l10n/html.js | 110 + addon-sdk-1.16/lib/sdk/l10n/loader.js | 71 + addon-sdk-1.16/lib/sdk/l10n/locale.js | 126 + addon-sdk-1.16/lib/sdk/l10n/plural-rules.js | 403 ++ addon-sdk-1.16/lib/sdk/l10n/prefs.js | 42 + addon-sdk-1.16/lib/sdk/lang/functional.js | 330 ++ addon-sdk-1.16/lib/sdk/lang/type.js | 364 ++ addon-sdk-1.16/lib/sdk/lang/weak-set.js | 70 + addon-sdk-1.16/lib/sdk/loader/cuddlefish.js | 152 + addon-sdk-1.16/lib/sdk/loader/sandbox.js | 54 + addon-sdk-1.16/lib/sdk/net/url.js | 119 + addon-sdk-1.16/lib/sdk/net/xhr.js | 36 + addon-sdk-1.16/lib/sdk/notifications.js | 102 + addon-sdk-1.16/lib/sdk/output/system.js | 71 + addon-sdk-1.16/lib/sdk/page-mod.js | 273 + .../lib/sdk/page-mod/match-pattern.js | 5 + addon-sdk-1.16/lib/sdk/page-worker.js | 170 + addon-sdk-1.16/lib/sdk/panel.js | 264 + addon-sdk-1.16/lib/sdk/panel/events.js | 26 + addon-sdk-1.16/lib/sdk/panel/utils.js | 380 ++ addon-sdk-1.16/lib/sdk/panel/window.js | 61 + addon-sdk-1.16/lib/sdk/passwords.js | 61 + addon-sdk-1.16/lib/sdk/passwords/utils.js | 104 + addon-sdk-1.16/lib/sdk/places/bookmarks.js | 394 ++ addon-sdk-1.16/lib/sdk/places/contract.js | 75 + addon-sdk-1.16/lib/sdk/places/events.js | 121 + addon-sdk-1.16/lib/sdk/places/favicon.js | 48 + addon-sdk-1.16/lib/sdk/places/history.js | 64 + .../lib/sdk/places/host/host-bookmarks.js | 241 + .../lib/sdk/places/host/host-query.js | 171 + .../lib/sdk/places/host/host-tags.js | 91 + addon-sdk-1.16/lib/sdk/places/utils.js | 250 + addon-sdk-1.16/lib/sdk/platform/xpcom.js | 229 + .../lib/sdk/preferences/event-target.js | 61 + addon-sdk-1.16/lib/sdk/preferences/service.js | 174 + addon-sdk-1.16/lib/sdk/private-browsing.js | 86 + .../lib/sdk/private-browsing/utils.js | 104 + .../lib/sdk/private-browsing/window/utils.js | 33 + addon-sdk-1.16/lib/sdk/querystring.js | 121 + addon-sdk-1.16/lib/sdk/request.js | 228 + addon-sdk-1.16/lib/sdk/selection.js | 467 ++ addon-sdk-1.16/lib/sdk/self.js | 39 + addon-sdk-1.16/lib/sdk/simple-prefs.js | 26 + addon-sdk-1.16/lib/sdk/simple-storage.js | 235 + addon-sdk-1.16/lib/sdk/stylesheet/style.js | 81 + addon-sdk-1.16/lib/sdk/stylesheet/utils.js | 79 + addon-sdk-1.16/lib/sdk/system.js | 146 + addon-sdk-1.16/lib/sdk/system/environment.js | 60 + addon-sdk-1.16/lib/sdk/system/events.js | 125 + addon-sdk-1.16/lib/sdk/system/globals.js | 40 + addon-sdk-1.16/lib/sdk/system/runtime.js | 27 + addon-sdk-1.16/lib/sdk/system/unload.js | 82 + addon-sdk-1.16/lib/sdk/system/xul-app.js | 184 + addon-sdk-1.16/lib/sdk/tab/events.js | 70 + addon-sdk-1.16/lib/sdk/tabs.js | 10 + addon-sdk-1.16/lib/sdk/tabs/common.js | 29 + addon-sdk-1.16/lib/sdk/tabs/events.js | 39 + addon-sdk-1.16/lib/sdk/tabs/helpers.js | 41 + addon-sdk-1.16/lib/sdk/tabs/namespace.js | 10 + addon-sdk-1.16/lib/sdk/tabs/observer.js | 98 + addon-sdk-1.16/lib/sdk/tabs/tab-fennec.js | 229 + addon-sdk-1.16/lib/sdk/tabs/tab-firefox.js | 297 + addon-sdk-1.16/lib/sdk/tabs/tab.js | 19 + addon-sdk-1.16/lib/sdk/tabs/tabs-firefox.js | 68 + addon-sdk-1.16/lib/sdk/tabs/tabs.js | 19 + addon-sdk-1.16/lib/sdk/tabs/utils.js | 368 ++ addon-sdk-1.16/lib/sdk/tabs/worker.js | 17 + addon-sdk-1.16/lib/sdk/test.js | 126 + addon-sdk-1.16/lib/sdk/test/assert.js | 357 ++ addon-sdk-1.16/lib/sdk/test/harness.js | 627 ++ addon-sdk-1.16/lib/sdk/test/httpd.js | 5212 +++++++++++++++++ addon-sdk-1.16/lib/sdk/test/loader.js | 114 + addon-sdk-1.16/lib/sdk/test/memory.js | 17 + addon-sdk-1.16/lib/sdk/test/runner.js | 125 + addon-sdk-1.16/lib/sdk/test/tmp-file.js | 73 + addon-sdk-1.16/lib/sdk/test/utils.js | 109 + addon-sdk-1.16/lib/sdk/timers.js | 105 + addon-sdk-1.16/lib/sdk/ui.js | 17 + addon-sdk-1.16/lib/sdk/ui/button/action.js | 105 + addon-sdk-1.16/lib/sdk/ui/button/contract.js | 60 + addon-sdk-1.16/lib/sdk/ui/button/toggle.js | 115 + addon-sdk-1.16/lib/sdk/ui/button/view.js | 223 + .../lib/sdk/ui/button/view/events.js | 16 + addon-sdk-1.16/lib/sdk/ui/frame.js | 27 + addon-sdk-1.16/lib/sdk/ui/frame/model.js | 154 + addon-sdk-1.16/lib/sdk/ui/frame/view.html | 18 + addon-sdk-1.16/lib/sdk/ui/frame/view.js | 144 + addon-sdk-1.16/lib/sdk/ui/id.js | 27 + addon-sdk-1.16/lib/sdk/ui/sidebar.js | 298 + addon-sdk-1.16/lib/sdk/ui/sidebar/actions.js | 10 + addon-sdk-1.16/lib/sdk/ui/sidebar/contract.js | 27 + .../lib/sdk/ui/sidebar/namespace.js | 11 + addon-sdk-1.16/lib/sdk/ui/sidebar/utils.js | 8 + addon-sdk-1.16/lib/sdk/ui/sidebar/view.js | 208 + addon-sdk-1.16/lib/sdk/ui/state.js | 236 + addon-sdk-1.16/lib/sdk/ui/state/events.js | 16 + addon-sdk-1.16/lib/sdk/ui/toolbar.js | 27 + addon-sdk-1.16/lib/sdk/ui/toolbar/model.js | 151 + addon-sdk-1.16/lib/sdk/ui/toolbar/view.js | 244 + addon-sdk-1.16/lib/sdk/url.js | 291 + addon-sdk-1.16/lib/sdk/url/utils.js | 29 + addon-sdk-1.16/lib/sdk/util/array.js | 123 + addon-sdk-1.16/lib/sdk/util/collection.js | 115 + addon-sdk-1.16/lib/sdk/util/contract.js | 51 + addon-sdk-1.16/lib/sdk/util/deprecate.js | 40 + addon-sdk-1.16/lib/sdk/util/iteration.js | 33 + addon-sdk-1.16/lib/sdk/util/list.js | 87 + addon-sdk-1.16/lib/sdk/util/match-pattern.js | 120 + addon-sdk-1.16/lib/sdk/util/object.js | 92 + addon-sdk-1.16/lib/sdk/util/registry.js | 59 + addon-sdk-1.16/lib/sdk/util/rules.js | 50 + addon-sdk-1.16/lib/sdk/util/sequence.js | 576 ++ addon-sdk-1.16/lib/sdk/util/uuid.js | 17 + addon-sdk-1.16/lib/sdk/view/core.js | 27 + addon-sdk-1.16/lib/sdk/widget.js | 1007 ++++ addon-sdk-1.16/lib/sdk/window/browser.js | 55 + addon-sdk-1.16/lib/sdk/window/events.js | 46 + addon-sdk-1.16/lib/sdk/window/helpers.js | 63 + addon-sdk-1.16/lib/sdk/window/namespace.js | 6 + addon-sdk-1.16/lib/sdk/window/utils.js | 427 ++ addon-sdk-1.16/lib/sdk/windows.js | 19 + addon-sdk-1.16/lib/sdk/windows/dom.js | 37 + addon-sdk-1.16/lib/sdk/windows/fennec.js | 83 + addon-sdk-1.16/lib/sdk/windows/firefox.js | 268 + addon-sdk-1.16/lib/sdk/windows/loader.js | 126 + addon-sdk-1.16/lib/sdk/windows/observer.js | 51 + addon-sdk-1.16/lib/sdk/windows/tabs-fennec.js | 168 + .../lib/sdk/windows/tabs-firefox.js | 209 + addon-sdk-1.16/lib/sdk/worker/utils.js | 19 + addon-sdk-1.16/lib/test.js | 11 + addon-sdk-1.16/lib/toolkit/loader.js | 918 +++ addon-sdk-1.16/mapping.json | 79 + addon-sdk-1.16/package.json | 10 + .../python-lib/cuddlefish/__init__.py | 937 +++ .../python-lib/cuddlefish/__init__.pyc | Bin 0 -> 25666 bytes .../python-lib/cuddlefish/_version.py | 171 + .../python-lib/cuddlefish/_version.pyc | Bin 0 -> 4450 bytes addon-sdk-1.16/python-lib/cuddlefish/bunch.py | 34 + .../python-lib/cuddlefish/bunch.pyc | Bin 0 -> 1658 bytes .../python-lib/cuddlefish/manifest.py | 795 +++ .../python-lib/cuddlefish/manifest.pyc | Bin 0 -> 26200 bytes .../cuddlefish/mobile-utils/bootstrap.js | 55 + .../cuddlefish/mobile-utils/install.rdf | 39 + .../python-lib/cuddlefish/options_defaults.py | 26 + .../python-lib/cuddlefish/options_xul.py | 101 + .../python-lib/cuddlefish/packaging.py | 471 ++ .../python-lib/cuddlefish/packaging.pyc | Bin 0 -> 17018 bytes .../python-lib/cuddlefish/preflight.py | 77 + .../python-lib/cuddlefish/preflight.pyc | Bin 0 -> 1808 bytes addon-sdk-1.16/python-lib/cuddlefish/prefs.py | 120 + .../python-lib/cuddlefish/prefs.pyc | Bin 0 -> 3657 bytes .../python-lib/cuddlefish/property_parser.py | 111 + addon-sdk-1.16/python-lib/cuddlefish/rdf.py | 195 + addon-sdk-1.16/python-lib/cuddlefish/rdf.pyc | Bin 0 -> 6931 bytes .../python-lib/cuddlefish/runner.py | 763 +++ .../python-lib/cuddlefish/runner.pyc | Bin 0 -> 20583 bytes .../python-lib/cuddlefish/templates.py | 32 + .../python-lib/cuddlefish/templates.pyc | Bin 0 -> 646 bytes .../python-lib/cuddlefish/tests/__init__.py | 52 + .../tests/addons/simplest-test/main.js | 17 + .../simplest-test/manifest-overload.json | 3 + .../tests/addons/simplest-test/package.json | 6 + .../simplest-test/tests/test-minimal.js | 7 + .../packages/explicit-icon/explicit-icon.png | 0 .../explicit-icon/explicit-icon64.png | 0 .../packages/explicit-icon/lib/main.js | 4 + .../packages/explicit-icon/package.json | 5 + .../packages/implicit-icon/icon.png | 0 .../packages/implicit-icon/icon64.png | 0 .../packages/implicit-icon/lib/main.js | 4 + .../packages/implicit-icon/package.json | 3 + .../packages/no-icon/lib/main.js | 4 + .../packages/no-icon/package.json | 3 + .../packages/bar/lib/bar-loader.js | 4 + .../packages/bar/package.json | 3 + .../packages/foo/lib/foo-loader.js | 4 + .../packages/foo/package.json | 4 + .../bug-611495-files/jspath-one/docs/main.md | 5 + .../bug-611495-files/jspath-one/lib/main.js | 8 + .../bug-611495-files/jspath-one/package.json | 5 + .../packages/commonjs-naming/doc/foo.md | 5 + .../commonjs-naming/lib/foo-loader.js | 5 + .../packages/commonjs-naming/package.json | 3 + .../packages/commonjs-naming/test/test-foo.js | 7 + .../packages/original-naming/docs/foo.md | 5 + .../original-naming/lib/foo-loader.js | 5 + .../packages/original-naming/package.json | 3 + .../original-naming/tests/test-foo.js | 7 + .../packages/default-lib/doc/foo.md | 5 + .../packages/default-lib/lib/foo.js | 5 + .../packages/default-lib/lib/loader.js | 5 + .../packages/default-lib/package.json | 3 + .../packages/default-lib/test/test-foo.js | 7 + .../default-locale/locale/emptyFolder | 0 .../packages/default-locale/package.json | 1 + .../packages/default-root/doc/foo.md | 5 + .../packages/default-root/foo.js | 5 + .../packages/default-root/loader.js | 5 + .../packages/default-root/package.json | 3 + .../packages/default-root/test/test-foo.js | 7 + .../packages/explicit-dir-lib/alt-lib/foo.js | 5 + .../explicit-dir-lib/alt-lib/loader.js | 5 + .../packages/explicit-dir-lib/doc/foo.md | 5 + .../packages/explicit-dir-lib/package.json | 4 + .../explicit-dir-lib/test/test-foo.js | 7 + .../packages/explicit-lib/alt2-lib/foo.js | 5 + .../packages/explicit-lib/alt2-lib/loader.js | 5 + .../packages/explicit-lib/doc/foo.md | 5 + .../packages/explicit-lib/package.json | 4 + .../packages/explicit-lib/test/test-foo.js | 7 + .../packages/extra-options/docs/main.md | 5 + .../packages/extra-options/lib/main.js | 8 + .../packages/extra-options/package.json | 6 + .../bug-715340-files/pkg-1-pack/package.json | 10 + .../pkg-2-unpack/package.json | 10 + .../bug-715340-files/pkg-3-pack/package.json | 9 + .../bug-906359-files/fullName/package.json | 9 + .../tests/bug-906359-files/none/package.json | 9 + .../tests/bug-906359-files/title/package.json | 9 + .../packages/foo/lib/bar-e10s-adapter.js | 11 + .../packages/foo/lib/bar.js | 5 + .../packages/foo/lib/foo.js | 5 + .../packages/foo/package.json | 1 + .../tests/linker-files/five/lib/main.js | 5 + .../tests/linker-files/five/package.json | 3 + .../linker-files/four-deps/four-a/lib/misc.js | 5 + .../four-deps/four-a/package.json | 4 + .../four-deps/four-a/topfiles/main.js | 5 + .../tests/linker-files/four/lib/main.js | 5 + .../tests/linker-files/four/package.json | 3 + .../tests/linker-files/one/lib/main.js | 9 + .../linker-files/one/lib/subdir/three.js | 6 + .../tests/linker-files/one/lib/two.js | 8 + .../tests/linker-files/one/package.json | 4 + .../tests/linker-files/seven/data/text.data | 1 + .../tests/linker-files/seven/lib/main.js | 6 + .../tests/linker-files/seven/lib/unused.js | 5 + .../tests/linker-files/seven/package.json | 4 + .../tests/linker-files/six/lib/unused.js | 5 + .../tests/linker-files/six/package.json | 3 + .../tests/linker-files/six/unreachable.js | 5 + .../three-deps/three-a/lib/main.js | 8 + .../three-deps/three-a/lib/subdir/subfile.js | 5 + .../three-deps/three-a/lib/unused.js | 5 + .../three-a/locale/fr-FR.properties | 5 + .../three-deps/three-a/package.json | 3 + .../three-deps/three-b/lib/main.js | 5 + .../three-b/locale/fr-FR.properties | 6 + .../three-deps/three-b/package.json | 3 + .../three-deps/three-c/lib/main.js | 5 + .../three-deps/three-c/lib/sub/foo.js | 6 + .../three-c/locale/fr-FR.properties | 9 + .../three-deps/three-c/package.json | 3 + .../tests/linker-files/three/data/msg.txt | 1 + .../linker-files/three/data/subdir/submsg.txt | 1 + .../tests/linker-files/three/lib/main.js | 8 + .../tests/linker-files/three/package.json | 3 + .../tests/linker-files/three/tests/nontest.js | 5 + .../linker-files/three/tests/test-one.js | 5 + .../linker-files/three/tests/test-two.js | 5 + .../packages/curly-id/lib/main.js | 4 + .../packages/curly-id/package.json | 14 + .../packages/no-prefs/lib/main.js | 4 + .../packages/no-prefs/package.json | 6 + .../packages/preferences-branch/lib/main.js | 4 + .../packages/preferences-branch/package.json | 14 + .../packages/simple-prefs/lib/main.js | 4 + .../packages/simple-prefs/package.json | 50 + .../packages/aardvark/doc/main.md | 0 .../packages/aardvark/lib/ignore_me | 3 + .../packages/aardvark/lib/main.js | 8 + .../aardvark/lib/surprise.js/ignore_me_too | 2 + .../packages/aardvark/package.json | 7 + .../packages/anteater_files/lib/main.js | 8 + .../packages/anteater_files/package.json | 8 + .../packages/api-utils/lib/loader.js | 7 + .../packages/api-utils/package.json | 5 + .../packages/barbeque/lib/bar-module.js | 7 + .../packages/barbeque/package.json | 4 + .../static-files/packages/minimal/lib/main.js | 8 + .../packages/minimal/package.json | 4 + .../packages/third_party/docs/third_party.md | 1 + .../packages/third_party/lib/third-party.js | 8 + .../packages/third_party/package.json | 7 + .../xpi-template/components/harness.js | 8 + .../python-lib/cuddlefish/tests/test_init.py | 241 + .../cuddlefish/tests/test_licenses.py | 88 + .../cuddlefish/tests/test_linker.py | 231 + .../cuddlefish/tests/test_manifest.py | 257 + .../cuddlefish/tests/test_packaging.py | 117 + .../cuddlefish/tests/test_preflight.py | 147 + .../cuddlefish/tests/test_property_parser.py | 93 + .../python-lib/cuddlefish/tests/test_rdf.py | 54 + .../cuddlefish/tests/test_runner.py | 27 + .../python-lib/cuddlefish/tests/test_util.py | 22 + .../cuddlefish/tests/test_version.py | 28 + .../python-lib/cuddlefish/tests/test_xpi.py | 516 ++ addon-sdk-1.16/python-lib/cuddlefish/util.py | 23 + addon-sdk-1.16/python-lib/cuddlefish/util.pyc | Bin 0 -> 1010 bytes .../cuddlefish/version_comparator.py | 206 + addon-sdk-1.16/python-lib/cuddlefish/xpi.py | 191 + addon-sdk-1.16/python-lib/cuddlefish/xpi.pyc | Bin 0 -> 5460 bytes addon-sdk-1.16/python-lib/jetpack_sdk_env.py | 66 + addon-sdk-1.16/python-lib/jetpack_sdk_env.pyc | Bin 0 -> 2170 bytes .../python-lib/mozrunner/__init__.py | 694 +++ .../python-lib/mozrunner/__init__.pyc | Bin 0 -> 25797 bytes .../python-lib/mozrunner/killableprocess.py | 329 ++ .../python-lib/mozrunner/killableprocess.pyc | Bin 0 -> 9371 bytes addon-sdk-1.16/python-lib/mozrunner/qijo.py | 166 + .../python-lib/mozrunner/winprocess.py | 379 ++ addon-sdk-1.16/python-lib/mozrunner/wpk.py | 80 + .../python-lib/plural-rules-generator.py | 176 + .../python-lib/simplejson/LICENSE.txt | 19 + .../python-lib/simplejson/__init__.py | 376 ++ .../python-lib/simplejson/__init__.pyc | Bin 0 -> 13942 bytes .../python-lib/simplejson/decoder.py | 343 ++ .../python-lib/simplejson/decoder.pyc | Bin 0 -> 11975 bytes .../python-lib/simplejson/encoder.py | 385 ++ .../python-lib/simplejson/encoder.pyc | Bin 0 -> 12573 bytes .../python-lib/simplejson/scanner.py | 67 + .../python-lib/simplejson/scanner.pyc | Bin 0 -> 2692 bytes addon-sdk-1.16/python-lib/simplejson/tool.py | 44 + .../test/addons/addon-page/data/index.html | 13 + addon-sdk-1.16/test/addons/addon-page/main.js | 167 + .../test/addons/addon-page/package.json | 3 + .../test/addons/chrome/chrome.manifest | 5 + .../chrome/chrome/content/new-window.xul | 4 + .../locale/en-US/description.properties | 1 + .../locale/ja-JP/description.properties | 1 + .../test/addons/chrome/chrome/skin/style.css | 1 + addon-sdk-1.16/test/addons/chrome/main.js | 68 + .../test/addons/chrome/package.json | 3 + .../test/addons/content-permissions/main.js | 88 + .../addons/content-permissions/package.json | 6 + .../test/addons/curly-id/lib/main.js | 43 + .../test/addons/curly-id/package.json | 14 + .../addons/l10n/data/test-localization.html | 24 + .../test/addons/l10n/locale/en-GB.properties | 22 + .../test/addons/l10n/locale/eo.properties | 5 + .../test/addons/l10n/locale/fr-FR.properties | 14 + addon-sdk-1.16/test/addons/l10n/main.js | 185 + addon-sdk-1.16/test/addons/l10n/package.json | 3 + .../test/addons/layout-change/main.js | 186 + .../test/addons/layout-change/package.json | 4 + addon-sdk-1.16/test/addons/main/main.js | 35 + addon-sdk-1.16/test/addons/main/package.json | 3 + addon-sdk-1.16/test/addons/packed/main.js | 20 + .../test/addons/packed/package.json | 4 + .../test/addons/places/favicon-helpers.js | 50 + addon-sdk-1.16/test/addons/places/main.js | 24 + .../test/addons/places/package.json | 3 + .../test/addons/places/places-helper.js | 237 + .../places/tests/test-places-bookmarks.js | 965 +++ .../addons/places/tests/test-places-events.js | 291 + .../places/tests/test-places-favicon.js | 188 + .../places/tests/test-places-history.js | 249 + .../addons/places/tests/test-places-host.js | 303 + .../addons/places/tests/test-places-utils.js | 79 + .../addons/predefined-id-with-at/lib/main.js | 34 + .../addons/predefined-id-with-at/package.json | 11 + .../addons/preferences-branch/lib/main.js | 35 + .../addons/preferences-branch/package.json | 14 + .../addons/private-browsing-supported/main.js | 30 + .../private-browsing-supported/package.json | 6 + .../sidebar/utils.js | 67 + .../test-global-private-browsing.js | 150 + .../test-page-mod.js | 111 + .../private-browsing-supported/test-panel.js | 97 + .../test-private-browsing.js | 164 + .../test-selection.js | 458 ++ .../test-sidebar.js | 214 + .../private-browsing-supported/test-tabs.js | 33 + .../test-window-tabs.js | 85 + .../test-windows.js | 255 + .../addons/privileged-panel/data/index.html | 3 + .../test/addons/privileged-panel/main.js | 34 + .../test/addons/privileged-panel/package.json | 3 + addon-sdk-1.16/test/addons/require/main.js | 77 + addon-sdk-1.16/test/addons/require/memory.js | 5 + .../test/addons/require/multiple/a.js | 5 + .../test/addons/require/multiple/b.js | 5 + .../test/addons/require/package.json | 5 + .../test/addons/require/packages/tabs/main.js | 5 + .../addons/require/packages/tabs/package.json | 3 + .../addons/require/packages/tabs/page-mod.js | 5 + .../test/addons/require/same-folder.js | 5 + .../test/addons/require/sub-folder/module.js | 5 + addon-sdk-1.16/test/addons/require/tabs.js | 5 + addon-sdk-1.16/test/addons/self/data/data.md | 1 + addon-sdk-1.16/test/addons/self/main.js | 20 + addon-sdk-1.16/test/addons/self/package.json | 3 + .../simple-prefs-l10n/locale/en.properties | 5 + .../test/addons/simple-prefs-l10n/main.js | 57 + .../addons/simple-prefs-l10n/package.json | 10 + .../app-extension/application.ini | 11 + .../app-extension/bootstrap.js | 337 ++ .../defaults/preferences/prefs.js | 3 + .../app-extension/install.rdf | 33 + .../app-extension/options.xul | 5 + .../simple-prefs-regression/lib/main.js | 97 + .../simple-prefs-regression/package.json | 24 + .../test/addons/simple-prefs/lib/main.js | 97 + .../test/addons/simple-prefs/package.json | 23 + .../test/addons/standard-id/lib/main.js | 44 + .../test/addons/standard-id/package.json | 14 + .../symbiont/data/test-trusted-document.html | 20 + addon-sdk-1.16/test/addons/symbiont/main.js | 39 + .../test/addons/symbiont/package.json | 3 + .../test/addons/translators/main.js | 24 + .../test/addons/translators/package.json | 6 + addon-sdk-1.16/test/addons/unpacked/main.js | 18 + .../test/addons/unpacked/package.json | 4 + .../test/buffers/test-read-types.js | 368 ++ .../test/buffers/test-write-types.js | 602 ++ .../test/commonjs-test-adapter/asserts.js | 54 + addon-sdk-1.16/test/event/helpers.js | 59 + addon-sdk-1.16/test/fixtures.js | 6 + .../addon-install-unit-test@mozilla.com.xpi | Bin 0 -> 1692 bytes .../chrome-worker/addEventListener.js | 6 + .../test/fixtures/chrome-worker/jsctypes.js | 6 + .../test/fixtures/chrome-worker/onerror.js | 6 + .../test/fixtures/chrome-worker/onmessage.js | 8 + .../test/fixtures/chrome-worker/setTimeout.js | 8 + .../test/fixtures/chrome-worker/xhr.js | 11 + addon-sdk-1.16/test/fixtures/es5.js | 8 + addon-sdk-1.16/test/fixtures/index.html | 13 + .../test/fixtures/jsm-package/Test.jsm | 11 + .../test/fixtures/jsm-package/index.js | 46 + .../test/fixtures/jsm-package/package.json | 3 + .../test/fixtures/loader/cycles/a.js | 7 + .../test/fixtures/loader/cycles/b.js | 7 + .../test/fixtures/loader/cycles/c.js | 7 + .../test/fixtures/loader/cycles/main.js | 14 + .../test/fixtures/loader/errors/boomer.js | 7 + .../test/fixtures/loader/errors/main.js | 9 + .../test/fixtures/loader/exceptions/boomer.js | 9 + .../test/fixtures/loader/exceptions/main.js | 11 + .../test/fixtures/loader/globals/main.js | 6 + .../test/fixtures/loader/json/invalid.json | 3 + .../test/fixtures/loader/json/manifest.json | 14 + .../fixtures/loader/json/nodotjson.json.js | 4 + .../test/fixtures/loader/json/test.json | 3 + .../test/fixtures/loader/json/test.json.js | 3 + .../fixtures/loader/missing-twice/file.json | 1 + .../fixtures/loader/missing-twice/main.js | 32 + .../test/fixtures/loader/missing/main.js | 10 + .../test/fixtures/loader/self/main.js | 8 + .../fixtures/loader/syntax-error/error.js | 11 + .../test/fixtures/loader/syntax-error/main.js | 10 + .../fixtures/loader/unsupported/fennec.js | 10 + .../fixtures/loader/unsupported/firefox.js | 10 + addon-sdk-1.16/test/fixtures/mofo_logo.SVG | 49 + addon-sdk-1.16/test/fixtures/moz_favicon.ico | Bin 0 -> 1406 bytes .../test/fixtures/native-addon-test/dir/a.js | 1 + .../fixtures/native-addon-test/dir/a/index.js | 1 + .../test/fixtures/native-addon-test/dir/b.js | 1 + .../test/fixtures/native-addon-test/dir/c.js | 1 + .../fixtures/native-addon-test/dir/dummy.js | 1 + .../fixtures/native-addon-test/dir/test.jsm | 2 + .../native-addon-test/expectedmap.json | 25 + .../test/fixtures/native-addon-test/index.js | 32 + .../native-addon-test/newmodule/index.js | 1 + .../native-addon-test/newmodule/lib/file.js | 1 + .../native-addon-test/newmodule/package.json | 3 + .../lib/custom-entry.js | 1 + .../test-custom-main-relative/package.json | 4 + .../test-custom-main/lib/custom-entry.js | 1 + .../test-custom-main/package.json | 4 + .../node_modules/test-default-main/index.js | 1 + .../test-default-main/package.json | 3 + .../node_modules/test-math/index.js | 3 + .../node_modules/test-math/lib/sqrt.js | 1 + .../test-math/node_modules/test-add/index.js | 1 + .../node_modules/test-add/package.json | 4 + .../node_modules/test-subtract/index.js | 1 + .../node_modules/test-subtract/package.json | 4 + .../node_modules/test-math/package.json | 8 + .../fixtures/native-addon-test/package.json | 9 + .../fixtures/native-addon-test/utils/index.js | 1 + .../fixtures/pagemod-css-include-file.css | 1 + .../fixtures/sandbox-complex-character.js | 5 + .../test/fixtures/sandbox-normal.js | 7 + .../test/fixtures/test-content-symbiont.js | 5 + .../test/fixtures/test-contentScriptFile.js | 5 + .../test/fixtures/test-context-menu.js | 5 + .../fixtures/test-iframe-postmessage.html | 20 + addon-sdk-1.16/test/fixtures/test-iframe.html | 9 + addon-sdk-1.16/test/fixtures/test-iframe.js | 13 + .../test/fixtures/test-message-manager.js | 6 + addon-sdk-1.16/test/fixtures/test-net-url.txt | 1 + .../test/fixtures/test-page-mod.html | 12 + .../test/fixtures/test-page-worker.html | 13 + .../test/fixtures/test-page-worker.js | 29 + .../fixtures/test-sidebar-addon-global.html | 10 + .../test/fixtures/test-trusted-document.html | 20 + addon-sdk-1.16/test/fixtures/test.html | 13 + .../test/fixtures/testLocalXhr.json | 1 + addon-sdk-1.16/test/loader/fixture.js | 7 + addon-sdk-1.16/test/modules/add.js | 9 + addon-sdk-1.16/test/modules/async1.js | 14 + addon-sdk-1.16/test/modules/async2.js | 8 + .../test/modules/badExportAndReturn.js | 10 + addon-sdk-1.16/test/modules/badFirst.js | 9 + addon-sdk-1.16/test/modules/badSecond.js | 8 + addon-sdk-1.16/test/modules/blue.js | 9 + addon-sdk-1.16/test/modules/castor.js | 10 + addon-sdk-1.16/test/modules/cheetah.js | 9 + addon-sdk-1.16/test/modules/color.js | 7 + addon-sdk-1.16/test/modules/dupe.js | 15 + addon-sdk-1.16/test/modules/dupeNested.js | 15 + addon-sdk-1.16/test/modules/dupeSetExports.js | 8 + addon-sdk-1.16/test/modules/exportsEquals.js | 5 + addon-sdk-1.16/test/modules/green.js | 10 + addon-sdk-1.16/test/modules/lion.js | 7 + addon-sdk-1.16/test/modules/orange.js | 10 + addon-sdk-1.16/test/modules/pollux.js | 10 + addon-sdk-1.16/test/modules/red.js | 16 + addon-sdk-1.16/test/modules/setExports.js | 5 + addon-sdk-1.16/test/modules/subtract.js | 9 + addon-sdk-1.16/test/modules/tiger.js | 8 + addon-sdk-1.16/test/modules/traditional1.js | 12 + addon-sdk-1.16/test/modules/traditional2.js | 6 + addon-sdk-1.16/test/modules/types/cat.js | 5 + addon-sdk-1.16/test/pagemod-test-helpers.js | 64 + .../test/private-browsing/global.js | 239 + .../test/private-browsing/helper.js | 100 + addon-sdk-1.16/test/private-browsing/tabs.js | 27 + .../test/private-browsing/windows.js | 139 + addon-sdk-1.16/test/sidebar/utils.js | 73 + addon-sdk-1.16/test/tabs/test-fennec-tabs.js | 584 ++ addon-sdk-1.16/test/tabs/test-firefox-tabs.js | 993 ++++ addon-sdk-1.16/test/test-addon-installer.js | 179 + addon-sdk-1.16/test/test-addon-window.js | 22 + addon-sdk-1.16/test/test-api-utils.js | 316 + addon-sdk-1.16/test/test-array.js | 103 + addon-sdk-1.16/test/test-base64.js | 75 + addon-sdk-1.16/test/test-browser-events.js | 101 + addon-sdk-1.16/test/test-buffer.js | 566 ++ addon-sdk-1.16/test/test-byte-streams.js | 169 + addon-sdk-1.16/test/test-chrome.js | 84 + addon-sdk-1.16/test/test-clipboard.js | 222 + addon-sdk-1.16/test/test-collection.js | 128 + .../test/test-commonjs-test-adapter.js | 11 + addon-sdk-1.16/test/test-content-events.js | 135 + addon-sdk-1.16/test/test-content-loader.js | 242 + addon-sdk-1.16/test/test-content-script.js | 849 +++ addon-sdk-1.16/test/test-content-symbiont.js | 160 + addon-sdk-1.16/test/test-content-worker.js | 875 +++ addon-sdk-1.16/test/test-context-menu.html | 81 + addon-sdk-1.16/test/test-context-menu.js | 4045 +++++++++++++ addon-sdk-1.16/test/test-cortex.js | 120 + addon-sdk-1.16/test/test-cuddlefish.js | 47 + addon-sdk-1.16/test/test-deprecate.js | 160 + addon-sdk-1.16/test/test-deprecated-list.js | 200 + addon-sdk-1.16/test/test-diffpatcher.js | 8 + addon-sdk-1.16/test/test-disposable.js | 222 + addon-sdk-1.16/test/test-dom.js | 88 + addon-sdk-1.16/test/test-environment.js | 50 + addon-sdk-1.16/test/test-errors.js | 72 + addon-sdk-1.16/test/test-event-core.js | 245 + addon-sdk-1.16/test/test-event-target.js | 205 + addon-sdk-1.16/test/test-event-utils.js | 282 + addon-sdk-1.16/test/test-events.js | 281 + addon-sdk-1.16/test/test-file.js | 275 + addon-sdk-1.16/test/test-frame-utils.js | 59 + addon-sdk-1.16/test/test-fs.js | 621 ++ addon-sdk-1.16/test/test-functional.js | 422 ++ addon-sdk-1.16/test/test-globals.js | 30 + addon-sdk-1.16/test/test-heritage.js | 302 + addon-sdk-1.16/test/test-hidden-frame.js | 71 + addon-sdk-1.16/test/test-host-events.js | 99 + addon-sdk-1.16/test/test-hotkeys.js | 164 + addon-sdk-1.16/test/test-httpd.js | 73 + addon-sdk-1.16/test/test-indexed-db.js | 128 + addon-sdk-1.16/test/test-keyboard-observer.js | 37 + addon-sdk-1.16/test/test-keyboard-utils.js | 62 + addon-sdk-1.16/test/test-l10n-locale.js | 143 + addon-sdk-1.16/test/test-l10n-plural-rules.js | 84 + addon-sdk-1.16/test/test-light-traits.js | 11 + addon-sdk-1.16/test/test-list.js | 58 + addon-sdk-1.16/test/test-loader.js | 346 ++ addon-sdk-1.16/test/test-match-pattern.js | 141 + addon-sdk-1.16/test/test-memory.js | 22 + addon-sdk-1.16/test/test-method.js | 7 + addon-sdk-1.16/test/test-module.js | 37 + addon-sdk-1.16/test/test-modules.js | 150 + addon-sdk-1.16/test/test-namespace.js | 121 + addon-sdk-1.16/test/test-native-loader.js | 234 + addon-sdk-1.16/test/test-net-url.js | 212 + addon-sdk-1.16/test/test-node-os.js | 33 + addon-sdk-1.16/test/test-notifications.js | 93 + addon-sdk-1.16/test/test-object.js | 36 + addon-sdk-1.16/test/test-packaging.js | 46 + addon-sdk-1.16/test/test-page-mod.js | 1202 ++++ 747 files changed, 76132 insertions(+), 5 deletions(-) create mode 100644 addon-sdk-1.16/LICENSE create mode 100644 addon-sdk-1.16/README create mode 100644 addon-sdk-1.16/app-extension/application.ini create mode 100644 addon-sdk-1.16/app-extension/bootstrap.js create mode 100644 addon-sdk-1.16/app-extension/install.rdf create mode 100644 addon-sdk-1.16/bin/activate create mode 100644 addon-sdk-1.16/bin/activate.bat create mode 100644 addon-sdk-1.16/bin/activate.fish create mode 100644 addon-sdk-1.16/bin/activate.ps1 create mode 100755 addon-sdk-1.16/bin/cfx create mode 100644 addon-sdk-1.16/bin/cfx.bat create mode 100644 addon-sdk-1.16/bin/deactivate.bat create mode 100755 addon-sdk-1.16/bin/integration-scripts/buildbot-run-cfx-helper create mode 100644 addon-sdk-1.16/bin/integration-scripts/integration-check create mode 100644 addon-sdk-1.16/examples/annotator/README.md create mode 100644 addon-sdk-1.16/examples/annotator/data/annotation/annotation.html create mode 100644 addon-sdk-1.16/examples/annotator/data/annotation/annotation.js create mode 100644 addon-sdk-1.16/examples/annotator/data/editor/annotation-editor.html create mode 100644 addon-sdk-1.16/examples/annotator/data/editor/annotation-editor.js create mode 100644 addon-sdk-1.16/examples/annotator/data/jquery-1.4.2.min.js create mode 100644 addon-sdk-1.16/examples/annotator/data/list/annotation-list.css create mode 100644 addon-sdk-1.16/examples/annotator/data/list/annotation-list.html create mode 100644 addon-sdk-1.16/examples/annotator/data/list/annotation-list.js create mode 100644 addon-sdk-1.16/examples/annotator/data/matcher.js create mode 100644 addon-sdk-1.16/examples/annotator/data/selector.js create mode 100644 addon-sdk-1.16/examples/annotator/data/widget/pencil-off.png create mode 100644 addon-sdk-1.16/examples/annotator/data/widget/pencil-on.png create mode 100644 addon-sdk-1.16/examples/annotator/data/widget/widget.js create mode 100644 addon-sdk-1.16/examples/annotator/lib/main.js create mode 100644 addon-sdk-1.16/examples/annotator/package.json create mode 100644 addon-sdk-1.16/examples/annotator/tests/test-main.js create mode 100755 addon-sdk-1.16/examples/library-detector/README.md create mode 100755 addon-sdk-1.16/examples/library-detector/data/icons/closure.ico create mode 100755 addon-sdk-1.16/examples/library-detector/data/icons/jquery.ico create mode 100755 addon-sdk-1.16/examples/library-detector/data/icons/jquery_ui.ico create mode 100755 addon-sdk-1.16/examples/library-detector/data/icons/modernizr.ico create mode 100755 addon-sdk-1.16/examples/library-detector/data/icons/mootools.png create mode 100755 addon-sdk-1.16/examples/library-detector/data/icons/yui.ico create mode 100755 addon-sdk-1.16/examples/library-detector/data/library-detector.js create mode 100644 addon-sdk-1.16/examples/library-detector/data/panel.html create mode 100755 addon-sdk-1.16/examples/library-detector/data/widget.html create mode 100755 addon-sdk-1.16/examples/library-detector/lib/main.js create mode 100755 addon-sdk-1.16/examples/library-detector/package.json create mode 100644 addon-sdk-1.16/examples/library-detector/test/test-main.js create mode 100644 addon-sdk-1.16/examples/reading-data/data/mom.png create mode 100644 addon-sdk-1.16/examples/reading-data/data/sample.html create mode 100644 addon-sdk-1.16/examples/reading-data/lib/main.js create mode 100644 addon-sdk-1.16/examples/reading-data/package.json create mode 100644 addon-sdk-1.16/examples/reading-data/tests/test-main.js create mode 100644 addon-sdk-1.16/examples/reddit-panel/README.md create mode 100644 addon-sdk-1.16/examples/reddit-panel/data/jquery-1.4.4.min.js create mode 100644 addon-sdk-1.16/examples/reddit-panel/data/panel.js create mode 100644 addon-sdk-1.16/examples/reddit-panel/lib/main.js create mode 100644 addon-sdk-1.16/examples/reddit-panel/package.json create mode 100644 addon-sdk-1.16/examples/reddit-panel/tests/test-main.js create mode 100644 addon-sdk-1.16/examples/toolbar-api/data/favicon.ico create mode 100644 addon-sdk-1.16/examples/toolbar-api/data/index.html create mode 100644 addon-sdk-1.16/examples/toolbar-api/lib/main.js create mode 100644 addon-sdk-1.16/examples/toolbar-api/package.json create mode 100644 addon-sdk-1.16/lib/diffpatcher/.travis.yml create mode 100644 addon-sdk-1.16/lib/diffpatcher/History.md create mode 100644 addon-sdk-1.16/lib/diffpatcher/License.md create mode 100644 addon-sdk-1.16/lib/diffpatcher/Readme.md create mode 100644 addon-sdk-1.16/lib/diffpatcher/diff.js create mode 100644 addon-sdk-1.16/lib/diffpatcher/index.js create mode 100644 addon-sdk-1.16/lib/diffpatcher/package.json create mode 100644 addon-sdk-1.16/lib/diffpatcher/patch.js create mode 100644 addon-sdk-1.16/lib/diffpatcher/rebase.js create mode 100644 addon-sdk-1.16/lib/diffpatcher/test/common.js create mode 100644 addon-sdk-1.16/lib/diffpatcher/test/diff.js create mode 100644 addon-sdk-1.16/lib/diffpatcher/test/index.js create mode 100644 addon-sdk-1.16/lib/diffpatcher/test/patch.js create mode 100644 addon-sdk-1.16/lib/diffpatcher/test/tap.js create mode 100644 addon-sdk-1.16/lib/main.js/main.js create mode 100644 addon-sdk-1.16/lib/method/.travis.yml create mode 100644 addon-sdk-1.16/lib/method/History.md create mode 100644 addon-sdk-1.16/lib/method/License.md create mode 100644 addon-sdk-1.16/lib/method/Readme.md create mode 100644 addon-sdk-1.16/lib/method/core.js create mode 100644 addon-sdk-1.16/lib/method/package.json create mode 100644 addon-sdk-1.16/lib/method/test/browser.js create mode 100644 addon-sdk-1.16/lib/method/test/common.js create mode 100644 addon-sdk-1.16/lib/node/os.js create mode 100644 addon-sdk-1.16/lib/sdk/addon-page.js create mode 100644 addon-sdk-1.16/lib/sdk/addon/events.js create mode 100644 addon-sdk-1.16/lib/sdk/addon/host.js create mode 100644 addon-sdk-1.16/lib/sdk/addon/installer.js create mode 100644 addon-sdk-1.16/lib/sdk/addon/runner.js create mode 100644 addon-sdk-1.16/lib/sdk/addon/window.js create mode 100644 addon-sdk-1.16/lib/sdk/base64.js create mode 100644 addon-sdk-1.16/lib/sdk/browser/events.js create mode 100644 addon-sdk-1.16/lib/sdk/clipboard.js create mode 100644 addon-sdk-1.16/lib/sdk/console/plain-text.js create mode 100644 addon-sdk-1.16/lib/sdk/console/traceback.js create mode 100644 addon-sdk-1.16/lib/sdk/content/content-worker.js create mode 100644 addon-sdk-1.16/lib/sdk/content/content.js create mode 100644 addon-sdk-1.16/lib/sdk/content/events.js create mode 100644 addon-sdk-1.16/lib/sdk/content/loader.js create mode 100644 addon-sdk-1.16/lib/sdk/content/mod.js create mode 100644 addon-sdk-1.16/lib/sdk/content/sandbox.js create mode 100644 addon-sdk-1.16/lib/sdk/content/thumbnail.js create mode 100644 addon-sdk-1.16/lib/sdk/content/utils.js create mode 100644 addon-sdk-1.16/lib/sdk/content/worker.js create mode 100644 addon-sdk-1.16/lib/sdk/context-menu.js create mode 100644 addon-sdk-1.16/lib/sdk/core/disposable.js create mode 100644 addon-sdk-1.16/lib/sdk/core/heritage.js create mode 100644 addon-sdk-1.16/lib/sdk/core/namespace.js create mode 100644 addon-sdk-1.16/lib/sdk/core/promise.js create mode 100644 addon-sdk-1.16/lib/sdk/deprecated/api-utils.js create mode 100644 addon-sdk-1.16/lib/sdk/deprecated/cortex.js create mode 100644 addon-sdk-1.16/lib/sdk/deprecated/errors.js create mode 100644 addon-sdk-1.16/lib/sdk/deprecated/events.js create mode 100644 addon-sdk-1.16/lib/sdk/deprecated/events/assembler.js create mode 100644 addon-sdk-1.16/lib/sdk/deprecated/light-traits.js create mode 100644 addon-sdk-1.16/lib/sdk/deprecated/list.js create mode 100644 addon-sdk-1.16/lib/sdk/deprecated/memory.js create mode 100644 addon-sdk-1.16/lib/sdk/deprecated/symbiont.js create mode 100644 addon-sdk-1.16/lib/sdk/deprecated/traits-worker.js create mode 100644 addon-sdk-1.16/lib/sdk/deprecated/traits.js create mode 100644 addon-sdk-1.16/lib/sdk/deprecated/traits/core.js create mode 100644 addon-sdk-1.16/lib/sdk/deprecated/unit-test-finder.js create mode 100644 addon-sdk-1.16/lib/sdk/deprecated/unit-test.js create mode 100644 addon-sdk-1.16/lib/sdk/deprecated/window-utils.js create mode 100644 addon-sdk-1.16/lib/sdk/dom/events.js create mode 100644 addon-sdk-1.16/lib/sdk/dom/events/keys.js create mode 100644 addon-sdk-1.16/lib/sdk/event/chrome.js create mode 100644 addon-sdk-1.16/lib/sdk/event/core.js create mode 100644 addon-sdk-1.16/lib/sdk/event/dom.js create mode 100644 addon-sdk-1.16/lib/sdk/event/target.js create mode 100644 addon-sdk-1.16/lib/sdk/event/utils.js create mode 100644 addon-sdk-1.16/lib/sdk/frame/hidden-frame.js create mode 100644 addon-sdk-1.16/lib/sdk/frame/utils.js create mode 100644 addon-sdk-1.16/lib/sdk/fs/path.js create mode 100644 addon-sdk-1.16/lib/sdk/hotkeys.js create mode 100644 addon-sdk-1.16/lib/sdk/indexed-db.js create mode 100644 addon-sdk-1.16/lib/sdk/input/browser.js create mode 100644 addon-sdk-1.16/lib/sdk/input/customizable-ui.js create mode 100644 addon-sdk-1.16/lib/sdk/input/frame.js create mode 100644 addon-sdk-1.16/lib/sdk/input/system.js create mode 100644 addon-sdk-1.16/lib/sdk/input/window.js create mode 100644 addon-sdk-1.16/lib/sdk/io/buffer.js create mode 100644 addon-sdk-1.16/lib/sdk/io/byte-streams.js create mode 100644 addon-sdk-1.16/lib/sdk/io/data.js create mode 100644 addon-sdk-1.16/lib/sdk/io/file.js create mode 100644 addon-sdk-1.16/lib/sdk/io/fs.js create mode 100644 addon-sdk-1.16/lib/sdk/io/stream.js create mode 100644 addon-sdk-1.16/lib/sdk/io/text-streams.js create mode 100644 addon-sdk-1.16/lib/sdk/keyboard/hotkeys.js create mode 100644 addon-sdk-1.16/lib/sdk/keyboard/observer.js create mode 100644 addon-sdk-1.16/lib/sdk/keyboard/utils.js create mode 100644 addon-sdk-1.16/lib/sdk/l10n.js create mode 100644 addon-sdk-1.16/lib/sdk/l10n/core.js create mode 100644 addon-sdk-1.16/lib/sdk/l10n/html.js create mode 100644 addon-sdk-1.16/lib/sdk/l10n/loader.js create mode 100644 addon-sdk-1.16/lib/sdk/l10n/locale.js create mode 100644 addon-sdk-1.16/lib/sdk/l10n/plural-rules.js create mode 100644 addon-sdk-1.16/lib/sdk/l10n/prefs.js create mode 100644 addon-sdk-1.16/lib/sdk/lang/functional.js create mode 100644 addon-sdk-1.16/lib/sdk/lang/type.js create mode 100644 addon-sdk-1.16/lib/sdk/lang/weak-set.js create mode 100644 addon-sdk-1.16/lib/sdk/loader/cuddlefish.js create mode 100644 addon-sdk-1.16/lib/sdk/loader/sandbox.js create mode 100644 addon-sdk-1.16/lib/sdk/net/url.js create mode 100644 addon-sdk-1.16/lib/sdk/net/xhr.js create mode 100644 addon-sdk-1.16/lib/sdk/notifications.js create mode 100644 addon-sdk-1.16/lib/sdk/output/system.js create mode 100644 addon-sdk-1.16/lib/sdk/page-mod.js create mode 100644 addon-sdk-1.16/lib/sdk/page-mod/match-pattern.js create mode 100644 addon-sdk-1.16/lib/sdk/page-worker.js create mode 100644 addon-sdk-1.16/lib/sdk/panel.js create mode 100644 addon-sdk-1.16/lib/sdk/panel/events.js create mode 100644 addon-sdk-1.16/lib/sdk/panel/utils.js create mode 100644 addon-sdk-1.16/lib/sdk/panel/window.js create mode 100644 addon-sdk-1.16/lib/sdk/passwords.js create mode 100644 addon-sdk-1.16/lib/sdk/passwords/utils.js create mode 100644 addon-sdk-1.16/lib/sdk/places/bookmarks.js create mode 100644 addon-sdk-1.16/lib/sdk/places/contract.js create mode 100644 addon-sdk-1.16/lib/sdk/places/events.js create mode 100644 addon-sdk-1.16/lib/sdk/places/favicon.js create mode 100644 addon-sdk-1.16/lib/sdk/places/history.js create mode 100644 addon-sdk-1.16/lib/sdk/places/host/host-bookmarks.js create mode 100644 addon-sdk-1.16/lib/sdk/places/host/host-query.js create mode 100644 addon-sdk-1.16/lib/sdk/places/host/host-tags.js create mode 100644 addon-sdk-1.16/lib/sdk/places/utils.js create mode 100644 addon-sdk-1.16/lib/sdk/platform/xpcom.js create mode 100644 addon-sdk-1.16/lib/sdk/preferences/event-target.js create mode 100644 addon-sdk-1.16/lib/sdk/preferences/service.js create mode 100644 addon-sdk-1.16/lib/sdk/private-browsing.js create mode 100644 addon-sdk-1.16/lib/sdk/private-browsing/utils.js create mode 100644 addon-sdk-1.16/lib/sdk/private-browsing/window/utils.js create mode 100644 addon-sdk-1.16/lib/sdk/querystring.js create mode 100644 addon-sdk-1.16/lib/sdk/request.js create mode 100644 addon-sdk-1.16/lib/sdk/selection.js create mode 100644 addon-sdk-1.16/lib/sdk/self.js create mode 100644 addon-sdk-1.16/lib/sdk/simple-prefs.js create mode 100644 addon-sdk-1.16/lib/sdk/simple-storage.js create mode 100644 addon-sdk-1.16/lib/sdk/stylesheet/style.js create mode 100644 addon-sdk-1.16/lib/sdk/stylesheet/utils.js create mode 100644 addon-sdk-1.16/lib/sdk/system.js create mode 100644 addon-sdk-1.16/lib/sdk/system/environment.js create mode 100644 addon-sdk-1.16/lib/sdk/system/events.js create mode 100644 addon-sdk-1.16/lib/sdk/system/globals.js create mode 100644 addon-sdk-1.16/lib/sdk/system/runtime.js create mode 100644 addon-sdk-1.16/lib/sdk/system/unload.js create mode 100644 addon-sdk-1.16/lib/sdk/system/xul-app.js create mode 100644 addon-sdk-1.16/lib/sdk/tab/events.js create mode 100644 addon-sdk-1.16/lib/sdk/tabs.js create mode 100644 addon-sdk-1.16/lib/sdk/tabs/common.js create mode 100644 addon-sdk-1.16/lib/sdk/tabs/events.js create mode 100644 addon-sdk-1.16/lib/sdk/tabs/helpers.js create mode 100644 addon-sdk-1.16/lib/sdk/tabs/namespace.js create mode 100644 addon-sdk-1.16/lib/sdk/tabs/observer.js create mode 100644 addon-sdk-1.16/lib/sdk/tabs/tab-fennec.js create mode 100644 addon-sdk-1.16/lib/sdk/tabs/tab-firefox.js create mode 100644 addon-sdk-1.16/lib/sdk/tabs/tab.js create mode 100644 addon-sdk-1.16/lib/sdk/tabs/tabs-firefox.js create mode 100644 addon-sdk-1.16/lib/sdk/tabs/tabs.js create mode 100644 addon-sdk-1.16/lib/sdk/tabs/utils.js create mode 100644 addon-sdk-1.16/lib/sdk/tabs/worker.js create mode 100644 addon-sdk-1.16/lib/sdk/test.js create mode 100644 addon-sdk-1.16/lib/sdk/test/assert.js create mode 100644 addon-sdk-1.16/lib/sdk/test/harness.js create mode 100644 addon-sdk-1.16/lib/sdk/test/httpd.js create mode 100644 addon-sdk-1.16/lib/sdk/test/loader.js create mode 100644 addon-sdk-1.16/lib/sdk/test/memory.js create mode 100644 addon-sdk-1.16/lib/sdk/test/runner.js create mode 100644 addon-sdk-1.16/lib/sdk/test/tmp-file.js create mode 100644 addon-sdk-1.16/lib/sdk/test/utils.js create mode 100644 addon-sdk-1.16/lib/sdk/timers.js create mode 100644 addon-sdk-1.16/lib/sdk/ui.js create mode 100644 addon-sdk-1.16/lib/sdk/ui/button/action.js create mode 100644 addon-sdk-1.16/lib/sdk/ui/button/contract.js create mode 100644 addon-sdk-1.16/lib/sdk/ui/button/toggle.js create mode 100644 addon-sdk-1.16/lib/sdk/ui/button/view.js create mode 100644 addon-sdk-1.16/lib/sdk/ui/button/view/events.js create mode 100644 addon-sdk-1.16/lib/sdk/ui/frame.js create mode 100644 addon-sdk-1.16/lib/sdk/ui/frame/model.js create mode 100644 addon-sdk-1.16/lib/sdk/ui/frame/view.html create mode 100644 addon-sdk-1.16/lib/sdk/ui/frame/view.js create mode 100644 addon-sdk-1.16/lib/sdk/ui/id.js create mode 100644 addon-sdk-1.16/lib/sdk/ui/sidebar.js create mode 100644 addon-sdk-1.16/lib/sdk/ui/sidebar/actions.js create mode 100644 addon-sdk-1.16/lib/sdk/ui/sidebar/contract.js create mode 100644 addon-sdk-1.16/lib/sdk/ui/sidebar/namespace.js create mode 100644 addon-sdk-1.16/lib/sdk/ui/sidebar/utils.js create mode 100644 addon-sdk-1.16/lib/sdk/ui/sidebar/view.js create mode 100644 addon-sdk-1.16/lib/sdk/ui/state.js create mode 100644 addon-sdk-1.16/lib/sdk/ui/state/events.js create mode 100644 addon-sdk-1.16/lib/sdk/ui/toolbar.js create mode 100644 addon-sdk-1.16/lib/sdk/ui/toolbar/model.js create mode 100644 addon-sdk-1.16/lib/sdk/ui/toolbar/view.js create mode 100644 addon-sdk-1.16/lib/sdk/url.js create mode 100644 addon-sdk-1.16/lib/sdk/url/utils.js create mode 100644 addon-sdk-1.16/lib/sdk/util/array.js create mode 100644 addon-sdk-1.16/lib/sdk/util/collection.js create mode 100644 addon-sdk-1.16/lib/sdk/util/contract.js create mode 100644 addon-sdk-1.16/lib/sdk/util/deprecate.js create mode 100644 addon-sdk-1.16/lib/sdk/util/iteration.js create mode 100644 addon-sdk-1.16/lib/sdk/util/list.js create mode 100644 addon-sdk-1.16/lib/sdk/util/match-pattern.js create mode 100644 addon-sdk-1.16/lib/sdk/util/object.js create mode 100644 addon-sdk-1.16/lib/sdk/util/registry.js create mode 100644 addon-sdk-1.16/lib/sdk/util/rules.js create mode 100644 addon-sdk-1.16/lib/sdk/util/sequence.js create mode 100644 addon-sdk-1.16/lib/sdk/util/uuid.js create mode 100644 addon-sdk-1.16/lib/sdk/view/core.js create mode 100644 addon-sdk-1.16/lib/sdk/widget.js create mode 100644 addon-sdk-1.16/lib/sdk/window/browser.js create mode 100644 addon-sdk-1.16/lib/sdk/window/events.js create mode 100644 addon-sdk-1.16/lib/sdk/window/helpers.js create mode 100644 addon-sdk-1.16/lib/sdk/window/namespace.js create mode 100644 addon-sdk-1.16/lib/sdk/window/utils.js create mode 100644 addon-sdk-1.16/lib/sdk/windows.js create mode 100644 addon-sdk-1.16/lib/sdk/windows/dom.js create mode 100644 addon-sdk-1.16/lib/sdk/windows/fennec.js create mode 100644 addon-sdk-1.16/lib/sdk/windows/firefox.js create mode 100644 addon-sdk-1.16/lib/sdk/windows/loader.js create mode 100644 addon-sdk-1.16/lib/sdk/windows/observer.js create mode 100644 addon-sdk-1.16/lib/sdk/windows/tabs-fennec.js create mode 100644 addon-sdk-1.16/lib/sdk/windows/tabs-firefox.js create mode 100644 addon-sdk-1.16/lib/sdk/worker/utils.js create mode 100644 addon-sdk-1.16/lib/test.js create mode 100644 addon-sdk-1.16/lib/toolkit/loader.js create mode 100644 addon-sdk-1.16/mapping.json create mode 100644 addon-sdk-1.16/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/__init__.py create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/__init__.pyc create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/_version.py create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/_version.pyc create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/bunch.py create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/bunch.pyc create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/manifest.py create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/manifest.pyc create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/mobile-utils/bootstrap.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/mobile-utils/install.rdf create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/options_defaults.py create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/options_xul.py create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/packaging.py create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/packaging.pyc create mode 100755 addon-sdk-1.16/python-lib/cuddlefish/preflight.py create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/preflight.pyc create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/prefs.py create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/prefs.pyc create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/property_parser.py create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/rdf.py create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/rdf.pyc create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/runner.py create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/runner.pyc create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/templates.py create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/templates.pyc create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/__init__.py create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/addons/simplest-test/main.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/addons/simplest-test/manifest-overload.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/addons/simplest-test/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/addons/simplest-test/tests/test-minimal.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/explicit-icon.png create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/explicit-icon64.png create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/lib/main.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-588119-files/packages/explicit-icon/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/icon.png create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/icon64.png create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/lib/main.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-588119-files/packages/implicit-icon/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-588119-files/packages/no-icon/lib/main.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-588119-files/packages/no-icon/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-588661-files/packages/bar/lib/bar-loader.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-588661-files/packages/bar/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-588661-files/packages/foo/lib/foo-loader.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-588661-files/packages/foo/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/docs/main.md create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/lib/main.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-611495-files/jspath-one/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/doc/foo.md create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/lib/foo-loader.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-614712-files/packages/commonjs-naming/test/test-foo.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/docs/foo.md create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/lib/foo-loader.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-614712-files/packages/original-naming/tests/test-foo.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/doc/foo.md create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/lib/foo.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/lib/loader.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-652227-files/packages/default-lib/test/test-foo.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-652227-files/packages/default-locale/locale/emptyFolder create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-652227-files/packages/default-locale/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/doc/foo.md create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/foo.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/loader.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-652227-files/packages/default-root/test/test-foo.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/alt-lib/foo.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/alt-lib/loader.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/doc/foo.md create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-dir-lib/test/test-foo.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/alt2-lib/foo.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/alt2-lib/loader.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/doc/foo.md create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-652227-files/packages/explicit-lib/test/test-foo.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/docs/main.md create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/lib/main.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-669274-files/packages/extra-options/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-715340-files/pkg-1-pack/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-715340-files/pkg-2-unpack/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-715340-files/pkg-3-pack/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-906359-files/fullName/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-906359-files/none/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/bug-906359-files/title/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/bar-e10s-adapter.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/bar.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/lib/foo.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/e10s-adapter-files/packages/foo/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/five/lib/main.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/five/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/lib/misc.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/four-deps/four-a/topfiles/main.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/four/lib/main.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/four/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/one/lib/main.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/one/lib/subdir/three.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/one/lib/two.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/one/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/seven/data/text.data create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/seven/lib/main.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/seven/lib/unused.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/seven/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/six/lib/unused.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/six/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/six/unreachable.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/main.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/subdir/subfile.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/lib/unused.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/locale/fr-FR.properties create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/three-deps/three-a/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/three-deps/three-b/lib/main.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/three-deps/three-b/locale/fr-FR.properties create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/three-deps/three-b/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/lib/main.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/lib/sub/foo.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/locale/fr-FR.properties create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/three-deps/three-c/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/three/data/msg.txt create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/three/data/subdir/submsg.txt create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/three/lib/main.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/three/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/three/tests/nontest.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/three/tests/test-one.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/linker-files/three/tests/test-two.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/preferences-files/packages/curly-id/lib/main.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/preferences-files/packages/curly-id/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/preferences-files/packages/no-prefs/lib/main.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/preferences-files/packages/no-prefs/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/preferences-files/packages/preferences-branch/lib/main.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/preferences-files/packages/preferences-branch/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/preferences-files/packages/simple-prefs/lib/main.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/preferences-files/packages/simple-prefs/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/static-files/packages/aardvark/doc/main.md create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/ignore_me create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/main.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/static-files/packages/aardvark/lib/surprise.js/ignore_me_too create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/static-files/packages/aardvark/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/static-files/packages/anteater_files/lib/main.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/static-files/packages/anteater_files/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/static-files/packages/api-utils/lib/loader.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/static-files/packages/api-utils/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/static-files/packages/barbeque/lib/bar-module.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/static-files/packages/barbeque/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/static-files/packages/minimal/lib/main.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/static-files/packages/minimal/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/static-files/packages/third_party/docs/third_party.md create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/static-files/packages/third_party/lib/third-party.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/static-files/packages/third_party/package.json create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/static-files/xpi-template/components/harness.js create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/test_init.py create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/test_licenses.py create mode 100755 addon-sdk-1.16/python-lib/cuddlefish/tests/test_linker.py create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/test_manifest.py create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/test_packaging.py create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/test_preflight.py create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/test_property_parser.py create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/test_rdf.py create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/test_runner.py create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/test_util.py create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/test_version.py create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/tests/test_xpi.py create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/util.py create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/util.pyc create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/version_comparator.py create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/xpi.py create mode 100644 addon-sdk-1.16/python-lib/cuddlefish/xpi.pyc create mode 100644 addon-sdk-1.16/python-lib/jetpack_sdk_env.py create mode 100644 addon-sdk-1.16/python-lib/jetpack_sdk_env.pyc create mode 100644 addon-sdk-1.16/python-lib/mozrunner/__init__.py create mode 100644 addon-sdk-1.16/python-lib/mozrunner/__init__.pyc create mode 100644 addon-sdk-1.16/python-lib/mozrunner/killableprocess.py create mode 100644 addon-sdk-1.16/python-lib/mozrunner/killableprocess.pyc create mode 100644 addon-sdk-1.16/python-lib/mozrunner/qijo.py create mode 100644 addon-sdk-1.16/python-lib/mozrunner/winprocess.py create mode 100644 addon-sdk-1.16/python-lib/mozrunner/wpk.py create mode 100644 addon-sdk-1.16/python-lib/plural-rules-generator.py create mode 100644 addon-sdk-1.16/python-lib/simplejson/LICENSE.txt create mode 100644 addon-sdk-1.16/python-lib/simplejson/__init__.py create mode 100644 addon-sdk-1.16/python-lib/simplejson/__init__.pyc create mode 100644 addon-sdk-1.16/python-lib/simplejson/decoder.py create mode 100644 addon-sdk-1.16/python-lib/simplejson/decoder.pyc create mode 100644 addon-sdk-1.16/python-lib/simplejson/encoder.py create mode 100644 addon-sdk-1.16/python-lib/simplejson/encoder.pyc create mode 100644 addon-sdk-1.16/python-lib/simplejson/scanner.py create mode 100644 addon-sdk-1.16/python-lib/simplejson/scanner.pyc create mode 100644 addon-sdk-1.16/python-lib/simplejson/tool.py create mode 100644 addon-sdk-1.16/test/addons/addon-page/data/index.html create mode 100644 addon-sdk-1.16/test/addons/addon-page/main.js create mode 100644 addon-sdk-1.16/test/addons/addon-page/package.json create mode 100644 addon-sdk-1.16/test/addons/chrome/chrome.manifest create mode 100644 addon-sdk-1.16/test/addons/chrome/chrome/content/new-window.xul create mode 100644 addon-sdk-1.16/test/addons/chrome/chrome/locale/en-US/description.properties create mode 100644 addon-sdk-1.16/test/addons/chrome/chrome/locale/ja-JP/description.properties create mode 100644 addon-sdk-1.16/test/addons/chrome/chrome/skin/style.css create mode 100644 addon-sdk-1.16/test/addons/chrome/main.js create mode 100644 addon-sdk-1.16/test/addons/chrome/package.json create mode 100644 addon-sdk-1.16/test/addons/content-permissions/main.js create mode 100644 addon-sdk-1.16/test/addons/content-permissions/package.json create mode 100644 addon-sdk-1.16/test/addons/curly-id/lib/main.js create mode 100644 addon-sdk-1.16/test/addons/curly-id/package.json create mode 100644 addon-sdk-1.16/test/addons/l10n/data/test-localization.html create mode 100644 addon-sdk-1.16/test/addons/l10n/locale/en-GB.properties create mode 100644 addon-sdk-1.16/test/addons/l10n/locale/eo.properties create mode 100644 addon-sdk-1.16/test/addons/l10n/locale/fr-FR.properties create mode 100644 addon-sdk-1.16/test/addons/l10n/main.js create mode 100644 addon-sdk-1.16/test/addons/l10n/package.json create mode 100644 addon-sdk-1.16/test/addons/layout-change/main.js create mode 100644 addon-sdk-1.16/test/addons/layout-change/package.json create mode 100644 addon-sdk-1.16/test/addons/main/main.js create mode 100644 addon-sdk-1.16/test/addons/main/package.json create mode 100644 addon-sdk-1.16/test/addons/packed/main.js create mode 100644 addon-sdk-1.16/test/addons/packed/package.json create mode 100644 addon-sdk-1.16/test/addons/places/favicon-helpers.js create mode 100644 addon-sdk-1.16/test/addons/places/main.js create mode 100644 addon-sdk-1.16/test/addons/places/package.json create mode 100644 addon-sdk-1.16/test/addons/places/places-helper.js create mode 100644 addon-sdk-1.16/test/addons/places/tests/test-places-bookmarks.js create mode 100644 addon-sdk-1.16/test/addons/places/tests/test-places-events.js create mode 100644 addon-sdk-1.16/test/addons/places/tests/test-places-favicon.js create mode 100644 addon-sdk-1.16/test/addons/places/tests/test-places-history.js create mode 100644 addon-sdk-1.16/test/addons/places/tests/test-places-host.js create mode 100644 addon-sdk-1.16/test/addons/places/tests/test-places-utils.js create mode 100644 addon-sdk-1.16/test/addons/predefined-id-with-at/lib/main.js create mode 100644 addon-sdk-1.16/test/addons/predefined-id-with-at/package.json create mode 100644 addon-sdk-1.16/test/addons/preferences-branch/lib/main.js create mode 100644 addon-sdk-1.16/test/addons/preferences-branch/package.json create mode 100644 addon-sdk-1.16/test/addons/private-browsing-supported/main.js create mode 100644 addon-sdk-1.16/test/addons/private-browsing-supported/package.json create mode 100644 addon-sdk-1.16/test/addons/private-browsing-supported/sidebar/utils.js create mode 100644 addon-sdk-1.16/test/addons/private-browsing-supported/test-global-private-browsing.js create mode 100644 addon-sdk-1.16/test/addons/private-browsing-supported/test-page-mod.js create mode 100644 addon-sdk-1.16/test/addons/private-browsing-supported/test-panel.js create mode 100644 addon-sdk-1.16/test/addons/private-browsing-supported/test-private-browsing.js create mode 100644 addon-sdk-1.16/test/addons/private-browsing-supported/test-selection.js create mode 100644 addon-sdk-1.16/test/addons/private-browsing-supported/test-sidebar.js create mode 100644 addon-sdk-1.16/test/addons/private-browsing-supported/test-tabs.js create mode 100644 addon-sdk-1.16/test/addons/private-browsing-supported/test-window-tabs.js create mode 100644 addon-sdk-1.16/test/addons/private-browsing-supported/test-windows.js create mode 100644 addon-sdk-1.16/test/addons/privileged-panel/data/index.html create mode 100644 addon-sdk-1.16/test/addons/privileged-panel/main.js create mode 100644 addon-sdk-1.16/test/addons/privileged-panel/package.json create mode 100644 addon-sdk-1.16/test/addons/require/main.js create mode 100644 addon-sdk-1.16/test/addons/require/memory.js create mode 100644 addon-sdk-1.16/test/addons/require/multiple/a.js create mode 100644 addon-sdk-1.16/test/addons/require/multiple/b.js create mode 100644 addon-sdk-1.16/test/addons/require/package.json create mode 100644 addon-sdk-1.16/test/addons/require/packages/tabs/main.js create mode 100644 addon-sdk-1.16/test/addons/require/packages/tabs/package.json create mode 100644 addon-sdk-1.16/test/addons/require/packages/tabs/page-mod.js create mode 100644 addon-sdk-1.16/test/addons/require/same-folder.js create mode 100644 addon-sdk-1.16/test/addons/require/sub-folder/module.js create mode 100644 addon-sdk-1.16/test/addons/require/tabs.js create mode 100644 addon-sdk-1.16/test/addons/self/data/data.md create mode 100644 addon-sdk-1.16/test/addons/self/main.js create mode 100644 addon-sdk-1.16/test/addons/self/package.json create mode 100644 addon-sdk-1.16/test/addons/simple-prefs-l10n/locale/en.properties create mode 100644 addon-sdk-1.16/test/addons/simple-prefs-l10n/main.js create mode 100644 addon-sdk-1.16/test/addons/simple-prefs-l10n/package.json create mode 100644 addon-sdk-1.16/test/addons/simple-prefs-regression/app-extension/application.ini create mode 100644 addon-sdk-1.16/test/addons/simple-prefs-regression/app-extension/bootstrap.js create mode 100644 addon-sdk-1.16/test/addons/simple-prefs-regression/app-extension/defaults/preferences/prefs.js create mode 100644 addon-sdk-1.16/test/addons/simple-prefs-regression/app-extension/install.rdf create mode 100644 addon-sdk-1.16/test/addons/simple-prefs-regression/app-extension/options.xul create mode 100644 addon-sdk-1.16/test/addons/simple-prefs-regression/lib/main.js create mode 100644 addon-sdk-1.16/test/addons/simple-prefs-regression/package.json create mode 100644 addon-sdk-1.16/test/addons/simple-prefs/lib/main.js create mode 100644 addon-sdk-1.16/test/addons/simple-prefs/package.json create mode 100644 addon-sdk-1.16/test/addons/standard-id/lib/main.js create mode 100644 addon-sdk-1.16/test/addons/standard-id/package.json create mode 100644 addon-sdk-1.16/test/addons/symbiont/data/test-trusted-document.html create mode 100644 addon-sdk-1.16/test/addons/symbiont/main.js create mode 100644 addon-sdk-1.16/test/addons/symbiont/package.json create mode 100644 addon-sdk-1.16/test/addons/translators/main.js create mode 100644 addon-sdk-1.16/test/addons/translators/package.json create mode 100644 addon-sdk-1.16/test/addons/unpacked/main.js create mode 100644 addon-sdk-1.16/test/addons/unpacked/package.json create mode 100644 addon-sdk-1.16/test/buffers/test-read-types.js create mode 100644 addon-sdk-1.16/test/buffers/test-write-types.js create mode 100644 addon-sdk-1.16/test/commonjs-test-adapter/asserts.js create mode 100644 addon-sdk-1.16/test/event/helpers.js create mode 100644 addon-sdk-1.16/test/fixtures.js create mode 100644 addon-sdk-1.16/test/fixtures/addon-install-unit-test@mozilla.com.xpi create mode 100644 addon-sdk-1.16/test/fixtures/chrome-worker/addEventListener.js create mode 100644 addon-sdk-1.16/test/fixtures/chrome-worker/jsctypes.js create mode 100644 addon-sdk-1.16/test/fixtures/chrome-worker/onerror.js create mode 100644 addon-sdk-1.16/test/fixtures/chrome-worker/onmessage.js create mode 100644 addon-sdk-1.16/test/fixtures/chrome-worker/setTimeout.js create mode 100644 addon-sdk-1.16/test/fixtures/chrome-worker/xhr.js create mode 100644 addon-sdk-1.16/test/fixtures/es5.js create mode 100644 addon-sdk-1.16/test/fixtures/index.html create mode 100644 addon-sdk-1.16/test/fixtures/jsm-package/Test.jsm create mode 100644 addon-sdk-1.16/test/fixtures/jsm-package/index.js create mode 100644 addon-sdk-1.16/test/fixtures/jsm-package/package.json create mode 100644 addon-sdk-1.16/test/fixtures/loader/cycles/a.js create mode 100644 addon-sdk-1.16/test/fixtures/loader/cycles/b.js create mode 100644 addon-sdk-1.16/test/fixtures/loader/cycles/c.js create mode 100644 addon-sdk-1.16/test/fixtures/loader/cycles/main.js create mode 100644 addon-sdk-1.16/test/fixtures/loader/errors/boomer.js create mode 100644 addon-sdk-1.16/test/fixtures/loader/errors/main.js create mode 100644 addon-sdk-1.16/test/fixtures/loader/exceptions/boomer.js create mode 100644 addon-sdk-1.16/test/fixtures/loader/exceptions/main.js create mode 100644 addon-sdk-1.16/test/fixtures/loader/globals/main.js create mode 100644 addon-sdk-1.16/test/fixtures/loader/json/invalid.json create mode 100644 addon-sdk-1.16/test/fixtures/loader/json/manifest.json create mode 100644 addon-sdk-1.16/test/fixtures/loader/json/nodotjson.json.js create mode 100644 addon-sdk-1.16/test/fixtures/loader/json/test.json create mode 100644 addon-sdk-1.16/test/fixtures/loader/json/test.json.js create mode 100644 addon-sdk-1.16/test/fixtures/loader/missing-twice/file.json create mode 100644 addon-sdk-1.16/test/fixtures/loader/missing-twice/main.js create mode 100644 addon-sdk-1.16/test/fixtures/loader/missing/main.js create mode 100644 addon-sdk-1.16/test/fixtures/loader/self/main.js create mode 100644 addon-sdk-1.16/test/fixtures/loader/syntax-error/error.js create mode 100644 addon-sdk-1.16/test/fixtures/loader/syntax-error/main.js create mode 100644 addon-sdk-1.16/test/fixtures/loader/unsupported/fennec.js create mode 100644 addon-sdk-1.16/test/fixtures/loader/unsupported/firefox.js create mode 100644 addon-sdk-1.16/test/fixtures/mofo_logo.SVG create mode 100644 addon-sdk-1.16/test/fixtures/moz_favicon.ico create mode 100644 addon-sdk-1.16/test/fixtures/native-addon-test/dir/a.js create mode 100644 addon-sdk-1.16/test/fixtures/native-addon-test/dir/a/index.js create mode 100644 addon-sdk-1.16/test/fixtures/native-addon-test/dir/b.js create mode 100644 addon-sdk-1.16/test/fixtures/native-addon-test/dir/c.js create mode 100644 addon-sdk-1.16/test/fixtures/native-addon-test/dir/dummy.js create mode 100644 addon-sdk-1.16/test/fixtures/native-addon-test/dir/test.jsm create mode 100644 addon-sdk-1.16/test/fixtures/native-addon-test/expectedmap.json create mode 100644 addon-sdk-1.16/test/fixtures/native-addon-test/index.js create mode 100644 addon-sdk-1.16/test/fixtures/native-addon-test/newmodule/index.js create mode 100644 addon-sdk-1.16/test/fixtures/native-addon-test/newmodule/lib/file.js create mode 100644 addon-sdk-1.16/test/fixtures/native-addon-test/newmodule/package.json create mode 100644 addon-sdk-1.16/test/fixtures/native-addon-test/node_modules/test-custom-main-relative/lib/custom-entry.js create mode 100644 addon-sdk-1.16/test/fixtures/native-addon-test/node_modules/test-custom-main-relative/package.json create mode 100644 addon-sdk-1.16/test/fixtures/native-addon-test/node_modules/test-custom-main/lib/custom-entry.js create mode 100644 addon-sdk-1.16/test/fixtures/native-addon-test/node_modules/test-custom-main/package.json create mode 100644 addon-sdk-1.16/test/fixtures/native-addon-test/node_modules/test-default-main/index.js create mode 100644 addon-sdk-1.16/test/fixtures/native-addon-test/node_modules/test-default-main/package.json create mode 100644 addon-sdk-1.16/test/fixtures/native-addon-test/node_modules/test-math/index.js create mode 100644 addon-sdk-1.16/test/fixtures/native-addon-test/node_modules/test-math/lib/sqrt.js create mode 100644 addon-sdk-1.16/test/fixtures/native-addon-test/node_modules/test-math/node_modules/test-add/index.js create mode 100644 addon-sdk-1.16/test/fixtures/native-addon-test/node_modules/test-math/node_modules/test-add/package.json create mode 100644 addon-sdk-1.16/test/fixtures/native-addon-test/node_modules/test-math/node_modules/test-subtract/index.js create mode 100644 addon-sdk-1.16/test/fixtures/native-addon-test/node_modules/test-math/node_modules/test-subtract/package.json create mode 100644 addon-sdk-1.16/test/fixtures/native-addon-test/node_modules/test-math/package.json create mode 100644 addon-sdk-1.16/test/fixtures/native-addon-test/package.json create mode 100644 addon-sdk-1.16/test/fixtures/native-addon-test/utils/index.js create mode 100644 addon-sdk-1.16/test/fixtures/pagemod-css-include-file.css create mode 100644 addon-sdk-1.16/test/fixtures/sandbox-complex-character.js create mode 100644 addon-sdk-1.16/test/fixtures/sandbox-normal.js create mode 100644 addon-sdk-1.16/test/fixtures/test-content-symbiont.js create mode 100644 addon-sdk-1.16/test/fixtures/test-contentScriptFile.js create mode 100644 addon-sdk-1.16/test/fixtures/test-context-menu.js create mode 100644 addon-sdk-1.16/test/fixtures/test-iframe-postmessage.html create mode 100644 addon-sdk-1.16/test/fixtures/test-iframe.html create mode 100644 addon-sdk-1.16/test/fixtures/test-iframe.js create mode 100644 addon-sdk-1.16/test/fixtures/test-message-manager.js create mode 100644 addon-sdk-1.16/test/fixtures/test-net-url.txt create mode 100644 addon-sdk-1.16/test/fixtures/test-page-mod.html create mode 100644 addon-sdk-1.16/test/fixtures/test-page-worker.html create mode 100644 addon-sdk-1.16/test/fixtures/test-page-worker.js create mode 100644 addon-sdk-1.16/test/fixtures/test-sidebar-addon-global.html create mode 100644 addon-sdk-1.16/test/fixtures/test-trusted-document.html create mode 100644 addon-sdk-1.16/test/fixtures/test.html create mode 100644 addon-sdk-1.16/test/fixtures/testLocalXhr.json create mode 100644 addon-sdk-1.16/test/loader/fixture.js create mode 100644 addon-sdk-1.16/test/modules/add.js create mode 100644 addon-sdk-1.16/test/modules/async1.js create mode 100644 addon-sdk-1.16/test/modules/async2.js create mode 100644 addon-sdk-1.16/test/modules/badExportAndReturn.js create mode 100644 addon-sdk-1.16/test/modules/badFirst.js create mode 100644 addon-sdk-1.16/test/modules/badSecond.js create mode 100644 addon-sdk-1.16/test/modules/blue.js create mode 100644 addon-sdk-1.16/test/modules/castor.js create mode 100644 addon-sdk-1.16/test/modules/cheetah.js create mode 100644 addon-sdk-1.16/test/modules/color.js create mode 100644 addon-sdk-1.16/test/modules/dupe.js create mode 100644 addon-sdk-1.16/test/modules/dupeNested.js create mode 100644 addon-sdk-1.16/test/modules/dupeSetExports.js create mode 100644 addon-sdk-1.16/test/modules/exportsEquals.js create mode 100644 addon-sdk-1.16/test/modules/green.js create mode 100644 addon-sdk-1.16/test/modules/lion.js create mode 100644 addon-sdk-1.16/test/modules/orange.js create mode 100644 addon-sdk-1.16/test/modules/pollux.js create mode 100644 addon-sdk-1.16/test/modules/red.js create mode 100644 addon-sdk-1.16/test/modules/setExports.js create mode 100644 addon-sdk-1.16/test/modules/subtract.js create mode 100644 addon-sdk-1.16/test/modules/tiger.js create mode 100644 addon-sdk-1.16/test/modules/traditional1.js create mode 100644 addon-sdk-1.16/test/modules/traditional2.js create mode 100644 addon-sdk-1.16/test/modules/types/cat.js create mode 100644 addon-sdk-1.16/test/pagemod-test-helpers.js create mode 100644 addon-sdk-1.16/test/private-browsing/global.js create mode 100644 addon-sdk-1.16/test/private-browsing/helper.js create mode 100644 addon-sdk-1.16/test/private-browsing/tabs.js create mode 100644 addon-sdk-1.16/test/private-browsing/windows.js create mode 100644 addon-sdk-1.16/test/sidebar/utils.js create mode 100644 addon-sdk-1.16/test/tabs/test-fennec-tabs.js create mode 100644 addon-sdk-1.16/test/tabs/test-firefox-tabs.js create mode 100644 addon-sdk-1.16/test/test-addon-installer.js create mode 100644 addon-sdk-1.16/test/test-addon-window.js create mode 100644 addon-sdk-1.16/test/test-api-utils.js create mode 100644 addon-sdk-1.16/test/test-array.js create mode 100644 addon-sdk-1.16/test/test-base64.js create mode 100644 addon-sdk-1.16/test/test-browser-events.js create mode 100644 addon-sdk-1.16/test/test-buffer.js create mode 100644 addon-sdk-1.16/test/test-byte-streams.js create mode 100644 addon-sdk-1.16/test/test-chrome.js create mode 100644 addon-sdk-1.16/test/test-clipboard.js create mode 100644 addon-sdk-1.16/test/test-collection.js create mode 100644 addon-sdk-1.16/test/test-commonjs-test-adapter.js create mode 100644 addon-sdk-1.16/test/test-content-events.js create mode 100644 addon-sdk-1.16/test/test-content-loader.js create mode 100644 addon-sdk-1.16/test/test-content-script.js create mode 100644 addon-sdk-1.16/test/test-content-symbiont.js create mode 100644 addon-sdk-1.16/test/test-content-worker.js create mode 100644 addon-sdk-1.16/test/test-context-menu.html create mode 100644 addon-sdk-1.16/test/test-context-menu.js create mode 100644 addon-sdk-1.16/test/test-cortex.js create mode 100644 addon-sdk-1.16/test/test-cuddlefish.js create mode 100644 addon-sdk-1.16/test/test-deprecate.js create mode 100644 addon-sdk-1.16/test/test-deprecated-list.js create mode 100644 addon-sdk-1.16/test/test-diffpatcher.js create mode 100644 addon-sdk-1.16/test/test-disposable.js create mode 100644 addon-sdk-1.16/test/test-dom.js create mode 100644 addon-sdk-1.16/test/test-environment.js create mode 100644 addon-sdk-1.16/test/test-errors.js create mode 100644 addon-sdk-1.16/test/test-event-core.js create mode 100644 addon-sdk-1.16/test/test-event-target.js create mode 100644 addon-sdk-1.16/test/test-event-utils.js create mode 100644 addon-sdk-1.16/test/test-events.js create mode 100644 addon-sdk-1.16/test/test-file.js create mode 100644 addon-sdk-1.16/test/test-frame-utils.js create mode 100644 addon-sdk-1.16/test/test-fs.js create mode 100644 addon-sdk-1.16/test/test-functional.js create mode 100644 addon-sdk-1.16/test/test-globals.js create mode 100644 addon-sdk-1.16/test/test-heritage.js create mode 100644 addon-sdk-1.16/test/test-hidden-frame.js create mode 100644 addon-sdk-1.16/test/test-host-events.js create mode 100644 addon-sdk-1.16/test/test-hotkeys.js create mode 100644 addon-sdk-1.16/test/test-httpd.js create mode 100644 addon-sdk-1.16/test/test-indexed-db.js create mode 100644 addon-sdk-1.16/test/test-keyboard-observer.js create mode 100644 addon-sdk-1.16/test/test-keyboard-utils.js create mode 100644 addon-sdk-1.16/test/test-l10n-locale.js create mode 100644 addon-sdk-1.16/test/test-l10n-plural-rules.js create mode 100644 addon-sdk-1.16/test/test-light-traits.js create mode 100644 addon-sdk-1.16/test/test-list.js create mode 100644 addon-sdk-1.16/test/test-loader.js create mode 100644 addon-sdk-1.16/test/test-match-pattern.js create mode 100644 addon-sdk-1.16/test/test-memory.js create mode 100644 addon-sdk-1.16/test/test-method.js create mode 100644 addon-sdk-1.16/test/test-module.js create mode 100644 addon-sdk-1.16/test/test-modules.js create mode 100644 addon-sdk-1.16/test/test-namespace.js create mode 100644 addon-sdk-1.16/test/test-native-loader.js create mode 100644 addon-sdk-1.16/test/test-net-url.js create mode 100644 addon-sdk-1.16/test/test-node-os.js create mode 100644 addon-sdk-1.16/test/test-notifications.js create mode 100644 addon-sdk-1.16/test/test-object.js create mode 100644 addon-sdk-1.16/test/test-packaging.js create mode 100644 addon-sdk-1.16/test/test-page-mod.js diff --git a/Gruntfile.js b/Gruntfile.js index 1ece9735..397c230e 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -5,7 +5,9 @@ var fs = require('fs'), module.exports = function (grunt){ grunt.loadNpmTasks('grunt-contrib-handlebars'); + grunt.loadNpmTasks('grunt-contrib-concat'); grunt.loadNpmTasks('grunt-contrib-copy'); + grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-version'); // Project configuration. @@ -14,8 +16,8 @@ module.exports = function (grunt){ watch : { scripts: { - files : ['lib/*'], - tasks : ['default'], + files : ['lib/**'], + tasks : ['firefox'], options: { nospawn: true } @@ -30,8 +32,38 @@ module.exports = function (grunt){ src: ['manifests/opera.json', 'manifests/chrome.json'] } }, + concat : { + options: { + separator: ';\n\n' + }, + firefox: { + src : grunt.file.readJSON("manifests/firefox.json").background.scripts, + dest: './firefox/twitch-now/lib/main.js' + } + }, copy : { - chrome: { + firefox: { + files: [ + { + expand : true, + src : 'lib/3rd/*.js', + dest : './firefox/twitch-now/lib/3rd', + flatten: true + }, + { + expand : true, + src : ['lib/main.js', 'lib/i18n-ff.js'], + dest : './firefox/twitch-now/lib', + flatten: true + }, + { + expand: true, + src : ["./_locales/**", "./dist/**", "./html/**", "./icons/**", "./css/**", "./lib/**"], + dest : './firefox/twitch-now/data' + } + ] + }, + chrome : { expand : true, src : 'manifests/chrome.json', dest : './', @@ -40,7 +72,7 @@ module.exports = function (grunt){ return dest + "manifest.json"; } }, - opera : { + opera : { expand : true, src : 'manifests/opera.json', dest : './', @@ -68,8 +100,17 @@ module.exports = function (grunt){ }); + grunt.registerTask('i18n', function (){ + var localesObj = {}; + var locales = fs.readdirSync("_locales"); + for ( var i = 0; i < locales.length; i++ ) { + var file = fs.readFileSync(__dirname + "/_locales/" + locales[i] + "/messages.json"); + localesObj[locales[i]] = JSON.parse(file); + } + fs.writeFileSync("dist/locales.json", JSON.stringify(localesObj, null, 2), "utf8"); + }); - grunt.registerTask('zip', '', function (){ + grunt.registerTask('zip', function (){ var done = this.async(); var zip = exec(' zip -r twitch_now.zip ./chrome-platform-analytics/google-analytics-bundle.js ./_locales/* ./audio/* ./lib/* ./oauth2/* ./icons/* ./css/* ./dist/* ./manifest.json ./html/* ', function (error, stdout, stderr){ console.log('stdout: ' + stdout); @@ -101,6 +142,7 @@ module.exports = function (grunt){ grunt.registerTask('default', 'copy:chrome handlebars'.split(' ')); grunt.registerTask('opera', 'bump version copy:opera handlebars'.split(' ')); + grunt.registerTask('firefox', 'i18n handlebars copy:firefox'.split(' ')); grunt.registerTask('chrome', 'bump version copy:chrome handlebars'.split(' ')); grunt.registerTask('prod', 'zip'.split(' ')); }; \ No newline at end of file diff --git a/addon-sdk-1.16/LICENSE b/addon-sdk-1.16/LICENSE new file mode 100644 index 00000000..22e1dc91 --- /dev/null +++ b/addon-sdk-1.16/LICENSE @@ -0,0 +1,30 @@ +The files which make up the SDK are developed by Mozilla and licensed +under the MPL 2.0 (http://mozilla.org/MPL/2.0/), with the exception of the +components listed below, which are made available by their authors under +the licenses listed alongside. + +syntaxhighlighter +------------------ +doc/static-files/syntaxhighlighter +Made available under the MIT license. + +jQuery +------ +examples/reddit-panel/data/jquery-1.4.4.min.js +examples/annotator/data/jquery-1.4.2.min.js +Made available under the MIT license. + +simplejson +---------- +python-lib/simplejson +Made available under the MIT license. + +Python Markdown +--------------- +python-lib/markdown +Made available under the BSD license. + +LibraryDetector +--------------- +examples/library-detector/data/library-detector.js +Made available under the MIT license. diff --git a/addon-sdk-1.16/README b/addon-sdk-1.16/README new file mode 100644 index 00000000..8c1c360e --- /dev/null +++ b/addon-sdk-1.16/README @@ -0,0 +1,41 @@ +Add-on SDK README +================== + +Before proceeding, please make sure you've installed Python 2.5, +2.6, or 2.7 (if it's not already on your system): + + http://python.org/download/ + +Note that Python 3 is not supported. + +For Windows users, MozillaBuild (https://wiki.mozilla.org/MozillaBuild) +will install the correct version of Python and the MSYS package, which +will make it easier to work with the SDK. + +To get started, first enter the same directory that this README file +is in (the SDK's root directory) using a shell program. On Unix systems +or on Windows with MSYS, you can execute the following command: + + source bin/activate + +Windows users using cmd.exe should instead run: + + bin\activate.bat + +Then go to https://developer.mozilla.org/en-US/Add-ons/SDK/ +to browse the SDK documentation. + +If you get an error when running cfx or have any other problems getting +started, see the "Troubleshooting" guide at: +https://developer.mozilla.org/en-US/Add-ons/SDK/Tutorials/Troubleshooting + +Bugs +------- + +* file a bug: https://bugzilla.mozilla.org/enter_bug.cgi?product=Add-on%20SDK + + +Style Guidelines +-------------------- + +* https://github.com/mozilla/addon-sdk/wiki/Coding-style-guide diff --git a/addon-sdk-1.16/app-extension/application.ini b/addon-sdk-1.16/app-extension/application.ini new file mode 100644 index 00000000..6cec69a1 --- /dev/null +++ b/addon-sdk-1.16/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/addon-sdk-1.16/app-extension/bootstrap.js b/addon-sdk-1.16/app-extension/bootstrap.js new file mode 100644 index 00000000..d430d70e --- /dev/null +++ b/addon-sdk-1.16/app-extension/bootstrap.js @@ -0,0 +1,339 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// @see http://mxr.mozilla.org/mozilla-central/source/js/src/xpconnect/loader/mozJSComponentLoader.cpp + +'use strict'; + +// IMPORTANT: Avoid adding any initialization tasks here, if you need to do +// something before add-on is loaded consider addon/runner module instead! + +const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu, + results: Cr, manager: Cm } = Components; +const ioService = Cc['@mozilla.org/network/io-service;1']. + getService(Ci.nsIIOService); +const resourceHandler = ioService.getProtocolHandler('resource'). + QueryInterface(Ci.nsIResProtocolHandler); +const systemPrincipal = CC('@mozilla.org/systemprincipal;1', 'nsIPrincipal')(); +const scriptLoader = Cc['@mozilla.org/moz/jssubscript-loader;1']. + getService(Ci.mozIJSSubScriptLoader); +const prefService = Cc['@mozilla.org/preferences-service;1']. + getService(Ci.nsIPrefService). + QueryInterface(Ci.nsIPrefBranch); +const appInfo = Cc["@mozilla.org/xre/app-info;1"]. + getService(Ci.nsIXULAppInfo); +const vc = Cc["@mozilla.org/xpcom/version-comparator;1"]. + getService(Ci.nsIVersionComparator); + + +const REASON = [ 'unknown', 'startup', 'shutdown', 'enable', 'disable', + 'install', 'uninstall', 'upgrade', 'downgrade' ]; + +const bind = Function.call.bind(Function.bind); + +let loader = null; +let unload = null; +let cuddlefishSandbox = null; +let nukeTimer = null; + +// Utility function that synchronously reads local resource from the given +// `uri` and returns content string. +function readURI(uri) { + let ioservice = Cc['@mozilla.org/network/io-service;1']. + getService(Ci.nsIIOService); + let channel = ioservice.newChannel(uri, 'UTF-8', null); + let stream = channel.open(); + + let cstream = Cc['@mozilla.org/intl/converter-input-stream;1']. + createInstance(Ci.nsIConverterInputStream); + cstream.init(stream, 'UTF-8', 0, 0); + + let str = {}; + let data = ''; + let read = 0; + do { + read = cstream.readString(0xffffffff, str); + data += str.value; + } while (read != 0); + + cstream.close(); + + return data; +} + +// We don't do anything on install & uninstall yet, but in a future +// we should allow add-ons to cleanup after uninstall. +function install(data, reason) {} +function uninstall(data, reason) {} + +function startup(data, reasonCode) { + try { + let reason = REASON[reasonCode]; + // URI for the root of the XPI file. + // 'jar:' URI if the addon is packed, 'file:' URI otherwise. + // (Used by l10n module in order to fetch `locale` folder) + let rootURI = data.resourceURI.spec; + + // TODO: Maybe we should perform read harness-options.json asynchronously, + // since we can't do anything until 'sessionstore-windows-restored' anyway. + let options = JSON.parse(readURI(rootURI + './harness-options.json')); + + let id = options.jetpackID; + let name = options.name; + + // Clean the metadata + options.metadata[name]['permissions'] = options.metadata[name]['permissions'] || {}; + + // freeze the permissionss + Object.freeze(options.metadata[name]['permissions']); + // freeze the metadata + Object.freeze(options.metadata[name]); + + // Register a new resource 'domain' for this addon which is mapping to + // XPI's `resources` folder. + // Generate the domain name by using jetpack ID, which is the extension ID + // by stripping common characters that doesn't work as a domain name: + let uuidRe = + /^\{([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\}$/; + + let domain = id. + toLowerCase(). + replace(/@/g, '-at-'). + replace(/\./g, '-dot-'). + replace(uuidRe, '$1'); + + let prefixURI = 'resource://' + domain + '/'; + let resourcesURI = ioService.newURI(rootURI + '/resources/', null, null); + resourceHandler.setSubstitution(domain, resourcesURI); + + // Create path to URLs mapping supported by loader. + let paths = { + // Relative modules resolve to add-on package lib + './': prefixURI + name + '/lib/', + './tests/': prefixURI + name + '/tests/', + '': 'resource://gre/modules/commonjs/' + }; + + // Maps addon lib and tests ressource folders for each package + paths = Object.keys(options.metadata).reduce(function(result, name) { + result[name + '/'] = prefixURI + name + '/lib/' + result[name + '/tests/'] = prefixURI + name + '/tests/' + return result; + }, paths); + + // We need to map tests folder when we run sdk tests whose package name + // is stripped + if (name == 'addon-sdk') + paths['tests/'] = prefixURI + name + '/tests/'; + + let useBundledSDK = options['force-use-bundled-sdk']; + if (!useBundledSDK) { + try { + useBundledSDK = prefService.getBoolPref("extensions.addon-sdk.useBundledSDK"); + } + catch (e) { + // Pref doesn't exist, allow using Firefox shipped SDK + } + } + + // Starting with Firefox 21.0a1, we start using modules shipped into firefox + // Still allow using modules from the xpi if the manifest tell us to do so. + // And only try to look for sdk modules in xpi if the xpi actually ship them + if (options['is-sdk-bundled'] && + (vc.compare(appInfo.version, '21.0a1') < 0 || useBundledSDK)) { + // Maps sdk module folders to their resource folder + paths[''] = prefixURI + 'addon-sdk/lib/'; + // test.js is usually found in root commonjs or SDK_ROOT/lib/ folder, + // so that it isn't shipped in the xpi. Keep a copy of it in sdk/ folder + // until we no longer support SDK modules in XPI: + paths['test'] = prefixURI + 'addon-sdk/lib/sdk/test.js'; + } + + // Retrieve list of module folder overloads based on preferences in order to + // eventually used a local modules instead of files shipped into Firefox. + let branch = prefService.getBranch('extensions.modules.' + id + '.path'); + paths = branch.getChildList('', {}).reduce(function (result, name) { + // Allows overloading of any sub folder by replacing . by / in pref name + let path = name.substr(1).split('.').join('/'); + // Only accept overloading folder by ensuring always ending with `/` + if (path) path += '/'; + let fileURI = branch.getCharPref(name); + + // On mobile, file URI has to end with a `/` otherwise, setSubstitution + // takes the parent folder instead. + if (fileURI[fileURI.length-1] !== '/') + fileURI += '/'; + + // Maps the given file:// URI to a resource:// in order to avoid various + // failure that happens with file:// URI and be close to production env + let resourcesURI = ioService.newURI(fileURI, null, null); + let resName = 'extensions.modules.' + domain + '.commonjs.path' + name; + resourceHandler.setSubstitution(resName, resourcesURI); + + result[path] = 'resource://' + resName + '/'; + return result; + }, paths); + + // Make version 2 of the manifest + let manifest = options.manifest; + + // Import `cuddlefish.js` module using a Sandbox and bootstrap loader. + let cuddlefishPath = 'loader/cuddlefish.js'; + let cuddlefishURI = 'resource://gre/modules/commonjs/sdk/' + cuddlefishPath; + if (paths['sdk/']) { // sdk folder has been overloaded + // (from pref, or cuddlefish is still in the xpi) + cuddlefishURI = paths['sdk/'] + cuddlefishPath; + } + else if (paths['']) { // root modules folder has been overloaded + cuddlefishURI = paths[''] + 'sdk/' + cuddlefishPath; + } + + cuddlefishSandbox = loadSandbox(cuddlefishURI); + let cuddlefish = cuddlefishSandbox.exports; + + // Normalize `options.mainPath` so that it looks like one that will come + // in a new version of linker. + let main = options.mainPath; + + unload = cuddlefish.unload; + loader = cuddlefish.Loader({ + paths: paths, + // modules manifest. + manifest: manifest, + + // Add-on ID used by different APIs as a unique identifier. + id: id, + // Add-on name. + name: name, + // Add-on version. + version: options.metadata[name].version, + // Add-on package descriptor. + metadata: options.metadata[name], + // Add-on load reason. + loadReason: reason, + + prefixURI: prefixURI, + // Add-on URI. + rootURI: rootURI, + // options used by system module. + // File to write 'OK' or 'FAIL' (exit code emulation). + resultFile: options.resultFile, + // Arguments passed as --static-args + staticArgs: options.staticArgs, + // Add-on preferences branch name + preferencesBranch: options.preferencesBranch, + + // Arguments related to test runner. + modules: { + '@test/options': { + allTestModules: options.allTestModules, + iterations: options.iterations, + filter: options.filter, + profileMemory: options.profileMemory, + stopOnError: options.stopOnError, + verbose: options.verbose, + parseable: options.parseable, + checkMemory: options.check_memory, + } + } + }); + + let module = cuddlefish.Module('sdk/loader/cuddlefish', cuddlefishURI); + let require = cuddlefish.Require(loader, module); + + require('sdk/addon/runner').startup(reason, { + loader: loader, + main: main, + prefsURI: rootURI + 'defaults/preferences/prefs.js' + }); + } catch (error) { + dump('Bootstrap error: ' + + (error.message ? error.message : String(error)) + '\n' + + (error.stack || error.fileName + ': ' + error.lineNumber) + '\n'); + throw error; + } +}; + +function loadSandbox(uri) { + let proto = { + sandboxPrototype: { + loadSandbox: loadSandbox, + ChromeWorker: ChromeWorker + } + }; + let sandbox = Cu.Sandbox(systemPrincipal, proto); + // Create a fake commonjs environnement just to enable loading loader.js + // correctly + sandbox.exports = {}; + sandbox.module = { uri: uri, exports: sandbox.exports }; + sandbox.require = function (id) { + if (id !== "chrome") + throw new Error("Bootstrap sandbox `require` method isn't implemented."); + + return Object.freeze({ Cc: Cc, Ci: Ci, Cu: Cu, Cr: Cr, Cm: Cm, + CC: bind(CC, Components), components: Components, + ChromeWorker: ChromeWorker }); + }; + scriptLoader.loadSubScript(uri, sandbox, 'UTF-8'); + return sandbox; +} + +function unloadSandbox(sandbox) { + if ("nukeSandbox" in Cu) + Cu.nukeSandbox(sandbox); +} + +function setTimeout(callback, delay) { + let timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer); + timer.initWithCallback({ notify: callback }, delay, + Ci.nsITimer.TYPE_ONE_SHOT); + return timer; +} + +function shutdown(data, reasonCode) { + let reason = REASON[reasonCode]; + if (loader) { + unload(loader, reason); + unload = null; + + // Don't waste time cleaning up if the application is shutting down + if (reason != "shutdown") { + // Avoid leaking all modules when something goes wrong with one particular + // module. Do not clean it up immediatly in order to allow executing some + // actions on addon disabling. + // We need to keep a reference to the timer, otherwise it is collected + // and won't ever fire. + nukeTimer = setTimeout(nukeModules, 1000); + } + } +}; + +function nukeModules() { + nukeTimer = null; + // module objects store `exports` which comes from sandboxes + // We should avoid keeping link to these object to avoid leaking sandboxes + for (let key in loader.modules) { + delete loader.modules[key]; + } + // Direct links to sandboxes should be removed too + for (let key in loader.sandboxes) { + let sandbox = loader.sandboxes[key]; + delete loader.sandboxes[key]; + // Bug 775067: From FF17 we can kill all CCW from a given sandbox + unloadSandbox(sandbox); + } + loader = null; + + // both `toolkit/loader` and `system/xul-app` are loaded as JSM's via + // `cuddlefish.js`, and needs to be unloaded to avoid memory leaks, when + // the addon is unload. + + unloadSandbox(cuddlefishSandbox.loaderSandbox); + unloadSandbox(cuddlefishSandbox.xulappSandbox); + + // Bug 764840: We need to unload cuddlefish otherwise it will stay alive + // and keep a reference to this compartment. + unloadSandbox(cuddlefishSandbox); + cuddlefishSandbox = null; +} diff --git a/addon-sdk-1.16/app-extension/install.rdf b/addon-sdk-1.16/app-extension/install.rdf new file mode 100644 index 00000000..c4bab078 --- /dev/null +++ b/addon-sdk-1.16/app-extension/install.rdf @@ -0,0 +1,33 @@ + + + + + + + xulapp@toolness.com + 1.0 + 2 + true + false + + + + + {ec8030f7-c20a-464f-9b0e-13a3a9e97384} + 21.0 + 29.0a1 + + + + + Test App + Harness for tests. + Mozilla Corporation + + + + + diff --git a/addon-sdk-1.16/bin/activate b/addon-sdk-1.16/bin/activate new file mode 100644 index 00000000..0104f7d9 --- /dev/null +++ b/addon-sdk-1.16/bin/activate @@ -0,0 +1,84 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# This file must be used with "source bin/activate" *from bash* +# you cannot run it directly + +deactivate () { + if [ -n "$_OLD_VIRTUAL_PATH" ] ; then + PATH="$_OLD_VIRTUAL_PATH" + export PATH + unset _OLD_VIRTUAL_PATH + fi + + # This should detect bash and zsh, which have a hash command that must + # be called to get it to forget past commands. Without forgetting + # past commands the $PATH changes we made may not be respected + if [ -n "$BASH" -o -n "$ZSH_VERSION" ] ; then + hash -r + fi + + if [ -n "$_OLD_VIRTUAL_PS1" ] ; then + PS1="$_OLD_VIRTUAL_PS1" + export PS1 + unset _OLD_VIRTUAL_PS1 + fi + + PYTHONPATH="$_OLD_PYTHONPATH" + export PYTHONPATH + unset _OLD_PYTHONPATH + + unset CUDDLEFISH_ROOT + + unset VIRTUAL_ENV + if [ ! "$1" = "nondestructive" ] ; then + # Self destruct! + unset deactivate + fi +} + +# unset irrelavent variables +deactivate nondestructive + +_OLD_PYTHONPATH="$PYTHONPATH" +_OLD_VIRTUAL_PATH="$PATH" + +VIRTUAL_ENV="`pwd`" + +if [ "x$OSTYPE" = "xmsys" ] ; then + CUDDLEFISH_ROOT="`pwd -W | sed s,/,\\\\\\\\,g`" + PATH="`pwd`/bin:$PATH" + # msys will convert any env vars with PATH in it to use msys + # form and will unconvert before launching + PYTHONPATH="`pwd -W`/python-lib;$PYTHONPATH" +else + CUDDLEFISH_ROOT="$VIRTUAL_ENV" + PYTHONPATH="$VIRTUAL_ENV/python-lib:$PYTHONPATH" + PATH="$VIRTUAL_ENV/bin:$PATH" +fi + +VIRTUAL_ENV="`pwd`" + +export CUDDLEFISH_ROOT +export PYTHONPATH +export PATH + +_OLD_VIRTUAL_PS1="$PS1" +if [ "`basename \"$VIRTUAL_ENV\"`" = "__" ] ; then + # special case for Aspen magic directories + # see http://www.zetadev.com/software/aspen/ + PS1="[`basename \`dirname \"$VIRTUAL_ENV\"\``] $PS1" +else + PS1="(`basename \"$VIRTUAL_ENV\"`)$PS1" +fi +export PS1 + +# This should detect bash and zsh, which have a hash command that must +# be called to get it to forget past commands. Without forgetting +# past commands the $PATH changes we made may not be respected +if [ -n "$BASH" -o -n "$ZSH_VERSION" ] ; then + hash -r +fi + +python -c "from jetpack_sdk_env import welcome; welcome()" diff --git a/addon-sdk-1.16/bin/activate.bat b/addon-sdk-1.16/bin/activate.bat new file mode 100644 index 00000000..7d1f968a --- /dev/null +++ b/addon-sdk-1.16/bin/activate.bat @@ -0,0 +1,134 @@ +@echo off +rem This Source Code Form is subject to the terms of the Mozilla Public +rem License, v. 2.0. If a copy of the MPL was not distributed with this +rem file, You can obtain one at http://mozilla.org/MPL/2.0/. + +set VIRTUAL_ENV=%~dp0 +set VIRTUAL_ENV=%VIRTUAL_ENV:~0,-5% +set CUDDLEFISH_ROOT=%VIRTUAL_ENV% + +SET PYTHONKEY=SOFTWARE\Python\PythonCore + +rem look for 32-bit windows and python, or 64-bit windows and python + +SET PYTHONVERSION=2.7 +call:CheckPython PYTHONINSTALL %PYTHONKEY%\%PYTHONVERSION%\InstallPath +if "%PYTHONINSTALL%" NEQ "" goto FoundPython + +SET PYTHONVERSION=2.6 +call:CheckPython PYTHONINSTALL %PYTHONKEY%\%PYTHONVERSION%\InstallPath +if "%PYTHONINSTALL%" NEQ "" goto FoundPython + +SET PYTHONVERSION=2.5 +call:CheckPython PYTHONINSTALL %PYTHONKEY%\%PYTHONVERSION%\InstallPath +if "%PYTHONINSTALL%" NEQ "" goto FoundPython + +if not defined ProgramFiles(x86) goto win32 + +rem look for 32-bit python on 64-bit windows + +SET PYTHONKEY=SOFTWARE\Wow6432Node\Python\PythonCore + +SET PYTHONVERSION=2.7 +call:CheckPython PYTHONINSTALL %PYTHONKEY%\%PYTHONVERSION%\InstallPath +if "%PYTHONINSTALL%" NEQ "" goto FoundPython + +SET PYTHONVERSION=2.6 +call:CheckPython PYTHONINSTALL %PYTHONKEY%\%PYTHONVERSION%\InstallPath +if "%PYTHONINSTALL%" NEQ "" goto FoundPython + +SET PYTHONVERSION=2.5 +call:CheckPython PYTHONINSTALL %PYTHONKEY%\%PYTHONVERSION%\InstallPath +if "%PYTHONINSTALL%" NEQ "" goto FoundPython + +:win32 + +SET PYTHONVERSION= +set PYTHONKEY= +echo Warning: Failed to find Python installation directory +goto :EOF + +:FoundPython + +if defined _OLD_PYTHONPATH ( + set PYTHONPATH=%_OLD_PYTHONPATH% +) +if not defined PYTHONPATH ( + set PYTHONPATH=; +) +set _OLD_PYTHONPATH=%PYTHONPATH% +set PYTHONPATH=%VIRTUAL_ENV%\python-lib;%PYTHONPATH% + +if not defined PROMPT ( + set PROMPT=$P$G +) + +if defined _OLD_VIRTUAL_PROMPT ( + set PROMPT=%_OLD_VIRTUAL_PROMPT% +) + +set _OLD_VIRTUAL_PROMPT=%PROMPT% +set PROMPT=(%VIRTUAL_ENV%) %PROMPT% + +if defined _OLD_VIRTUAL_PATH goto OLDPATH +goto SKIPPATH +:OLDPATH +PATH %_OLD_VIRTUAL_PATH% + +:SKIPPATH +set _OLD_VIRTUAL_PATH=%PATH% +PATH %VIRTUAL_ENV%\bin;%PYTHONINSTALL%;%PATH% +set PYTHONKEY= +set PYTHONINSTALL= +set PYTHONVERSION= +set key= +set reg= +set _tokens= +python -c "from jetpack_sdk_env import welcome; welcome()" +GOTO :EOF + +:CheckPython +::CheckPython(retVal, key) +::Reads the registry at %2% and checks if a Python exists there. +::Checks both HKLM and HKCU, then checks the executable actually exists. +SET key=%2% +SET "%~1=" +SET reg=reg +if defined ProgramFiles(x86) ( + rem 32-bit cmd on 64-bit windows + if exist %WINDIR%\sysnative\reg.exe SET reg=%WINDIR%\sysnative\reg.exe +) +rem On Vista+, the last line of output is: +rem (default) REG_SZ the_value +rem (but note the word "default" will be localized. +rem On XP, the last line of output is: +rem \tREG_SZ\tthe_value +rem (not sure if "NO NAME" is localized or not!) +rem SO: we use ")>" as the tokens to split on, then nuke +rem the REG_SZ and any tabs or spaces. +FOR /F "usebackq tokens=2 delims=)>" %%A IN (`%reg% QUERY HKLM\%key% /ve 2^>NUL`) DO SET "%~1=%%A" +rem Remove the REG_SZ +set PYTHONINSTALL=%PYTHONINSTALL:REG_SZ=% +rem Remove tabs (note the literal \t in the next line +set PYTHONINSTALL=%PYTHONINSTALL: =% +rem Remove spaces. +set PYTHONINSTALL=%PYTHONINSTALL: =% +if exist %PYTHONINSTALL%\python.exe goto :EOF +rem It may be a 32bit Python directory built from source, in which case the +rem executable is in the PCBuild directory. +if exist %PYTHONINSTALL%\PCBuild\python.exe (set "PYTHONINSTALL=%PYTHONINSTALL%\PCBuild" & goto :EOF) +rem Or maybe a 64bit build directory. +if exist %PYTHONINSTALL%\PCBuild\amd64\python.exe (set "PYTHONINSTALL=%PYTHONINSTALL%\PCBuild\amd64" & goto :EOF) + +rem And try HKCU +FOR /F "usebackq tokens=2 delims=)>" %%A IN (`%reg% QUERY HKCU\%key% /ve 2^>NUL`) DO SET "%~1=%%A" +set PYTHONINSTALL=%PYTHONINSTALL:REG_SZ=% +set PYTHONINSTALL=%PYTHONINSTALL: =% +set PYTHONINSTALL=%PYTHONINSTALL: =% +if exist %PYTHONINSTALL%\python.exe goto :EOF +if exist %PYTHONINSTALL%\PCBuild\python.exe (set "PYTHONINSTALL=%PYTHONINSTALL%\PCBuild" & goto :EOF) +if exist %PYTHONINSTALL%\PCBuild\amd64\python.exe (set "PYTHONINSTALL=%PYTHONINSTALL%\PCBuild\amd64" & goto :EOF) +rem can't find it here, so arrange to try the next key +set PYTHONINSTALL= + +GOTO :EOF diff --git a/addon-sdk-1.16/bin/activate.fish b/addon-sdk-1.16/bin/activate.fish new file mode 100644 index 00000000..1f728b69 --- /dev/null +++ b/addon-sdk-1.16/bin/activate.fish @@ -0,0 +1,66 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# This file must be used with "source bin/activate.fish" *from fish* +# you cannot run it directly + +# Much of this code is based off of the activate.fish file for the +# virtualenv project. http://ur1.ca/ehmd6 + +function deactivate -d "Exit addon-sdk and return to normal shell environment" + if test -n "$_OLD_VIRTUAL_PATH" + set -gx PATH $_OLD_VIRTUAL_PATH + set -e _OLD_VIRTUAL_PATH + end + + if test -n "$_OLD_PYTHONPATH" + set -gx PYTHONPATH $_OLD_PYTHONPATH + set -e _OLD_PYTHONPATH + end + + if test -n "$_OLD_FISH_PROMPT_OVERRIDE" + functions -e fish_prompt + set -e _OLD_FISH_PROMPT_OVERRIDE + . ( begin + printf "function fish_prompt\n\t#" + functions _old_fish_prompt + end | psub ) + + functions -e _old_fish_prompt + end + + set -e CUDDLEFISH_ROOT + set -e VIRTUAL_ENV + + if test "$argv[1]" != "nondestructive" + functions -e deactivate + end +end + +# unset irrelavent variables +deactivate nondestructive + +set -gx _OLD_PYTHONPATH $PYTHONPATH +set -gx _OLD_VIRTUAL_PATH $PATH +set -gx _OLD_FISH_PROMPT_OVERRIDE "true" + +set VIRTUAL_ENV (pwd) + +set -gx CUDDLEFISH_ROOT $VIRTUAL_ENV +set -gx PYTHONPATH "$VIRTUAL_ENV/python-lib" $PYTHONPATH +set -gx PATH "$VIRTUAL_ENV/bin" $PATH + +# save the current fish_prompt function as the function _old_fish_prompt +. ( begin + printf "function _old_fish_prompt\n\t#" + functions fish_prompt + end | psub ) + +# with the original prompt function renamed, we can override with our own. +function fish_prompt + printf "(%s)%s%s" (basename "$VIRTUAL_ENV") (set_color normal) (_old_fish_prompt) + return +end + +python -c "from jetpack_sdk_env import welcome; welcome()" diff --git a/addon-sdk-1.16/bin/activate.ps1 b/addon-sdk-1.16/bin/activate.ps1 new file mode 100644 index 00000000..5d939d46 --- /dev/null +++ b/addon-sdk-1.16/bin/activate.ps1 @@ -0,0 +1,99 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +$Env:VIRTUAL_ENV = (gl); +$Env:CUDDLEFISH_ROOT = $Env:VIRTUAL_ENV; + +# http://stackoverflow.com/questions/5648931/powershell-test-if-registry-value-exists/5652674#5652674 +Function Test-RegistryValue { + param( + [Alias("PSPath")] + [Parameter(Position = 0, Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)] + [String]$Path + , + [Parameter(Position = 1, Mandatory = $true)] + [String]$Name + , + [Switch]$PassThru + ) + + process { + if (Test-Path $Path) { + $Key = Get-Item -LiteralPath $Path + if ($Key.GetValue($Name, $null) -ne $null) { + if ($PassThru) { + Get-ItemProperty $Path $Name + } else { + $true + } + } else { + $false + } + } else { + $false + } + } +} + +$WINCURVERKEY = 'HKLM:SOFTWARE\Microsoft\Windows\CurrentVersion'; +$WIN64 = (Test-RegistryValue $WINCURVERKEY 'ProgramFilesDir (x86)'); + +if($WIN64) { + $PYTHONKEY='HKLM:SOFTWARE\Wow6432Node\Python\PythonCore'; +} +else { + $PYTHONKEY='HKLM:SOFTWARE\Python\PythonCore'; +} + +$Env:PYTHONVERSION = ''; +$Env:PYTHONINSTALL = ''; + +foreach ($version in @('2.6', '2.5', '2.4')) { + if (Test-RegistryValue "$PYTHONKEY\$version\InstallPath" '(default)') { + $Env:PYTHONVERSION = $version; + } +} + +if ($Env:PYTHONVERSION) { + $Env:PYTHONINSTALL = (Get-Item "$PYTHONKEY\$version\InstallPath)").'(default)'; +} + +if ($Env:PYTHONINSTALL) { + $Env:Path += ";$Env:PYTHONINSTALL"; +} + +if (Test-Path Env:_OLD_PYTHONPATH) { + $Env:PYTHONPATH = $Env:_OLD_PYTHONPATH; +} +else { + $Env:PYTHONPATH = ''; +} + +$Env:_OLD_PYTHONPATH=$Env:PYTHONPATH; +$Env:PYTHONPATH= "$Env:VIRTUAL_ENV\python-lib;$Env:PYTHONPATH"; + +if (Test-Path Function:_OLD_VIRTUAL_PROMPT) { + Set-Content Function:Prompt (Get-Content Function:_OLD_VIRTUAL_PROMPT); +} +else { + function global:_OLD_VIRTUAL_PROMPT {} +} + +Set-Content Function:_OLD_VIRTUAL_PROMPT (Get-Content Function:Prompt); + +function global:prompt { "($Env:VIRTUAL_ENV) $(_OLD_VIRTUAL_PROMPT)"; }; + +if (Test-Path Env:_OLD_VIRTUAL_PATH) { + $Env:PATH = $Env:_OLD_VIRTUAL_PATH; +} +else { + $Env:_OLD_VIRTUAL_PATH = $Env:PATH; +} + +$Env:Path="$Env:VIRTUAL_ENV\bin;$Env:Path" + +[System.Console]::WriteLine("Note: this PowerShell SDK activation script is experimental.") + +python -c "from jetpack_sdk_env import welcome; welcome()" + diff --git a/addon-sdk-1.16/bin/cfx b/addon-sdk-1.16/bin/cfx new file mode 100755 index 00000000..3e781faf --- /dev/null +++ b/addon-sdk-1.16/bin/cfx @@ -0,0 +1,33 @@ +#! /usr/bin/env python +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +import os +import sys + +# set the cuddlefish "root directory" for this process if it's not already +# set in the environment +cuddlefish_root = os.path.dirname(os.path.dirname(os.path.realpath(sys.argv[0]))) + +if 'CUDDLEFISH_ROOT' not in os.environ: + os.environ['CUDDLEFISH_ROOT'] = cuddlefish_root + +# add our own python-lib path to the python module search path. +python_lib_dir = os.path.join(cuddlefish_root, "python-lib") +if python_lib_dir not in sys.path: + sys.path.insert(0, python_lib_dir) + +# now export to env so sub-processes get it too +if 'PYTHONPATH' not in os.environ: + os.environ['PYTHONPATH'] = python_lib_dir +elif python_lib_dir not in os.environ['PYTHONPATH'].split(os.pathsep): + paths = os.environ['PYTHONPATH'].split(os.pathsep) + paths.insert(0, python_lib_dir) + os.environ['PYTHONPATH'] = os.pathsep.join(paths) + +import cuddlefish + +if __name__ == '__main__': + cuddlefish.run() diff --git a/addon-sdk-1.16/bin/cfx.bat b/addon-sdk-1.16/bin/cfx.bat new file mode 100644 index 00000000..215b034f --- /dev/null +++ b/addon-sdk-1.16/bin/cfx.bat @@ -0,0 +1,6 @@ +@echo off +rem This Source Code Form is subject to the terms of the Mozilla Public +rem License, v. 2.0. If a copy of the MPL was not distributed with this +rem file, You can obtain one at http://mozilla.org/MPL/2.0/. + +python "%VIRTUAL_ENV%\bin\cfx" %* diff --git a/addon-sdk-1.16/bin/deactivate.bat b/addon-sdk-1.16/bin/deactivate.bat new file mode 100644 index 00000000..e6bcd929 --- /dev/null +++ b/addon-sdk-1.16/bin/deactivate.bat @@ -0,0 +1,23 @@ +@echo off +rem This Source Code Form is subject to the terms of the Mozilla Public +rem License, v. 2.0. If a copy of the MPL was not distributed with this +rem file, You can obtain one at http://mozilla.org/MPL/2.0/. + +if defined _OLD_VIRTUAL_PROMPT ( + set "PROMPT=%_OLD_VIRTUAL_PROMPT%" +) +set _OLD_VIRTUAL_PROMPT= + +if defined _OLD_VIRTUAL_PATH ( + set "PATH=%_OLD_VIRTUAL_PATH%" +) +set _OLD_VIRTUAL_PATH= + +if defined _OLD_PYTHONPATH ( + set "PYTHONPATH=%_OLD_PYTHONPATH%" +) +set _OLD_PYTHONPATH= + +set CUDDLEFISH_ROOT= + +:END diff --git a/addon-sdk-1.16/bin/integration-scripts/buildbot-run-cfx-helper b/addon-sdk-1.16/bin/integration-scripts/buildbot-run-cfx-helper new file mode 100755 index 00000000..56c76ad3 --- /dev/null +++ b/addon-sdk-1.16/bin/integration-scripts/buildbot-run-cfx-helper @@ -0,0 +1,14 @@ +#!/bin/bash +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + + +source ./bin/activate +if [ type -P xvfb-run ] +then + xvfb-run cfx $* +else + cfx $* +fi +deactivate diff --git a/addon-sdk-1.16/bin/integration-scripts/integration-check b/addon-sdk-1.16/bin/integration-scripts/integration-check new file mode 100644 index 00000000..2505c15d --- /dev/null +++ b/addon-sdk-1.16/bin/integration-scripts/integration-check @@ -0,0 +1,364 @@ +#!/usr/bin/env python +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +import os +import signal +import threading +import urllib2, urllib +import zipfile +import tarfile +import subprocess +import optparse +import sys, re +#import win32api + + +class SDK: + def __init__(self): + try: + # Take the current working directory + self.default_path = os.getcwd() + if sys.platform == "win32": + self.mswindows = True + else: + self.mswindows = False + # Take the default home path of the user. + home = os.path.expanduser('~') + + # The following are the parameters that can be used to pass a dynamic URL, a specific path or a binry. The binary is not used yet. It will be used in version 2.0 + # If a dynamic path is to be mentioned, it should start with a '/'. For eg. "/Desktop" + parser = optparse.OptionParser() + parser.add_option('-u', '--url', dest = 'url', default = 'https://ftp.mozilla.org/pub/mozilla.org/labs/jetpack/addon-sdk-latest.zip') + parser.add_option('-p', '--path', dest = 'path', default = self.default_path) + parser.add_option('-b', '--binary', dest = 'binary')#, default='/Applications/Firefox.app') + (options, args) = parser.parse_args() + + # Get the URL from the parameter + self.link = options.url + # Set the base path for the user. If the user supplies the path, use the home variable as well. Else, take the default path of this script as the installation directory. + if options.path!=self.default_path: + if self.mswindows: + self.base_path = home + str(options.path).strip() + '\\' + else: + self.base_path = home + str(options.path).strip() + '/' + else: + if self.mswindows: + self.base_path = str(options.path).strip() + '\\' + else: + self.base_path = str(options.path).strip() + '/' + assert ' ' not in self.base_path, "You cannot have a space in your home path. Please remove the space before you continue." + print('Your Base path is =' + self.base_path) + + # This assignment is not used in this program. It will be used in version 2 of this script. + self.bin = options.binary + # if app or bin is empty, dont pass anything + + # Search for the .zip file or tarball file in the URL. + i = self.link.rfind('/') + + self.fname = self.link[i+1:] + z = re.search('zip',self.fname,re.I) + g = re.search('gz',self.fname,re.I) + if z: + print 'zip file present in the URL.' + self.zip = True + self.gz = False + elif g: + print 'gz file present in the URL' + self.gz = True + self.zip = False + else: + print 'zip/gz file not present. Check the URL.' + return + print("File name is =" + self.fname) + + # Join the base path and the zip/tar file name to crate a complete Local file path. + self.fpath = self.base_path + self.fname + print('Your local file path will be=' + self.fpath) + except AssertionError, e: + print e.args[0] + sys.exit(1) + + # Download function - to download the SDK from the URL to the local machine. + def download(self,url,fpath,fname): + try: + # Start the download + print("Downloading...Please be patient!") + urllib.urlretrieve(url,filename = fname) + print('Download was successful.') + except ValueError: # Handles broken URL errors. + print 'The URL is ether broken or the file does not exist. Please enter the correct URL.' + raise + except urllib2.URLError: # Handles URL errors + print '\nURL not correct. Check again!' + raise + + # Function to extract the downloaded zipfile. + def extract(self, zipfilepath, extfile): + try: + # Timeout is set to 30 seconds. + timeout = 30 + # Change the directory to the location of the zip file. + try: + os.chdir(zipfilepath) + except OSError: + # Will reach here if zip file doesnt exist + print 'O/S Error:' + zipfilepath + 'does not exist' + raise + + # Get the folder name of Jetpack to get the exact version number. + if self.zip: + try: + f = zipfile.ZipFile(extfile, "r") + except IOError as (errno, strerror): # Handles file errors + print "I/O error - Cannot perform extract operation: {1}".format(errno, strerror) + raise + list = f.namelist()[0] + temp_name = list.split('/') + print('Folder Name= ' +temp_name[0]) + self.folder_name = temp_name[0] + elif self.gz: + try: + f = tarfile.open(extfile,'r') + except IOError as (errno, strerror): # Handles file errors + print "I/O error - Cannot perform extract operation: {1}".format(errno, strerror) + raise + list = f.getnames()[0] + temp_name = list.split('/') + print('Folder Name= ' +temp_name[0]) + self.folder_name = temp_name[0] + + print ('Starting to Extract...') + + # Timeout code. The subprocess.popen exeutes the command and the thread waits for a timeout. If the process does not finish within the mentioned- + # timeout, the process is killed. + kill_check = threading.Event() + + if self.zip: + # Call the command to unzip the file. + if self.mswindows: + zipfile.ZipFile.extractall(f) + else: + p = subprocess.Popen('unzip '+extfile, stdout=subprocess.PIPE, shell=True) + pid = p.pid + elif self.gz: + # Call the command to untar the file. + if self.mswindows: + tarfile.TarFile.extractall(f) + else: + p = subprocess.Popen('tar -xf '+extfile, stdout=subprocess.PIPE, shell=True) + pid = p.pid + + #No need to handle for windows because windows automatically replaces old files with new files. It does not ask the user(as it does in Mac/Unix) + if self.mswindows==False: + watch = threading.Timer(timeout, kill_process, args=(pid, kill_check, self.mswindows )) + watch.start() + (stdout, stderr) = p.communicate() + watch.cancel() # if it's still waiting to run + success = not kill_check.isSet() + + # Abort process if process fails. + if not success: + raise RuntimeError + kill_check.clear() + print('Extraction Successful.') + except RuntimeError: + print "Ending the program" + sys.exit(1) + except: + print "Error during file extraction: ", sys.exc_info()[0] + raise + + # Function to run the cfx testall comands and to make sure the SDK is not broken. + def run_testall(self, home_path, folder_name): + try: + timeout = 500 + + self.new_dir = home_path + folder_name + try: + os.chdir(self.new_dir) + except OSError: + # Will reach here if the jetpack 0.X directory doesnt exist + print 'O/S Error: Jetpack directory does not exist at ' + self.new_dir + raise + print '\nStarting tests...' + # Timeout code. The subprocess.popen exeutes the command and the thread waits for a timeout. If the process does not finish within the mentioned- + # timeout, the process is killed. + kill_check = threading.Event() + + # Set the path for the logs. They will be in the parent directory of the Jetpack SDK. + log_path = home_path + 'tests.log' + + # Subprocess call to set up the jetpack environment and to start the tests. Also sends the output to a log file. + if self.bin != None: + if self.mswindows: + p = subprocess.Popen("bin\\activate && cfx testall -a firefox -b \"" + self.bin + "\"" , stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) + proc_handle = p._handle + (stdout,stderr) = p.communicate() + else: + p = subprocess.Popen('. bin/activate; cfx testall -a firefox -b ' + self.bin , stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) + pid = p.pid + (stdout,stderr) = p.communicate() + elif self.bin == None: + if self.mswindows: + p=subprocess.Popen('bin\\activate && cfx testall -a firefox > '+log_path, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) + proc_handle = p._handle + (stdout,stderr) = p.communicate() + else: + p = subprocess.Popen('. bin/activate; cfx testall -a firefox > '+log_path, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) + pid = p.pid + (stdout,stderr) = p.communicate() + + #Write the output to log file + f=open(log_path,"w") + f.write(stdout+stderr) + f.close() + + #Watchdog for timeout process + if self.mswindows: + watch = threading.Timer(timeout, kill_process, args=(proc_handle, kill_check, self.mswindows)) + else: + watch = threading.Timer(timeout, kill_process, args=(pid, kill_check, self.mswindows)) + watch.start() + watch.cancel() # if it's still waiting to run + success = not kill_check.isSet() + if not success: + raise RuntimeError + kill_check.clear() + + if p.returncode!=0: + print('\nAll tests were not successful. Check the test-logs in the jetpack directory.') + result_sdk(home_path) + #sys.exit(1) + raise RuntimeError + else: + ret_code=result_sdk(home_path) + if ret_code==0: + print('\nAll tests were successful. Yay \o/ . Running a sample package test now...') + else: + print ('\nThere were errors during the tests.Take a look at logs') + raise RuntimeError + except RuntimeError: + print "Ending the program" + sys.exit(1) + except: + print "Error during the testall command execution:", sys.exc_info()[0] + raise + + def package(self, example_dir): + try: + timeout = 30 + + print '\nNow Running packaging tests...' + + kill_check = threading.Event() + + # Set the path for the example logs. They will be in the parent directory of the Jetpack SDK. + exlog_path = example_dir + 'test-example.log' + # Subprocess call to test the sample example for packaging. + if self.bin!=None: + if self.mswindows: + p = subprocess.Popen('bin\\activate && cfx run --pkgdir examples\\reading-data --static-args="{\\"quitWhenDone\\":true}" -b \"" + self.bin + "\"' , stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) + proc_handle = p._handle + (stdout, stderr) = p.communicate() + else: + p = subprocess.Popen('. bin/activate; cfx run --pkgdir examples/reading-data --static-args=\'{\"quitWhenDone\":true}\' -b ' + self.bin , stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) + pid = p.pid + (stdout, stderr) = p.communicate() + elif self.bin==None: + if self.mswindows: + p = subprocess.Popen('bin\\activate && cfx run --pkgdir examples\\reading-data --static-args="{\\"quitWhenDone\\":true}"', stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) + proc_handle = p._handle + (stdout, stderr) = p.communicate() + else: + p = subprocess.Popen('. bin/activate; cfx run --pkgdir examples/reading-data --static-args=\'{\"quitWhenDone\":true}\' ', stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) + pid = p.pid + (stdout, stderr) = p.communicate() + + #Write the output to log file + f=open(exlog_path,"w") + f.write(stdout+stderr) + f.close() + + #Watch dog for timeout process + if self.mswindows: + watch = threading.Timer(timeout, kill_process, args=(proc_handle, kill_check, self.mswindows)) + else: + watch = threading.Timer(timeout, kill_process, args=(pid, kill_check, self.mswindows)) + watch.start() + watch.cancel() # if it's still waiting to run + success = not kill_check.isSet() + if not success: + raise RuntimeError + kill_check.clear() + + if p.returncode != 0: + print('\nSample tests were not executed correctly. Check the test-example log in jetpack diretory.') + result_example(example_dir) + raise RuntimeError + else: + ret_code=result_example(example_dir) + if ret_code==0: + print('\nAll tests pass. The SDK is working! Yay \o/') + else: + print ('\nTests passed with warning.Take a look at logs') + sys.exit(1) + + except RuntimeError: + print "Ending program" + sys.exit(1) + except: + print "Error during running sample tests:", sys.exc_info()[0] + raise + +def result_sdk(sdk_dir): + log_path = sdk_dir + 'tests.log' + print 'Results are logged at:' + log_path + try: + f = open(log_path,'r') + # Handles file errors + except IOError : + print 'I/O error - Cannot open test log at ' + log_path + raise + + for line in reversed(open(log_path).readlines()): + if line.strip()=='FAIL': + print ('\nOverall result - FAIL. Look at the test log at '+log_path) + return 1 + return 0 + + +def result_example(sdk_dir): + exlog_path = sdk_dir + 'test-example.log' + print 'Sample test results are logged at:' + exlog_path + try: + f = open(exlog_path,'r') + # Handles file errors + except IOError : + print 'I/O error - Cannot open sample test log at ' + exlog_path + raise + + #Read the file in reverse and check for the keyword 'FAIL'. + for line in reversed(open(exlog_path).readlines()): + if line.strip()=='FAIL': + print ('\nOverall result for Sample tests - FAIL. Look at the test log at '+exlog_path) + return 1 + return 0 + +def kill_process(process, kill_check, mswindows): + print '\nProcess Timedout. Killing the process. Please Rerun this script.' + if mswindows: + win32api.TerminateProcess(process, -1) + else: + os.kill(process, signal.SIGKILL) + kill_check.set()# tell the main routine to kill. Used SIGKILL to hard kill the process. + return + +if __name__ == "__main__": + obj = SDK() + obj.download(obj.link,obj.fpath,obj.fname) + obj.extract(obj.base_path,obj.fname) + obj.run_testall(obj.base_path,obj.folder_name) + obj.package(obj.base_path) diff --git a/addon-sdk-1.16/examples/annotator/README.md b/addon-sdk-1.16/examples/annotator/README.md new file mode 100644 index 00000000..d376240b --- /dev/null +++ b/addon-sdk-1.16/examples/annotator/README.md @@ -0,0 +1,46 @@ + + +This add-on enables users to add notes, or annotations, to Web pages. + +Usage +----- + +To switch the annotator on, left-click the pencil icon in the Add-on Bar. The +icon should turn yellow: this indicates that the annotator is active. To switch +it off, click it again. Switching it on/off only stops you from entering +annotations: existing annotations are still displayed. + +When the annotator is active and the user moves the mouse over a page element +that can be annotated, the annotator highlights that elements by giving it a +yellow background. + +If the user clicks on a highlighted element the add-on opens a dialog for the +user to enter the annotation. When the user hits the annotation is +saved. + +Elements which have been annotated are displayed with a yellow border: when the +user moves the mouse over one of these elements, the add-on displays the +annotation associated with that element. + +To view all annotations in a list, right-click the pencil icon. + +The add-on is deactivated in private browsing mode, meaning that new annotations +can't be created although existing ones are still shown. On exiting private +browsing the add-on returns to its previous activation state. + +Known Issues/Limitations +------------------------ + +It is not possible to delete annotations, or to edit them after creating them, +but it would be simple to add this. + +When right-clicking the annotator icon the add-on bar's context-menu is shown: +this is tracked by +[bug 626326](https://bugzilla.mozilla.org/show_bug.cgi?id=626326). + +The list of annotations should be anchored to the widget. The annotation +editor, and the annotation itself, should be anchored to the element which is +annotated. The will be done when the implementation of panel-anchoring is +extended. diff --git a/addon-sdk-1.16/examples/annotator/data/annotation/annotation.html b/addon-sdk-1.16/examples/annotator/data/annotation/annotation.html new file mode 100644 index 00000000..f61c5e14 --- /dev/null +++ b/addon-sdk-1.16/examples/annotator/data/annotation/annotation.html @@ -0,0 +1,31 @@ + + + + + + + Annotation + + + + + + +
+
+ + + diff --git a/addon-sdk-1.16/examples/annotator/data/annotation/annotation.js b/addon-sdk-1.16/examples/annotator/data/annotation/annotation.js new file mode 100644 index 00000000..c1f57aa1 --- /dev/null +++ b/addon-sdk-1.16/examples/annotator/data/annotation/annotation.js @@ -0,0 +1,11 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +Initialize annotation content. +*/ + +self.on('message', function(message) { + $('#annotation').text(message); +}); diff --git a/addon-sdk-1.16/examples/annotator/data/editor/annotation-editor.html b/addon-sdk-1.16/examples/annotator/data/editor/annotation-editor.html new file mode 100644 index 00000000..6125999c --- /dev/null +++ b/addon-sdk-1.16/examples/annotator/data/editor/annotation-editor.html @@ -0,0 +1,39 @@ + + + + + + + Annotation + + + + + + + + + + + diff --git a/addon-sdk-1.16/examples/annotator/data/editor/annotation-editor.js b/addon-sdk-1.16/examples/annotator/data/editor/annotation-editor.js new file mode 100644 index 00000000..2fe5888c --- /dev/null +++ b/addon-sdk-1.16/examples/annotator/data/editor/annotation-editor.js @@ -0,0 +1,23 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +On a return key, send the content of the textArea back to the add-on, +and zero the textArea for the next time. +*/ + +var textArea = document.getElementById('annotation-box'); + +textArea.onkeyup = function(event) { + if (event.keyCode == 13) { + self.postMessage(textArea.value); + textArea.value = ''; + } +}; + +self.on('message', function() { + var textArea = document.getElementById('annotation-box'); + textArea.value = ''; + textArea.focus(); +}); diff --git a/addon-sdk-1.16/examples/annotator/data/jquery-1.4.2.min.js b/addon-sdk-1.16/examples/annotator/data/jquery-1.4.2.min.js new file mode 100644 index 00000000..7c243080 --- /dev/null +++ b/addon-sdk-1.16/examples/annotator/data/jquery-1.4.2.min.js @@ -0,0 +1,154 @@ +/*! + * jQuery JavaScript Library v1.4.2 + * http://jquery.com/ + * + * Copyright 2010, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2010, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Sat Feb 13 22:33:48 2010 -0500 + */ +(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/, +Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&& +(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this, +a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b=== +"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this, +function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b
a"; +var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected, +parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent= +false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n= +s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true, +applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando]; +else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this, +a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b=== +w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i, +cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected= +c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed"); +a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g, +function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split("."); +k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a), +C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B=0){a.type= +e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&& +f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive; +if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data", +e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a, +"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a, +d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, +e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift(); +t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D|| +g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()}, +CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m, +g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)}, +text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}}, +setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return hl[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h= +h[3];l=0;for(m=h.length;l=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m=== +"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g, +h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&& +q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML=""; +if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="

";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}(); +(function(){var g=s.createElement("div");g.innerHTML="
";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}: +function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f0)for(var j=d;j0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j= +{},i;if(f&&a.length){e=0;for(var o=a.length;e-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a=== +"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode", +d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")? +a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType=== +1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/"},F={option:[1,""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div
","
"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d= +c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this}, +wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})}, +prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b, +this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild); +return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja, +""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]); +return this}else{e=0;for(var j=d.length;e0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["", +""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]===""&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e= +c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]? +c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja= +function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter= +Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a, +"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f= +a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b= +a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=//gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!== +"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("
").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this}, +serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), +function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href, +global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&& +e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)? +"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache=== +false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B= +false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since", +c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E|| +d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x); +g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status=== +1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b=== +"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional; +if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration=== +"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]|| +c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start; +this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now= +this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem, +e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b
"; +a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b); +c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a, +d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top- +f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset": +"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in +e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window); diff --git a/addon-sdk-1.16/examples/annotator/data/list/annotation-list.css b/addon-sdk-1.16/examples/annotator/data/list/annotation-list.css new file mode 100644 index 00000000..9063682f --- /dev/null +++ b/addon-sdk-1.16/examples/annotator/data/list/annotation-list.css @@ -0,0 +1,40 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#annotation-list .annotation-details + { + padding: 10px; + margin: 10px; + border: solid 3px #EEE; + background-color: white; + } + +#annotation-list .url, .selection-text, .annotation-text + { + padding: 5px; + margin: 5px; + } + +#annotation-list .selection-text,#annotation-list .annotation-text + { + border: solid 1px #EEE; + } + +#annotation-list .annotation-text + { + font-style: italic; + } + +body + { + background-color: #F5F5F5; + font: 100% arial, helvetica, sans-serif; + } + +h1 + { + font-family: georgia,serif; + font-size: 1.5em; + text-align:center; + } diff --git a/addon-sdk-1.16/examples/annotator/data/list/annotation-list.html b/addon-sdk-1.16/examples/annotator/data/list/annotation-list.html new file mode 100644 index 00000000..32a54096 --- /dev/null +++ b/addon-sdk-1.16/examples/annotator/data/list/annotation-list.html @@ -0,0 +1,26 @@ + + + + + + Saved annotations + + + + +
+
+ +
+
+ +
+
+
+
+ + + + diff --git a/addon-sdk-1.16/examples/annotator/data/list/annotation-list.js b/addon-sdk-1.16/examples/annotator/data/list/annotation-list.js new file mode 100644 index 00000000..5653ba58 --- /dev/null +++ b/addon-sdk-1.16/examples/annotator/data/list/annotation-list.js @@ -0,0 +1,31 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +Construct the HTML for the annotation list. + +Bind a function to click events on the link that send a message back to +the add-on code, so it can open the link in the main browser. +*/ + +self.on("message", function onMessage(storedAnnotations) { + var annotationList = $('#annotation-list'); + annotationList.empty(); + storedAnnotations.forEach( + function(storedAnnotation) { + var annotationHtml = $('#template .annotation-details').clone(); + annotationHtml.find('.url').text(storedAnnotation.url) + .attr('href', storedAnnotation.url); + annotationHtml.find('.url').bind('click', function(event) { + event.stopPropagation(); + event.preventDefault(); + self.postMessage(storedAnnotation.url); + }); + annotationHtml.find('.selection-text') + .text(storedAnnotation.anchorText); + annotationHtml.find('.annotation-text') + .text(storedAnnotation.annotationText); + annotationList.append(annotationHtml); + }); +}); diff --git a/addon-sdk-1.16/examples/annotator/data/matcher.js b/addon-sdk-1.16/examples/annotator/data/matcher.js new file mode 100644 index 00000000..86d6bc76 --- /dev/null +++ b/addon-sdk-1.16/examples/annotator/data/matcher.js @@ -0,0 +1,50 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +Locate anchors for annotations and prepare to display the annotations. + +For each annotation, if its URL matches this page, +- get the ancestor whose ID matches the ID in the anchor +- look for a

element whose content contains the anchor text + +That's considered a match. Then we: +- highlight the anchor element +- add an 'annotated' class to tell the selector to skip this element +- embed the annottion text as a new attribute + +For all annotated elements: +- bind 'mouseenter' and 'mouseleave' events to the element, to send 'show' + and 'hide' messages back to the add-on. +*/ + +self.on('message', function onMessage(annotations) { + annotations.forEach( + function(annotation) { + if(annotation.url == document.location.toString()) { + createAnchor(annotation); + } + }); + + $('.annotated').css('border', 'solid 3px yellow'); + + $('.annotated').bind('mouseenter', function(event) { + self.port.emit('show', $(this).attr('annotation')); + event.stopPropagation(); + event.preventDefault(); + }); + + $('.annotated').bind('mouseleave', function() { + self.port.emit('hide'); + }); +}); + + +function createAnchor(annotation) { + annotationAnchorAncestor = $('#' + annotation.ancestorId)[0] || document.body; + annotationAnchor = $(annotationAnchorAncestor).parent().find( + ':contains("' + annotation.anchorText + '")').last(); + annotationAnchor.addClass('annotated'); + annotationAnchor.attr('annotation', annotation.annotationText); +} diff --git a/addon-sdk-1.16/examples/annotator/data/selector.js b/addon-sdk-1.16/examples/annotator/data/selector.js new file mode 100644 index 00000000..f42dbfa5 --- /dev/null +++ b/addon-sdk-1.16/examples/annotator/data/selector.js @@ -0,0 +1,73 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/* +The selector locates elements that are suitable for annotation and enables +the user to select them. + +On 'mouseenter' events associated with

elements: +- if the selector is active and the element is not already annotated +- find the nearest ancestor which has an id attribute: this is supposed to +make identification of this element more accurate +- highlight the element +- bind 'click' for the element to send a message back to the add-on, including +all the information associated with the anchor. +*/ + +var matchedElement = null; +var originalBgColor = null; +var active = false; + +function resetMatchedElement() { + if (matchedElement) { + matchedElement.css('background-color', originalBgColor); + matchedElement.unbind('click.annotator'); + } +} + +self.on('message', function onMessage(activation) { + active = activation; + if (!active) { + resetMatchedElement(); + } +}); + +function getInnerText(element) { + // jQuery.text() returns content of + + + diff --git a/addon-sdk-1.16/examples/library-detector/data/widget.html b/addon-sdk-1.16/examples/library-detector/data/widget.html new file mode 100755 index 00000000..4ca5b503 --- /dev/null +++ b/addon-sdk-1.16/examples/library-detector/data/widget.html @@ -0,0 +1,50 @@ + + + + + + Library detector + + + + + diff --git a/addon-sdk-1.16/examples/library-detector/lib/main.js b/addon-sdk-1.16/examples/library-detector/lib/main.js new file mode 100755 index 00000000..133665e8 --- /dev/null +++ b/addon-sdk-1.16/examples/library-detector/lib/main.js @@ -0,0 +1,67 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const tabs = require('sdk/tabs'); +const widgets = require('sdk/widget'); +const data = require('sdk/self').data; +const pageMod = require('sdk/page-mod'); +const panel = require('sdk/panel'); + +const ICON_WIDTH = 16; + +function updateWidgetView(tab) { + let widgetView = widget.getView(tab.window); + if (!tab.libraries) { + tab.libraries = []; + } + widgetView.port.emit("update", tab.libraries); + widgetView.width = tab.libraries.length * ICON_WIDTH; +} + +var widget = widgets.Widget({ + id: "library-detector", + label: "Library Detector", + contentURL: data.url("widget.html"), + panel: panel.Panel({ + width: 240, + height: 60, + contentURL: data.url("panel.html") + }) +}); + +widget.port.on('setLibraryInfo', function(libraryInfo) { + widget.panel.postMessage(libraryInfo); +}); + +pageMod.PageMod({ + include: "*", + contentScriptWhen: 'end', + contentScriptFile: (data.url('library-detector.js')), + onAttach: function(worker) { + worker.on('message', function(libraryList) { + if (!worker.tab.libraries) { + worker.tab.libraries = []; + } + libraryList.forEach(function(library) { + if (worker.tab.libraries.indexOf(library) == -1) { + worker.tab.libraries.push(library); + } + }); + if (worker.tab == tabs.activeTab) { + updateWidgetView(worker.tab); + } + }); + } +}); + +tabs.on('activate', function(tab) { + updateWidgetView(tab); +}); + +/* +For change of location +*/ +tabs.on('ready', function(tab) { + tab.libraries = []; +}); \ No newline at end of file diff --git a/addon-sdk-1.16/examples/library-detector/package.json b/addon-sdk-1.16/examples/library-detector/package.json new file mode 100755 index 00000000..60cce530 --- /dev/null +++ b/addon-sdk-1.16/examples/library-detector/package.json @@ -0,0 +1,9 @@ +{ + "name": "library-detector-sdk", + "license": "MPL 2.0", + "author": "", + "version": "0.1", + "title": "library-detector-sdk", + "id": "jid1-R4rSVNkBANnvGQ", + "description": "a basic add-on" +} diff --git a/addon-sdk-1.16/examples/library-detector/test/test-main.js b/addon-sdk-1.16/examples/library-detector/test/test-main.js new file mode 100644 index 00000000..72fedf45 --- /dev/null +++ b/addon-sdk-1.16/examples/library-detector/test/test-main.js @@ -0,0 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +exports.testMain = function(test) { + test.pass("TODO: Write some tests."); +}; diff --git a/addon-sdk-1.16/examples/reading-data/data/mom.png b/addon-sdk-1.16/examples/reading-data/data/mom.png new file mode 100644 index 0000000000000000000000000000000000000000..4ba89a2c1ef900e9363746d45820f0b9e857e51f GIT binary patch literal 4778 zcmV;b5>@SqP)Px#32;bRa{vGf6951U69E94oEQKA00(qQO+^RV1P~P=F&#t@^Z)<=B6LMqbVF}# zZDnqB07G(RVRU6=Aa`kWXdp*PO;8|jZXjVGO<`k)sCPpE01^&KL_t(|+U;F^P!w0T zKYR_K14D+-*}%v~BTH5Sg7`2HXTVZPBryruL~6HUqTxe{*~Ha#qY=oswWYy_8aD{I zR@4_In?)&nIrjL1Y#9s~>@iv9k$J$Htg?w;>a%>p+7ZUEfm2EYw~o7@1n0dSKW05o@$vEDVTM~NRjSmYB4@#XB)IeZ`SBlrw2<0485xciRz3jeb-G54 zMz7PESnuyYCnIC==FOB$G=Iaf!NLB2|9j-_U8ln^Y%nyHm$x1{@>6fG&CdS8!Scr+ zcZ9rh2B4*?s$tu<@u4C7Nz~G%X~o5y9t%U#-_gK8!z+MMp2%v}w_Ae`}`|v5+SKcr+y??)BG&5`Vo; z*M9u??Uoh`uq_6-uS{nC;n?8d-Hr|hoc!eSh57l38#mhXkQ@N0+pqz1KO!+Py|h%o zaY_k8Xk1)UVIglAZ1R0}c=(g+*4gABgS;XNi;oXaNZ_ZPSOI{>e(RAV#KVUGkdT|3 zT2$l+(?kh_Bp~4Fk`hjGMtL7W6bM#ghHt(Z8yqyh6c`ot)Q%loZ_5e*JobHN&C1xj zHz6<2q2^Jq(_Jnr(>FBS|LG^Lwt2c_jQm`k^}@iv1W~h0;C-EJmzNhOYM!?TCTjZ0YJsP zdC1jLs#H1W&pXRqL{8sOclW~w4*)>Dd-s2B*#ZEy>(;d&Kkn({^YH$C+*$Am0(&yo z7A!Gf0zj|Rom#zme0W$Qm1ckWrPBij`-6j#-J2aAzWm_9wqwWeh(ok^^X4aVa%M+F zI5E&9`e;)JFOZ)+-S-l#)VeBifJb&_})59iTZQXk1qmPJ*2^98tFghxRQEyEj zM@0=A3;^IC60)wjnRzsLGFbWUix*LQPDX|pUM!=ubWMG|Y|b12z})wHaDK4w>QxQ^;0U;P*+2w}CzHroII!-WA9C*8Sr%~b%f$Q;-J0K;XGq7w;Wx%{UHq9e={ zk(7iQ|M#E&aCXE*ON!)VtSYacJ}u@)4Ty|H4HU$kCIBqnyxBJ}5CA5|#~XL;a&5v2K`M2#QZNA61W^G1Dl4J78e(Fgx*8sT z+|5eCR1ljWDgeN>YjE!#bag>rANh$^3vavu&pZQfya8IRo4Mv50QL!NOF-z8Pol=e z*cj8jzxxgd0`A;_zCNg|gg^ZWdV2wv3u?9S$tR#z&u{>+7QsG&Z3!@UA1D`@kc7+d zKm8OoYyiC;%!)OkvlCu?afZu*x${MrUH7DPAHw3}QM>*4aofLBQv&_4V-CXHZ`cf&`Fzd!e-zy1Rki%crJ^3Fz*I)>dG#I@M0X&KE;& zk$M^$920{Y{nxLvwkDoXPykvj{Qd8+YZsuzVNVYf6o7{Z%$o-*S3*t>tXv6iy=5;3 zS}mlcz~aS_lf(H|1_Nx`1S?iRWu>D5z|I$T$JhdZ%c1Xfbg=pCPPh0JGIzaQZZql<;SLdOD=1!}s6A_&78-1J#;uCc1td>gu4k7ykNJ@j7C- z9D20BpUDg$3JioDJHS|pN7CB0R;o=3ayg)}P*nw6wm?%8xABW+S=g?~=3s z*u9&$ecSSCPYJ1|izj`txR|J@Ae2ht(MO5x z+sT`=q3UY#4stnlhlU11ttR~ZCjA`xQz(dv3W7EL^Rv$o5BGRW_`ksV4)X;~Q@%$fVq(L_T7K@fyOVfs)eBeJr{&p83GZyymIO+-Zz zVPS+^PRQkizrX3W5{V@SetxWeg*5=Qu^^>NWm;u0c_|iK!nGL2>I{-fN=*L2?Af-p zkX8#Hd_cBUDwBKh+;gTrgM)D93~3%%>WcgKAw3=5dk;E0;pR=~?S=k+=L6acJP z0VhvFT^*E`g3&jE(NTE*c>xVLQ!p-k;X>5-S51w;im$jBP~oax4>ps!nEPrqXtgHr zN=l%v4&Htn%$-bRrStP`<2pJDS}p0jNF)>;mMnqnY*?`ZwrqiO=ioQL0UsX#fT1BM zC}8swtcpAtB&g-^VI)EOWJ!UV&_i6+nel=l0sn~l9JdPurJoE zK@9w#)KtzkfFOvWo}PVjIl4v5;lpfIJf_GKAAhNP7Z;k>}MuLZ%tWQq{BQo2*x!R6am*;x5$}paj|X9rdC5k15{K% zNC=W{ICF+p#m$-p_4SssX8`iM05E3`#kUj+sH}vF3J4B{@o~^<;n*>{$HAKy+`o5^ zXF?`WsU+3e&6}3juzO8cdyQ2yWgt+YFx_GQep>C1mcZ}b%OS3ppAT2Bnoy@(;P2@P z@$nEAW)(j(h9HB2n0^8;UFqtg3j~cAJ>kbPGkF4lUE({}t_fcRn4LYzTeglF;9oSB z!m%ZV0-k=FY&SQ9Bgz-?h@^=bH<5ii%=*_+f*g**H~Qn4izE(8vY=QiTGSg9|?y z#e}AJ7WB8jk&VPeKKE3s0dL{xh{a4800i@7x=~w;B5}SlS>ncxJpBMfzj*m&)aY(( zv>g``iVOz0e3@+j>Q^q83BZF@k(|tPuLyH36dD%?01rk+E|--}(HopO11OgylW{fx zb`rL(#yJdE((82Hjg1z=mp=efg(9i25Vdbzx+LI(G7;84ix&g$m6|wLLz9oCCX9DW z3sW~-v0#;Kxm;HEU}OXULgV6WZnMFvE{HlhjRVfi56X}5=7cL&@K<7(M{#i+z8DZd z$63p~0-|NhEQWvAS6`uaQemOZb-t_spiWY%)ueO8#zhh;kjI|>{AaQe7B)$SjPV(u zi_c(-O`pb^)Xg|a;W7MyePvv@aasDUOP5RzK}iV^1Z{eYUP#&U!X7$uglxR|=A_TD z-;O7}JCcX{AYoXdU>9HZ%wGEG9-+DIKGl?pu3tgJNUWO&#o{Im=O zuLc944f#Vtu!YCV1(hll`#pX*0Kk3^9Fn$g2ZaLi@~9^vSaKy2ib0C40=5(EmCx3# zWIHd9PibCU9OUJJLIK;iGsT)vpFjQ)J-qqwPvx|`D#3+pWC%DBb80t`{y{Vw>hIDS?6l zFc?500ks$SUy#FtEHJ=-QYl$NVqHj* zmq$4Ck*sv`(d1-%xB*j-IXAKx-3)&ciBN-@J?Y+mAMv3~CgZ0MfZq={S5^w6h}grh zb}f;aNi11H$Yj(2urIO`h`O}e{|*d9OwhgmKEIprKgOzh*)n8#A`%m`zx;CQc9%04 zpsmd^$0CQG)(E)1qaDYDmdY$gn3ooGKl(-%nF%1ZxDk2F8$o}FB z-n*;vU#6;5snYlCA?IO-4^LgPOa!FY>F{|*>3jCrv$w5X>x2{X^6+n;UB75hS3r#riTJ&&Lp zZB)C2_i7=WnTU=CfSWoU{y$w$M@8I6hw8|pqnDMHp?7yO0GQ(#18GDAUhO?yx&Q2u zBZ7=Gd(RVg#LR5wa36%-EhV+6C?YWt0LF)grqiUzaoiV71(BJRfB^aY`8O|I7#kQE z8yL_xG)SaUsX`&ag&r%10lWFIN*G3>IUB`2=HE0)S2&DFFCAtfFU6_*W$G7sZo!O8Vw4f zcu6Fo3l^mO;SV;?0d{!+=#-MLOct4zHf2XB>UFw?ty@uQ7o3q>bus`>v<=oG=oI-A z1psujRR_Uf&3Z{dK>D6Nb|x2H9ss0{Z`9Vd9X*N<(**#DRGPGDlTaqn!aoOqsHIEO zii@3P|HTCW!yvVPQfPfoG0cEP3IL^rw1;ID17AE55m5s`=R&6z6{WE*M-YjzbyWc9 zrb%ot(Cc)U%gXc(4fl<^mWnVG=o$b}!oYw5lACW29vnw2xx_c{T@nC9I7|-!+o%r7 zw6+{#j)A-0IbvwkH)=H2RcY4hJ~S>asjzVFs#T)R0=b?9(CM77Km4%uFMoMp-1XCE z_UuJ(zb*a*+;lj!+*;>2*SDH+67WeS*u33roHvj+t07*qoM6N<$ Eg7NXsSO5S3 literal 0 HcmV?d00001 diff --git a/addon-sdk-1.16/examples/reading-data/data/sample.html b/addon-sdk-1.16/examples/reading-data/data/sample.html new file mode 100644 index 00000000..c7c09cb9 --- /dev/null +++ b/addon-sdk-1.16/examples/reading-data/data/sample.html @@ -0,0 +1,7 @@ + + + +

Hello World

+ diff --git a/addon-sdk-1.16/examples/reading-data/lib/main.js b/addon-sdk-1.16/examples/reading-data/lib/main.js new file mode 100644 index 00000000..71c8be6b --- /dev/null +++ b/addon-sdk-1.16/examples/reading-data/lib/main.js @@ -0,0 +1,44 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +var self = require("sdk/self"); +var panels = require("sdk/panel"); +var widgets = require("sdk/widget"); + +function replaceMom(html) { + return html.replace("World", "Mom"); +} +exports.replaceMom = replaceMom; + +exports.main = function(options, callbacks) { + console.log("My ID is " + self.id); + + // Load the sample HTML into a string. + var helloHTML = self.data.load("sample.html"); + + // Let's now modify it... + helloHTML = replaceMom(helloHTML); + + // ... and then create a panel that displays it. + var myPanel = panels.Panel({ + contentURL: "data:text/html," + helloHTML + }); + + // Load the URL of the sample image. + var iconURL = self.data.url("mom.png"); + + // Create a widget that displays the image. We'll attach the panel to it. + // When you click the widget, the panel will pop up. + widgets.Widget({ + id: "test-widget", + label: "Mom", + contentURL: iconURL, + panel: myPanel + }); + + // If you run cfx with --static-args='{"quitWhenDone":true}' this program + // will automatically quit Firefox when it's done. + if (options.staticArgs.quitWhenDone) + callbacks.quit(); +} diff --git a/addon-sdk-1.16/examples/reading-data/package.json b/addon-sdk-1.16/examples/reading-data/package.json new file mode 100644 index 00000000..5fc6b2b5 --- /dev/null +++ b/addon-sdk-1.16/examples/reading-data/package.json @@ -0,0 +1,9 @@ +{ + "name": "reading-data", + "description": "A demonstration of reading bundled data.", + "keywords": [], + "author": "Brian Warner", + "contributors": [], + "license": "MPL 2.0", + "id": "reading-data-example@jetpack.mozillalabs.com" +} diff --git a/addon-sdk-1.16/examples/reading-data/tests/test-main.js b/addon-sdk-1.16/examples/reading-data/tests/test-main.js new file mode 100644 index 00000000..c2c2f12e --- /dev/null +++ b/addon-sdk-1.16/examples/reading-data/tests/test-main.js @@ -0,0 +1,24 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +var m = require("main"); +var self = require("sdk/self"); + +exports.testReplace = function(test) { + var input = "Hello World"; + var output = m.replaceMom(input); + test.assertEqual(output, "Hello Mom"); + var callbacks = { quit: function() {} }; + + // Make sure it doesn't crash... + m.main({ staticArgs: {} }, callbacks); +}; + +exports.testID = function(test) { + // The ID is randomly generated during tests, so we cannot compare it against + // anything in particular. Just assert that it is not empty. + test.assert(self.id.length > 0); + test.assertEqual(self.data.url("sample.html"), + "resource://reading-data-example-at-jetpack-dot-mozillalabs-dot-com/reading-data/data/sample.html"); +}; diff --git a/addon-sdk-1.16/examples/reddit-panel/README.md b/addon-sdk-1.16/examples/reddit-panel/README.md new file mode 100644 index 00000000..5edfb709 --- /dev/null +++ b/addon-sdk-1.16/examples/reddit-panel/README.md @@ -0,0 +1,14 @@ + + +The Reddit Panel example add-on displays Reddit in a panel you open +by clicking a widget in the add-on bar. When you click a Reddit story +in the panel, the story opens in a new tab. + +The add-on demonstrates the Panel and Widget APIs as well as content scripts +and using jQuery as a content script. + +Due to a bug in Firefox 4.0b7, this example doesn't work in that version +of Firefox and requires a recent Firefox 4.0b8pre nightly build, Firefox 4.0b8 +itself, or a newer version of Firefox. diff --git a/addon-sdk-1.16/examples/reddit-panel/data/jquery-1.4.4.min.js b/addon-sdk-1.16/examples/reddit-panel/data/jquery-1.4.4.min.js new file mode 100644 index 00000000..8f3ca2e2 --- /dev/null +++ b/addon-sdk-1.16/examples/reddit-panel/data/jquery-1.4.4.min.js @@ -0,0 +1,167 @@ +/*! + * jQuery JavaScript Library v1.4.4 + * http://jquery.com/ + * + * Copyright 2010, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2010, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Thu Nov 11 19:04:53 2010 -0500 + */ +(function(E,B){function ka(a,b,d){if(d===B&&a.nodeType===1){d=a.getAttribute("data-"+b);if(typeof d==="string"){try{d=d==="true"?true:d==="false"?false:d==="null"?null:!c.isNaN(d)?parseFloat(d):Ja.test(d)?c.parseJSON(d):d}catch(e){}c.data(a,b,d)}else d=B}return d}function U(){return false}function ca(){return true}function la(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function Ka(a){var b,d,e,f,h,l,k,o,x,r,A,C=[];f=[];h=c.data(this,this.nodeType?"events":"__events__");if(typeof h==="function")h= +h.events;if(!(a.liveFired===this||!h||!h.live||a.button&&a.type==="click")){if(a.namespace)A=RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)");a.liveFired=this;var J=h.live.slice(0);for(k=0;kd)break;a.currentTarget=f.elem;a.data=f.handleObj.data;a.handleObj=f.handleObj;A=f.handleObj.origHandler.apply(f.elem,arguments);if(A===false||a.isPropagationStopped()){d=f.level;if(A===false)b=false;if(a.isImmediatePropagationStopped())break}}return b}}function Y(a,b){return(a&&a!=="*"?a+".":"")+b.replace(La, +"`").replace(Ma,"&")}function ma(a,b,d){if(c.isFunction(b))return c.grep(a,function(f,h){return!!b.call(f,h,f)===d});else if(b.nodeType)return c.grep(a,function(f){return f===b===d});else if(typeof b==="string"){var e=c.grep(a,function(f){return f.nodeType===1});if(Na.test(b))return c.filter(b,e,!d);else b=c.filter(b,e)}return c.grep(a,function(f){return c.inArray(f,b)>=0===d})}function na(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var e=c.data(a[d++]),f=c.data(this, +e);if(e=e&&e.events){delete f.handle;f.events={};for(var h in e)for(var l in e[h])c.event.add(this,h,e[h][l],e[h][l].data)}}})}function Oa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function oa(a,b,d){var e=b==="width"?a.offsetWidth:a.offsetHeight;if(d==="border")return e;c.each(b==="width"?Pa:Qa,function(){d||(e-=parseFloat(c.css(a,"padding"+this))||0);if(d==="margin")e+=parseFloat(c.css(a, +"margin"+this))||0;else e-=parseFloat(c.css(a,"border"+this+"Width"))||0});return e}function da(a,b,d,e){if(c.isArray(b)&&b.length)c.each(b,function(f,h){d||Ra.test(a)?e(a,h):da(a+"["+(typeof h==="object"||c.isArray(h)?f:"")+"]",h,d,e)});else if(!d&&b!=null&&typeof b==="object")c.isEmptyObject(b)?e(a,""):c.each(b,function(f,h){da(a+"["+f+"]",h,d,e)});else e(a,b)}function S(a,b){var d={};c.each(pa.concat.apply([],pa.slice(0,b)),function(){d[this]=a});return d}function qa(a){if(!ea[a]){var b=c("<"+ +a+">").appendTo("body"),d=b.css("display");b.remove();if(d==="none"||d==="")d="block";ea[a]=d}return ea[a]}function fa(a){return c.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var t=E.document,c=function(){function a(){if(!b.isReady){try{t.documentElement.doScroll("left")}catch(j){setTimeout(a,1);return}b.ready()}}var b=function(j,s){return new b.fn.init(j,s)},d=E.jQuery,e=E.$,f,h=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/,l=/\S/,k=/^\s+/,o=/\s+$/,x=/\W/,r=/\d/,A=/^<(\w+)\s*\/?>(?:<\/\1>)?$/, +C=/^[\],:{}\s]*$/,J=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,w=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,I=/(?:^|:|,)(?:\s*\[)+/g,L=/(webkit)[ \/]([\w.]+)/,g=/(opera)(?:.*version)?[ \/]([\w.]+)/,i=/(msie) ([\w.]+)/,n=/(mozilla)(?:.*? rv:([\w.]+))?/,m=navigator.userAgent,p=false,q=[],u,y=Object.prototype.toString,F=Object.prototype.hasOwnProperty,M=Array.prototype.push,N=Array.prototype.slice,O=String.prototype.trim,D=Array.prototype.indexOf,R={};b.fn=b.prototype={init:function(j, +s){var v,z,H;if(!j)return this;if(j.nodeType){this.context=this[0]=j;this.length=1;return this}if(j==="body"&&!s&&t.body){this.context=t;this[0]=t.body;this.selector="body";this.length=1;return this}if(typeof j==="string")if((v=h.exec(j))&&(v[1]||!s))if(v[1]){H=s?s.ownerDocument||s:t;if(z=A.exec(j))if(b.isPlainObject(s)){j=[t.createElement(z[1])];b.fn.attr.call(j,s,true)}else j=[H.createElement(z[1])];else{z=b.buildFragment([v[1]],[H]);j=(z.cacheable?z.fragment.cloneNode(true):z.fragment).childNodes}return b.merge(this, +j)}else{if((z=t.getElementById(v[2]))&&z.parentNode){if(z.id!==v[2])return f.find(j);this.length=1;this[0]=z}this.context=t;this.selector=j;return this}else if(!s&&!x.test(j)){this.selector=j;this.context=t;j=t.getElementsByTagName(j);return b.merge(this,j)}else return!s||s.jquery?(s||f).find(j):b(s).find(j);else if(b.isFunction(j))return f.ready(j);if(j.selector!==B){this.selector=j.selector;this.context=j.context}return b.makeArray(j,this)},selector:"",jquery:"1.4.4",length:0,size:function(){return this.length}, +toArray:function(){return N.call(this,0)},get:function(j){return j==null?this.toArray():j<0?this.slice(j)[0]:this[j]},pushStack:function(j,s,v){var z=b();b.isArray(j)?M.apply(z,j):b.merge(z,j);z.prevObject=this;z.context=this.context;if(s==="find")z.selector=this.selector+(this.selector?" ":"")+v;else if(s)z.selector=this.selector+"."+s+"("+v+")";return z},each:function(j,s){return b.each(this,j,s)},ready:function(j){b.bindReady();if(b.isReady)j.call(t,b);else q&&q.push(j);return this},eq:function(j){return j=== +-1?this.slice(j):this.slice(j,+j+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(N.apply(this,arguments),"slice",N.call(arguments).join(","))},map:function(j){return this.pushStack(b.map(this,function(s,v){return j.call(s,v,s)}))},end:function(){return this.prevObject||b(null)},push:M,sort:[].sort,splice:[].splice};b.fn.init.prototype=b.fn;b.extend=b.fn.extend=function(){var j,s,v,z,H,G=arguments[0]||{},K=1,Q=arguments.length,ga=false; +if(typeof G==="boolean"){ga=G;G=arguments[1]||{};K=2}if(typeof G!=="object"&&!b.isFunction(G))G={};if(Q===K){G=this;--K}for(;K0))if(q){var s=0,v=q;for(q=null;j=v[s++];)j.call(t,b);b.fn.trigger&&b(t).trigger("ready").unbind("ready")}}},bindReady:function(){if(!p){p=true;if(t.readyState==="complete")return setTimeout(b.ready,1);if(t.addEventListener){t.addEventListener("DOMContentLoaded",u,false);E.addEventListener("load",b.ready,false)}else if(t.attachEvent){t.attachEvent("onreadystatechange",u);E.attachEvent("onload", +b.ready);var j=false;try{j=E.frameElement==null}catch(s){}t.documentElement.doScroll&&j&&a()}}},isFunction:function(j){return b.type(j)==="function"},isArray:Array.isArray||function(j){return b.type(j)==="array"},isWindow:function(j){return j&&typeof j==="object"&&"setInterval"in j},isNaN:function(j){return j==null||!r.test(j)||isNaN(j)},type:function(j){return j==null?String(j):R[y.call(j)]||"object"},isPlainObject:function(j){if(!j||b.type(j)!=="object"||j.nodeType||b.isWindow(j))return false;if(j.constructor&& +!F.call(j,"constructor")&&!F.call(j.constructor.prototype,"isPrototypeOf"))return false;for(var s in j);return s===B||F.call(j,s)},isEmptyObject:function(j){for(var s in j)return false;return true},error:function(j){throw j;},parseJSON:function(j){if(typeof j!=="string"||!j)return null;j=b.trim(j);if(C.test(j.replace(J,"@").replace(w,"]").replace(I,"")))return E.JSON&&E.JSON.parse?E.JSON.parse(j):(new Function("return "+j))();else b.error("Invalid JSON: "+j)},noop:function(){},globalEval:function(j){if(j&& +l.test(j)){var s=t.getElementsByTagName("head")[0]||t.documentElement,v=t.createElement("script");v.type="text/javascript";if(b.support.scriptEval)v.appendChild(t.createTextNode(j));else v.text=j;s.insertBefore(v,s.firstChild);s.removeChild(v)}},nodeName:function(j,s){return j.nodeName&&j.nodeName.toUpperCase()===s.toUpperCase()},each:function(j,s,v){var z,H=0,G=j.length,K=G===B||b.isFunction(j);if(v)if(K)for(z in j){if(s.apply(j[z],v)===false)break}else for(;H
a";var f=d.getElementsByTagName("*"),h=d.getElementsByTagName("a")[0],l=t.createElement("select"), +k=l.appendChild(t.createElement("option"));if(!(!f||!f.length||!h)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(h.getAttribute("style")),hrefNormalized:h.getAttribute("href")==="/a",opacity:/^0.55$/.test(h.style.opacity),cssFloat:!!h.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:k.selected,deleteExpando:true,optDisabled:false,checkClone:false, +scriptEval:false,noCloneEvent:true,boxModel:null,inlineBlockNeedsLayout:false,shrinkWrapBlocks:false,reliableHiddenOffsets:true};l.disabled=true;c.support.optDisabled=!k.disabled;b.type="text/javascript";try{b.appendChild(t.createTextNode("window."+e+"=1;"))}catch(o){}a.insertBefore(b,a.firstChild);if(E[e]){c.support.scriptEval=true;delete E[e]}try{delete b.test}catch(x){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function r(){c.support.noCloneEvent= +false;d.detachEvent("onclick",r)});d.cloneNode(true).fireEvent("onclick")}d=t.createElement("div");d.innerHTML="";a=t.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var r=t.createElement("div");r.style.width=r.style.paddingLeft="1px";t.body.appendChild(r);c.boxModel=c.support.boxModel=r.offsetWidth===2;if("zoom"in r.style){r.style.display="inline";r.style.zoom= +1;c.support.inlineBlockNeedsLayout=r.offsetWidth===2;r.style.display="";r.innerHTML="
";c.support.shrinkWrapBlocks=r.offsetWidth!==2}r.innerHTML="
t
";var A=r.getElementsByTagName("td");c.support.reliableHiddenOffsets=A[0].offsetHeight===0;A[0].style.display="";A[1].style.display="none";c.support.reliableHiddenOffsets=c.support.reliableHiddenOffsets&&A[0].offsetHeight===0;r.innerHTML="";t.body.removeChild(r).style.display= +"none"});a=function(r){var A=t.createElement("div");r="on"+r;var C=r in A;if(!C){A.setAttribute(r,"return;");C=typeof A[r]==="function"}return C};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=f=h=null}})();var ra={},Ja=/^(?:\{.*\}|\[.*\])$/;c.extend({cache:{},uuid:0,expando:"jQuery"+c.now(),noData:{embed:true,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:true},data:function(a,b,d){if(c.acceptData(a)){a=a==E?ra:a;var e=a.nodeType,f=e?a[c.expando]:null,h= +c.cache;if(!(e&&!f&&typeof b==="string"&&d===B)){if(e)f||(a[c.expando]=f=++c.uuid);else h=a;if(typeof b==="object")if(e)h[f]=c.extend(h[f],b);else c.extend(h,b);else if(e&&!h[f])h[f]={};a=e?h[f]:h;if(d!==B)a[b]=d;return typeof b==="string"?a[b]:a}}},removeData:function(a,b){if(c.acceptData(a)){a=a==E?ra:a;var d=a.nodeType,e=d?a[c.expando]:a,f=c.cache,h=d?f[e]:e;if(b){if(h){delete h[b];d&&c.isEmptyObject(h)&&c.removeData(a)}}else if(d&&c.support.deleteExpando)delete a[c.expando];else if(a.removeAttribute)a.removeAttribute(c.expando); +else if(d)delete f[e];else for(var l in a)delete a[l]}},acceptData:function(a){if(a.nodeName){var b=c.noData[a.nodeName.toLowerCase()];if(b)return!(b===true||a.getAttribute("classid")!==b)}return true}});c.fn.extend({data:function(a,b){var d=null;if(typeof a==="undefined"){if(this.length){var e=this[0].attributes,f;d=c.data(this[0]);for(var h=0,l=e.length;h-1)return true;return false},val:function(a){if(!arguments.length){var b=this[0];if(b){if(c.nodeName(b,"option")){var d=b.attributes.value;return!d||d.specified?b.value:b.text}if(c.nodeName(b,"select")){var e=b.selectedIndex;d=[];var f=b.options;b=b.type==="select-one"; +if(e<0)return null;var h=b?e:0;for(e=b?e+1:f.length;h=0;else if(c.nodeName(this,"select")){var A=c.makeArray(r);c("option",this).each(function(){this.selected=c.inArray(c(this).val(),A)>=0});if(!A.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true}, +attr:function(a,b,d,e){if(!a||a.nodeType===3||a.nodeType===8)return B;if(e&&b in c.attrFn)return c(a)[b](d);e=a.nodeType!==1||!c.isXMLDoc(a);var f=d!==B;b=e&&c.props[b]||b;var h=Ta.test(b);if((b in a||a[b]!==B)&&e&&!h){if(f){b==="type"&&Ua.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");if(d===null)a.nodeType===1&&a.removeAttribute(b);else a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&& +b.specified?b.value:Va.test(a.nodeName)||Wa.test(a.nodeName)&&a.href?0:B;return a[b]}if(!c.support.style&&e&&b==="style"){if(f)a.style.cssText=""+d;return a.style.cssText}f&&a.setAttribute(b,""+d);if(!a.attributes[b]&&a.hasAttribute&&!a.hasAttribute(b))return B;a=!c.support.hrefNormalized&&e&&h?a.getAttribute(b,2):a.getAttribute(b);return a===null?B:a}});var X=/\.(.*)$/,ia=/^(?:textarea|input|select)$/i,La=/\./g,Ma=/ /g,Xa=/[^\w\s.|`]/g,Ya=function(a){return a.replace(Xa,"\\$&")},ua={focusin:0,focusout:0}; +c.event={add:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(c.isWindow(a)&&a!==E&&!a.frameElement)a=E;if(d===false)d=U;else if(!d)return;var f,h;if(d.handler){f=d;d=f.handler}if(!d.guid)d.guid=c.guid++;if(h=c.data(a)){var l=a.nodeType?"events":"__events__",k=h[l],o=h.handle;if(typeof k==="function"){o=k.handle;k=k.events}else if(!k){a.nodeType||(h[l]=h=function(){});h.events=k={}}if(!o)h.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem, +arguments):B};o.elem=a;b=b.split(" ");for(var x=0,r;l=b[x++];){h=f?c.extend({},f):{handler:d,data:e};if(l.indexOf(".")>-1){r=l.split(".");l=r.shift();h.namespace=r.slice(0).sort().join(".")}else{r=[];h.namespace=""}h.type=l;if(!h.guid)h.guid=d.guid;var A=k[l],C=c.event.special[l]||{};if(!A){A=k[l]=[];if(!C.setup||C.setup.call(a,e,r,o)===false)if(a.addEventListener)a.addEventListener(l,o,false);else a.attachEvent&&a.attachEvent("on"+l,o)}if(C.add){C.add.call(a,h);if(!h.handler.guid)h.handler.guid= +d.guid}A.push(h);c.event.global[l]=true}a=null}}},global:{},remove:function(a,b,d,e){if(!(a.nodeType===3||a.nodeType===8)){if(d===false)d=U;var f,h,l=0,k,o,x,r,A,C,J=a.nodeType?"events":"__events__",w=c.data(a),I=w&&w[J];if(w&&I){if(typeof I==="function"){w=I;I=I.events}if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(f in I)c.event.remove(a,f+b)}else{for(b=b.split(" ");f=b[l++];){r=f;k=f.indexOf(".")<0;o=[];if(!k){o=f.split(".");f=o.shift();x=RegExp("(^|\\.)"+ +c.map(o.slice(0).sort(),Ya).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(A=I[f])if(d){r=c.event.special[f]||{};for(h=e||0;h=0){a.type=f=f.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[f]&&c.each(c.cache,function(){this.events&&this.events[f]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType=== +8)return B;a.result=B;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(e=d.nodeType?c.data(d,"handle"):(c.data(d,"__events__")||{}).handle)&&e.apply(d,b);e=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+f]&&d["on"+f].apply(d,b)===false){a.result=false;a.preventDefault()}}catch(h){}if(!a.isPropagationStopped()&&e)c.event.trigger(a,b,e,true);else if(!a.isDefaultPrevented()){var l;e=a.target;var k=f.replace(X,""),o=c.nodeName(e,"a")&&k=== +"click",x=c.event.special[k]||{};if((!x._default||x._default.call(d,a)===false)&&!o&&!(e&&e.nodeName&&c.noData[e.nodeName.toLowerCase()])){try{if(e[k]){if(l=e["on"+k])e["on"+k]=null;c.event.triggered=true;e[k]()}}catch(r){}if(l)e["on"+k]=l;c.event.triggered=false}}},handle:function(a){var b,d,e,f;d=[];var h=c.makeArray(arguments);a=h[0]=c.event.fix(a||E.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;if(!b){e=a.type.split(".");a.type=e.shift();d=e.slice(0).sort();e=RegExp("(^|\\.)"+ +d.join("\\.(?:.*\\.)?")+"(\\.|$)")}a.namespace=a.namespace||d.join(".");f=c.data(this,this.nodeType?"events":"__events__");if(typeof f==="function")f=f.events;d=(f||{})[a.type];if(f&&d){d=d.slice(0);f=0;for(var l=d.length;f-1?c.map(a.options,function(e){return e.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},Z=function(a,b){var d=a.target,e,f;if(!(!ia.test(d.nodeName)||d.readOnly)){e=c.data(d,"_change_data");f=xa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",f);if(!(e===B||f===e))if(e!=null||f){a.type="change";a.liveFired= +B;return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:Z,beforedeactivate:Z,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return Z.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return Z.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,"_change_data",xa(a))}},setup:function(){if(this.type=== +"file")return false;for(var a in V)c.event.add(this,a+".specialChange",V[a]);return ia.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return ia.test(this.nodeName)}};V=c.event.special.change.filters;V.focus=V.beforeactivate}t.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(e){e=c.event.fix(e);e.type=b;return c.event.trigger(e,null,e.target)}c.event.special[b]={setup:function(){ua[b]++===0&&t.addEventListener(a,d,true)},teardown:function(){--ua[b]=== +0&&t.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,e,f){if(typeof d==="object"){for(var h in d)this[b](h,e,d[h],f);return this}if(c.isFunction(e)||e===false){f=e;e=B}var l=b==="one"?c.proxy(f,function(o){c(this).unbind(o,l);return f.apply(this,arguments)}):f;if(d==="unload"&&b!=="one")this.one(d,e,f);else{h=0;for(var k=this.length;h0?this.bind(b,d,e):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});E.attachEvent&&!E.addEventListener&&c(E).bind("unload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}}); +(function(){function a(g,i,n,m,p,q){p=0;for(var u=m.length;p0){F=y;break}}y=y[g]}m[p]=F}}}var d=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e=0,f=Object.prototype.toString,h=false,l=true;[0,0].sort(function(){l=false;return 0});var k=function(g,i,n,m){n=n||[];var p=i=i||t;if(i.nodeType!==1&&i.nodeType!==9)return[];if(!g||typeof g!=="string")return n;var q,u,y,F,M,N=true,O=k.isXML(i),D=[],R=g;do{d.exec("");if(q=d.exec(R)){R=q[3];D.push(q[1]);if(q[2]){F=q[3]; +break}}}while(q);if(D.length>1&&x.exec(g))if(D.length===2&&o.relative[D[0]])u=L(D[0]+D[1],i);else for(u=o.relative[D[0]]?[i]:k(D.shift(),i);D.length;){g=D.shift();if(o.relative[g])g+=D.shift();u=L(g,u)}else{if(!m&&D.length>1&&i.nodeType===9&&!O&&o.match.ID.test(D[0])&&!o.match.ID.test(D[D.length-1])){q=k.find(D.shift(),i,O);i=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]}if(i){q=m?{expr:D.pop(),set:C(m)}:k.find(D.pop(),D.length===1&&(D[0]==="~"||D[0]==="+")&&i.parentNode?i.parentNode:i,O);u=q.expr?k.filter(q.expr, +q.set):q.set;if(D.length>0)y=C(u);else N=false;for(;D.length;){q=M=D.pop();if(o.relative[M])q=D.pop();else M="";if(q==null)q=i;o.relative[M](y,q,O)}}else y=[]}y||(y=u);y||k.error(M||g);if(f.call(y)==="[object Array]")if(N)if(i&&i.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&k.contains(i,y[g])))n.push(u[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&n.push(u[g]);else n.push.apply(n,y);else C(y,n);if(F){k(F,p,n,m);k.uniqueSort(n)}return n};k.uniqueSort=function(g){if(w){h= +l;g.sort(w);if(h)for(var i=1;i0};k.find=function(g,i,n){var m;if(!g)return[];for(var p=0,q=o.order.length;p":function(g,i){var n,m=typeof i==="string",p=0,q=g.length;if(m&&!/\W/.test(i))for(i=i.toLowerCase();p=0))n||m.push(u);else if(n)i[q]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},CHILD:function(g){if(g[1]==="nth"){var i=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=i[1]+(i[2]||1)-0;g[3]=i[3]-0}g[0]=e++;return g},ATTR:function(g,i,n, +m,p,q){i=g[1].replace(/\\/g,"");if(!q&&o.attrMap[i])g[1]=o.attrMap[i];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,i,n,m,p){if(g[1]==="not")if((d.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,i);else{g=k.filter(g[3],i,n,true^p);n||m.push.apply(m,g);return false}else if(o.match.POS.test(g[0])||o.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled=== +true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,i,n){return!!k(n[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"=== +g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},setFilters:{first:function(g,i){return i===0},last:function(g,i,n,m){return i===m.length-1},even:function(g,i){return i%2===0},odd:function(g,i){return i%2===1},lt:function(g,i,n){return in[3]-0},nth:function(g,i,n){return n[3]- +0===i},eq:function(g,i,n){return n[3]-0===i}},filter:{PSEUDO:function(g,i,n,m){var p=i[1],q=o.filters[p];if(q)return q(g,n,i,m);else if(p==="contains")return(g.textContent||g.innerText||k.getText([g])||"").indexOf(i[3])>=0;else if(p==="not"){i=i[3];n=0;for(m=i.length;n=0}},ID:function(g,i){return g.nodeType===1&&g.getAttribute("id")===i},TAG:function(g,i){return i==="*"&&g.nodeType===1||g.nodeName.toLowerCase()=== +i},CLASS:function(g,i){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(i)>-1},ATTR:function(g,i){var n=i[1];n=o.attrHandle[n]?o.attrHandle[n](g):g[n]!=null?g[n]:g.getAttribute(n);var m=n+"",p=i[2],q=i[4];return n==null?p==="!=":p==="="?m===q:p==="*="?m.indexOf(q)>=0:p==="~="?(" "+m+" ").indexOf(q)>=0:!q?m&&n!==false:p==="!="?m!==q:p==="^="?m.indexOf(q)===0:p==="$="?m.substr(m.length-q.length)===q:p==="|="?m===q||m.substr(0,q.length+1)===q+"-":false},POS:function(g,i,n,m){var p=o.setFilters[i[2]]; +if(p)return p(g,n,i,m)}}},x=o.match.POS,r=function(g,i){return"\\"+(i-0+1)},A;for(A in o.match){o.match[A]=RegExp(o.match[A].source+/(?![^\[]*\])(?![^\(]*\))/.source);o.leftMatch[A]=RegExp(/(^(?:.|\r|\n)*?)/.source+o.match[A].source.replace(/\\(\d+)/g,r))}var C=function(g,i){g=Array.prototype.slice.call(g,0);if(i){i.push.apply(i,g);return i}return g};try{Array.prototype.slice.call(t.documentElement.childNodes,0)}catch(J){C=function(g,i){var n=0,m=i||[];if(f.call(g)==="[object Array]")Array.prototype.push.apply(m, +g);else if(typeof g.length==="number")for(var p=g.length;n";n.insertBefore(g,n.firstChild);if(t.getElementById(i)){o.find.ID=function(m,p,q){if(typeof p.getElementById!=="undefined"&&!q)return(p=p.getElementById(m[1]))?p.id===m[1]||typeof p.getAttributeNode!=="undefined"&&p.getAttributeNode("id").nodeValue===m[1]?[p]:B:[]};o.filter.ID=function(m,p){var q=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&q&&q.nodeValue===p}}n.removeChild(g); +n=g=null})();(function(){var g=t.createElement("div");g.appendChild(t.createComment(""));if(g.getElementsByTagName("*").length>0)o.find.TAG=function(i,n){var m=n.getElementsByTagName(i[1]);if(i[1]==="*"){for(var p=[],q=0;m[q];q++)m[q].nodeType===1&&p.push(m[q]);m=p}return m};g.innerHTML="";if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")o.attrHandle.href=function(i){return i.getAttribute("href",2)};g=null})();t.querySelectorAll&& +function(){var g=k,i=t.createElement("div");i.innerHTML="

";if(!(i.querySelectorAll&&i.querySelectorAll(".TEST").length===0)){k=function(m,p,q,u){p=p||t;m=m.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!u&&!k.isXML(p))if(p.nodeType===9)try{return C(p.querySelectorAll(m),q)}catch(y){}else if(p.nodeType===1&&p.nodeName.toLowerCase()!=="object"){var F=p.getAttribute("id"),M=F||"__sizzle__";F||p.setAttribute("id",M);try{return C(p.querySelectorAll("#"+M+" "+m),q)}catch(N){}finally{F|| +p.removeAttribute("id")}}return g(m,p,q,u)};for(var n in g)k[n]=g[n];i=null}}();(function(){var g=t.documentElement,i=g.matchesSelector||g.mozMatchesSelector||g.webkitMatchesSelector||g.msMatchesSelector,n=false;try{i.call(t.documentElement,"[test!='']:sizzle")}catch(m){n=true}if(i)k.matchesSelector=function(p,q){q=q.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(p))try{if(n||!o.match.PSEUDO.test(q)&&!/!=/.test(q))return i.call(p,q)}catch(u){}return k(q,null,null,[p]).length>0}})();(function(){var g= +t.createElement("div");g.innerHTML="
";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){o.order.splice(1,0,"CLASS");o.find.CLASS=function(i,n,m){if(typeof n.getElementsByClassName!=="undefined"&&!m)return n.getElementsByClassName(i[1])};g=null}}})();k.contains=t.documentElement.contains?function(g,i){return g!==i&&(g.contains?g.contains(i):true)}:t.documentElement.compareDocumentPosition? +function(g,i){return!!(g.compareDocumentPosition(i)&16)}:function(){return false};k.isXML=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false};var L=function(g,i){for(var n,m=[],p="",q=i.nodeType?[i]:i;n=o.match.PSEUDO.exec(g);){p+=n[0];g=g.replace(o.match.PSEUDO,"")}g=o.relative[g]?g+"*":g;n=0;for(var u=q.length;n0)for(var h=d;h0},closest:function(a,b){var d=[],e,f,h=this[0];if(c.isArray(a)){var l,k={},o=1;if(h&&a.length){e=0;for(f=a.length;e-1:c(h).is(e))d.push({selector:l,elem:h,level:o})}h= +h.parentNode;o++}}return d}l=cb.test(a)?c(a,b||this.context):null;e=0;for(f=this.length;e-1:c.find.matchesSelector(h,a)){d.push(h);break}else{h=h.parentNode;if(!h||!h.ownerDocument||h===b)break}d=d.length>1?c.unique(d):d;return this.pushStack(d,"closest",a)},index:function(a){if(!a||typeof a==="string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var d=typeof a==="string"?c(a,b||this.context): +c.makeArray(a),e=c.merge(this.get(),d);return this.pushStack(!d[0]||!d[0].parentNode||d[0].parentNode.nodeType===11||!e[0]||!e[0].parentNode||e[0].parentNode.nodeType===11?e:c.unique(e))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a, +2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a, +b){c.fn[a]=function(d,e){var f=c.map(this,b,d);Za.test(a)||(e=d);if(e&&typeof e==="string")f=c.filter(e,f);f=this.length>1?c.unique(f):f;if((this.length>1||ab.test(e))&&$a.test(a))f=f.reverse();return this.pushStack(f,a,bb.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return b.length===1?c.find.matchesSelector(b[0],a)?[b[0]]:[]:c.find.matches(a,b)},dir:function(a,b,d){var e=[];for(a=a[b];a&&a.nodeType!==9&&(d===B||a.nodeType!==1||!c(a).is(d));){a.nodeType===1&& +e.push(a);a=a[b]}return e},nth:function(a,b,d){b=b||1;for(var e=0;a;a=a[d])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var za=/ jQuery\d+="(?:\d+|null)"/g,$=/^\s+/,Aa=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,Ba=/<([\w:]+)/,db=/\s]+\/)>/g,P={option:[1, +""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]};P.optgroup=P.option;P.tbody=P.tfoot=P.colgroup=P.caption=P.thead;P.th=P.td;if(!c.support.htmlSerialize)P._default=[1,"div
","
"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d= +c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==B)return this.empty().append((this[0]&&this[0].ownerDocument||t).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this}, +wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})}, +prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b, +this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,e;(e=this[d])!=null;d++)if(!a||c.filter(a,[e]).length){if(!b&&e.nodeType===1){c.cleanData(e.getElementsByTagName("*"));c.cleanData([e])}e.parentNode&&e.parentNode.removeChild(e)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild); +return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,e=this.ownerDocument;if(!d){d=e.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(za,"").replace(fb,'="$1">').replace($,"")],e)[0]}else return this.cloneNode(true)});if(a===true){na(this,b);na(this.find("*"),b.find("*"))}return b},html:function(a){if(a===B)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(za,""):null; +else if(typeof a==="string"&&!Ca.test(a)&&(c.support.leadingWhitespace||!$.test(a))&&!P[(Ba.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Aa,"<$1>");try{for(var b=0,d=this.length;b0||e.cacheable||this.length>1?h.cloneNode(true):h)}k.length&&c.each(k,Oa)}return this}});c.buildFragment=function(a,b,d){var e,f,h;b=b&&b[0]?b[0].ownerDocument||b[0]:t;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===t&&!Ca.test(a[0])&&(c.support.checkClone||!Da.test(a[0]))){f=true;if(h=c.fragments[a[0]])if(h!==1)e=h}if(!e){e=b.createDocumentFragment();c.clean(a,b,e,d)}if(f)c.fragments[a[0]]=h?e:1;return{fragment:e,cacheable:f}};c.fragments={};c.each({appendTo:"append", +prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var e=[];d=c(d);var f=this.length===1&&this[0].parentNode;if(f&&f.nodeType===11&&f.childNodes.length===1&&d.length===1){d[b](this[0]);return this}else{f=0;for(var h=d.length;f0?this.clone(true):this).get();c(d[f])[b](l);e=e.concat(l)}return this.pushStack(e,a,d.selector)}}});c.extend({clean:function(a,b,d,e){b=b||t;if(typeof b.createElement==="undefined")b=b.ownerDocument|| +b[0]&&b[0].ownerDocument||t;for(var f=[],h=0,l;(l=a[h])!=null;h++){if(typeof l==="number")l+="";if(l){if(typeof l==="string"&&!eb.test(l))l=b.createTextNode(l);else if(typeof l==="string"){l=l.replace(Aa,"<$1>");var k=(Ba.exec(l)||["",""])[1].toLowerCase(),o=P[k]||P._default,x=o[0],r=b.createElement("div");for(r.innerHTML=o[1]+l+o[2];x--;)r=r.lastChild;if(!c.support.tbody){x=db.test(l);k=k==="table"&&!x?r.firstChild&&r.firstChild.childNodes:o[1]===""&&!x?r.childNodes:[];for(o=k.length- +1;o>=0;--o)c.nodeName(k[o],"tbody")&&!k[o].childNodes.length&&k[o].parentNode.removeChild(k[o])}!c.support.leadingWhitespace&&$.test(l)&&r.insertBefore(b.createTextNode($.exec(l)[0]),r.firstChild);l=r.childNodes}if(l.nodeType)f.push(l);else f=c.merge(f,l)}}if(d)for(h=0;f[h];h++)if(e&&c.nodeName(f[h],"script")&&(!f[h].type||f[h].type.toLowerCase()==="text/javascript"))e.push(f[h].parentNode?f[h].parentNode.removeChild(f[h]):f[h]);else{f[h].nodeType===1&&f.splice.apply(f,[h+1,0].concat(c.makeArray(f[h].getElementsByTagName("script")))); +d.appendChild(f[h])}return f},cleanData:function(a){for(var b,d,e=c.cache,f=c.event.special,h=c.support.deleteExpando,l=0,k;(k=a[l])!=null;l++)if(!(k.nodeName&&c.noData[k.nodeName.toLowerCase()]))if(d=k[c.expando]){if((b=e[d])&&b.events)for(var o in b.events)f[o]?c.event.remove(k,o):c.removeEvent(k,o,b.handle);if(h)delete k[c.expando];else k.removeAttribute&&k.removeAttribute(c.expando);delete e[d]}}});var Ea=/alpha\([^)]*\)/i,gb=/opacity=([^)]*)/,hb=/-([a-z])/ig,ib=/([A-Z])/g,Fa=/^-?\d+(?:px)?$/i, +jb=/^-?\d/,kb={position:"absolute",visibility:"hidden",display:"block"},Pa=["Left","Right"],Qa=["Top","Bottom"],W,Ga,aa,lb=function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){if(arguments.length===2&&b===B)return this;return c.access(this,a,b,true,function(d,e,f){return f!==B?c.style(d,e,f):c.css(d,e)})};c.extend({cssHooks:{opacity:{get:function(a,b){if(b){var d=W(a,"opacity","opacity");return d===""?"1":d}else return a.style.opacity}}},cssNumber:{zIndex:true,fontWeight:true,opacity:true, +zoom:true,lineHeight:true},cssProps:{"float":c.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,b,d,e){if(!(!a||a.nodeType===3||a.nodeType===8||!a.style)){var f,h=c.camelCase(b),l=a.style,k=c.cssHooks[h];b=c.cssProps[h]||h;if(d!==B){if(!(typeof d==="number"&&isNaN(d)||d==null)){if(typeof d==="number"&&!c.cssNumber[h])d+="px";if(!k||!("set"in k)||(d=k.set(a,d))!==B)try{l[b]=d}catch(o){}}}else{if(k&&"get"in k&&(f=k.get(a,false,e))!==B)return f;return l[b]}}},css:function(a,b,d){var e,f=c.camelCase(b), +h=c.cssHooks[f];b=c.cssProps[f]||f;if(h&&"get"in h&&(e=h.get(a,true,d))!==B)return e;else if(W)return W(a,b,f)},swap:function(a,b,d){var e={},f;for(f in b){e[f]=a.style[f];a.style[f]=b[f]}d.call(a);for(f in b)a.style[f]=e[f]},camelCase:function(a){return a.replace(hb,lb)}});c.curCSS=c.css;c.each(["height","width"],function(a,b){c.cssHooks[b]={get:function(d,e,f){var h;if(e){if(d.offsetWidth!==0)h=oa(d,b,f);else c.swap(d,kb,function(){h=oa(d,b,f)});if(h<=0){h=W(d,b,b);if(h==="0px"&&aa)h=aa(d,b,b); +if(h!=null)return h===""||h==="auto"?"0px":h}if(h<0||h==null){h=d.style[b];return h===""||h==="auto"?"0px":h}return typeof h==="string"?h:h+"px"}},set:function(d,e){if(Fa.test(e)){e=parseFloat(e);if(e>=0)return e+"px"}else return e}}});if(!c.support.opacity)c.cssHooks.opacity={get:function(a,b){return gb.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var d=a.style;d.zoom=1;var e=c.isNaN(b)?"":"alpha(opacity="+b*100+")",f= +d.filter||"";d.filter=Ea.test(f)?f.replace(Ea,e):d.filter+" "+e}};if(t.defaultView&&t.defaultView.getComputedStyle)Ga=function(a,b,d){var e;d=d.replace(ib,"-$1").toLowerCase();if(!(b=a.ownerDocument.defaultView))return B;if(b=b.getComputedStyle(a,null)){e=b.getPropertyValue(d);if(e===""&&!c.contains(a.ownerDocument.documentElement,a))e=c.style(a,d)}return e};if(t.documentElement.currentStyle)aa=function(a,b){var d,e,f=a.currentStyle&&a.currentStyle[b],h=a.style;if(!Fa.test(f)&&jb.test(f)){d=h.left; +e=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;h.left=b==="fontSize"?"1em":f||0;f=h.pixelLeft+"px";h.left=d;a.runtimeStyle.left=e}return f===""?"auto":f};W=Ga||aa;if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=a.offsetHeight;return a.offsetWidth===0&&b===0||!c.support.reliableHiddenOffsets&&(a.style.display||c.css(a,"display"))==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var mb=c.now(),nb=/)<[^<]*)*<\/script>/gi, +ob=/^(?:select|textarea)/i,pb=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,qb=/^(?:GET|HEAD)$/,Ra=/\[\]$/,T=/\=\?(&|$)/,ja=/\?/,rb=/([?&])_=[^&]*/,sb=/^(\w+:)?\/\/([^\/?#]+)/,tb=/%20/g,ub=/#.*$/,Ha=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!=="string"&&Ha)return Ha.apply(this,arguments);else if(!this.length)return this;var e=a.indexOf(" ");if(e>=0){var f=a.slice(e,a.length);a=a.slice(0,e)}e="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b=== +"object"){b=c.param(b,c.ajaxSettings.traditional);e="POST"}var h=this;c.ajax({url:a,type:e,dataType:"html",data:b,complete:function(l,k){if(k==="success"||k==="notmodified")h.html(f?c("
").append(l.responseText.replace(nb,"")).find(f):l.responseText);d&&h.each(d,[l.responseText,k,l])}});return this},serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&& +!this.disabled&&(this.checked||ob.test(this.nodeName)||pb.test(this.type))}).map(function(a,b){var d=c(this).val();return d==null?null:c.isArray(d)?c.map(d,function(e){return{name:b.name,value:e}}):{name:b.name,value:d}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,e){if(c.isFunction(b)){e=e||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:e})}, +getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,e){if(c.isFunction(b)){e=e||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:e})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:function(){return new E.XMLHttpRequest},accepts:{xml:"application/xml, text/xml",html:"text/html", +script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},ajax:function(a){var b=c.extend(true,{},c.ajaxSettings,a),d,e,f,h=b.type.toUpperCase(),l=qb.test(h);b.url=b.url.replace(ub,"");b.context=a&&a.context!=null?a.context:b;if(b.data&&b.processData&&typeof b.data!=="string")b.data=c.param(b.data,b.traditional);if(b.dataType==="jsonp"){if(h==="GET")T.test(b.url)||(b.url+=(ja.test(b.url)?"&":"?")+(b.jsonp||"callback")+"=?");else if(!b.data|| +!T.test(b.data))b.data=(b.data?b.data+"&":"")+(b.jsonp||"callback")+"=?";b.dataType="json"}if(b.dataType==="json"&&(b.data&&T.test(b.data)||T.test(b.url))){d=b.jsonpCallback||"jsonp"+mb++;if(b.data)b.data=(b.data+"").replace(T,"="+d+"$1");b.url=b.url.replace(T,"="+d+"$1");b.dataType="script";var k=E[d];E[d]=function(m){if(c.isFunction(k))k(m);else{E[d]=B;try{delete E[d]}catch(p){}}f=m;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);r&&r.removeChild(A)}}if(b.dataType==="script"&&b.cache===null)b.cache= +false;if(b.cache===false&&l){var o=c.now(),x=b.url.replace(rb,"$1_="+o);b.url=x+(x===b.url?(ja.test(b.url)?"&":"?")+"_="+o:"")}if(b.data&&l)b.url+=(ja.test(b.url)?"&":"?")+b.data;b.global&&c.active++===0&&c.event.trigger("ajaxStart");o=(o=sb.exec(b.url))&&(o[1]&&o[1].toLowerCase()!==location.protocol||o[2].toLowerCase()!==location.host);if(b.dataType==="script"&&h==="GET"&&o){var r=t.getElementsByTagName("head")[0]||t.documentElement,A=t.createElement("script");if(b.scriptCharset)A.charset=b.scriptCharset; +A.src=b.url;if(!d){var C=false;A.onload=A.onreadystatechange=function(){if(!C&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){C=true;c.handleSuccess(b,w,e,f);c.handleComplete(b,w,e,f);A.onload=A.onreadystatechange=null;r&&A.parentNode&&r.removeChild(A)}}}r.insertBefore(A,r.firstChild);return B}var J=false,w=b.xhr();if(w){b.username?w.open(h,b.url,b.async,b.username,b.password):w.open(h,b.url,b.async);try{if(b.data!=null&&!l||a&&a.contentType)w.setRequestHeader("Content-Type", +b.contentType);if(b.ifModified){c.lastModified[b.url]&&w.setRequestHeader("If-Modified-Since",c.lastModified[b.url]);c.etag[b.url]&&w.setRequestHeader("If-None-Match",c.etag[b.url])}o||w.setRequestHeader("X-Requested-With","XMLHttpRequest");w.setRequestHeader("Accept",b.dataType&&b.accepts[b.dataType]?b.accepts[b.dataType]+", */*; q=0.01":b.accepts._default)}catch(I){}if(b.beforeSend&&b.beforeSend.call(b.context,w,b)===false){b.global&&c.active--===1&&c.event.trigger("ajaxStop");w.abort();return false}b.global&& +c.triggerGlobal(b,"ajaxSend",[w,b]);var L=w.onreadystatechange=function(m){if(!w||w.readyState===0||m==="abort"){J||c.handleComplete(b,w,e,f);J=true;if(w)w.onreadystatechange=c.noop}else if(!J&&w&&(w.readyState===4||m==="timeout")){J=true;w.onreadystatechange=c.noop;e=m==="timeout"?"timeout":!c.httpSuccess(w)?"error":b.ifModified&&c.httpNotModified(w,b.url)?"notmodified":"success";var p;if(e==="success")try{f=c.httpData(w,b.dataType,b)}catch(q){e="parsererror";p=q}if(e==="success"||e==="notmodified")d|| +c.handleSuccess(b,w,e,f);else c.handleError(b,w,e,p);d||c.handleComplete(b,w,e,f);m==="timeout"&&w.abort();if(b.async)w=null}};try{var g=w.abort;w.abort=function(){w&&Function.prototype.call.call(g,w);L("abort")}}catch(i){}b.async&&b.timeout>0&&setTimeout(function(){w&&!J&&L("timeout")},b.timeout);try{w.send(l||b.data==null?null:b.data)}catch(n){c.handleError(b,w,null,n);c.handleComplete(b,w,e,f)}b.async||L();return w}},param:function(a,b){var d=[],e=function(h,l){l=c.isFunction(l)?l():l;d[d.length]= +encodeURIComponent(h)+"="+encodeURIComponent(l)};if(b===B)b=c.ajaxSettings.traditional;if(c.isArray(a)||a.jquery)c.each(a,function(){e(this.name,this.value)});else for(var f in a)da(f,a[f],b,e);return d.join("&").replace(tb,"+")}});c.extend({active:0,lastModified:{},etag:{},handleError:function(a,b,d,e){a.error&&a.error.call(a.context,b,d,e);a.global&&c.triggerGlobal(a,"ajaxError",[b,a,e])},handleSuccess:function(a,b,d,e){a.success&&a.success.call(a.context,e,d,b);a.global&&c.triggerGlobal(a,"ajaxSuccess", +[b,a])},handleComplete:function(a,b,d){a.complete&&a.complete.call(a.context,b,d);a.global&&c.triggerGlobal(a,"ajaxComplete",[b,a]);a.global&&c.active--===1&&c.event.trigger("ajaxStop")},triggerGlobal:function(a,b,d){(a.context&&a.context.url==null?c(a.context):c.event).trigger(b,d)},httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===1223}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"), +e=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(e)c.etag[b]=e;return a.status===304},httpData:function(a,b,d){var e=a.getResponseHeader("content-type")||"",f=b==="xml"||!b&&e.indexOf("xml")>=0;a=f?a.responseXML:a.responseText;f&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b==="json"||!b&&e.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&e.indexOf("javascript")>=0)c.globalEval(a);return a}}); +if(E.ActiveXObject)c.ajaxSettings.xhr=function(){if(E.location.protocol!=="file:")try{return new E.XMLHttpRequest}catch(a){}try{return new E.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}};c.support.ajax=!!c.ajaxSettings.xhr();var ea={},vb=/^(?:toggle|show|hide)$/,wb=/^([+\-]=)?([\d+.\-]+)(.*)$/,ba,pa=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b,d){if(a||a===0)return this.animate(S("show", +3),a,b,d);else{d=0;for(var e=this.length;d=0;e--)if(d[e].elem===this){b&&d[e](true);d.splice(e,1)}});b||this.dequeue();return this}});c.each({slideDown:S("show",1),slideUp:S("hide",1),slideToggle:S("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){c.fn[a]=function(d,e,f){return this.animate(b, +d,e,f)}});c.extend({speed:function(a,b,d){var e=a&&typeof a==="object"?c.extend({},a):{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};e.duration=c.fx.off?0:typeof e.duration==="number"?e.duration:e.duration in c.fx.speeds?c.fx.speeds[e.duration]:c.fx.speeds._default;e.old=e.complete;e.complete=function(){e.queue!==false&&c(this).dequeue();c.isFunction(e.old)&&e.old.call(this)};return e},easing:{linear:function(a,b,d,e){return d+e*a},swing:function(a,b,d,e){return(-Math.cos(a* +Math.PI)/2+0.5)*e+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||c.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a=parseFloat(c.css(this.elem,this.prop));return a&&a>-1E4?a:0},custom:function(a,b,d){function e(l){return f.step(l)} +var f=this,h=c.fx;this.startTime=c.now();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;this.pos=this.state=0;e.elem=this.elem;if(e()&&c.timers.push(e)&&!ba)ba=setInterval(h.tick,h.interval)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true; +this.custom(this.cur(),0)},step:function(a){var b=c.now(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var e in this.options.curAnim)if(this.options.curAnim[e]!==true)d=false;if(d){if(this.options.overflow!=null&&!c.support.shrinkWrapBlocks){var f=this.elem,h=this.options;c.each(["","X","Y"],function(k,o){f.style["overflow"+o]=h.overflow[k]})}this.options.hide&&c(this.elem).hide();if(this.options.hide|| +this.options.show)for(var l in this.options.curAnim)c.style(this.elem,l,this.options.orig[l]);this.options.complete.call(this.elem)}return false}else{a=b-this.startTime;this.state=a/this.options.duration;b=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||b](this.state,a,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a= +c.timers,b=0;b-1;e={};var x={};if(o)x=f.position();l=o?x.top:parseInt(l,10)||0;k=o?x.left:parseInt(k,10)||0;if(c.isFunction(b))b=b.call(a,d,h);if(b.top!=null)e.top=b.top-h.top+l;if(b.left!=null)e.left=b.left-h.left+k;"using"in b?b.using.call(a, +e):f.css(e)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),e=Ia.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.css(a,"marginTop"))||0;d.left-=parseFloat(c.css(a,"marginLeft"))||0;e.top+=parseFloat(c.css(b[0],"borderTopWidth"))||0;e.left+=parseFloat(c.css(b[0],"borderLeftWidth"))||0;return{top:d.top-e.top,left:d.left-e.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||t.body;a&&!Ia.test(a.nodeName)&& +c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(e){var f=this[0],h;if(!f)return null;if(e!==B)return this.each(function(){if(h=fa(this))h.scrollTo(!a?e:c(h).scrollLeft(),a?e:c(h).scrollTop());else this[d]=e});else return(h=fa(f))?"pageXOffset"in h?h[a?"pageYOffset":"pageXOffset"]:c.support.boxModel&&h.document.documentElement[d]||h.document.body[d]:f[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase(); +c.fn["inner"+b]=function(){return this[0]?parseFloat(c.css(this[0],d,"padding")):null};c.fn["outer"+b]=function(e){return this[0]?parseFloat(c.css(this[0],d,e?"margin":"border")):null};c.fn[d]=function(e){var f=this[0];if(!f)return e==null?null:this;if(c.isFunction(e))return this.each(function(l){var k=c(this);k[d](e.call(this,l,k[d]()))});if(c.isWindow(f))return f.document.compatMode==="CSS1Compat"&&f.document.documentElement["client"+b]||f.document.body["client"+b];else if(f.nodeType===9)return Math.max(f.documentElement["client"+ +b],f.body["scroll"+b],f.documentElement["scroll"+b],f.body["offset"+b],f.documentElement["offset"+b]);else if(e===B){f=c.css(f,d);var h=parseFloat(f);return c.isNaN(h)?f:h}else return this.css(d,typeof e==="string"?e:e+"px")}})})(window); diff --git a/addon-sdk-1.16/examples/reddit-panel/data/panel.js b/addon-sdk-1.16/examples/reddit-panel/data/panel.js new file mode 100644 index 00000000..03f803ce --- /dev/null +++ b/addon-sdk-1.16/examples/reddit-panel/data/panel.js @@ -0,0 +1,35 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// This is a content script. It executes inside the context of the Reddit page +// loaded into the panel and has access to that page's window object and other +// global objects (although the page does not have access to globals defined by +// this script unless they are explicitly attached to the window object). +// +// This content script is injected into the context of the Reddit page +// by the Panel API, which is accessed by the main add-on script in lib/main.js. +// See that script for more information about how the panel is created. + +$(window).click(function (event) { + var t = event.target; + + // Don't intercept the click if it isn't on a link. + if (t.nodeName != "A") + return; + + // Don't intercept the click if it was on one of the links in the header + // or next/previous footer, since those links should load in the panel itself. + if ($(t).parents('#header').length || $(t).parents('.nextprev').length) + return; + + // Intercept the click, passing it to the addon, which will load it in a tab. + event.stopPropagation(); + event.preventDefault(); + self.port.emit('click', t.toString()); +}); + +// Panels have an OS-specific background color by default, and the Mac OS X +// background color is dark grey, but Reddit expects its background to be white +// and looks odd when it isn't, so set it to white. +$("body").css("background", "white"); diff --git a/addon-sdk-1.16/examples/reddit-panel/lib/main.js b/addon-sdk-1.16/examples/reddit-panel/lib/main.js new file mode 100644 index 00000000..95da781c --- /dev/null +++ b/addon-sdk-1.16/examples/reddit-panel/lib/main.js @@ -0,0 +1,31 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +var data = require("sdk/self").data; + +var reddit_panel = require("sdk/panel").Panel({ + width: 240, + height: 320, + contentURL: "http://www.reddit.com/.mobile?keep_extension=True", + contentScriptFile: [data.url("jquery-1.4.4.min.js"), + data.url("panel.js")] +}); + +reddit_panel.port.on("click", function(url) { + require("sdk/tabs").open(url); +}); + +require("sdk/widget").Widget({ + id: "open-reddit-btn", + label: "Reddit", + contentURL: "http://www.reddit.com/static/favicon.ico", + panel: reddit_panel +}); + +exports.main = function(options, callbacks) { + // If you run cfx with --static-args='{"quitWhenDone":true}' this program + // will automatically quit Firefox when it's done. + if (options.staticArgs.quitWhenDone) + callbacks.quit(); +}; diff --git a/addon-sdk-1.16/examples/reddit-panel/package.json b/addon-sdk-1.16/examples/reddit-panel/package.json new file mode 100644 index 00000000..b183fac5 --- /dev/null +++ b/addon-sdk-1.16/examples/reddit-panel/package.json @@ -0,0 +1,9 @@ +{ + "license": "MPL 2.0", + "name": "reddit-panel", + "contributors": [], + "author": "Myk Melez", + "keywords": [], + "description": "Displays Reddit in a panel.", + "id": "anonid0-reddit-panel" +} diff --git a/addon-sdk-1.16/examples/reddit-panel/tests/test-main.js b/addon-sdk-1.16/examples/reddit-panel/tests/test-main.js new file mode 100644 index 00000000..09c7c086 --- /dev/null +++ b/addon-sdk-1.16/examples/reddit-panel/tests/test-main.js @@ -0,0 +1,21 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +var m = require("main"); +var self = require("sdk/self"); + +exports.testMain = function(test) { + var callbacks = { quit: function() { + test.pass(); + test.done(); + } }; + + test.waitUntilDone(); + // Make sure it doesn't crash... + m.main({ staticArgs: {quitWhenDone: true} }, callbacks); +}; + +exports.testData = function(test) { + test.assert(self.data.load("panel.js").length > 0); +}; diff --git a/addon-sdk-1.16/examples/toolbar-api/data/favicon.ico b/addon-sdk-1.16/examples/toolbar-api/data/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..ae5084bc03888f4e21da00633b349507248780ac GIT binary patch literal 15086 zcmeHu2UJzZ_wLnLQIsH3MBs8O3ifVd1yMlk4J+7C5wW9*3Kr}!*4S$-vG?8^f`VP_ z3L+g8YfMzW_w57M7=Q7X^78)ct@YknYcuDZd(N3}_Uzd+^X-jFl~47B%ECg$QmyJ& zK&7gwQmNGH-0Pose+BPVuAFzRSE)Qpt5hz0hClI*-1{v4>QR&rI#8-n8dI83YE!fn zb4otSA00%YDqldQ8D(iT?2@DN%GXZicI7x$*qKwd{PLI9hN*YVzij;jpQ%HUIzBy0 zSXu^7*XTFB(bqZ+y?ZS5&96b<@+S0cZ$sbeDx7L>$tht!a4BCb^XbcK!<-JzqkNF!}r?Vfxh#7=zBbbzV9RGgC9fRJ06a$ujM#4IXUy=_sf(CD&I^_N?-?+BlL+m!Oz1}>K+|GZj#cwh4NTwA)LIp))GP1$vCeMy z;p(vhHH_`xt#L#PO=&c8)v2iqN`>jJzLmLch zUKV|NH^m5#QV6VS4!`P7aKH643<>X0X2XqFruQ8?U3sVP!utBg!_es(YBn2(p&ct= zN#|l%?a!_yS?i^KESbq72bHI%xLl3?ilmJ8MV`#rm zL90HKuyL>fyN8y=rhcWd*v}j@TNc6cJ)7}ERt~g#;*w1DJ9IxYN8jfW41*}+lAwu* zN5jE45fnZTr>B*|k#VK5cepKn>|=+myOv;TCZLK+{O-g1ZFhO<{cgc9I1#!DNl?#? z$MjLPuw#}zt}ZW#D@%&v>@*AP7+ncxGP5xw@j1*RQ+!SD+xgwHa_)QY{m9W{-wzEB zgzwS~7&)Ug;?~JB-?I7q8z+@Y z-W5NirZl98fBF{>u`Tf_1t~AZ0Y1s~^s>H7>qguyCaazAxiRbn+!d@e3^f+adib znCsvkCW>a6ptNEjQ@A2;DV(CjkwURQGcdh_d?7j96l!`LJeRjST_ zMmzUyk>V|9^IM)2nWsb#e|Cu89aIH%}0DaZtN?VOG4|rT;omX?~YITcq z5a*M2ccJe7!*n>lLreWqQt02LA`je4}f38`>r@Z^~BP z9K>&P6#v<=sgSelp^JtJhheOB3P$Nu&r6D4^{qdumv!p9v)+Kbdiy3QbE$S{GPEO8 zp&vk>&G|~t-*uhuLG5!RN9}p3@1ND~T1LbMjNp8=QB7Dg7S8t^Iyzeb2qs_?&h790tqU zk@nger{3hYTk585$vQ*&SDC-rUeRsuhp58LL1$?=E!Po)G^47O;mw}p?REvj`N37eBI zV7v7x?Dl2DadigNv(w<*=ho#vs^7-vLQQ>R>ReghaV$%Ji+y*Ybj!76L!Z30L@;e@ z0By_fCd$=1h{mox(4l4(^sZbQ0ToK4t*$hhxoA-6#%q*b`xItZe}UcMXRzP<6lKPz zzE=e&ihudtp>1>JM}1R{ZEuck8DC;+V&m;;SKZm?{b@%-SntS0R2Xpy<-+em`&|k& z^HZT-l!A(@;xM?51^QI6K$mi*(AwDwzT1w&aZMKN5_1#`#V-B~hZRp@JtKM8?<(ho zuKm>{y$|aaz317v*qXi>F?G2|8%{u_L9yuFx*5i~n_;YHN%;5dgeK$X!)MYo3~O5s z!@e>{uSzA+!_5MIE*9wN(-8K%Gtl?puQ0p!4*4Ixht+{sa9EXf3$2Nn(Hvdt1zbd6yn;iAZE766P`yjErd}Gickk;2&!RDJ(w#v zN}qWM-$!rZcKcT}Ob04#jf8gn{p?TcSNq*Qr>y7cM-APmZyDe89|qQItZ)Es4fkWR zcQH(FWsU{y3t(Y~f>_isSLU@VgvrecVInb%ZBz)M^^0IgT{8@*ZjO-a_tBQ?LfeG* znDrd+x$qeEhtJ~*<@bkkz}=UM5BJOUrHZW$V4sJu&qLS`{?}1^)INAmT7lI)isQ$C z5?I^Q0&9C&V0qW#nA6q_v)hqYOf-lor#{#Ja~F#3*f9z!=d85)k`2HzdP#(uWg7Ho$dgGB7HHOLn0 z`;@}EUY1zUp*X&8ZHB4rqgm{qySJ|(?C>%8KYEYjtlyn$eB;>IXCErgJJAkEn5OT+ zwu0AaUG*P_X29b;`aW!LAnO$$lc#&_lw^+aG-yWAFSNM= z7tSjcxS#18I0ENqmqhfek~lY`6pn?L#?f&$IP$F{_6>K!cJ|T!1w9e@8VFCz!9>}g zJb3r?KPt|d6 z!ohFtaeh@GUcG&b;VC&d^!yzLow|X-w||y34SyRtgV!es5!bT<4xH>N`uEovy}(r9Mcx zz7Ol74`cs}9Be*(4i&a!{Kx+6Qy`jQ36FJs?xn`A_fLzOQynKFoN;QhE%q#|h~}Gj zq2i$g9NY309`Dj2d2dBZB|P43;C|4C7>eThN;_O%T^^U0e}x0fyW!A__qY~&8uh-v zeyB{`vr?bd_vy8ELV9oQq|DI?rw6+x9PE*JV|4?>E^uZ4s;T3WIJvzB&hGO<>i*iu zIP9TF+QItVQ&z>JJw`m-?t*J8+z_?K4>zBtAuekB%eqr8E>>;Y^cU8CxoGUdlC0z` zb^Rcl%#*=KlJ<3=zU#4#E=btn#=7bt?T{xjj`|?um@m?gP}c_<;qjj8xWCB_4~~z- zlZU%=vZBXrnXzVu^`~w8yVn`fBf4Zo4!oCnvK!KmwnF-0U+$kR3 z)Cw6V1CSXReC5g6!9IVtoVSdFG~)e%KM$zByYaP-xWH&JWDl2)(gU4$hQho^8KG`55FxOiRf46 z3N^)*LTH;m@-Qua>ODb5s`r$kr(Tm2#LQ^a1yMnvEA;pGDr+)i75F&YlRP>x)T@G<1R zaxs$|A^&BpcP^JBX?!7L2$7-xVE*jmr1xTbc)b=jgB@BNsRMI^lAJ!>TEm$)7c#j)~VO9GWL_3fCZ9 zhwxh7jFI>Vm-ZWQbg z#Ai5iXw@!b`~4=ZW>1dMg*tbmzD>A=_air9Y)l?ZbSfN$39s;e!j-h<+EF+v*}n^a zpm1}-&r#akf=+((zK-(s52IY=gK%(IcAE8yEs0+c-&Nr6j%#^`LO*D5adKF-U(vZM zaSMkf+@07p`8#7n+OgnnN?nVNKVZ(o+Wun>k=%puO~UPo_>t=~op;J@i{AGJv>vhV zovQ5ETlCBBvgS~H(?7tUPi4`{LZjKV-RMHhqVp;on_?@XbMk-2k8!hJz3`r*V-xQw zJdJ`kcmFDUjlz@llcRnv1XH@S9#&Upm*1?ry^LS4*b~$2#lFA9=4}uTEcB zm-ZrdEor|0G_ojaPCPN>{y=Gs=R|DC`AabU`yeGnas6I3W+DXFcW80@Wdo!2$(UulE3&Y9INRcI?F?2Kb5wl01}>|FeX*tpn==v;Vq zlfHYAA5eJUW2oD71{&A!MDK*nuDlA!yB`}f%|;JolP zbYoJ;Z4kdmh_vGoTzg%Fr<)fl)!ziCmbaJu8Ga}C=sNo9r+!i9BC#16r{d2Q?BwwT zyUEUlR}$=E-#y8Z_jwH09+yz9^*VS~Y=)K&78EOZm$8DUjRl(8lthcN77$*!VS`|l z3yDL;z_W~@rb4%Z_2Rn%P zIyoqucnh|xaV1YQQM;h3{}MQ_cnaK!g z`v*-vhu_}&vO)j#*%yj$m-%1(qr{=a$9&Mcj4NUnK27*M8J9ipqh|e?XyW7r52u#! zRC~bJ*$r)-ZP1>ab$gv1+ELo-?8zUK+t*OY={L8xVxL&RLt}(~-X%C6d&H;<#4bKk=6@NhChR7^Xu|GK z+ixESqxVJnrMy^_%xTq*qI|i{sHK^J=GwaG>{0?<%atboZOe08<|f#pBjW+mE?+0c zNG#1zz4usHMZSUE2F3+8QI0%=!}ce+@d1euOefD4{P?}P>&=FLgr8fud|J=g!^XzM z(va9?9U%E2;%8*6DlvoK>|L2_WX=>0U1DPNkxHCQ&Y4r8_}5zJP~NZ=jh%X;v%v;E z429rdz6AU$Sd+uHMpsvBbkN(tr;H^$DpZ8U*<@Jke+|23Stxe-4XhaFu;2L<4m;V7 zwVBX{J%!WgRIL#nUC^(jpE~lkCk|u@UcUMm!rLo zN+vIy1jA^a52YXNaveJNv#9F+BkK9jLYtO;=vUbseXEp0AI3#G8A`*~(F!fB3q!MR zA4){!z+rX<^51-mqKuDNoq7T56VG9H=o$UgQ#ejczhgBm!~Ekmi0;d_yWyqvihY;6 z{-Vu`pOp2L#6QJH(q=^05(ibrtzf5rGYsZjDzQ1vLFGd4qf+QKxQvg7dS({X^Rmco zXF#(g9S*CrQDzs=bnFrgsa_BRYg(XpB@6b$mr6{fm32Y19y1PKpLqd?1(~pody4`O z-ofn38x*_v1|?!&!+vcxV@*$BJu|K8$N0xLIxH&S)^mw>CNijzjh7=NM;8fj#Znu=O#T z&sc@w&D0oCwaxr||d!5$-C4dzHel&h;@Uzz3lnDr16se&zS!Uz^c> z%#}6-RA3tzSL)2TlApc=eBB$tVqXUO-Tf6_=U>5`HdG{u1W6)bahhY`$0sPe{&}=2 zJp9AHhI2x3UHhB&4YE#eA-yJf;dqer@Nw zR}F3HH@Khp5FhPI|E|ofEG1W}ALC|qxR$GW6gsZ;>oH$!8gWU?>^tB5N?c}k>jIeD zMkGJxu@sT>IlM2iG>K=8bI*-sjrh7KhBBTtsFt}B)9PEv4Bfsmp#0Tj)FR%ZjO&?Q z{{_8L-=pVKp!S2e=-SN>HK*TEas!lY#~xeu`}t9@lA5uAu-O-uL@vXD;HeJdWt>I z-(ocL*6K69H89~V4*mj!WB?78??mn9t5I<4v)U&7RwHkjtNX{F(08LRlrhU#W3Jr` zc48IXi?0+vFR>rSz6@c>&`n{Cc}^B=m%T*81(8_as~BT#U*e~}rLZxO!aO=fc)mKo z0t-78Q{sRtx)sN~_GU_3WUNR#q(3HlSl81M+k#4ch+pmuv1k0yUb&W-*%rn#H}$tx+PAc`Ip(ts5?h_= zTLck|KTmC@!p3i!A>m3Sh7Jk92-;6q#ubCvhWo_dhu4KpKS|d2xE5Dt&Cia%!#_AN z*w8Ks z*kN;jD}*ZX5?I-zq!MG6YZ)^#-X}FsVakdH2&d2KpA5zrfupaP>mc~!-$klo zQ1D~yaxOkp9g@7y(2sr4lbB_`;{Hup-^jQX+ZoBWG3ITUK;6&e8lACM$2I9N`ksMj z`)CZF7mtwH@tC?T4I8$s$B}RwoQ|-@Nya@-POxJ;?67;74Ym);jR$XHTO=kdHnw?y zH8%8Ro7jfM%pX{B`UDoefD#{`LA&SD2P(FM^VptGv74^dLlT>524=i7a35um{8X`h ziB}7LS)Z9~XHu^1sF!BIaa9_09;cvbbPi6;qbbYvkrO$e#)Gb7?%>9VnPtWpvLzy! z*LrqpX`GyBjeViHvOly89QqJdXC$ER z{6y5~e;nTp^u(0~1ram1q!NRU{+@B@nT*4-EvF~5UnZ8pv2hN$xdM!f?;7lYt^G^l z*qU+RvInPL(dW=+rsv@|7yT!^YxgPckFRyUF})4lxb6+&I=Daair-=+d0m{Gug0ZC=D5mSV2Pt&Uc^}X0&~R7E1|^pm3Byf z(6)|++2imx%y}54!R`^|koNEzXgXNKyysw!T`pbmZTdSDd6bi`dWa9}hL3TZ&UH=C z^>!i0ro`IGa~PP1uHcuL`v<>C+h@*)dQAqbw!Xl?hD|2nCA2Y0tTrt{FR0 zvk`RmAyi3NX2SWovTUbot&LvX!+dp$>&+QROnRw$ib|iuZMv@+aJz=S$IZ8fR$OB? z=3dq-8m)r&qH*Y1R2g#Uas2scP*(kp+9tRfH#J#mDCEwuw=7LJ@ z!NaX3pa}C2kjAc&7gB&30 zS}d9ki-jTP%~TV{|5iHozxt)N|AR;Rzz5KDyn{)hb8%*x3-*S&VawQR*gUQZ_D*oZ ziP;Y5w`2`0w>*d0p||M1b1(A)%#g692okp0AaRES5_dZ13eV-9wB_EW-24t@EX=pV zQW_rdET~=0!|AzbH-<(G$&NqyC z2D`w-tq%_ckH5dEB`(aZ#{3ZtPBQ1>{Emd%}1388~pSJ>uYI+~1)? z67w!32P0)~HA+?GT<$&EVb9zU3+5~oW`0U3++6EOamBTj)#;b)v2T1OY`k{|H;Dap zViZP3Y=_yD$CC`ZpId#5@6XPSW8$5iBc3MNhG#V7%$6_xbjX-TyF29ESXmF}=T$(| zY#qe`G4pM3i{thBdUG7!;foyyhT!(zdPrl=MmlphG7jFQ6h4C8uPU z6XJI|;r`b0i2Jb$E->Hc_>4L@bMHD5-~Wp2+q)1tbBa#c! zlsOqqk#^V}DZHP=c0Ar&MafCIvq6JP3+!?EM?b_T-^S|~ckuG^^!UANCiGMFf7-xQ z_rFy>yELIjR%GzmtYh713*O90slz-W=B{kl;n7Z4>bE-cRq7L~;Pz$?OY>Yg+MKy1 z-bg*z7)g6;BXO4-?MRP1>nkGu$WXj|d=#&5FUGU8K|9lrx6u7tc>nM_$&1aN#f%Bb zJ{SDp+35ge9c!WF*d*_3fE51!Na_Jk+6(hnk5~z z{Ndxwa>^?ks8q~%Qlij&U5SR9O6D#p*PoX0dH0o)Sr?}AgxqID{hyZc^8QEPGhHk7 z^S%j(3E%HZC;tDJyl*PmF6BB*rFwrNul@X>q8ENh(UT;4fhzeR@w7d;mgBebe*rNM BA8G&q literal 0 HcmV?d00001 diff --git a/addon-sdk-1.16/examples/toolbar-api/data/index.html b/addon-sdk-1.16/examples/toolbar-api/data/index.html new file mode 100644 index 00000000..4c91a63a --- /dev/null +++ b/addon-sdk-1.16/examples/toolbar-api/data/index.html @@ -0,0 +1,21 @@ + + + + + + + + diff --git a/addon-sdk-1.16/examples/toolbar-api/lib/main.js b/addon-sdk-1.16/examples/toolbar-api/lib/main.js new file mode 100644 index 00000000..788d9645 --- /dev/null +++ b/addon-sdk-1.16/examples/toolbar-api/lib/main.js @@ -0,0 +1,48 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const { Toolbar } = require("sdk/ui/toolbar"); +const { Frame } = require("sdk/ui/frame"); +const { ActionButton } = require("sdk/ui/button/action"); + +let button = new ActionButton({ + id: "button", + label: "send!", + icon: "./favicon.ico", + onClick: () => { + frame.postMessage({ + hello: "content" + }); + } +}); + +let frame = new Frame({ + url: "./index.html", + onAttach: () => { + console.log("frame was attached"); + }, + onReady: () => { + console.log("frame document was loaded"); + }, + onLoad: () => { + console.log("frame load complete"); + }, + onMessage: (event) => { + console.log("got message from frame content", event); + if (event.data === "ping!") + event.source.postMessage("pong!", event.source.origin); + } +}); +let toolbar = new Toolbar({ + items: [frame], + title: "Addon Demo", + hidden: false, + onShow: () => { + console.log("toolbar was shown"); + }, + onHide: () => { + console.log("toolbar was hidden"); + } +}); diff --git a/addon-sdk-1.16/examples/toolbar-api/package.json b/addon-sdk-1.16/examples/toolbar-api/package.json new file mode 100644 index 00000000..a883c1e4 --- /dev/null +++ b/addon-sdk-1.16/examples/toolbar-api/package.json @@ -0,0 +1,9 @@ +{ + "name": "toolbar-api", + "title": "toolbar-api", + "id": "toolbar-api", + "description": "a toolbar api example", + "author": "", + "license": "MPL 2.0", + "version": "0.1" +} diff --git a/addon-sdk-1.16/lib/diffpatcher/.travis.yml b/addon-sdk-1.16/lib/diffpatcher/.travis.yml new file mode 100644 index 00000000..780731a4 --- /dev/null +++ b/addon-sdk-1.16/lib/diffpatcher/.travis.yml @@ -0,0 +1,5 @@ +language: node_js +node_js: + - 0.4 + - 0.5 + - 0.6 diff --git a/addon-sdk-1.16/lib/diffpatcher/History.md b/addon-sdk-1.16/lib/diffpatcher/History.md new file mode 100644 index 00000000..d3897880 --- /dev/null +++ b/addon-sdk-1.16/lib/diffpatcher/History.md @@ -0,0 +1,14 @@ +# Changes + +## 1.0.1 / 2013-05-01 + + - Update method library version. + +## 1.0.0 / 2012-11-09 + + - Test integration for browsers. + - New method library. + +## 0.0.1 / 2012-10-22 + + - Initial release diff --git a/addon-sdk-1.16/lib/diffpatcher/License.md b/addon-sdk-1.16/lib/diffpatcher/License.md new file mode 100644 index 00000000..ed76489a --- /dev/null +++ b/addon-sdk-1.16/lib/diffpatcher/License.md @@ -0,0 +1,18 @@ +Copyright 2012 Irakli Gozalishvili. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/addon-sdk-1.16/lib/diffpatcher/Readme.md b/addon-sdk-1.16/lib/diffpatcher/Readme.md new file mode 100644 index 00000000..1520b1c3 --- /dev/null +++ b/addon-sdk-1.16/lib/diffpatcher/Readme.md @@ -0,0 +1,70 @@ +# diffpatcher + +[![Build Status](https://secure.travis-ci.org/Gozala/diffpatcher.png)](http://travis-ci.org/Gozala/diffpatcher) + +[![Browser support](https://ci.testling.com/Gozala/diffpatcher.png)](http://ci.testling.com/Gozala/diffpatcher) + +Diffpatcher is a small library that lets you treat hashes as if they were +git repositories. + +## diff + +Diff function that takes two hashes and returns delta hash. + +```js +var diff = require("diffpatcher/diff") + +diff({ a: { b: 1 }, c: { d: 2 } }, // hash#1 + { a: { e: 3 }, c: { d: 4 } }) // hash#2 + +// => { // delta +// a: { +// b: null, // - +// e: 3 // + +// }, +// c: { +// d: 4 // ± +// } +// } +``` + +As you can see from the example above `delta` makes no real distinction between +proprety upadate and property addition. Try to think of additions as an update +from `undefined` to whatever it's being updated to. + +## patch + +Patch fuction takes a `hash` and a `delta` and returns a new `hash` which is +just like orginial but with delta applied to it. Let's apply delta from the +previous example to the first hash from the same example + + +```js +var patch = require("diffpatcher/patch") + +patch({ a: { b: 1 }, c: { d: 2 } }, // hash#1 + { // delta + a: { + b: null, // - + e: 3 // + + }, + c: { + d: 4 // ± + } + }) + +// => { a: { e: 3 }, c: { d: 4 } } // hash#2 +``` + +That's about it really, just diffing hashes and applying thes diffs on them. + + +### rebase + +And as Linus mentioned everything in git can be expressed with `rebase`, that +also pretty much the case for `diffpatcher`. `rebase` takes `target` hash, +and rebases `parent` onto it with `diff` applied. + +## Install + + npm install diffpatcher diff --git a/addon-sdk-1.16/lib/diffpatcher/diff.js b/addon-sdk-1.16/lib/diffpatcher/diff.js new file mode 100644 index 00000000..967c137e --- /dev/null +++ b/addon-sdk-1.16/lib/diffpatcher/diff.js @@ -0,0 +1,45 @@ +"use strict"; + +var method = require("../method/core") + +// Method is designed to work with data structures representing application +// state. Calling it with a state should return object representing `delta` +// that has being applied to a previous state to get to a current state. +// +// Example +// +// diff(state) // => { "item-id-1": { title: "some title" } "item-id-2": null } +var diff = method("diff@diffpatcher") + +// diff between `null` / `undefined` to any hash is a hash itself. +diff.define(null, function(from, to) { return to }) +diff.define(undefined, function(from, to) { return to }) +diff.define(Object, function(from, to) { + return calculate(from, to || {}) || {} +}) + +function calculate(from, to) { + var diff = {} + var changes = 0 + Object.keys(from).forEach(function(key) { + changes = changes + 1 + if (!(key in to) && from[key] != null) diff[key] = null + else changes = changes - 1 + }) + Object.keys(to).forEach(function(key) { + changes = changes + 1 + var previous = from[key] + var current = to[key] + if (previous === current) return (changes = changes - 1) + if (typeof(current) !== "object") return diff[key] = current + if (typeof(previous) !== "object") return diff[key] = current + var delta = calculate(previous, current) + if (delta) diff[key] = delta + else changes = changes - 1 + }) + return changes ? diff : null +} + +diff.calculate = calculate + +module.exports = diff diff --git a/addon-sdk-1.16/lib/diffpatcher/index.js b/addon-sdk-1.16/lib/diffpatcher/index.js new file mode 100644 index 00000000..91ddba42 --- /dev/null +++ b/addon-sdk-1.16/lib/diffpatcher/index.js @@ -0,0 +1,5 @@ +"use strict"; + +exports.diff = require("./diff") +exports.patch = require("./patch") +exports.rebase = require("./rebase") diff --git a/addon-sdk-1.16/lib/diffpatcher/package.json b/addon-sdk-1.16/lib/diffpatcher/package.json new file mode 100644 index 00000000..54e085d2 --- /dev/null +++ b/addon-sdk-1.16/lib/diffpatcher/package.json @@ -0,0 +1,54 @@ +{ + "name": "diffpatcher", + "id": "diffpatcher", + "version": "1.2.0", + "description": "Utilities for diff-ing & patch-ing hashes", + "keywords": [ + "diff", "patch", "rebase", "hash", "changes", "versions" + ], + "author": "Irakli Gozalishvili (http://jeditoolkit.com)", + "homepage": "https://github.com/Gozala/diffpatcher", + "repository": { + "type": "git", + "url": "https://github.com/Gozala/diffpatcher.git", + "web": "https://github.com/Gozala/diffpatcher" + }, + "bugs": { + "url": "http://github.com/Gozala/diffpatcher/issues/" + }, + "dependencies": { + "method": "~2.0.0" + }, + "devDependencies": { + "test": "~0.x.0", + "phantomify": "~0.x.0", + "retape": "~0.x.0", + "tape": "~0.1.5" + }, + "main": "./index.js", + "scripts": { + "test": "npm run test-node && npm run test-browser", + "test-browser": "node ./node_modules/phantomify/bin/cmd.js ./test/common.js", + "test-node": "node ./test/common.js", + "test-tap": "node ./test/tap.js" + }, + "testling": { + "files": "test/tap.js", + "browsers": [ + "ie/9..latest", + "chrome/25..latest", + "firefox/20..latest", + "safari/6..latest", + "opera/11.0..latest", + "iphone/6..latest", + "ipad/6..latest", + "android-browser/4.2..latest" + ] + }, + "licenses": [ + { + "type": "MIT", + "url": "https://github.com/Gozala/diffpatcher/License.md" + } + ] +} diff --git a/addon-sdk-1.16/lib/diffpatcher/patch.js b/addon-sdk-1.16/lib/diffpatcher/patch.js new file mode 100644 index 00000000..9271e889 --- /dev/null +++ b/addon-sdk-1.16/lib/diffpatcher/patch.js @@ -0,0 +1,21 @@ +"use strict"; + +var method = require("../method/core") +var rebase = require("./rebase") + +// Method is designed to work with data structures representing application +// state. Calling it with a state and delta should return object representing +// new state, with changes in `delta` being applied to previous. +// +// ## Example +// +// patch(state, { +// "item-id-1": { completed: false }, // update +// "item-id-2": null // delete +// }) +var patch = method("patch@diffpatcher") +patch.define(Object, function patch(hash, delta) { + return rebase({}, hash, delta) +}) + +module.exports = patch diff --git a/addon-sdk-1.16/lib/diffpatcher/rebase.js b/addon-sdk-1.16/lib/diffpatcher/rebase.js new file mode 100644 index 00000000..03c756fe --- /dev/null +++ b/addon-sdk-1.16/lib/diffpatcher/rebase.js @@ -0,0 +1,36 @@ +"use strict"; + +var nil = {} +var owns = ({}).hasOwnProperty + +function rebase(result, parent, delta) { + var key, current, previous, update + for (key in parent) { + if (owns.call(parent, key)) { + previous = parent[key] + update = owns.call(delta, key) ? delta[key] : nil + if (previous === null) continue + else if (previous === void(0)) continue + else if (update === null) continue + else if (update === void(0)) continue + else result[key] = previous + } + } + for (key in delta) { + if (owns.call(delta, key)) { + update = delta[key] + current = owns.call(result, key) ? result[key] : nil + if (current === update) continue + else if (update === null) continue + else if (update === void(0)) continue + else if (current === nil) result[key] = update + else if (typeof(update) !== "object") result[key] = update + else if (typeof(current) !== "object") result[key] = update + else result[key]= rebase({}, current, update) + } + } + + return result +} + +module.exports = rebase diff --git a/addon-sdk-1.16/lib/diffpatcher/test/common.js b/addon-sdk-1.16/lib/diffpatcher/test/common.js new file mode 100644 index 00000000..dbc79013 --- /dev/null +++ b/addon-sdk-1.16/lib/diffpatcher/test/common.js @@ -0,0 +1,3 @@ +"use strict"; + +require("test").run(require("./index")) diff --git a/addon-sdk-1.16/lib/diffpatcher/test/diff.js b/addon-sdk-1.16/lib/diffpatcher/test/diff.js new file mode 100644 index 00000000..d1d67400 --- /dev/null +++ b/addon-sdk-1.16/lib/diffpatcher/test/diff.js @@ -0,0 +1,59 @@ +"use strict"; + +var diff = require("../diff") + +exports["test diff from null"] = function(assert) { + var to = { a: 1, b: 2 } + assert.equal(diff(null, to), to, "diff null to x returns x") + assert.equal(diff(void(0), to), to, "diff undefined to x returns x") + +} + +exports["test diff to null"] = function(assert) { + var from = { a: 1, b: 2 } + assert.deepEqual(diff({ a: 1, b: 2 }, null), + { a: null, b: null }, + "diff x null returns x with all properties nullified") +} + +exports["test diff identical"] = function(assert) { + assert.deepEqual(diff({}, {}), {}, "diff on empty objects is {}") + + assert.deepEqual(diff({ a: 1, b: 2 }, { a: 1, b: 2 }), {}, + "if properties match diff is {}") + + assert.deepEqual(diff({ a: 1, b: { c: { d: 3, e: 4 } } }, + { a: 1, b: { c: { d: 3, e: 4 } } }), {}, + "diff between identical nested hashes is {}") + +} + +exports["test diff delete"] = function(assert) { + assert.deepEqual(diff({ a: 1, b: 2 }, { b: 2 }), { a: null }, + "missing property is deleted") + assert.deepEqual(diff({ a: 1, b: 2 }, { a: 2 }), { a: 2, b: null }, + "missing property is deleted another updated") + assert.deepEqual(diff({ a: 1, b: 2 }, {}), { a: null, b: null }, + "missing propertes are deleted") + assert.deepEqual(diff({ a: 1, b: { c: { d: 2 } } }, {}), + { a: null, b: null }, + "missing deep propertes are deleted") + assert.deepEqual(diff({ a: 1, b: { c: { d: 2 } } }, { b: { c: {} } }), + { a: null, b: { c: { d: null } } }, + "missing nested propertes are deleted") +} + +exports["test add update"] = function(assert) { + assert.deepEqual(diff({ a: 1, b: 2 }, { b: 2, c: 3 }), { a: null, c: 3 }, + "delete and add") + assert.deepEqual(diff({ a: 1, b: 2 }, { a: 2, c: 3 }), { a: 2, b: null, c: 3 }, + "delete and adds") + assert.deepEqual(diff({}, { a: 1, b: 2 }), { a: 1, b: 2 }, + "diff on empty objcet returns equivalen of to") + assert.deepEqual(diff({ a: 1, b: { c: { d: 2 } } }, { d: 3 }), + { a: null, b: null, d: 3 }, + "missing deep propertes are deleted") + assert.deepEqual(diff({ b: { c: {} }, d: null }, { a: 1, b: { c: { d: 2 } } }), + { a: 1, b: { c: { d: 2 } } }, + "missing nested propertes are deleted") +} diff --git a/addon-sdk-1.16/lib/diffpatcher/test/index.js b/addon-sdk-1.16/lib/diffpatcher/test/index.js new file mode 100644 index 00000000..c06407e7 --- /dev/null +++ b/addon-sdk-1.16/lib/diffpatcher/test/index.js @@ -0,0 +1,14 @@ +"use strict"; + +var diff = require("../diff") +var patch = require("../patch") + +exports["test diff"] = require("./diff") +exports["test patch"] = require("./patch") + +exports["test patch(a, diff(a, b)) => b"] = function(assert) { + var a = { a: { b: 1 }, c: { d: 2 } } + var b = { a: { e: 3 }, c: { d: 4 } } + + assert.deepEqual(patch(a, diff(a, b)), b, "patch(a, diff(a, b)) => b") +} diff --git a/addon-sdk-1.16/lib/diffpatcher/test/patch.js b/addon-sdk-1.16/lib/diffpatcher/test/patch.js new file mode 100644 index 00000000..dc2e3822 --- /dev/null +++ b/addon-sdk-1.16/lib/diffpatcher/test/patch.js @@ -0,0 +1,83 @@ +"use strict"; + +var patch = require("../patch") + +exports["test patch delete"] = function(assert) { + var hash = { a: 1, b: 2 } + + assert.deepEqual(patch(hash, { a: null }), { b: 2 }, "null removes property") +} + +exports["test patch delete with void"] = function(assert) { + var hash = { a: 1, b: 2 } + + assert.deepEqual(patch(hash, { a: void(0) }), { b: 2 }, + "void(0) removes property") +} + +exports["test patch delete missing"] = function(assert) { + assert.deepEqual(patch({ a: 1, b: 2 }, { c: null }), + { a: 1, b: 2 }, + "null removes property if exists"); + + assert.deepEqual(patch({ a: 1, b: 2 }, { c: void(0) }), + { a: 1, b: 2 }, + "void removes property if exists"); +} + +exports["test delete deleted"] = function(assert) { + assert.deepEqual(patch({ a: null, b: 2, c: 3, d: void(0)}, + { a: void(0), b: null, d: null }), + {c: 3}, + "removed all existing and non existing"); +} + +exports["test update deleted"] = function(assert) { + assert.deepEqual(patch({ a: null, b: void(0), c: 3}, + { a: { b: 2 } }), + { a: { b: 2 }, c: 3 }, + "replace deleted"); +} + +exports["test patch delete with void"] = function(assert) { + var hash = { a: 1, b: 2 } + + assert.deepEqual(patch(hash, { a: void(0) }), { b: 2 }, + "void(0) removes property") +} + + +exports["test patch addition"] = function(assert) { + var hash = { a: 1, b: 2 } + + assert.deepEqual(patch(hash, { c: 3 }), { a: 1, b: 2, c: 3 }, + "new properties are added") +} + +exports["test patch addition"] = function(assert) { + var hash = { a: 1, b: 2 } + + assert.deepEqual(patch(hash, { c: 3 }), { a: 1, b: 2, c: 3 }, + "new properties are added") +} + +exports["test hash on itself"] = function(assert) { + var hash = { a: 1, b: 2 } + + assert.deepEqual(patch(hash, hash), hash, + "applying hash to itself returns hash itself") +} + +exports["test patch with empty delta"] = function(assert) { + var hash = { a: 1, b: 2 } + + assert.deepEqual(patch(hash, {}), hash, + "applying empty delta results in no changes") +} + +exports["test patch nested data"] = function(assert) { + assert.deepEqual(patch({ a: { b: 1 }, c: { d: 2 } }, + { a: { b: null, e: 3 }, c: { d: 4 } }), + { a: { e: 3 }, c: { d: 4 } }, + "nested structures can also be patched") +} diff --git a/addon-sdk-1.16/lib/diffpatcher/test/tap.js b/addon-sdk-1.16/lib/diffpatcher/test/tap.js new file mode 100644 index 00000000..e550b82f --- /dev/null +++ b/addon-sdk-1.16/lib/diffpatcher/test/tap.js @@ -0,0 +1,3 @@ +"use strict"; + +require("retape")(require("./index")) diff --git a/addon-sdk-1.16/lib/main.js/main.js b/addon-sdk-1.16/lib/main.js/main.js new file mode 100644 index 00000000..fbbbd652 --- /dev/null +++ b/addon-sdk-1.16/lib/main.js/main.js @@ -0,0 +1,20 @@ +//var jQuery = require("lib/3rd/jquery-1.8.2.min.js"); +//var _ = require("lib/3rd/underscore.js"); +//var Backbone = require("lib/3rd/backbone.js"); +var buttons = require('sdk/ui/button/action'); +var tabs = require("sdk/tabs"); + +var button = buttons.ActionButton({ + id: "mozilla-link", + label: "Visit Mozilla", + icon: { + "16": "./icon-16.png", + "32": "./icon-32.png", + "64": "./icon-64.png" + }, + onClick: handleClick +}); + +function handleClick(state) { + tabs.open("http://www.mozilla.org/"); +} \ No newline at end of file diff --git a/addon-sdk-1.16/lib/method/.travis.yml b/addon-sdk-1.16/lib/method/.travis.yml new file mode 100644 index 00000000..780731a4 --- /dev/null +++ b/addon-sdk-1.16/lib/method/.travis.yml @@ -0,0 +1,5 @@ +language: node_js +node_js: + - 0.4 + - 0.5 + - 0.6 diff --git a/addon-sdk-1.16/lib/method/History.md b/addon-sdk-1.16/lib/method/History.md new file mode 100644 index 00000000..95258c45 --- /dev/null +++ b/addon-sdk-1.16/lib/method/History.md @@ -0,0 +1,55 @@ +# Changes + +## 1.0.2 / 2012-12-26 + + - Delegate to polymorphic methods from `.define` and `.implement` so, they + can be overidden. + +## 1.0.1 / 2012-11-11 + + - Fix issues with different `Error` types as they all inherit from + `Error`. + +## 1.0.0 / 2012-11-09 + + - Add browser test integration. + - Fix cross-browser incompatibilities & test failures. + - Add support for host objects. + - Add optional `hint` argument for method to ease debugging. + - Remove default implementation at definition time. + +## 0.1.1 / 2012-10-15 + + - Fix regression causing custom type implementation to be stored on objects. + +## 0.1.0 / 2012-10-15 + + - Remove dependency on name module. + - Implement fallback for engines that do not support ES5. + - Add support for built-in type extensions without extending their prototypes. + - Make API for default definitions more intuitive. + Skipping type argument now defines default: + + isFoo.define(function(value) { + return false + }) + + - Make exposed `define` and `implement` polymorphic. + - Removed dev dependency on swank-js. + - Primitive types `string, number, boolean` no longer inherit method + implementations from `Object`. + +## 0.0.3 / 2012-07-17 + + - Remove module boilerplate + +## 0.0.2 / 2012-06-26 + + - Name changes to make it less conflicting with other library conventions. + - Expose function version of `define` & `implement` methods. + - Expose `Null` and `Undefined` object holding implementations for an + associated types. + +## 0.0.1 / 2012-06-25 + + - Initial release diff --git a/addon-sdk-1.16/lib/method/License.md b/addon-sdk-1.16/lib/method/License.md new file mode 100644 index 00000000..ed76489a --- /dev/null +++ b/addon-sdk-1.16/lib/method/License.md @@ -0,0 +1,18 @@ +Copyright 2012 Irakli Gozalishvili. All rights reserved. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. diff --git a/addon-sdk-1.16/lib/method/Readme.md b/addon-sdk-1.16/lib/method/Readme.md new file mode 100644 index 00000000..9584c916 --- /dev/null +++ b/addon-sdk-1.16/lib/method/Readme.md @@ -0,0 +1,117 @@ +# method + +[![Build Status](https://secure.travis-ci.org/Gozala/method.png)](http://travis-ci.org/Gozala/method) + +Library provides an API for defining polymorphic methods that dispatch on the +first argument type. This provides a powerful way for decouple abstraction +interface definition from an actual implementation per type, without risks +of interference with other libraries. + +### Motivation + + - Provide a high-performance, dynamic polymorphism construct as an + alternative to existing object methods that does not provides any + mechanics for guarding against name conflicts. + - Allow independent extension of types, and implementations of methods + on types, by different parties. + +## Install + + npm install method + +## Use + +```js +var method = require("method") + +// Define `isWatchable` method that can be implemented for any type. +var isWatchable = method("isWatchable") + +// If you call it on any object it will +// throw as nothing implements that method yet. +//isWatchable({}) // => Exception: method is not implemented + +// If you define private method on `Object.prototype` +// all objects will inherit it. +Object.prototype[isWatchable] = function() { + return false; +} + +isWatchable({}) // => false + + +// Although `isWatchable` property above will be enumerable and there for +// may damage some assumbtions made by other libraries. There for it"s +// recomended to use built-in helpers methods that will define extension +// without breaking assumbtions made by other libraries: + +isWatchable.define(Object, function() { return false }) + + +// There are primitive types in JS that won"t inherit methods from Object: +isWatchable(null) // => Exception: method is not implemented + +// One could either implement methods for such types: +isWatchable.define(null, function() { return false }) +isWatchable.define(undefined, function() { return false }) + +// Or simply define default implementation: +isWatchable.define(function() { return false }) + +// Alternatively default implementation may be provided at creation: +isWatchable = method(function() { return false }) + +// Method dispatches on an first argument type. That allows us to create +// new types with an alternative implementations: +function Watchable() {} +isWatchable.define(Watchable, function() { return true }) + +// This will make all `Watchable` instances watchable! +isWatchable(new Watchable()) // => true + +// Arbitrary objects can also be extended to implement given method. For example +// any object can simply made watchable: +function watchable(object) { + return isWatchable.implement(objct, function() { return true }) +} + +isWatchable(watchable({})) // => true + +// Full protocols can be defined with such methods: +var observers = "observers@" + module.filename +var watchers = method("watchers") +var watch = method("watch") +var unwatch = method("unwatch") + +watchers.define(Watchable, function(target) { + return target[observers] || (target[observers] = []) +}) + +watch.define(Watchable, function(target, watcher) { + var observers = watchers(target) + if (observers.indexOf(watcher) < 0) observers.push(watcher) + return target +}) +unwatch.define(Watchable, function(target, watcher) { + var observers = watchers(target) + var index = observers.indexOf(watcher) + if (observers.indexOf(watcher) >= 0) observers.unshift(watcher) + return target +}) + +// Define type Port that inherits form Watchable + +function Port() {} +Port.prototype = Object.create(Watchable.prototype) + +var emit = method("emit") +emit.define(Port, function(port, message) { + watchers(port).slice().forEach(function(watcher) { + watcher(message) + }) +}) + +var p = new Port() +watch(p, console.log) +emit(p, "hello world") // => info: "hello world" +``` diff --git a/addon-sdk-1.16/lib/method/core.js b/addon-sdk-1.16/lib/method/core.js new file mode 100644 index 00000000..a6a5261e --- /dev/null +++ b/addon-sdk-1.16/lib/method/core.js @@ -0,0 +1,225 @@ +"use strict"; + +var defineProperty = Object.defineProperty || function(object, name, property) { + object[name] = property.value + return object +} + +// Shortcut for `Object.prototype.toString` for faster access. +var typefy = Object.prototype.toString + +// Map to for jumping from typeof(value) to associated type prefix used +// as a hash in the map of builtin implementations. +var types = { "function": "Object", "object": "Object" } + +// Array is used to save method implementations for the host objects in order +// to avoid extending them with non-primitive values that could cause leaks. +var host = [] +// Hash map is used to save method implementations for builtin types in order +// to avoid extending their prototypes. This also allows to share method +// implementations for types across diff contexts / frames / compartments. +var builtin = {} + +function Primitive() {} +function ObjectType() {} +ObjectType.prototype = new Primitive() +function ErrorType() {} +ErrorType.prototype = new ObjectType() + +var Default = builtin.Default = Primitive.prototype +var Null = builtin.Null = new Primitive() +var Void = builtin.Void = new Primitive() +builtin.String = new Primitive() +builtin.Number = new Primitive() +builtin.Boolean = new Primitive() + +builtin.Object = ObjectType.prototype +builtin.Error = ErrorType.prototype + +builtin.EvalError = new ErrorType() +builtin.InternalError = new ErrorType() +builtin.RangeError = new ErrorType() +builtin.ReferenceError = new ErrorType() +builtin.StopIteration = new ErrorType() +builtin.SyntaxError = new ErrorType() +builtin.TypeError = new ErrorType() +builtin.URIError = new ErrorType() + + +function Method(hint) { + /** + Private Method is a callable private name that dispatches on the first + arguments same named Method: + + method(object, ...rest) => object[method](...rest) + + Optionally hint string may be provided that will be used in generated names + to ease debugging. + + ## Example + + var foo = Method() + + // Implementation for any types + foo.define(function(value, arg1, arg2) { + // ... + }) + + // Implementation for a specific type + foo.define(BarType, function(bar, arg1, arg2) { + // ... + }) + **/ + + // Create an internal unique name if `hint` is provided it is used to + // prefix name to ease debugging. + var name = (hint || "") + "#" + Math.random().toString(32).substr(2) + + function dispatch(value) { + // Method dispatches on type of the first argument. + // If first argument is `null` or `void` associated implementation is + // looked up in the `builtin` hash where implementations for built-ins + // are stored. + var type = null + var method = value === null ? Null[name] : + value === void(0) ? Void[name] : + // Otherwise attempt to use method with a generated private + // `name` that is supposedly in the prototype chain of the + // `target`. + value[name] || + // Otherwise assume it's one of the built-in type instances, + // in which case implementation is stored in a `builtin` hash. + // Attempt to find a implementation for the given built-in + // via constructor name and method name. + ((type = builtin[(value.constructor || "").name]) && + type[name]) || + // Otherwise assume it's a host object. For host objects + // actual method implementations are stored in the `host` + // array and only index for the implementation is stored + // in the host object's prototype chain. This avoids memory + // leaks that otherwise could happen when saving JS objects + // on host object. + host[value["!" + name] || void(0)] || + // Otherwise attempt to lookup implementation for builtins by + // a type of the value. This basically makes sure that all + // non primitive values will delegate to an `Object`. + ((type = builtin[types[typeof(value)]]) && type[name]) + + + // If method implementation for the type is still not found then + // just fallback for default implementation. + method = method || Default[name] + + + // If implementation is still not found (which also means there is no + // default) just throw an error with a descriptive message. + if (!method) throw TypeError("Type does not implements method: " + name) + + // If implementation was found then just delegate. + return method.apply(method, arguments) + } + + // Make `toString` of the dispatch return a private name, this enables + // method definition without sugar: + // + // var method = Method() + // object[method] = function() { /***/ } + dispatch.toString = function toString() { return name } + + // Copy utility methods for convenient API. + dispatch.implement = implementMethod + dispatch.define = defineMethod + + return dispatch +} + +// Create method shortcuts form functions. +var defineMethod = function defineMethod(Type, lambda) { + return define(this, Type, lambda) +} +var implementMethod = function implementMethod(object, lambda) { + return implement(this, object, lambda) +} + +// Define `implement` and `define` polymorphic methods to allow definitions +// and implementations through them. +var implement = Method("implement") +var define = Method("define") + + +function _implement(method, object, lambda) { + /** + Implements `Method` for the given `object` with a provided `implementation`. + Calling `Method` with `object` as a first argument will dispatch on provided + implementation. + **/ + return defineProperty(object, method.toString(), { + enumerable: false, + configurable: false, + writable: false, + value: lambda + }) +} + +function _define(method, Type, lambda) { + /** + Defines `Method` for the given `Type` with a provided `implementation`. + Calling `Method` with a first argument of this `Type` will dispatch on + provided `implementation`. If `Type` is a `Method` default implementation + is defined. If `Type` is a `null` or `undefined` `Method` is implemented + for that value type. + **/ + + // Attempt to guess a type via `Object.prototype.toString.call` hack. + var type = Type && typefy.call(Type.prototype) + + // If only two arguments are passed then `Type` is actually an implementation + // for a default type. + if (!lambda) Default[method] = Type + // If `Type` is `null` or `void` store implementation accordingly. + else if (Type === null) Null[method] = lambda + else if (Type === void(0)) Void[method] = lambda + // If `type` hack indicates built-in type and type has a name us it to + // store a implementation into associated hash. If hash for this type does + // not exists yet create one. + else if (type !== "[object Object]" && Type.name) { + var Bulitin = builtin[Type.name] || (builtin[Type.name] = new ObjectType()) + Bulitin[method] = lambda + } + // If `type` hack indicates an object, that may be either object or any + // JS defined "Class". If name of the constructor is `Object`, assume it's + // built-in `Object` and store implementation accordingly. + else if (Type.name === "Object") + builtin.Object[method] = lambda + // Host objects are pain!!! Every browser does some crazy stuff for them + // So far all browser seem to not implement `call` method for host object + // constructors. If that is a case here, assume it's a host object and + // store implementation in a `host` array and store `index` in the array + // in a `Type.prototype` itself. This avoids memory leaks that could be + // caused by storing JS objects on a host objects. + else if (Type.call === void(0)) { + var index = host.indexOf(lambda) + if (index < 0) index = host.push(lambda) - 1 + // Prefix private name with `!` so it can be dispatched from the method + // without type checks. + implement("!" + method, Type.prototype, index) + } + // If Got that far `Type` is user defined JS `Class`. Define private name + // as hidden property on it's prototype. + else + implement(method, Type.prototype, lambda) +} + +// And provided implementations for a polymorphic equivalents. +_define(define, _define) +_define(implement, _implement) + +// Define exports on `Method` as it's only thing being exported. +Method.implement = implement +Method.define = define +Method.Method = Method +Method.method = Method +Method.builtin = builtin +Method.host = host + +module.exports = Method diff --git a/addon-sdk-1.16/lib/method/package.json b/addon-sdk-1.16/lib/method/package.json new file mode 100644 index 00000000..7bb004e2 --- /dev/null +++ b/addon-sdk-1.16/lib/method/package.json @@ -0,0 +1,41 @@ +{ + "name": "method", + "id": "method", + "version": "1.0.2", + "description": "Functional polymorphic method dispatch", + "keywords": [ + "method", + "dispatch", + "protocol", + "polymorphism", + "type dispatch" + ], + "author": "Irakli Gozalishvili (http://jeditoolkit.com)", + "homepage": "https://github.com/Gozala/method", + "main": "./core.js", + "repository": { + "type": "git", + "url": "https://github.com/Gozala/method.git", + "web": "https://github.com/Gozala/method" + }, + "bugs": { + "url": "http://github.com/Gozala/method/issues/" + }, + "devDependencies": { + "test": "~0.x.0", + "repl-utils": "~2.0.1", + "phantomify": "~0.1.0" + }, + "scripts": { + "test": "npm run test-node && npm run test-browser", + "test-browser": "node ./node_modules/phantomify/bin/cmd.js ./test/browser.js", + "test-node": "node ./test/common.js", + "repl": "node node_modules/repl-utils" + }, + "licenses": [ + { + "type": "MIT", + "url": "https://github.com/Gozala/method/License.md" + } + ] +} diff --git a/addon-sdk-1.16/lib/method/test/browser.js b/addon-sdk-1.16/lib/method/test/browser.js new file mode 100644 index 00000000..7c8e6cd5 --- /dev/null +++ b/addon-sdk-1.16/lib/method/test/browser.js @@ -0,0 +1,20 @@ +"use strict"; + +exports["test common"] = require("./common") + +var Method = require("../core") + +exports["test host objects"] = function(assert) { + var isElement = Method("is-element") + isElement.define(function() { return false }) + + isElement.define(Element, function() { return true }) + + assert.notDeepEqual(typeof(Element.prototype[isElement]), "number", + "Host object's prototype is extended with a number value") + + assert.ok(!isElement({}), "object is not an Element") + assert.ok(document.createElement("div"), "Element is an element") +} + +require("test").run(exports) diff --git a/addon-sdk-1.16/lib/method/test/common.js b/addon-sdk-1.16/lib/method/test/common.js new file mode 100644 index 00000000..0418c3a2 --- /dev/null +++ b/addon-sdk-1.16/lib/method/test/common.js @@ -0,0 +1,272 @@ +"use strict"; + +var Method = require("../core") + +function type(value) { + return Object.prototype.toString.call(value). + split(" "). + pop(). + split("]"). + shift(). + toLowerCase() +} + +var values = [ + null, // 0 + undefined, // 1 + Infinity, // 2 + NaN, // 3 + 5, // 4 + {}, // 5 + Object.create({}), // 6 + Object.create(null), // 7 + [], // 8 + /foo/, // 9 + new Date(), // 10 + Function, // 11 + function() {}, // 12 + true, // 13 + false, // 14 + "string" // 15 +] + +function True() { return true } +function False() { return false } + +var trues = values.map(True) +var falses = values.map(False) + +exports["test throws if not implemented"] = function(assert) { + var method = Method("nope") + + assert.throws(function() { + method({}) + }, /not implement/i, "method throws if not implemented") + + assert.throws(function() { + method(null) + }, /not implement/i, "method throws on null") +} + +exports["test all types inherit from default"] = function(assert) { + var isImplemented = Method("isImplemented") + isImplemented.define(function() { return true }) + + values.forEach(function(value) { + assert.ok(isImplemented(value), + type(value) + " inherits deafult implementation") + }) +} + +exports["test default can be implemented later"] = function(assert) { + var isImplemented = Method("isImplemented") + isImplemented.define(function() { + return true + }) + + values.forEach(function(value) { + assert.ok(isImplemented(value), + type(value) + " inherits deafult implementation") + }) +} + +exports["test dispatch not-implemented"] = function(assert) { + var isDefault = Method("isDefault") + values.forEach(function(value) { + assert.throws(function() { + isDefault(value) + }, /not implement/, type(value) + " throws if not implemented") + }) +} + +exports["test dispatch default"] = function(assert) { + var isDefault = Method("isDefault") + + // Implement default + isDefault.define(True) + assert.deepEqual(values.map(isDefault), trues, + "all implementation inherit from default") + +} + +exports["test dispatch null"] = function(assert) { + var isNull = Method("isNull") + + // Implement default + isNull.define(False) + isNull.define(null, True) + assert.deepEqual(values.map(isNull), + [ true ]. + concat(falses.slice(1)), + "only null gets methods defined for null") +} + +exports["test dispatch undefined"] = function(assert) { + var isUndefined = Method("isUndefined") + + // Implement default + isUndefined.define(False) + isUndefined.define(undefined, True) + assert.deepEqual(values.map(isUndefined), + [ false, true ]. + concat(falses.slice(2)), + "only undefined gets methods defined for undefined") +} + +exports["test dispatch object"] = function(assert) { + var isObject = Method("isObject") + + // Implement default + isObject.define(False) + isObject.define(Object, True) + assert.deepEqual(values.map(isObject), + [ false, false, false, false, false ]. + concat(trues.slice(5, 13)). + concat([false, false, false]), + "all values except primitives inherit Object methods") + +} + +exports["test dispatch number"] = function(assert) { + var isNumber = Method("isNumber") + isNumber.define(False) + isNumber.define(Number, True) + + assert.deepEqual(values.map(isNumber), + falses.slice(0, 2). + concat(true, true, true). + concat(falses.slice(5)), + "all numbers inherit from Number method") +} + +exports["test dispatch string"] = function(assert) { + var isString = Method("isString") + isString.define(False) + isString.define(String, True) + + assert.deepEqual(values.map(isString), + falses.slice(0, 15). + concat(true), + "all strings inherit from String method") +} + +exports["test dispatch function"] = function(assert) { + var isFunction = Method("isFunction") + isFunction.define(False) + isFunction.define(Function, True) + + assert.deepEqual(values.map(isFunction), + falses.slice(0, 11). + concat(true, true). + concat(falses.slice(13)), + "all functions inherit from Function method") +} + +exports["test dispatch date"] = function(assert) { + var isDate = Method("isDate") + isDate.define(False) + isDate.define(Date, True) + + assert.deepEqual(values.map(isDate), + falses.slice(0, 10). + concat(true). + concat(falses.slice(11)), + "all dates inherit from Date method") +} + +exports["test dispatch RegExp"] = function(assert) { + var isRegExp = Method("isRegExp") + isRegExp.define(False) + isRegExp.define(RegExp, True) + + assert.deepEqual(values.map(isRegExp), + falses.slice(0, 9). + concat(true). + concat(falses.slice(10)), + "all regexps inherit from RegExp method") +} + +exports["test redefine for descendant"] = function(assert) { + var isFoo = Method("isFoo") + var ancestor = {} + isFoo.implement(ancestor, function() { return true }) + var descendant = Object.create(ancestor) + isFoo.implement(descendant, function() { return false }) + + assert.ok(isFoo(ancestor), "defined on ancestor") + assert.ok(!isFoo(descendant), "overrided for descendant") +} + +exports["test on custom types"] = function(assert) { + function Bar() {} + var isBar = Method("isBar") + + isBar.define(function() { return false }) + isBar.define(Bar, function() { return true }) + + assert.ok(!isBar({}), "object is get's default implementation") + assert.ok(isBar(new Bar()), "Foo type objects get own implementation") + + var isObject = Method("isObject") + isObject.define(function() { return false }) + isObject.define(Object, function() { return true }) + + assert.ok(isObject(new Bar()), "foo inherits implementation from object") + + + isObject.define(Bar, function() { return false }) + + assert.ok(!isObject(new Bar()), + "implementation inherited form object can be overrided") +} + + +exports["test error types"] = function(assert) { + var isError = Method("isError") + isError.define(function() { return false }) + isError.define(Error, function() { return true }) + + assert.ok(isError(Error("boom")), "error is error") + assert.ok(isError(TypeError("boom")), "type error is an error") + assert.ok(isError(EvalError("boom")), "eval error is an error") + assert.ok(isError(RangeError("boom")), "range error is an error") + assert.ok(isError(ReferenceError("boom")), "reference error is an error") + assert.ok(isError(SyntaxError("boom")), "syntax error is an error") + assert.ok(isError(URIError("boom")), "URI error is an error") +} + +exports["test override define polymorphic method"] = function(assert) { + var define = Method.define + var implement = Method.implement + + var fn = Method("fn") + var methods = {} + implement(define, fn, function(method, label, implementation) { + methods[label] = implementation + }) + + function foo() {} + + define(fn, "foo-case", foo) + + assert.equal(methods["foo-case"], foo, "define set property") +} + +exports["test override define via method API"] = function(assert) { + var define = Method.define + var implement = Method.implement + + var fn = Method("fn") + var methods = {} + define.implement(fn, function(method, label, implementation) { + methods[label] = implementation + }) + + function foo() {} + + define(fn, "foo-case", foo) + + assert.equal(methods["foo-case"], foo, "define set property") +} + +require("test").run(exports) diff --git a/addon-sdk-1.16/lib/node/os.js b/addon-sdk-1.16/lib/node/os.js new file mode 100644 index 00000000..52733da3 --- /dev/null +++ b/addon-sdk-1.16/lib/node/os.js @@ -0,0 +1,76 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +'use strict'; + +module.metadata = { + "stability": "unstable" +}; + +const { Cc, Ci } = require('chrome'); +const system = require('../sdk/system'); +const runtime = require('../sdk/system/runtime'); +const oscpu = Cc["@mozilla.org/network/protocol;1?name=http"] + .getService(Ci.nsIHttpProtocolHandler).oscpu; +const hostname = Cc["@mozilla.org/network/dns-service;1"] + .getService(Ci.nsIDNSService).myHostName; +const isWindows = system.platform === 'win32'; +const endianness = ((new Uint32Array((new Uint8Array([1,2,3,4])).buffer))[0] === 0x04030201) ? 'LE' : 'BE'; + +/** + * Returns a path to a temp directory + */ +exports.tmpdir = () => system.pathFor('TmpD'); + +/** + * Returns the endianness of the architecture: either 'LE' or 'BE' + */ +exports.endianness = () => endianness; + +/** + * Returns hostname of the machine + */ +exports.hostname = () => hostname; + +/** + * Name of the OS type + * Possible values: + * https://developer.mozilla.org/en/OS_TARGET + */ +exports.type = () => runtime.OS; + +/** + * Name of the OS Platform in lower case string. + * Possible values: + * https://developer.mozilla.org/en/OS_TARGET + */ +exports.platform = () => system.platform; + +/** + * Type of processor architecture running: + * 'arm', 'ia32', 'x86', 'x64' + */ +exports.arch = () => system.architecture; + +/** + * Returns the operating system release. + */ +exports.release = () => { + let match = oscpu.match(/(\d[\.\d]*)/); + return match && match.length > 1 ? match[1] : oscpu; +}; + +/** + * Returns EOL character for the OS + */ +exports.EOL = isWindows ? '\r\n' : '\n'; + +/** + * Returns [0, 0, 0], as this is not implemented. + */ +exports.loadavg = () => [0, 0, 0]; + +['uptime', 'totalmem', 'freemem', 'cpus'].forEach(method => { + exports[method] = () => { throw new Error('os.' + method + ' is not supported.'); }; +}); diff --git a/addon-sdk-1.16/lib/sdk/addon-page.js b/addon-sdk-1.16/lib/sdk/addon-page.js new file mode 100644 index 00000000..1ea1e99e --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/addon-page.js @@ -0,0 +1,73 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +'use strict'; + +module.metadata = { + 'stability': 'deprecated' +}; + +const { WindowTracker } = require('./deprecated/window-utils'); +const { isXULBrowser } = require('./window/utils'); +const { add, remove } = require('./util/array'); +const { getTabs, closeTab, getURI } = require('./tabs/utils'); +const { data } = require('./self'); +const { ns } = require("./core/namespace"); + +const addonURL = data.url('index.html'); + +const windows = ns(); + +require("./util/deprecate").deprecateUsage( + "The addon-page module is deprecated." + + "In the new Firefox UI design all pages will include navigational elements;" + + "once the new design ships, using the addon-page module will not have any effect." +); + +WindowTracker({ + onTrack: function onTrack(window) { + if (!isXULBrowser(window) || windows(window).hideChromeForLocation) + return; + + let { XULBrowserWindow } = window; + let { hideChromeForLocation } = XULBrowserWindow; + + windows(window).hideChromeForLocation = hideChromeForLocation; + + // Augmenting the behavior of `hideChromeForLocation` method, as + // suggested by https://developer.mozilla.org/en-US/docs/Hiding_browser_chrome + XULBrowserWindow.hideChromeForLocation = function(url) { + return isAddonURL(url) || hideChromeForLocation.call(this, url); + } + }, + + onUntrack: function onUntrack(window) { + if (isXULBrowser(window)) + getTabs(window).filter(tabFilter).forEach(untrackTab.bind(null, window)); + } +}); + +function isAddonURL(url) { + if (url.indexOf(addonURL) === 0) { + let rest = url.substr(addonURL.length); + return ((rest.length === 0) || (['#','?'].indexOf(rest.charAt(0)) > -1)); + } + return false; +} + +function tabFilter(tab) { + return isAddonURL(getURI(tab)); +} + +function untrackTab(window, tab) { + // Note: `onUntrack` will be called for all windows on add-on unloads, + // so we want to clean them up from these URLs. + let { hideChromeForLocation } = windows(window); + + if (hideChromeForLocation) { + window.XULBrowserWindow.hideChromeForLocation = hideChromeForLocation.bind(window.XULBrowserWindow); + windows(window).hideChromeForLocation = null; + } + + closeTab(tab); +} diff --git a/addon-sdk-1.16/lib/sdk/addon/events.js b/addon-sdk-1.16/lib/sdk/addon/events.js new file mode 100644 index 00000000..e8379f8d --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/addon/events.js @@ -0,0 +1,54 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +'use strict'; + +module.metadata = { + 'stability': 'experimental' +}; + +let { request: hostReq, response: hostRes } = require('./host'); +let { defer: async } = require('../lang/functional'); +let { defer } = require('../core/promise'); +let { emit: emitSync, on, off } = require('../event/core'); +let { uuid } = require('../util/uuid'); +let emit = async(emitSync); + +// Map of IDs to deferreds +let requests = new Map(); + +// May not be necessary to wrap this in `async` +// once promises are async via bug 881047 +let receive = async(function ({data, id, error}) { + let request = requests.get(id); + if (request) { + if (error) request.reject(error); + else request.resolve(clone(data)); + requests.delete(id); + } +}); +on(hostRes, 'data', receive); + +/* + * Send is a helper to be used in client APIs to send + * a request to host + */ +function send (eventName, data) { + let id = uuid(); + let deferred = defer(); + requests.set(id, deferred); + emit(hostReq, 'data', { + id: id, + data: clone(data), + event: eventName + }); + return deferred.promise; +} +exports.send = send; + +/* + * Implement internal structured cloning algorithm in the future? + * http://www.whatwg.org/specs/web-apps/current-work/multipage/common-dom-interfaces.html#internal-structured-cloning-algorithm + */ +function clone (obj) JSON.parse(JSON.stringify(obj || {})) diff --git a/addon-sdk-1.16/lib/sdk/addon/host.js b/addon-sdk-1.16/lib/sdk/addon/host.js new file mode 100644 index 00000000..91aa0e86 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/addon/host.js @@ -0,0 +1,12 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "experimental" +}; + +exports.request = {}; +exports.response = {}; diff --git a/addon-sdk-1.16/lib/sdk/addon/installer.js b/addon-sdk-1.16/lib/sdk/addon/installer.js new file mode 100644 index 00000000..1242ae96 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/addon/installer.js @@ -0,0 +1,117 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +module.metadata = { + "stability": "experimental" +}; + +const { Cc, Ci, Cu } = require("chrome"); +const { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm"); +const { defer } = require("../core/promise"); +const { setTimeout } = require("../timers"); + +/** + * `install` method error codes: + * + * https://developer.mozilla.org/en/Addons/Add-on_Manager/AddonManager#AddonInstall_errors + */ +exports.ERROR_NETWORK_FAILURE = AddonManager.ERROR_NETWORK_FAILURE; +exports.ERROR_INCORRECT_HASH = AddonManager.ERROR_INCORRECT_HASH; +exports.ERROR_CORRUPT_FILE = AddonManager.ERROR_CORRUPT_FILE; +exports.ERROR_FILE_ACCESS = AddonManager.ERROR_FILE_ACCESS; + +/** + * Immediatly install an addon. + * + * @param {String} xpiPath + * file path to an xpi file to install + * @return {Promise} + * A promise resolved when the addon is finally installed. + * Resolved with addon id as value or rejected with an error code. + */ +exports.install = function install(xpiPath) { + let { promise, resolve, reject } = defer(); + + // Create nsIFile for the xpi file + let file = Cc['@mozilla.org/file/local;1'].createInstance(Ci.nsILocalFile); + try { + file.initWithPath(xpiPath); + } + catch(e) { + reject(exports.ERROR_FILE_ACCESS); + return promise; + } + + // Listen for installation end + let listener = { + onInstallEnded: function(aInstall, aAddon) { + aInstall.removeListener(listener); + // Bug 749745: on FF14+, onInstallEnded is called just before `startup()` + // is called, but we expect to resolve the promise only after it. + // As startup is called synchronously just after onInstallEnded, + // a simple setTimeout(0) is enough + setTimeout(resolve, 0, aAddon.id); + }, + onInstallFailed: function (aInstall) { + console.log("failed"); + aInstall.removeListener(listener); + reject(aInstall.error); + }, + onDownloadFailed: function(aInstall) { + this.onInstallFailed(aInstall); + } + }; + + // Order AddonManager to install the addon + AddonManager.getInstallForFile(file, function(install) { + install.addListener(listener); + install.install(); + }); + + return promise; +}; + +exports.uninstall = function uninstall(addonId) { + let { promise, resolve, reject } = defer(); + + // Listen for uninstallation end + let listener = { + onUninstalled: function onUninstalled(aAddon) { + if (aAddon.id != addonId) + return; + AddonManager.removeAddonListener(listener); + resolve(); + } + }; + AddonManager.addAddonListener(listener); + + // Order Addonmanager to uninstall the addon + getAddon(addonId).then(addon => addon.uninstall(), reject); + + return promise; +}; + +exports.disable = function disable(addonId) { + return getAddon(addonId).then(addon => { + addon.userDisabled = true; + return addonId; + }); +}; + +exports.enable = function enabled(addonId) { + return getAddon(addonId).then(addon => { + addon.userDisabled = false; + return addonId; + }); +}; + +exports.isActive = function isActive(addonId) { + return getAddon(addonId).then(addon => addon.isActive && !addon.appDisabled); +}; + +function getAddon (id) { + let { promise, resolve, reject } = defer(); + AddonManager.getAddonByID(id, addon => addon ? resolve(addon) : reject()); + return promise; +} diff --git a/addon-sdk-1.16/lib/sdk/addon/runner.js b/addon-sdk-1.16/lib/sdk/addon/runner.js new file mode 100644 index 00000000..5325941b --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/addon/runner.js @@ -0,0 +1,179 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +module.metadata = { + "stability": "experimental" +}; + +const { Cc, Ci } = require('chrome'); +const { descriptor, Sandbox, evaluate, main, resolveURI } = require('toolkit/loader'); +const { once } = require('../system/events'); +const { exit, env, staticArgs } = require('../system'); +const { when: unload } = require('../system/unload'); +const { loadReason } = require('../self'); +const { rootURI } = require("@loader/options"); +const globals = require('../system/globals'); +const xulApp = require('../system/xul-app'); +const appShellService = Cc['@mozilla.org/appshell/appShellService;1']. + getService(Ci.nsIAppShellService); + +const NAME2TOPIC = { + 'Firefox': 'sessionstore-windows-restored', + 'Fennec': 'sessionstore-windows-restored', + 'SeaMonkey': 'sessionstore-windows-restored', + 'Thunderbird': 'mail-startup-done' +}; + +// Set 'final-ui-startup' as default topic for unknown applications +let appStartup = 'final-ui-startup'; + +// Gets the topic that fit best as application startup event, in according with +// the current application (e.g. Firefox, Fennec, Thunderbird...) +for (let name of Object.keys(NAME2TOPIC)) { + if (xulApp.is(name)) { + appStartup = NAME2TOPIC[name]; + break; + } +} + +// Initializes default preferences +function setDefaultPrefs(prefsURI) { + const prefs = Cc['@mozilla.org/preferences-service;1']. + getService(Ci.nsIPrefService). + QueryInterface(Ci.nsIPrefBranch2); + const branch = prefs.getDefaultBranch(''); + const sandbox = Sandbox({ + name: prefsURI, + prototype: { + pref: function(key, val) { + switch (typeof val) { + case 'boolean': + branch.setBoolPref(key, val); + break; + case 'number': + if (val % 1 == 0) // number must be a integer, otherwise ignore it + branch.setIntPref(key, val); + break; + case 'string': + branch.setCharPref(key, val); + break; + } + } + } + }); + // load preferences. + evaluate(sandbox, prefsURI); +} + +function definePseudo(loader, id, exports) { + let uri = resolveURI(id, loader.mapping); + loader.modules[uri] = { exports: exports }; +} + +function wait(reason, options) { + once(appStartup, function() { + startup(null, options); + }); +} + +function startup(reason, options) { + // Try accessing hidden window to guess if we are running during firefox + // startup, so that we should wait for session restore event before + // running the addon + let initialized = false; + try { + appShellService.hiddenDOMWindow; + initialized = true; + } + catch(e) {} + if (reason === 'startup' || !initialized) { + return wait(reason, options); + } + + // Inject globals ASAP in order to have console API working ASAP + Object.defineProperties(options.loader.globals, descriptor(globals)); + + // NOTE: Module is intentionally required only now because it relies + // on existence of hidden window, which does not exists until startup. + let { ready } = require('../addon/window'); + // Load localization manifest and .properties files. + // Run the addon even in case of error (best effort approach) + require('../l10n/loader'). + load(rootURI). + then(null, function failure(error) { + console.info("Error while loading localization: " + error.message); + }). + then(function onLocalizationReady(data) { + // Exports data to a pseudo module so that api-utils/l10n/core + // can get access to it + definePseudo(options.loader, '@l10n/data', data ? data : null); + return ready; + }).then(function() { + run(options); + }).then(null, console.exception); + return void 0; // otherwise we raise a warning, see bug 910304 +} + +function run(options) { + try { + // Try initializing HTML localization before running main module. Just print + // an exception in case of error, instead of preventing addon to be run. + try { + // Do not enable HTML localization while running test as it is hard to + // disable. Because unit tests are evaluated in a another Loader who + // doesn't have access to this current loader. + if (options.main !== 'test-harness/run-tests') + require('../l10n/html').enable(); + } + catch(error) { + console.exception(error); + } + // Initialize inline options localization, without preventing addon to be + // run in case of error + try { + require('../l10n/prefs'); + } + catch(error) { + console.exception(error); + } + + // TODO: When bug 564675 is implemented this will no longer be needed + // Always set the default prefs, because they disappear on restart + if (options.prefsURI) { + // Only set if `prefsURI` specified + setDefaultPrefs(options.prefsURI); + } + + // this is where the addon's main.js finally run. + let program = main(options.loader, options.main); + + if (typeof(program.onUnload) === 'function') + unload(program.onUnload); + + if (typeof(program.main) === 'function') { + + program.main({ + loadReason: loadReason, + staticArgs: staticArgs + }, { + print: function print(_) { dump(_ + '\n') }, + quit: exit + }); + } + } catch (error) { + console.exception(error); + throw error; + } +} +exports.startup = startup; + +// If add-on is lunched via `cfx run` we need to use `system.exit` to let +// cfx know we're done (`cfx test` will take care of exit so we don't do +// anything here). +if (env.CFX_COMMAND === 'run') { + unload(function(reason) { + if (reason === 'shutdown') + exit(0); + }); +} diff --git a/addon-sdk-1.16/lib/sdk/addon/window.js b/addon-sdk-1.16/lib/sdk/addon/window.js new file mode 100644 index 00000000..00faec76 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/addon/window.js @@ -0,0 +1,68 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +module.metadata = { + "stability": "experimental" +}; + +const { Ci, Cc } = require("chrome"); +const { make: makeWindow, getHiddenWindow } = require("../window/utils"); +const { create: makeFrame, getDocShell } = require("../frame/utils"); +const { defer } = require("../core/promise"); +const { when: unload } = require("../system/unload"); +const cfxArgs = require("@test/options"); + +let addonPrincipal = Cc["@mozilla.org/systemprincipal;1"]. + createInstance(Ci.nsIPrincipal); + +let hiddenWindow = getHiddenWindow(); + +if (cfxArgs.parseable) { + console.info("hiddenWindow document.documentURI:" + + hiddenWindow.document.documentURI); + console.info("hiddenWindow document.readyState:" + + hiddenWindow.document.readyState); +} + +// Once Bug 565388 is fixed and shipped we'll be able to make invisible, +// permanent docShells. Meanwhile we create hidden top level window and +// use it's docShell. +let frame = makeFrame(hiddenWindow.document, { + nodeName: "iframe", + namespaceURI: "http://www.w3.org/1999/xhtml", + allowJavascript: true, + allowPlugins: true +}) +let docShell = getDocShell(frame); +let eventTarget = docShell.chromeEventHandler; + +// We need to grant docShell system principals in order to load XUL document +// from data URI into it. +docShell.createAboutBlankContentViewer(addonPrincipal); + +// Get a reference to the DOM window of the given docShell and load +// such document into that would allow us to create XUL iframes, that +// are necessary for hidden frames etc.. +let window = docShell.contentViewer.DOMDocument.defaultView; +window.location = "data:application/vnd.mozilla.xul+xml;charset=utf-8,"; + +// Create a promise that is delivered once add-on window is interactive, +// used by add-on runner to defer add-on loading until window is ready. +let { promise, resolve } = defer(); +eventTarget.addEventListener("DOMContentLoaded", function handler(event) { + eventTarget.removeEventListener("DOMContentLoaded", handler, false); + resolve(); +}, false); + + + +exports.ready = promise; +exports.window = window; + +// Still close window on unload to claim memory back early. +unload(function() { + window.close() + frame.parentNode.removeChild(frame); +}); diff --git a/addon-sdk-1.16/lib/sdk/base64.js b/addon-sdk-1.16/lib/sdk/base64.js new file mode 100644 index 00000000..5879a02f --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/base64.js @@ -0,0 +1,40 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "unstable" +}; + +const { Cu } = require("chrome"); + +// Passing an empty object as second argument to avoid scope's pollution +const { atob, btoa } = Cu.import("resource://gre/modules/Services.jsm", {}); + +function isUTF8(charset) { + let type = typeof charset; + + if (type === "undefined") + return false; + + if (type === "string" && charset.toLowerCase() === "utf-8") + return true; + + throw new Error("The charset argument can be only 'utf-8'"); +} + +exports.decode = function (data, charset) { + if (isUTF8(charset)) + return decodeURIComponent(escape(atob(data))) + + return atob(data); +} + +exports.encode = function (data, charset) { + if (isUTF8(charset)) + return btoa(unescape(encodeURIComponent(data))) + + return btoa(data); +} diff --git a/addon-sdk-1.16/lib/sdk/browser/events.js b/addon-sdk-1.16/lib/sdk/browser/events.js new file mode 100644 index 00000000..250cbd04 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/browser/events.js @@ -0,0 +1,20 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +module.metadata = { + "stability": "unstable" +}; + +const { events } = require("../window/events"); +const { filter } = require("../event/utils"); +const { isBrowser } = require("../window/utils"); + +// TODO: `isBrowser` detects weather window is a browser by checking +// `windowtype` attribute, which means that all 'open' events will be +// filtered out since document is not loaded yet. Maybe we can find a better +// implementation for `isBrowser`. Either way it's not really needed yet +// neither window tracker provides this event. + +exports.events = filter(events, function({target}) isBrowser(target)); diff --git a/addon-sdk-1.16/lib/sdk/clipboard.js b/addon-sdk-1.16/lib/sdk/clipboard.js new file mode 100644 index 00000000..e2312874 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/clipboard.js @@ -0,0 +1,335 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "stable", + "engines": { + // TODO Fennec Support 789757 + "Firefox": "*" + } +}; + +const { Cc, Ci } = require("chrome"); +const { DataURL } = require("./url"); +const errors = require("./deprecated/errors"); +const apiUtils = require("./deprecated/api-utils"); +/* +While these data flavors resemble Internet media types, they do +no directly map to them. +*/ +const kAllowableFlavors = [ + "text/unicode", + "text/html", + "image/png" + /* CURRENTLY UNSUPPORTED FLAVORS + "text/plain", + "image/jpg", + "image/jpeg", + "image/gif", + "text/x-moz-text-internal", + "AOLMAIL", + "application/x-moz-file", + "text/x-moz-url", + "text/x-moz-url-data", + "text/x-moz-url-desc", + "text/x-moz-url-priv", + "application/x-moz-nativeimage", + "application/x-moz-nativehtml", + "application/x-moz-file-promise-url", + "application/x-moz-file-promise-dest-filename", + "application/x-moz-file-promise", + "application/x-moz-file-promise-dir" + */ +]; + +/* +Aliases for common flavors. Not all flavors will +get an alias. New aliases must be approved by a +Jetpack API druid. +*/ +const kFlavorMap = [ + { short: "text", long: "text/unicode" }, + { short: "html", long: "text/html" }, + { short: "image", long: "image/png" } +]; + +let clipboardService = Cc["@mozilla.org/widget/clipboard;1"]. + getService(Ci.nsIClipboard); + +let clipboardHelper = Cc["@mozilla.org/widget/clipboardhelper;1"]. + getService(Ci.nsIClipboardHelper); + +let imageTools = Cc["@mozilla.org/image/tools;1"]. + getService(Ci.imgITools); + +exports.set = function(aData, aDataType) { + + let options = { + data: aData, + datatype: aDataType || "text" + }; + + // If `aDataType` is not given or if it's "image", the data is parsed as + // data URL to detect a better datatype + if (aData && (!aDataType || aDataType === "image")) { + try { + let dataURL = new DataURL(aData); + + options.datatype = dataURL.mimeType; + options.data = dataURL.data; + } + catch (e if e.name === "URIError") { + // Not a valid Data URL + } + } + + options = apiUtils.validateOptions(options, { + data: { + is: ["string"] + }, + datatype: { + is: ["string"] + } + }); + + let flavor = fromJetpackFlavor(options.datatype); + + if (!flavor) + throw new Error("Invalid flavor for " + options.datatype); + + // Additional checks for using the simple case + if (flavor == "text/unicode") { + clipboardHelper.copyString(options.data); + return true; + } + + // Below are the more complex cases where we actually have to work with a + // nsITransferable object + var xferable = Cc["@mozilla.org/widget/transferable;1"]. + createInstance(Ci.nsITransferable); + if (!xferable) + throw new Error("Couldn't set the clipboard due to an internal error " + + "(couldn't create a Transferable object)."); + // Bug 769440: Starting with FF16, transferable have to be inited + if ("init" in xferable) + xferable.init(null); + + switch (flavor) { + case "text/html": + // add text/html flavor + let (str = Cc["@mozilla.org/supports-string;1"]. + createInstance(Ci.nsISupportsString)) + { + str.data = options.data; + xferable.addDataFlavor(flavor); + xferable.setTransferData(flavor, str, str.data.length * 2); + } + + // add a text/unicode flavor (html converted to plain text) + let (str = Cc["@mozilla.org/supports-string;1"]. + createInstance(Ci.nsISupportsString), + converter = Cc["@mozilla.org/feed-textconstruct;1"]. + createInstance(Ci.nsIFeedTextConstruct)) + { + converter.type = "html"; + converter.text = options.data; + str.data = converter.plainText(); + xferable.addDataFlavor("text/unicode"); + xferable.setTransferData("text/unicode", str, str.data.length * 2); + } + break; + + // Set images to the clipboard is not straightforward, to have an idea how + // it works on platform side, see: + // http://mxr.mozilla.org/mozilla-central/source/content/base/src/nsCopySupport.cpp?rev=7857c5bff017#530 + case "image/png": + let image = options.data; + + let container = {}; + + try { + let input = Cc["@mozilla.org/io/string-input-stream;1"]. + createInstance(Ci.nsIStringInputStream); + + input.setData(image, image.length); + + imageTools.decodeImageData(input, flavor, container); + } + catch (e) { + throw new Error("Unable to decode data given in a valid image."); + } + + // Store directly the input stream makes the cliboard's data available + // for Firefox but not to the others application or to the OS. Therefore, + // a `nsISupportsInterfacePointer` object that reference an `imgIContainer` + // with the image is needed. + var imgPtr = Cc["@mozilla.org/supports-interface-pointer;1"]. + createInstance(Ci.nsISupportsInterfacePointer); + + imgPtr.data = container.value; + + xferable.addDataFlavor(flavor); + xferable.setTransferData(flavor, imgPtr, -1); + + break; + default: + throw new Error("Unable to handle the flavor " + flavor + "."); + } + + // TODO: Not sure if this will ever actually throw. -zpao + try { + clipboardService.setData( + xferable, + null, + clipboardService.kGlobalClipboard + ); + } catch (e) { + throw new Error("Couldn't set clipboard data due to an internal error: " + e); + } + return true; +}; + + +exports.get = function(aDataType) { + let options = { + datatype: aDataType + }; + + // Figure out the best data type for the clipboard's data, if omitted + if (!aDataType) { + if (~currentFlavors().indexOf("image")) + options.datatype = "image"; + else + options.datatype = "text"; + } + + options = apiUtils.validateOptions(options, { + datatype: { + is: ["string"] + } + }); + + var xferable = Cc["@mozilla.org/widget/transferable;1"]. + createInstance(Ci.nsITransferable); + if (!xferable) + throw new Error("Couldn't set the clipboard due to an internal error " + + "(couldn't create a Transferable object)."); + // Bug 769440: Starting with FF16, transferable have to be inited + if ("init" in xferable) + xferable.init(null); + + var flavor = fromJetpackFlavor(options.datatype); + + // Ensure that the user hasn't requested a flavor that we don't support. + if (!flavor) + throw new Error("Getting the clipboard with the flavor '" + flavor + + "' is not supported."); + + // TODO: Check for matching flavor first? Probably not worth it. + + xferable.addDataFlavor(flavor); + // Get the data into our transferable. + clipboardService.getData( + xferable, + clipboardService.kGlobalClipboard + ); + + var data = {}; + var dataLen = {}; + try { + xferable.getTransferData(flavor, data, dataLen); + } catch (e) { + // Clipboard doesn't contain data in flavor, return null. + return null; + } + + // There's no data available, return. + if (data.value === null) + return null; + + // TODO: Add flavors here as we support more in kAllowableFlavors. + switch (flavor) { + case "text/unicode": + case "text/html": + data = data.value.QueryInterface(Ci.nsISupportsString).data; + break; + case "image/png": + let dataURL = new DataURL(); + + dataURL.mimeType = flavor; + dataURL.base64 = true; + + let image = data.value; + + // Due to the differences in how images could be stored in the clipboard + // the checks below are needed. The clipboard could already provide the + // image as byte streams, but also as pointer, or as image container. + // If it's not possible obtain a byte stream, the function returns `null`. + if (image instanceof Ci.nsISupportsInterfacePointer) + image = image.data; + + if (image instanceof Ci.imgIContainer) + image = imageTools.encodeImage(image, flavor); + + if (image instanceof Ci.nsIInputStream) { + let binaryStream = Cc["@mozilla.org/binaryinputstream;1"]. + createInstance(Ci.nsIBinaryInputStream); + + binaryStream.setInputStream(image); + + dataURL.data = binaryStream.readBytes(binaryStream.available()); + + data = dataURL.toString(); + } + else + data = null; + + break; + default: + data = null; + } + + return data; +}; + +function currentFlavors() { + // Loop over kAllowableFlavors, calling hasDataMatchingFlavors for each. + // This doesn't seem like the most efficient way, but we can't get + // confirmation for specific flavors any other way. This is supposed to be + // an inexpensive call, so performance shouldn't be impacted (much). + var currentFlavors = []; + for each (var flavor in kAllowableFlavors) { + var matches = clipboardService.hasDataMatchingFlavors( + [flavor], + 1, + clipboardService.kGlobalClipboard + ); + if (matches) + currentFlavors.push(toJetpackFlavor(flavor)); + } + return currentFlavors; +}; + +Object.defineProperty(exports, "currentFlavors", { get : currentFlavors }); + +// SUPPORT FUNCTIONS //////////////////////////////////////////////////////// + +function toJetpackFlavor(aFlavor) { + for each (let flavorMap in kFlavorMap) + if (flavorMap.long == aFlavor) + return flavorMap.short; + // Return null in the case where we don't match + return null; +} + +function fromJetpackFlavor(aJetpackFlavor) { + // TODO: Handle proper flavors better + for each (let flavorMap in kFlavorMap) + if (flavorMap.short == aJetpackFlavor || flavorMap.long == aJetpackFlavor) + return flavorMap.long; + // Return null in the case where we don't match. + return null; +} diff --git a/addon-sdk-1.16/lib/sdk/console/plain-text.js b/addon-sdk-1.16/lib/sdk/console/plain-text.js new file mode 100644 index 00000000..9bcc6ae0 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/console/plain-text.js @@ -0,0 +1,79 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "unstable" +}; + +const { Cc, Ci, Cu, Cr } = require("chrome"); +const self = require("../self"); +const prefs = require("../preferences/service"); +const { merge } = require("../util/object"); +const { ConsoleAPI } = Cu.import("resource://gre/modules/devtools/Console.jsm"); + +const DEFAULT_LOG_LEVEL = "error"; +const ADDON_LOG_LEVEL_PREF = "extensions." + self.id + ".sdk.console.logLevel"; +const SDK_LOG_LEVEL_PREF = "extensions.sdk.console.logLevel"; + +let logLevel = DEFAULT_LOG_LEVEL; +function setLogLevel() { + logLevel = prefs.get(ADDON_LOG_LEVEL_PREF, + prefs.get(SDK_LOG_LEVEL_PREF, + DEFAULT_LOG_LEVEL)); +} +setLogLevel(); + +let logLevelObserver = { + QueryInterface: function(iid) { + if (!iid.equals(Ci.nsIObserver) && + !iid.equals(Ci.nsISupportsWeakReference) && + !iid.equals(Ci.nsISupports)) + throw Cr.NS_ERROR_NO_INTERFACE; + return this; + }, + observe: function(subject, topic, data) { + setLogLevel(); + } +}; +let branch = Cc["@mozilla.org/preferences-service;1"]. + getService(Ci.nsIPrefService). + getBranch(null); +branch.addObserver(ADDON_LOG_LEVEL_PREF, logLevelObserver, true); +branch.addObserver(SDK_LOG_LEVEL_PREF, logLevelObserver, true); + +function PlainTextConsole(print) { + + let consoleOptions = { + prefix: self.name + ": ", + maxLogLevel: logLevel, + dump: print + }; + let console = new ConsoleAPI(consoleOptions); + + // As we freeze the console object, we can't modify this property afterward + Object.defineProperty(console, "maxLogLevel", { + get: function() { + return logLevel; + } + }); + + // We defined the `__exposedProps__` in our console chrome object. + // Although it seems redundant, because we use `createObjectIn` too, in + // worker.js, we are following what `ConsoleAPI` does. See: + // http://mxr.mozilla.org/mozilla-central/source/dom/base/ConsoleAPI.js#132 + // + // Meanwhile we're investigating with the platform team if `__exposedProps__` + // are needed, or are just a left-over. + + console.__exposedProps__ = Object.keys(ConsoleAPI.prototype).reduce(function(exposed, prop) { + exposed[prop] = "r"; + return exposed; + }, {}); + + Object.freeze(console); + return console; +}; +exports.PlainTextConsole = PlainTextConsole; diff --git a/addon-sdk-1.16/lib/sdk/console/traceback.js b/addon-sdk-1.16/lib/sdk/console/traceback.js new file mode 100644 index 00000000..93e387b2 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/console/traceback.js @@ -0,0 +1,89 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "experimental" +}; + +const { Cc, Ci, components } = require("chrome"); +const { parseStack, sourceURI } = require("toolkit/loader"); +const { readURISync } = require("../net/url"); + +exports.sourceURI = sourceURI + +function safeGetFileLine(path, line) { + try { + var scheme = require("../url").URL(path).scheme; + // TODO: There should be an easier, more accurate way to figure out + // what's the case here. + if (!(scheme == "http" || scheme == "https")) + return readURISync(path).split("\n")[line - 1]; + } catch (e) {} + return null; +} + +function nsIStackFramesToJSON(frame) { + var stack = []; + + while (frame) { + if (frame.filename) { + stack.unshift({ + fileName: sourceURI(frame.filename), + lineNumber: frame.lineNumber, + name: frame.name + }); + } + frame = frame.caller; + } + + return stack; +}; + +var fromException = exports.fromException = function fromException(e) { + if (e instanceof Ci.nsIException) + return nsIStackFramesToJSON(e.location); + if (e.stack && e.stack.length) + return parseStack(e.stack); + if (e.fileName && typeof(e.lineNumber == "number")) + return [{fileName: sourceURI(e.fileName), + lineNumber: e.lineNumber, + name: null}]; + return []; +}; + +var get = exports.get = function get() { + return nsIStackFramesToJSON(components.stack.caller); +}; + +var format = exports.format = function format(tbOrException) { + if (tbOrException === undefined) { + tbOrException = get(); + tbOrException.pop(); + } + + var tb; + if (typeof(tbOrException) == "object" && + tbOrException.constructor.name == "Array") + tb = tbOrException; + else + tb = fromException(tbOrException); + + var lines = ["Traceback (most recent call last):"]; + + tb.forEach( + function(frame) { + if (!(frame.fileName || frame.lineNumber || frame.name)) + return; + + lines.push(' File "' + frame.fileName + '", line ' + + frame.lineNumber + ', in ' + frame.name); + var sourceLine = safeGetFileLine(frame.fileName, frame.lineNumber); + if (sourceLine) + lines.push(' ' + sourceLine.trim()); + }); + + return lines.join("\n"); +}; diff --git a/addon-sdk-1.16/lib/sdk/content/content-worker.js b/addon-sdk-1.16/lib/sdk/content/content-worker.js new file mode 100644 index 00000000..eba94913 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/content/content-worker.js @@ -0,0 +1,351 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const ContentWorker = Object.freeze({ + // TODO: Bug 727854 Use same implementation than common JS modules, + // i.e. EventEmitter module + + /** + * Create an EventEmitter instance. + */ + createEventEmitter: function createEventEmitter(emit) { + let listeners = Object.create(null); + let eventEmitter = Object.freeze({ + emit: emit, + on: function on(name, callback) { + if (typeof callback !== "function") + return this; + if (!(name in listeners)) + listeners[name] = []; + listeners[name].push(callback); + return this; + }, + once: function once(name, callback) { + eventEmitter.on(name, function onceCallback() { + eventEmitter.removeListener(name, onceCallback); + callback.apply(callback, arguments); + }); + }, + removeListener: function removeListener(name, callback) { + if (!(name in listeners)) + return; + let index = listeners[name].indexOf(callback); + if (index == -1) + return; + listeners[name].splice(index, 1); + } + }); + function onEvent(name) { + if (!(name in listeners)) + return []; + let args = Array.slice(arguments, 1); + let results = []; + for each (let callback in listeners[name]) { + results.push(callback.apply(null, args)); + } + return results; + } + function hasListenerFor(name) { + if (!(name in listeners)) + return false; + return listeners[name].length > 0; + } + return { + eventEmitter: eventEmitter, + emit: onEvent, + hasListenerFor: hasListenerFor + }; + }, + + /** + * Create an EventEmitter instance to communicate with chrome module + * by passing only strings between compartments. + * This function expects `emitToChrome` function, that allows to send + * events to the chrome module. It returns the EventEmitter as `pipe` + * attribute, and, `onChromeEvent` a function that allows chrome module + * to send event into the EventEmitter. + * + * pipe.emit --> emitToChrome + * onChromeEvent --> callback registered through pipe.on + */ + createPipe: function createPipe(emitToChrome) { + function onEvent() { + // Convert to real array + let args = Array.slice(arguments); + // JSON.stringify is buggy with cross-sandbox values, + // it may return "{}" on functions. Use a replacer to match them correctly. + function replacer(k, v) { + return typeof v === "function" ? undefined : v; + } + let str = JSON.stringify(args, replacer); + emitToChrome(str); + } + + let { eventEmitter, emit, hasListenerFor } = + ContentWorker.createEventEmitter(onEvent); + + return { + pipe: eventEmitter, + onChromeEvent: function onChromeEvent(array) { + // We either receive a stringified array, or a real array. + // We still allow to pass an array of objects, in WorkerSandbox.emitSync + // in order to allow sending DOM node reference between content script + // and modules (only used for context-menu API) + let args = typeof array == "string" ? JSON.parse(array) : array; + return emit.apply(null, args); + }, + hasListenerFor: hasListenerFor + }; + }, + + injectConsole: function injectConsole(exports, pipe) { + exports.console = Object.freeze({ + log: pipe.emit.bind(null, "console", "log"), + info: pipe.emit.bind(null, "console", "info"), + warn: pipe.emit.bind(null, "console", "warn"), + error: pipe.emit.bind(null, "console", "error"), + debug: pipe.emit.bind(null, "console", "debug"), + exception: pipe.emit.bind(null, "console", "exception"), + trace: pipe.emit.bind(null, "console", "trace") + }); + }, + + injectTimers: function injectTimers(exports, chromeAPI, pipe, console) { + // wrapped functions from `'timer'` module. + // Wrapper adds `try catch` blocks to the callbacks in order to + // emit `error` event on a symbiont if exception is thrown in + // the Worker global scope. + // @see http://www.w3.org/TR/workers/#workerutils + + // List of all living timeouts/intervals + let _timers = Object.create(null); + + // Keep a reference to original timeout functions + let { + setTimeout: chromeSetTimeout, + setInterval: chromeSetInterval, + clearTimeout: chromeClearTimeout, + clearInterval: chromeClearInterval + } = chromeAPI.timers; + + function registerTimer(timer) { + let registerMethod = null; + if (timer.kind == "timeout") + registerMethod = chromeSetTimeout; + else if (timer.kind == "interval") + registerMethod = chromeSetInterval; + else + throw new Error("Unknown timer kind: " + timer.kind); + + if (typeof timer.fun == 'string') { + let code = timer.fun; + timer.fun = () => chromeAPI.sandbox.evaluate(exports, code); + } else if (typeof timer.fun != 'function') { + throw new Error('Unsupported callback type' + typeof timer.fun); + } + + let id = registerMethod(onFire, timer.delay); + function onFire() { + try { + if (timer.kind == "timeout") + delete _timers[id]; + timer.fun.apply(null, timer.args); + } catch(e) { + console.exception(e); + let wrapper = { + instanceOfError: instanceOf(e, Error), + value: e, + }; + if (wrapper.instanceOfError) { + wrapper.value = { + message: e.message, + fileName: e.fileName, + lineNumber: e.lineNumber, + stack: e.stack, + name: e.name, + }; + } + pipe.emit('error', wrapper); + } + } + _timers[id] = timer; + return id; + } + + // copied from sdk/lang/type.js since modules are not available here + function instanceOf(value, Type) { + var isConstructorNameSame; + var isConstructorSourceSame; + + // If `instanceof` returned `true` we know result right away. + var isInstanceOf = value instanceof Type; + + // If `instanceof` returned `false` we do ducktype check since `Type` may be + // from a different sandbox. If a constructor of the `value` or a constructor + // of the value's prototype has same name and source we assume that it's an + // instance of the Type. + if (!isInstanceOf && value) { + isConstructorNameSame = value.constructor.name === Type.name; + isConstructorSourceSame = String(value.constructor) == String(Type); + isInstanceOf = (isConstructorNameSame && isConstructorSourceSame) || + instanceOf(Object.getPrototypeOf(value), Type); + } + return isInstanceOf; + } + + function unregisterTimer(id) { + if (!(id in _timers)) + return; + let { kind } = _timers[id]; + delete _timers[id]; + if (kind == "timeout") + chromeClearTimeout(id); + else if (kind == "interval") + chromeClearInterval(id); + else + throw new Error("Unknown timer kind: " + kind); + } + + function disableAllTimers() { + Object.keys(_timers).forEach(unregisterTimer); + } + + exports.setTimeout = function ContentScriptSetTimeout(callback, delay) { + return registerTimer({ + kind: "timeout", + fun: callback, + delay: delay, + args: Array.slice(arguments, 2) + }); + }; + exports.clearTimeout = function ContentScriptClearTimeout(id) { + unregisterTimer(id); + }; + + exports.setInterval = function ContentScriptSetInterval(callback, delay) { + return registerTimer({ + kind: "interval", + fun: callback, + delay: delay, + args: Array.slice(arguments, 2) + }); + }; + exports.clearInterval = function ContentScriptClearInterval(id) { + unregisterTimer(id); + }; + + // On page-hide, save a list of all existing timers before disabling them, + // in order to be able to restore them on page-show. + // These events are fired when the page goes in/out of bfcache. + // https://developer.mozilla.org/En/Working_with_BFCache + let frozenTimers = []; + pipe.on("pageshow", function onPageShow() { + frozenTimers.forEach(registerTimer); + }); + pipe.on("pagehide", function onPageHide() { + frozenTimers = []; + for (let id in _timers) + frozenTimers.push(_timers[id]); + disableAllTimers(); + // Some other pagehide listeners may register some timers that won't be + // frozen as this particular pagehide listener is called first. + // So freeze these timers on next cycle. + chromeSetTimeout(function () { + for (let id in _timers) + frozenTimers.push(_timers[id]); + disableAllTimers(); + }, 0); + }); + + // Unregister all timers when the page is destroyed + // (i.e. when it is removed from bfcache) + pipe.on("detach", function clearTimeouts() { + disableAllTimers(); + _timers = {}; + frozenTimers = []; + }); + }, + + injectMessageAPI: function injectMessageAPI(exports, pipe, console) { + + let { eventEmitter: port, emit : portEmit } = + ContentWorker.createEventEmitter(pipe.emit.bind(null, "event")); + pipe.on("event", portEmit); + + let self = { + port: port, + postMessage: pipe.emit.bind(null, "message"), + on: pipe.on.bind(null), + once: pipe.once.bind(null), + removeListener: pipe.removeListener.bind(null), + }; + Object.defineProperty(exports, "self", { + value: self + }); + + // Deprecated use of on/postMessage from globals + exports.postMessage = function deprecatedPostMessage() { + console.error("DEPRECATED: The global `postMessage()` function in " + + "content scripts is deprecated in favor of the " + + "`self.postMessage()` function, which works the same. " + + "Replace calls to `postMessage()` with calls to " + + "`self.postMessage()`." + + "For more info on `self.on`, see " + + "."); + return self.postMessage.apply(null, arguments); + }; + exports.on = function deprecatedOn() { + console.error("DEPRECATED: The global `on()` function in content " + + "scripts is deprecated in favor of the `self.on()` " + + "function, which works the same. Replace calls to `on()` " + + "with calls to `self.on()`" + + "For more info on `self.on`, see " + + "."); + return self.on.apply(null, arguments); + }; + + // Deprecated use of `onMessage` from globals + let onMessage = null; + Object.defineProperty(exports, "onMessage", { + get: function () onMessage, + set: function (v) { + if (onMessage) + self.removeListener("message", onMessage); + console.error("DEPRECATED: The global `onMessage` function in content" + + "scripts is deprecated in favor of the `self.on()` " + + "function. Replace `onMessage = function (data){}` " + + "definitions with calls to `self.on('message', " + + "function (data){})`. " + + "For more info on `self.on`, see " + + "."); + onMessage = v; + if (typeof onMessage == "function") + self.on("message", onMessage); + } + }); + }, + + injectOptions: function (exports, options) { + Object.defineProperty( exports.self, "options", { value: JSON.parse( options ) }); + }, + + inject: function (exports, chromeAPI, emitToChrome, options) { + let { pipe, onChromeEvent, hasListenerFor } = + ContentWorker.createPipe(emitToChrome); + + ContentWorker.injectConsole(exports, pipe); + ContentWorker.injectTimers(exports, chromeAPI, pipe, exports.console); + ContentWorker.injectMessageAPI(exports, pipe, exports.console); + if ( options !== undefined ) { + ContentWorker.injectOptions(exports, options); + } + + Object.freeze( exports.self ); + + return { + emitToContent: onChromeEvent, + hasListenerFor: hasListenerFor + }; + } +}); diff --git a/addon-sdk-1.16/lib/sdk/content/content.js b/addon-sdk-1.16/lib/sdk/content/content.js new file mode 100644 index 00000000..5a4697eb --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/content/content.js @@ -0,0 +1,13 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +module.metadata = { + "stability": "unstable" +}; + +exports.Loader = require('./loader').Loader; +exports.Symbiont = require('../deprecated/symbiont').Symbiont; +exports.Worker = require('./worker').Worker; + diff --git a/addon-sdk-1.16/lib/sdk/content/events.js b/addon-sdk-1.16/lib/sdk/content/events.js new file mode 100644 index 00000000..c2313fc8 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/content/events.js @@ -0,0 +1,57 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "experimental" +}; + +const { Ci } = require("chrome"); +const { open } = require("../event/dom"); +const { observe } = require("../event/chrome"); +const { filter, merge, map, expand } = require("../event/utils"); +const { windows } = require("../window/utils"); +const { events: windowEvents } = require("sdk/window/events"); + +// Note: Please note that even though pagehide event is included +// it's not observable reliably since it's not always triggered +// when closing tabs. Implementation can be imrpoved once that +// event will be necessary. +let TYPES = ["DOMContentLoaded", "load", "pageshow", "pagehide"]; + +let insert = observe("document-element-inserted"); +let windowCreate = merge([ + observe("content-document-global-created"), + observe("chrome-document-global-created") +]); +let create = map(windowCreate, function({target, data, type}) { + return { target: target.document, type: type, data: data } +}); + +function streamEventsFrom({document}) { + // Map supported event types to a streams of those events on the given + // `window` for the inserted document and than merge these streams into + // single form stream off all window state change events. + let stateChanges = TYPES.map(function(type) { + return open(document, type, { capture: true }); + }); + + // Since load events on document occur for every loded resource + return filter(merge(stateChanges), function({target}) { + return target instanceof Ci.nsIDOMDocument + }) +} +exports.streamEventsFrom = streamEventsFrom; + +let opened = windows(null, { includePrivate: true }); +let state = merge(opened.map(streamEventsFrom)); + + +let futureReady = filter(windowEvents, function({type}) + type === "DOMContentLoaded"); +let futureWindows = map(futureReady, function({target}) target); +let futureState = expand(futureWindows, streamEventsFrom); + +exports.events = merge([insert, create, state, futureState]); diff --git a/addon-sdk-1.16/lib/sdk/content/loader.js b/addon-sdk-1.16/lib/sdk/content/loader.js new file mode 100644 index 00000000..77e4a697 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/content/loader.js @@ -0,0 +1,207 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "unstable" +}; + +const { EventEmitter } = require('../deprecated/events'); +const { validateOptions } = require('../deprecated/api-utils'); +const { isValidURI, URL } = require('../url'); +const file = require('../io/file'); +const { contract } = require('../util/contract'); +const { isString, instanceOf } = require('../lang/type'); + +const LOCAL_URI_SCHEMES = ['resource', 'data']; + +// Returns `null` if `value` is `null` or `undefined`, otherwise `value`. +function ensureNull(value) value == null ? null : value + +// map of property validations +const valid = { + contentURL: { + map: function(url) !url ? ensureNull(url) : url.toString(), + is: ['undefined', 'null', 'string'], + ok: function (url) { + if (url === null) + return true; + return isValidURI(url); + }, + msg: 'The `contentURL` option must be a valid URL.' + }, + contentScriptFile: { + is: ['undefined', 'null', 'string', 'array', 'object'], + map: ensureNull, + ok: function(value) { + if (value === null) + return true; + + value = [].concat(value); + + // Make sure every item is a string or an + // URL instance, and also a local file URL. + return value.every(function (item) { + + if (!isString(item) && !(item instanceof URL)) + return false; + + try { + return ~LOCAL_URI_SCHEMES.indexOf(URL(item).scheme); + } + catch(e) { + return false; + } + }); + + }, + msg: 'The `contentScriptFile` option must be a local URL or an array of URLs.' + }, + contentScript: { + is: ['undefined', 'null', 'string', 'array'], + map: ensureNull, + ok: function(value) { + return !Array.isArray(value) || value.every( + function(item) { return typeof item === 'string' } + ); + }, + msg: 'The `contentScript` option must be a string or an array of strings.' + }, + contentScriptWhen: { + is: ['string'], + ok: function(value) { return ~['start', 'ready', 'end'].indexOf(value) }, + map: function(value) { + return value || 'end'; + }, + msg: 'The `contentScriptWhen` option must be either "start", "ready" or "end".' + }, + contentScriptOptions: { + ok: function(value) { + if ( value === undefined ) { return true; } + try { JSON.parse( JSON.stringify( value ) ); } catch(e) { return false; } + return true; + }, + map: function(value) 'undefined' === getTypeOf(value) ? null : value, + msg: 'The contentScriptOptions should be a jsonable value.' + } +}; +exports.validationAttributes = valid; + +/** + * Shortcut function to validate property with validation. + * @param {Object|Number|String} suspect + * value to validate + * @param {Object} validation + * validation rule passed to `api-utils` + */ +function validate(suspect, validation) validateOptions( + { $: suspect }, + { $: validation } +).$ + +function Allow(script) ({ + get script() script, + set script(value) script = !!value +}) + +/** + * Trait is intended to be used in some composition. It provides set of core + * properties and bounded validations to them. Trait is useful for all the + * compositions providing high level APIs for interaction with content. + * Property changes emit `"propertyChange"` events on instances. + */ +const Loader = EventEmitter.compose({ + /** + * Permissions for the content, with the following keys: + * @property {Object} [allow = { script: true }] + * @property {Boolean} [allow.script = true] + * Whether or not to execute script in the content. Defaults to true. + */ + get allow() this._allow || (this._allow = Allow(true)), + set allow(value) this.allow.script = value && value.script, + _allow: null, + /** + * The content to load. Either a string of HTML or a URL. + * @type {String} + */ + get contentURL() this._contentURL, + set contentURL(value) { + value = validate(value, valid.contentURL); + if (this._contentURL != value) { + this._emit('propertyChange', { + contentURL: this._contentURL = value + }); + } + }, + _contentURL: null, + /** + * When to load the content scripts. + * Possible values are "end" (default), which loads them once all page + * contents have been loaded, "ready", which loads them once DOM nodes are + * ready (ie like DOMContentLoaded event), and "start", which loads them once + * the `window` object for the page has been created, but before any scripts + * specified by the page have been loaded. + * Property change emits `propertyChange` event on instance with this key + * and new value. + * @type {'start'|'ready'|'end'} + */ + get contentScriptWhen() this._contentScriptWhen, + set contentScriptWhen(value) { + value = validate(value, valid.contentScriptWhen); + if (value !== this._contentScriptWhen) { + this._emit('propertyChange', { + contentScriptWhen: this._contentScriptWhen = value + }); + } + }, + _contentScriptWhen: 'end', + /** + * Options avalaible from the content script as `self.options`. + * The value of options can be of any type (object, array, string, etc.) + * but only jsonable values will be available as frozen objects from the + * content script. + * Property change emits `propertyChange` event on instance with this key + * and new value. + * @type {Object} + */ + get contentScriptOptions() this._contentScriptOptions, + set contentScriptOptions(value) this._contentScriptOptions = value, + _contentScriptOptions: null, + /** + * The URLs of content scripts. + * Property change emits `propertyChange` event on instance with this key + * and new value. + * @type {String[]} + */ + get contentScriptFile() this._contentScriptFile, + set contentScriptFile(value) { + value = validate(value, valid.contentScriptFile); + if (value != this._contentScriptFile) { + this._emit('propertyChange', { + contentScriptFile: this._contentScriptFile = value + }); + } + }, + _contentScriptFile: null, + /** + * The texts of content script. + * Property change emits `propertyChange` event on instance with this key + * and new value. + * @type {String|undefined} + */ + get contentScript() this._contentScript, + set contentScript(value) { + value = validate(value, valid.contentScript); + if (value != this._contentScript) { + this._emit('propertyChange', { + contentScript: this._contentScript = value + }); + } + }, + _contentScript: null +}); +exports.Loader = Loader; + +exports.contract = contract(valid); diff --git a/addon-sdk-1.16/lib/sdk/content/mod.js b/addon-sdk-1.16/lib/sdk/content/mod.js new file mode 100644 index 00000000..6ce0c760 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/content/mod.js @@ -0,0 +1,58 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +module.metadata = { + "stability": "experimental" +}; + +const { Ci } = require("chrome"); +const method = require("../../method/core"); +const { add, remove, iterator } = require("../lang/weak-set"); + +let getTargetWindow = method("getTargetWindow"); + +getTargetWindow.define(function (target) { + if (target instanceof Ci.nsIDOMWindow) + return target; + if (target instanceof Ci.nsIDOMDocument) + return target.defaultView || null; + + return null; +}); + +exports.getTargetWindow = getTargetWindow; + +let attachTo = method("attachTo"); +exports.attachTo = attachTo; + +let detachFrom = method("detatchFrom"); +exports.detachFrom = detachFrom; + +function attach(modification, target) { + let window = getTargetWindow(target); + + attachTo(modification, window); + + // modification are stored per content; `window` reference can still be the + // same even if the content is changed, therefore `document` is used instead. + add(modification, window.document); +} +exports.attach = attach; + +function detach(modification, target) { + if (target) { + let window = getTargetWindow(target); + detachFrom(modification, window); + remove(modification, window.document); + } + else { + let documents = iterator(modification); + for (let document of documents) { + detachFrom(modification, document.defaultView); + remove(modification, document); + } + } +} +exports.detach = detach; diff --git a/addon-sdk-1.16/lib/sdk/content/sandbox.js b/addon-sdk-1.16/lib/sdk/content/sandbox.js new file mode 100644 index 00000000..7034a86c --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/content/sandbox.js @@ -0,0 +1,404 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +'use strict'; + +module.metadata = { + 'stability': 'unstable' +}; + +const { Class } = require('../core/heritage'); +const { EventTarget } = require('../event/target'); +const { on, off, emit } = require('../event/core'); +const { + requiresAddonGlobal, + attach, detach, destroy +} = require('./utils'); +const { delay: async } = require('../lang/functional'); +const { Ci, Cu, Cc } = require('chrome'); +const timer = require('../timers'); +const { URL } = require('../url'); +const { sandbox, evaluate, load } = require('../loader/sandbox'); +const { merge } = require('../util/object'); +const xulApp = require('../system/xul-app'); +const USE_JS_PROXIES = !xulApp.versionInRange(xulApp.platformVersion, + '17.0a2', '*'); +const { getTabForContentWindow } = require('../tabs/utils'); + +// WeakMap of sandboxes so we can access private values +const sandboxes = new WeakMap(); + +/* Trick the linker in order to ensure shipping these files in the XPI. + require('./content-worker.js'); + Then, retrieve URL of these files in the XPI: +*/ +let prefix = module.uri.split('sandbox.js')[0]; +const CONTENT_WORKER_URL = prefix + 'content-worker.js'; + +// Fetch additional list of domains to authorize access to for each content +// script. It is stored in manifest `metadata` field which contains +// package.json data. This list is originaly defined by authors in +// `permissions` attribute of their package.json addon file. +const permissions = require('@loader/options').metadata['permissions'] || {}; +const EXPANDED_PRINCIPALS = permissions['cross-domain-content'] || []; + +const JS_VERSION = '1.8'; + +const WorkerSandbox = Class({ + + implements: [ + EventTarget + ], + + /** + * Emit a message to the worker content sandbox + */ + emit: function emit(...args) { + // Ensure having an asynchronous behavior + let self = this; + async(function () { + emitToContent(self, JSON.stringify(args, replacer)); + }); + }, + + /** + * Synchronous version of `emit`. + * /!\ Should only be used when it is strictly mandatory /!\ + * Doesn't ensure passing only JSON values. + * Mainly used by context-menu in order to avoid breaking it. + */ + emitSync: function emitSync(...args) { + return emitToContent(this, args); + }, + + /** + * Tells if content script has at least one listener registered for one event, + * through `self.on('xxx', ...)`. + * /!\ Shouldn't be used. Implemented to avoid breaking context-menu API. + */ + hasListenerFor: function hasListenerFor(name) { + return modelFor(this).hasListenerFor(name); + }, + + /** + * Configures sandbox and loads content scripts into it. + * @param {Worker} worker + * content worker + */ + initialize: function WorkerSandbox(worker, window) { + let model = {}; + sandboxes.set(this, model); + model.worker = worker; + // We receive a wrapped window, that may be an xraywrapper if it's content + let proto = window; + + // TODO necessary? + // Ensure that `emit` has always the right `this` + this.emit = this.emit.bind(this); + this.emitSync = this.emitSync.bind(this); + + // Eventually use expanded principal sandbox feature, if some are given. + // + // But prevent it when the Worker isn't used for a content script but for + // injecting `addon` object into a Panel, Widget, ... scope. + // That's because: + // 1/ It is useless to use multiple domains as the worker is only used + // to communicate with the addon, + // 2/ By using it it would prevent the document to have access to any JS + // value of the worker. As JS values coming from multiple domain principals + // can't be accessed by 'mono-principals' (principal with only one domain). + // Even if this principal is for a domain that is specified in the multiple + // domain principal. + let principals = window; + let wantGlobalProperties = []; + if (EXPANDED_PRINCIPALS.length > 0 && !requiresAddonGlobal(worker)) { + principals = EXPANDED_PRINCIPALS.concat(window); + // We have to replace XHR constructor of the content document + // with a custom cross origin one, automagically added by platform code: + delete proto.XMLHttpRequest; + wantGlobalProperties.push('XMLHttpRequest'); + } + + // Instantiate trusted code in another Sandbox in order to prevent content + // script from messing with standard classes used by proxy and API code. + let apiSandbox = sandbox(principals, { wantXrays: true, sameZoneAs: window }); + apiSandbox.console = console; + + // Create the sandbox and bind it to window in order for content scripts to + // have access to all standard globals (window, document, ...) + let content = sandbox(principals, { + sandboxPrototype: proto, + wantXrays: true, + wantGlobalProperties: wantGlobalProperties, + sameZoneAs: window, + metadata: { SDKContentScript: true } + }); + model.sandbox = content; + + // We have to ensure that window.top and window.parent are the exact same + // object than window object, i.e. the sandbox global object. But not + // always, in case of iframes, top and parent are another window object. + let top = window.top === window ? content : content.top; + let parent = window.parent === window ? content : content.parent; + merge(content, { + // We need 'this === window === top' to be true in toplevel scope: + get window() content, + get top() top, + get parent() parent, + // Use the Greasemonkey naming convention to provide access to the + // unwrapped window object so the content script can access document + // JavaScript values. + // NOTE: this functionality is experimental and may change or go away + // at any time! + get unsafeWindow() window.wrappedJSObject + }); + + // Load trusted code that will inject content script API. + // We need to expose JS objects defined in same principal in order to + // avoid having any kind of wrapper. + load(apiSandbox, CONTENT_WORKER_URL); + + // prepare a clean `self.options` + let options = 'contentScriptOptions' in worker ? + JSON.stringify(worker.contentScriptOptions) : + undefined; + + // Then call `inject` method and communicate with this script + // by trading two methods that allow to send events to the other side: + // - `onEvent` called by content script + // - `result.emitToContent` called by addon script + // Bug 758203: We have to explicitely define `__exposedProps__` in order + // to allow access to these chrome object attributes from this sandbox with + // content priviledges + // https://developer.mozilla.org/en/XPConnect_wrappers#Other_security_wrappers + let onEvent = onContentEvent.bind(null, this); + // `ContentWorker` is defined in CONTENT_WORKER_URL file + let chromeAPI = createChromeAPI(); + let result = apiSandbox.ContentWorker.inject(content, chromeAPI, onEvent, options); + + // Merge `emitToContent` and `hasListenerFor` into our private + // model of the WorkerSandbox so we can communicate with content + // script + merge(model, result); + + // Handle messages send by this script: + setListeners(this); + + // Inject `addon` global into target document if document is trusted, + // `addon` in document is equivalent to `self` in content script. + if (requiresAddonGlobal(worker)) { + Object.defineProperty(getUnsafeWindow(window), 'addon', { + value: content.self + } + ); + } + + // Inject our `console` into target document if worker doesn't have a tab + // (e.g Panel, PageWorker, Widget). + // `worker.tab` can't be used because bug 804935. + if (!getTabForContentWindow(window)) { + let win = getUnsafeWindow(window); + + // export our chrome console to content window, using the same approach + // of `ConsoleAPI`: + // http://mxr.mozilla.org/mozilla-central/source/dom/base/ConsoleAPI.js#150 + // + // and described here: + // https://developer.mozilla.org/en-US/docs/Components.utils.createObjectIn + let con = Cu.createObjectIn(win); + + let genPropDesc = function genPropDesc(fun) { + return { enumerable: true, configurable: true, writable: true, + value: console[fun] }; + } + + const properties = { + log: genPropDesc('log'), + info: genPropDesc('info'), + warn: genPropDesc('warn'), + error: genPropDesc('error'), + debug: genPropDesc('debug'), + trace: genPropDesc('trace'), + dir: genPropDesc('dir'), + group: genPropDesc('group'), + groupCollapsed: genPropDesc('groupCollapsed'), + groupEnd: genPropDesc('groupEnd'), + time: genPropDesc('time'), + timeEnd: genPropDesc('timeEnd'), + profile: genPropDesc('profile'), + profileEnd: genPropDesc('profileEnd'), + __noSuchMethod__: { enumerable: true, configurable: true, writable: true, + value: function() {} } + }; + + Object.defineProperties(con, properties); + Cu.makeObjectPropsNormal(con); + + win.console = con; + }; + + // The order of `contentScriptFile` and `contentScript` evaluation is + // intentional, so programs can load libraries like jQuery from script URLs + // and use them in scripts. + let contentScriptFile = ('contentScriptFile' in worker) ? worker.contentScriptFile + : null, + contentScript = ('contentScript' in worker) ? worker.contentScript : null; + + if (contentScriptFile) + importScripts.apply(null, [this].concat(contentScriptFile)); + if (contentScript) { + evaluateIn( + this, + Array.isArray(contentScript) ? contentScript.join(';\n') : contentScript + ); + } + }, + destroy: function destroy() { + this.emitSync('detach'); + let model = modelFor(this); + model.sandbox = null + model.worker = null; + }, + +}); + +exports.WorkerSandbox = WorkerSandbox; + +/** + * Imports scripts to the sandbox by reading files under urls and + * evaluating its source. If exception occurs during evaluation + * `'error'` event is emitted on the worker. + * This is actually an analog to the `importScript` method in web + * workers but in our case it's not exposed even though content + * scripts may be able to do it synchronously since IO operation + * takes place in the UI process. + */ +function importScripts (workerSandbox, ...urls) { + let { worker, sandbox } = modelFor(workerSandbox); + for (let i in urls) { + let contentScriptFile = urls[i]; + try { + let uri = URL(contentScriptFile); + if (uri.scheme === 'resource') + load(sandbox, String(uri)); + else + throw Error('Unsupported `contentScriptFile` url: ' + String(uri)); + } + catch(e) { + emit(worker, 'error', e); + } + } +} + +function setListeners (workerSandbox) { + let { worker } = modelFor(workerSandbox); + // console.xxx calls + workerSandbox.on('console', function consoleListener (kind, ...args) { + console[kind].apply(console, args); + }); + + // self.postMessage calls + workerSandbox.on('message', function postMessage(data) { + // destroyed? + if (worker) + emit(worker, 'message', data); + }); + + // self.port.emit calls + workerSandbox.on('event', function portEmit (...eventArgs) { + // If not destroyed, emit event information to worker + // `eventArgs` has the event name as first element, + // and remaining elements are additional arguments to pass + if (worker) + emit.apply(null, [worker.port].concat(eventArgs)); + }); + + // unwrap, recreate and propagate async Errors thrown from content-script + workerSandbox.on('error', function onError({instanceOfError, value}) { + if (worker) { + let error = value; + if (instanceOfError) { + error = new Error(value.message, value.fileName, value.lineNumber); + error.stack = value.stack; + error.name = value.name; + } + emit(worker, 'error', error); + } + }); +} + +/** + * Evaluates code in the sandbox. + * @param {String} code + * JavaScript source to evaluate. + * @param {String} [filename='javascript:' + code] + * Name of the file + */ +function evaluateIn (workerSandbox, code, filename) { + let { worker, sandbox } = modelFor(workerSandbox); + try { + evaluate(sandbox, code, filename || 'javascript:' + code); + } + catch(e) { + emit(worker, 'error', e); + } +} + +/** + * Method called by the worker sandbox when it needs to send a message + */ +function onContentEvent (workerSandbox, args) { + // As `emit`, we ensure having an asynchronous behavior + async(function () { + // We emit event to chrome/addon listeners + emit.apply(null, [workerSandbox].concat(JSON.parse(args))); + }); +} + + +function modelFor (workerSandbox) { + return sandboxes.get(workerSandbox); +} + +/** + * JSON.stringify is buggy with cross-sandbox values, + * it may return '{}' on functions. Use a replacer to match them correctly. + */ +function replacer (k, v) { + return typeof v === 'function' ? undefined : v; +} + +function getUnsafeWindow (win) { + return win.wrappedJSObject || win; +} + +function emitToContent (workerSandbox, args) { + return modelFor(workerSandbox).emitToContent(args); +} + +function createChromeAPI () { + return { + timers: { + setTimeout: timer.setTimeout, + setInterval: timer.setInterval, + clearTimeout: timer.clearTimeout, + clearInterval: timer.clearInterval, + __exposedProps__: { + setTimeout: 'r', + setInterval: 'r', + clearTimeout: 'r', + clearInterval: 'r' + }, + }, + sandbox: { + evaluate: evaluate, + __exposedProps__: { + evaluate: 'r' + } + }, + __exposedProps__: { + timers: 'r', + sandbox: 'r' + } + }; +} diff --git a/addon-sdk-1.16/lib/sdk/content/thumbnail.js b/addon-sdk-1.16/lib/sdk/content/thumbnail.js new file mode 100644 index 00000000..9e57274c --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/content/thumbnail.js @@ -0,0 +1,46 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +'use strict'; + +module.metadata = { + 'stability': 'unstable' +}; + +const { Cc, Ci, Cu } = require('chrome'); +const AppShellService = Cc['@mozilla.org/appshell/appShellService;1']. + getService(Ci.nsIAppShellService); + +const NS = 'http://www.w3.org/1999/xhtml'; +const COLOR = 'rgb(255,255,255)'; + +/** + * Creates canvas element with a thumbnail of the passed window. + * @param {Window} window + * @returns {Element} + */ +function getThumbnailCanvasForWindow(window) { + let aspectRatio = 0.5625; // 16:9 + let thumbnail = AppShellService.hiddenDOMWindow.document + .createElementNS(NS, 'canvas'); + thumbnail.mozOpaque = true; + thumbnail.width = Math.ceil(window.screen.availWidth / 5.75); + thumbnail.height = Math.round(thumbnail.width * aspectRatio); + let ctx = thumbnail.getContext('2d'); + let snippetWidth = window.innerWidth * .6; + let scale = thumbnail.width / snippetWidth; + ctx.scale(scale, scale); + ctx.drawWindow(window, window.scrollX, window.scrollY, snippetWidth, + snippetWidth * aspectRatio, COLOR); + return thumbnail; +} +exports.getThumbnailCanvasForWindow = getThumbnailCanvasForWindow; + +/** + * Creates Base64 encoded data URI of the thumbnail for the passed window. + * @param {Window} window + * @returns {String} + */ +exports.getThumbnailURIForWindow = function getThumbnailURIForWindow(window) { + return getThumbnailCanvasForWindow(window).toDataURL() +}; diff --git a/addon-sdk-1.16/lib/sdk/content/utils.js b/addon-sdk-1.16/lib/sdk/content/utils.js new file mode 100644 index 00000000..058750e4 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/content/utils.js @@ -0,0 +1,82 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +'use strict'; + +module.metadata = { + 'stability': 'unstable' +}; + +let { merge } = require('../util/object'); +let assetsURI = require('../self').data.url(); +let isArray = Array.isArray; +let method = require('../../method/core'); + +function isAddonContent({ contentURL }) { + return typeof(contentURL) === 'string' && contentURL.indexOf(assetsURI) === 0; +} +exports.isAddonContent = isAddonContent; + +function hasContentScript({ contentScript, contentScriptFile }) { + return (isArray(contentScript) ? contentScript.length > 0 : + !!contentScript) || + (isArray(contentScriptFile) ? contentScriptFile.length > 0 : + !!contentScriptFile); +} +exports.hasContentScript = hasContentScript; + +function requiresAddonGlobal(model) { + return model.injectInDocument || (isAddonContent(model) && !hasContentScript(model)); +} +exports.requiresAddonGlobal = requiresAddonGlobal; + +function getAttachEventType(model) { + if (!model) return null; + let when = model.contentScriptWhen; + return requiresAddonGlobal(model) ? 'document-element-inserted' : + when === 'start' ? 'document-element-inserted' : + when === 'end' ? 'load' : + when === 'ready' ? 'DOMContentLoaded' : + null; +} +exports.getAttachEventType = getAttachEventType; + +let attach = method('worker-attach'); +exports.attach = attach; + +let detach = method('worker-detach'); +exports.detach = detach; + +let destroy = method('worker-destroy'); +exports.destroy = destroy; + +function WorkerHost (workerFor) { + // Define worker properties that just proxy to underlying worker + return ['postMessage', 'port', 'url', 'tab'].reduce(function(proto, name) { + // Use descriptor properties instead so we can call + // the worker function in the context of the worker so we + // don't have to create new functions with `fn.bind(worker)` + let descriptorProp = { + value: function (...args) { + let worker = workerFor(this); + return worker[name].apply(worker, args); + } + }; + + let accessorProp = { + get: function () { return workerFor(this)[name]; }, + set: function (value) { workerFor(this)[name] = value; } + }; + + Object.defineProperty(proto, name, merge({ + enumerable: true, + configurable: false, + }, isDescriptor(name) ? descriptorProp : accessorProp)); + return proto; + }, {}); + + function isDescriptor (prop) { + return ~['postMessage'].indexOf(prop); + } +} +exports.WorkerHost = WorkerHost; diff --git a/addon-sdk-1.16/lib/sdk/content/worker.js b/addon-sdk-1.16/lib/sdk/content/worker.js new file mode 100644 index 00000000..3b3433f8 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/content/worker.js @@ -0,0 +1,282 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +module.metadata = { + "stability": "unstable" +}; + +const { Class } = require('../core/heritage'); +const { EventTarget } = require('../event/target'); +const { on, off, emit, setListeners } = require('../event/core'); +const { + attach, detach, destroy +} = require('./utils'); +const { method } = require('../lang/functional'); +const { Ci, Cu, Cc } = require('chrome'); +const unload = require('../system/unload'); +const events = require('../system/events'); +const { getInnerId } = require("../window/utils"); +const { WorkerSandbox } = require('./sandbox'); +const { getTabForWindow } = require('../tabs/helpers'); + +// A weak map of workers to hold private attributes that +// should not be exposed +const workers = new WeakMap(); + +let modelFor = (worker) => workers.get(worker); + +const ERR_DESTROYED = + "Couldn't find the worker to receive this message. " + + "The script may not be initialized yet, or may already have been unloaded."; + +const ERR_FROZEN = "The page is currently hidden and can no longer be used " + + "until it is visible again."; + + +/** + * Message-passing facility for communication between code running + * in the content and add-on process. + * @see https://addons.mozilla.org/en-US/developers/docs/sdk/latest/modules/sdk/content/worker.html + */ +const Worker = Class({ + implements: [EventTarget], + initialize: function WorkerConstructor (options) { + // Save model in weak map to not expose properties + let model = createModel(); + workers.set(this, model); + + options = options || {}; + + if ('contentScriptFile' in options) + this.contentScriptFile = options.contentScriptFile; + if ('contentScriptOptions' in options) + this.contentScriptOptions = options.contentScriptOptions; + if ('contentScript' in options) + this.contentScript = options.contentScript; + if ('injectInDocument' in options) + this.injectInDocument = !!options.injectInDocument; + + setListeners(this, options); + + unload.ensure(this, "destroy"); + + // Ensure that worker.port is initialized for contentWorker to be able + // to send events during worker initialization. + this.port = createPort(this); + + model.documentUnload = documentUnload.bind(this); + model.pageShow = pageShow.bind(this); + model.pageHide = pageHide.bind(this); + + if ('window' in options) + attach(this, options.window); + }, + + /** + * Sends a message to the worker's global scope. Method takes single + * argument, which represents data to be sent to the worker. The data may + * be any primitive type value or `JSON`. Call of this method asynchronously + * emits `message` event with data value in the global scope of this + * symbiont. + * + * `message` event listeners can be set either by calling + * `self.on` with a first argument string `"message"` or by + * implementing `onMessage` function in the global scope of this worker. + * @param {Number|String|JSON} data + */ + postMessage: function (...data) { + let model = modelFor(this); + let args = ['message'].concat(data); + if (!model.inited) { + model.earlyEvents.push(args); + return; + } + processMessage.apply(null, [this].concat(args)); + }, + + get url () { + let model = modelFor(this); + // model.window will be null after detach + return model.window ? model.window.document.location.href : null; + }, + + get contentURL () { + let model = modelFor(this); + return model.window ? model.window.document.URL : null; + }, + + get tab () { + let model = modelFor(this); + // model.window will be null after detach + if (model.window) + return getTabForWindow(model.window); + return null; + }, + + // Implemented to provide some of the previous features of exposing sandbox + // so that Worker can be extended + getSandbox: function () { + return modelFor(this).contentWorker; + }, + + toString: function () { return '[object Worker]'; }, + attach: method(attach), + detach: method(detach), + destroy: method(destroy) +}); +exports.Worker = Worker; + +attach.define(Worker, function (worker, window) { + let model = modelFor(worker); + model.window = window; + // Track document unload to destroy this worker. + // We can't watch for unload event on page's window object as it + // prevents bfcache from working: + // https://developer.mozilla.org/En/Working_with_BFCache + model.windowID = getInnerId(model.window); + events.on("inner-window-destroyed", model.documentUnload); + + // Listen to pagehide event in order to freeze the content script + // while the document is frozen in bfcache: + model.window.addEventListener("pageshow", model.pageShow, true); + model.window.addEventListener("pagehide", model.pageHide, true); + + // will set model.contentWorker pointing to the private API: + model.contentWorker = WorkerSandbox(worker, model.window); + + // Mainly enable worker.port.emit to send event to the content worker + model.inited = true; + model.frozen = false; + + // Fire off `attach` event + emit(worker, 'attach', window); + + // Process all events and messages that were fired before the + // worker was initialized. + model.earlyEvents.forEach(args => processMessage.apply(null, [worker].concat(args))); +}); + +/** + * Remove all internal references to the attached document + * Tells _port to unload itself and removes all the references from itself. + */ +detach.define(Worker, function (worker) { + let model = modelFor(worker); + // maybe unloaded before content side is created + if (model.contentWorker) + model.contentWorker.destroy(); + model.contentWorker = null; + if (model.window) { + model.window.removeEventListener("pageshow", model.pageShow, true); + model.window.removeEventListener("pagehide", model.pageHide, true); + } + model.window = null; + // This method may be called multiple times, + // avoid dispatching `detach` event more than once + if (model.windowID) { + model.windowID = null; + events.off("inner-window-destroyed", model.documentUnload); + model.earlyEvents.length = 0; + emit(worker, 'detach'); + } + model.inited = false; +}); + +/** + * Tells content worker to unload itself and + * removes all the references from itself. + */ +destroy.define(Worker, function (worker) { + detach(worker); + modelFor(worker).inited = true; + // Specifying no type or listener removes all listeners + // from target + off(worker); +}); + +/** + * Events fired by workers + */ +function documentUnload ({ subject, data }) { + let model = modelFor(this); + let innerWinID = subject.QueryInterface(Ci.nsISupportsPRUint64).data; + if (innerWinID != model.windowID) return false; + detach(this); + return true; +} + +function pageShow () { + let model = modelFor(this); + model.contentWorker.emitSync('pageshow'); + emit(this, 'pageshow'); + model.frozen = false; +} + +function pageHide () { + let model = modelFor(this); + model.contentWorker.emitSync('pagehide'); + emit(this, 'pagehide'); + model.frozen = true; +} + +/** + * Fired from postMessage and emitEventToContent, or from the earlyMessage + * queue when fired before the content is loaded. Sends arguments to + * contentWorker if able + */ + +function processMessage (worker, ...args) { + let model = modelFor(worker) || {}; + if (!model.contentWorker) + throw new Error(ERR_DESTROYED); + if (model.frozen) + throw new Error(ERR_FROZEN); + + model.contentWorker.emit.apply(null, args); +} + +function createModel () { + return { + // List of messages fired before worker is initialized + earlyEvents: [], + // Is worker connected to the content worker sandbox ? + inited: false, + // Is worker being frozen? i.e related document is frozen in bfcache. + // Content script should not be reachable if frozen. + frozen: true, + /** + * Reference to the content side of the worker. + * @type {WorkerGlobalScope} + */ + contentWorker: null, + /** + * Reference to the window that is accessible from + * the content scripts. + * @type {Object} + */ + window: null + }; +} + +function createPort (worker) { + let port = EventTarget(); + port.emit = emitEventToContent.bind(null, worker); + return port; +} + +/** + * Emit a custom event to the content script, + * i.e. emit this event on `self.port` + */ +function emitEventToContent (worker, ...eventArgs) { + let model = modelFor(worker); + let args = ['event'].concat(eventArgs); + if (!model.inited) { + model.earlyEvents.push(args); + return; + } + processMessage.apply(null, [worker].concat(args)); +} + diff --git a/addon-sdk-1.16/lib/sdk/context-menu.js b/addon-sdk-1.16/lib/sdk/context-menu.js new file mode 100644 index 00000000..0c9ebe0c --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/context-menu.js @@ -0,0 +1,1202 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +module.metadata = { + "stability": "stable", + "engines": { + // TODO Fennec support Bug 788334 + "Firefox": "*" + } +}; + +const { Class, mix } = require("./core/heritage"); +const { addCollectionProperty } = require("./util/collection"); +const { ns } = require("./core/namespace"); +const { validateOptions, getTypeOf } = require("./deprecated/api-utils"); +const { URL, isValidURI } = require("./url"); +const { WindowTracker, browserWindowIterator } = require("./deprecated/window-utils"); +const { isBrowser, getInnerId } = require("./window/utils"); +const { Ci } = require("chrome"); +const { MatchPattern } = require("./util/match-pattern"); +const { Worker } = require("./content/worker"); +const { EventTarget } = require("./event/target"); +const { emit } = require('./event/core'); +const { when } = require('./system/unload'); +const selection = require('./selection'); + +// All user items we add have this class. +const ITEM_CLASS = "addon-context-menu-item"; + +// Items in the top-level context menu also have this class. +const TOPLEVEL_ITEM_CLASS = "addon-context-menu-item-toplevel"; + +// Items in the overflow submenu also have this class. +const OVERFLOW_ITEM_CLASS = "addon-context-menu-item-overflow"; + +// The class of the menu separator that separates standard context menu items +// from our user items. +const SEPARATOR_CLASS = "addon-context-menu-separator"; + +// If more than this number of items are added to the context menu, all items +// overflow into a "Jetpack" submenu. +const OVERFLOW_THRESH_DEFAULT = 10; +const OVERFLOW_THRESH_PREF = + "extensions.addon-sdk.context-menu.overflowThreshold"; + +// The label of the overflow sub-xul:menu. +// +// TODO: Localize this. +const OVERFLOW_MENU_LABEL = "Add-ons"; + +// The class of the overflow sub-xul:menu. +const OVERFLOW_MENU_CLASS = "addon-content-menu-overflow-menu"; + +// The class of the overflow submenu's xul:menupopup. +const OVERFLOW_POPUP_CLASS = "addon-content-menu-overflow-popup"; + +//These are used by PageContext.isCurrent below. If the popupNode or any of +//its ancestors is one of these, Firefox uses a tailored context menu, and so +//the page context doesn't apply. +const NON_PAGE_CONTEXT_ELTS = [ + Ci.nsIDOMHTMLAnchorElement, + Ci.nsIDOMHTMLAppletElement, + Ci.nsIDOMHTMLAreaElement, + Ci.nsIDOMHTMLButtonElement, + Ci.nsIDOMHTMLCanvasElement, + Ci.nsIDOMHTMLEmbedElement, + Ci.nsIDOMHTMLImageElement, + Ci.nsIDOMHTMLInputElement, + Ci.nsIDOMHTMLMapElement, + Ci.nsIDOMHTMLMediaElement, + Ci.nsIDOMHTMLMenuElement, + Ci.nsIDOMHTMLObjectElement, + Ci.nsIDOMHTMLOptionElement, + Ci.nsIDOMHTMLSelectElement, + Ci.nsIDOMHTMLTextAreaElement, +]; + +// Holds private properties for API objects +let internal = ns(); + +function getScheme(spec) { + try { + return URL(spec).scheme; + } + catch(e) { + return null; + } +} + +let Context = Class({ + // Returns the node that made this context current + adjustPopupNode: function adjustPopupNode(popupNode) { + return popupNode; + }, + + // Returns whether this context is current for the current node + isCurrent: function isCurrent(popupNode) { + return false; + } +}); + +// Matches when the context-clicked node doesn't have any of +// NON_PAGE_CONTEXT_ELTS in its ancestors +let PageContext = Class({ + extends: Context, + + isCurrent: function isCurrent(popupNode) { + // If there is a selection in the window then this context does not match + if (!popupNode.ownerDocument.defaultView.getSelection().isCollapsed) + return false; + + // If the clicked node or any of its ancestors is one of the blacklisted + // NON_PAGE_CONTEXT_ELTS then this context does not match + while (!(popupNode instanceof Ci.nsIDOMDocument)) { + if (NON_PAGE_CONTEXT_ELTS.some(function(type) popupNode instanceof type)) + return false; + + popupNode = popupNode.parentNode; + } + + return true; + } +}); +exports.PageContext = PageContext; + +// Matches when there is an active selection in the window +let SelectionContext = Class({ + extends: Context, + + isCurrent: function isCurrent(popupNode) { + if (!popupNode.ownerDocument.defaultView.getSelection().isCollapsed) + return true; + + try { + // The node may be a text box which has selectionStart and selectionEnd + // properties. If not this will throw. + let { selectionStart, selectionEnd } = popupNode; + return !isNaN(selectionStart) && !isNaN(selectionEnd) && + selectionStart !== selectionEnd; + } + catch (e) { + return false; + } + } +}); +exports.SelectionContext = SelectionContext; + +// Matches when the context-clicked node or any of its ancestors matches the +// selector given +let SelectorContext = Class({ + extends: Context, + + initialize: function initialize(selector) { + let options = validateOptions({ selector: selector }, { + selector: { + is: ["string"], + msg: "selector must be a string." + } + }); + internal(this).selector = options.selector; + }, + + adjustPopupNode: function adjustPopupNode(popupNode) { + let selector = internal(this).selector; + + while (!(popupNode instanceof Ci.nsIDOMDocument)) { + if (popupNode.mozMatchesSelector(selector)) + return popupNode; + + popupNode = popupNode.parentNode; + } + + return null; + }, + + isCurrent: function isCurrent(popupNode) { + return !!this.adjustPopupNode(popupNode); + } +}); +exports.SelectorContext = SelectorContext; + +// Matches when the page url matches any of the patterns given +let URLContext = Class({ + extends: Context, + + initialize: function initialize(patterns) { + patterns = Array.isArray(patterns) ? patterns : [patterns]; + + try { + internal(this).patterns = patterns.map(function (p) new MatchPattern(p)); + } + catch (err) { + throw new Error("Patterns must be a string, regexp or an array of " + + "strings or regexps: " + err); + } + + }, + + isCurrent: function isCurrent(popupNode) { + let url = popupNode.ownerDocument.URL; + return internal(this).patterns.some(function (p) p.test(url)); + } +}); +exports.URLContext = URLContext; + +// Matches when the user-supplied predicate returns true +let PredicateContext = Class({ + extends: Context, + + initialize: function initialize(predicate) { + let options = validateOptions({ predicate: predicate }, { + predicate: { + is: ["function"], + msg: "predicate must be a function." + } + }); + internal(this).predicate = options.predicate; + }, + + isCurrent: function isCurrent(popupNode) { + return internal(this).predicate(populateCallbackNodeData(popupNode)); + } +}); +exports.PredicateContext = PredicateContext; + +// List all editable types of inputs. Or is it better to have a list +// of non-editable inputs? +let editableInputs = { + email: true, + number: true, + password: true, + search: true, + tel: true, + text: true, + textarea: true, + url: true +}; + +function populateCallbackNodeData(node) { + let window = node.ownerDocument.defaultView; + let data = {}; + + data.documentType = node.ownerDocument.contentType; + + data.documentURL = node.ownerDocument.location.href; + data.targetName = node.nodeName.toLowerCase(); + data.targetID = node.id || null ; + + if ((data.targetName === 'input' && editableInputs[node.type]) || + data.targetName === 'textarea') { + data.isEditable = !node.readOnly && !node.disabled; + } + else { + data.isEditable = node.isContentEditable; + } + + data.selectionText = selection.text; + + data.srcURL = node.src || null; + data.linkURL = node.href || null; + data.value = node.value || null; + + return data; +} + +function removeItemFromArray(array, item) { + return array.filter(function(i) i !== item); +} + +// Converts anything that isn't false, null or undefined into a string +function stringOrNull(val) val ? String(val) : val; + +// Shared option validation rules for Item and Menu +let baseItemRules = { + parentMenu: { + is: ["object", "undefined"], + ok: function (v) { + if (!v) + return true; + return (v instanceof ItemContainer) || (v instanceof Menu); + }, + msg: "parentMenu must be a Menu or not specified." + }, + context: { + is: ["undefined", "object", "array"], + ok: function (v) { + if (!v) + return true; + let arr = Array.isArray(v) ? v : [v]; + return arr.every(function (o) o instanceof Context); + }, + msg: "The 'context' option must be a Context object or an array of " + + "Context objects." + }, + contentScript: { + is: ["string", "array", "undefined"], + ok: function (v) { + return !Array.isArray(v) || + v.every(function (s) typeof(s) === "string"); + } + }, + contentScriptFile: { + is: ["string", "array", "undefined"], + ok: function (v) { + if (!v) + return true; + let arr = Array.isArray(v) ? v : [v]; + return arr.every(function (s) { + return getTypeOf(s) === "string" && + getScheme(s) === 'resource'; + }); + }, + msg: "The 'contentScriptFile' option must be a local file URL or " + + "an array of local file URLs." + }, + onMessage: { + is: ["function", "undefined"] + } +}; + +let labelledItemRules = mix(baseItemRules, { + label: { + map: stringOrNull, + is: ["string"], + ok: function (v) !!v, + msg: "The item must have a non-empty string label." + }, + image: { + map: stringOrNull, + is: ["string", "undefined", "null"], + ok: function (url) { + if (!url) + return true; + return isValidURI(url); + }, + msg: "Image URL validation failed" + } +}); + +// Additional validation rules for Item +let itemRules = mix(labelledItemRules, { + data: { + map: stringOrNull, + is: ["string", "undefined", "null"] + } +}); + +// Additional validation rules for Menu +let menuRules = mix(labelledItemRules, { + items: { + is: ["array", "undefined"], + ok: function (v) { + if (!v) + return true; + return v.every(function (item) { + return item instanceof BaseItem; + }); + }, + msg: "items must be an array, and each element in the array must be an " + + "Item, Menu, or Separator." + } +}); + +let ContextWorker = Class({ + implements: [ Worker ], + + //Returns true if any context listeners are defined in the worker's port. + anyContextListeners: function anyContextListeners() { + return this.getSandbox().hasListenerFor("context"); + }, + + // Calls the context workers context listeners and returns the first result + // that is either a string or a value that evaluates to true. If all of the + // listeners returned false then returns false. If there are no listeners + // then returns null. + getMatchedContext: function getCurrentContexts(popupNode) { + let results = this.getSandbox().emitSync("context", popupNode); + return results.reduce(function(val, result) val || result, null); + }, + + // Emits a click event in the worker's port. popupNode is the node that was + // context-clicked, and clickedItemData is the data of the item that was + // clicked. + fireClick: function fireClick(popupNode, clickedItemData) { + this.getSandbox().emitSync("click", popupNode, clickedItemData); + } +}); + +// Returns true if any contexts match. If there are no contexts then a +// PageContext is tested instead +function hasMatchingContext(contexts, popupNode) { + for (let context in contexts) { + if (!context.isCurrent(popupNode)) + return false; + } + + return true; +} + +// Gets the matched context from any worker for this item. If there is no worker +// or no matched context then returns false. +function getCurrentWorkerContext(item, popupNode) { + let worker = getItemWorkerForWindow(item, popupNode.ownerDocument.defaultView); + if (!worker || !worker.anyContextListeners()) + return true; + return worker.getMatchedContext(popupNode); +} + +// Tests whether an item should be visible or not based on its contexts and +// content scripts +function isItemVisible(item, popupNode, defaultVisibility) { + if (!item.context.length) { + let worker = getItemWorkerForWindow(item, popupNode.ownerDocument.defaultView); + if (!worker || !worker.anyContextListeners()) + return defaultVisibility; + } + + if (!hasMatchingContext(item.context, popupNode)) + return false; + + let context = getCurrentWorkerContext(item, popupNode); + if (typeof(context) === "string" && context != "") + item.label = context; + + return !!context; +} + +// Gets the item's content script worker for a window, creating one if necessary +// Once created it will be automatically destroyed when the window unloads. +// If there is not content scripts for the item then null will be returned. +function getItemWorkerForWindow(item, window) { + if (!item.contentScript && !item.contentScriptFile) + return null; + + let id = getInnerId(window); + let worker = internal(item).workerMap.get(id); + + if (worker) + return worker; + + worker = ContextWorker({ + window: window, + contentScript: item.contentScript, + contentScriptFile: item.contentScriptFile, + onMessage: function(msg) { + emit(item, "message", msg); + }, + onDetach: function() { + internal(item).workerMap.delete(id); + } + }); + + internal(item).workerMap.set(id, worker); + + return worker; +} + +// Called when an item is clicked to send out click events to the content +// scripts +function itemClicked(item, clickedItem, popupNode) { + let worker = getItemWorkerForWindow(item, popupNode.ownerDocument.defaultView); + + if (worker) { + let adjustedNode = popupNode; + for (let context in item.context) + adjustedNode = context.adjustPopupNode(adjustedNode); + worker.fireClick(adjustedNode, clickedItem.data); + } + + if (item.parentMenu) + itemClicked(item.parentMenu, clickedItem, popupNode); +} + +// All things that appear in the context menu extend this +let BaseItem = Class({ + initialize: function initialize() { + addCollectionProperty(this, "context"); + + // Used to cache content script workers and the windows they have been + // created for + internal(this).workerMap = new Map(); + + if ("context" in internal(this).options && internal(this).options.context) { + let contexts = internal(this).options.context; + if (Array.isArray(contexts)) { + for (let context of contexts) + this.context.add(context); + } + else { + this.context.add(contexts); + } + } + + let parentMenu = internal(this).options.parentMenu; + if (!parentMenu) + parentMenu = contentContextMenu; + + parentMenu.addItem(this); + + Object.defineProperty(this, "contentScript", { + enumerable: true, + value: internal(this).options.contentScript + }); + + Object.defineProperty(this, "contentScriptFile", { + enumerable: true, + value: internal(this).options.contentScriptFile + }); + }, + + destroy: function destroy() { + if (this.parentMenu) + this.parentMenu.removeItem(this); + }, + + get parentMenu() { + return internal(this).parentMenu; + }, +}); + +// All things that have a label on the context menu extend this +let LabelledItem = Class({ + extends: BaseItem, + implements: [ EventTarget ], + + initialize: function initialize(options) { + BaseItem.prototype.initialize.call(this); + EventTarget.prototype.initialize.call(this, options); + }, + + destroy: function destroy() { + for (let [,worker] of internal(this).workerMap) + worker.destroy(); + + BaseItem.prototype.destroy.call(this); + }, + + get label() { + return internal(this).options.label; + }, + + set label(val) { + internal(this).options.label = val; + + MenuManager.updateItem(this); + }, + + get image() { + return internal(this).options.image; + }, + + set image(val) { + internal(this).options.image = val; + + MenuManager.updateItem(this); + }, + + get data() { + return internal(this).options.data; + }, + + set data(val) { + internal(this).options.data = val; + } +}); + +let Item = Class({ + extends: LabelledItem, + + initialize: function initialize(options) { + internal(this).options = validateOptions(options, itemRules); + + LabelledItem.prototype.initialize.call(this, options); + }, + + toString: function toString() { + return "[object Item \"" + this.label + "\"]"; + }, + + get data() { + return internal(this).options.data; + }, + + set data(val) { + internal(this).options.data = val; + + MenuManager.updateItem(this); + }, +}); +exports.Item = Item; + +let ItemContainer = Class({ + initialize: function initialize() { + internal(this).children = []; + }, + + destroy: function destroy() { + // Destroys the entire hierarchy + for (let item of internal(this).children) + item.destroy(); + }, + + addItem: function addItem(item) { + let oldParent = item.parentMenu; + + // Don't just call removeItem here as that would remove the corresponding + // UI element which is more costly than just moving it to the right place + if (oldParent) + internal(oldParent).children = removeItemFromArray(internal(oldParent).children, item); + + let after = null; + let children = internal(this).children; + if (children.length > 0) + after = children[children.length - 1]; + + children.push(item); + internal(item).parentMenu = this; + + // If there was an old parent then we just have to move the item, otherwise + // it needs to be created + if (oldParent) + MenuManager.moveItem(item, after); + else + MenuManager.createItem(item, after); + }, + + removeItem: function removeItem(item) { + // If the item isn't a child of this menu then ignore this call + if (item.parentMenu !== this) + return; + + MenuManager.removeItem(item); + + internal(this).children = removeItemFromArray(internal(this).children, item); + internal(item).parentMenu = null; + }, + + get items() { + return internal(this).children.slice(0); + }, + + set items(val) { + // Validate the arguments before making any changes + if (!Array.isArray(val)) + throw new Error(menuOptionRules.items.msg); + + for (let item of val) { + if (!(item instanceof BaseItem)) + throw new Error(menuOptionRules.items.msg); + } + + // Remove the old items and add the new ones + for (let item of internal(this).children) + this.removeItem(item); + + for (let item of val) + this.addItem(item); + }, +}); + +let Menu = Class({ + extends: LabelledItem, + implements: [ItemContainer], + + initialize: function initialize(options) { + internal(this).options = validateOptions(options, menuRules); + + LabelledItem.prototype.initialize.call(this, options); + ItemContainer.prototype.initialize.call(this); + + if (internal(this).options.items) { + for (let item of internal(this).options.items) + this.addItem(item); + } + }, + + destroy: function destroy() { + ItemContainer.prototype.destroy.call(this); + LabelledItem.prototype.destroy.call(this); + }, + + toString: function toString() { + return "[object Menu \"" + this.label + "\"]"; + }, +}); +exports.Menu = Menu; + +let Separator = Class({ + extends: BaseItem, + + initialize: function initialize(options) { + internal(this).options = validateOptions(options, baseItemRules); + + BaseItem.prototype.initialize.call(this); + }, + + toString: function toString() { + return "[object Separator]"; + } +}); +exports.Separator = Separator; + +// Holds items for the content area context menu +let contentContextMenu = ItemContainer(); +exports.contentContextMenu = contentContextMenu; + +when(function() { + contentContextMenu.destroy(); +}); + +// App specific UI code lives here, it should handle populating the context +// menu and passing clicks etc. through to the items. + +function countVisibleItems(nodes) { + return Array.reduce(nodes, function(sum, node) { + return node.hidden ? sum : sum + 1; + }, 0); +} + +let MenuWrapper = Class({ + initialize: function initialize(winWrapper, items, contextMenu) { + this.winWrapper = winWrapper; + this.window = winWrapper.window; + this.items = items; + this.contextMenu = contextMenu; + this.populated = false; + this.menuMap = new Map(); + + // updateItemVisibilities will run first, updateOverflowState will run after + // all other instances of this module have run updateItemVisibilities + this._updateItemVisibilities = this.updateItemVisibilities.bind(this); + this.contextMenu.addEventListener("popupshowing", this._updateItemVisibilities, true); + this._updateOverflowState = this.updateOverflowState.bind(this); + this.contextMenu.addEventListener("popupshowing", this._updateOverflowState, false); + }, + + destroy: function destroy() { + this.contextMenu.removeEventListener("popupshowing", this._updateOverflowState, false); + this.contextMenu.removeEventListener("popupshowing", this._updateItemVisibilities, true); + + if (!this.populated) + return; + + // If we're getting unloaded at runtime then we must remove all the + // generated XUL nodes + let oldParent = null; + for (let item of internal(this.items).children) { + let xulNode = this.getXULNodeForItem(item); + oldParent = xulNode.parentNode; + oldParent.removeChild(xulNode); + } + + if (oldParent) + this.onXULRemoved(oldParent); + }, + + get separator() { + return this.contextMenu.querySelector("." + SEPARATOR_CLASS); + }, + + get overflowMenu() { + return this.contextMenu.querySelector("." + OVERFLOW_MENU_CLASS); + }, + + get overflowPopup() { + return this.contextMenu.querySelector("." + OVERFLOW_POPUP_CLASS); + }, + + get topLevelItems() { + return this.contextMenu.querySelectorAll("." + TOPLEVEL_ITEM_CLASS); + }, + + get overflowItems() { + return this.contextMenu.querySelectorAll("." + OVERFLOW_ITEM_CLASS); + }, + + getXULNodeForItem: function getXULNodeForItem(item) { + return this.menuMap.get(item); + }, + + // Recurses through the item hierarchy creating XUL nodes for everything + populate: function populate(menu) { + for (let i = 0; i < internal(menu).children.length; i++) { + let item = internal(menu).children[i]; + let after = i === 0 ? null : internal(menu).children[i - 1]; + this.createItem(item, after); + + if (item instanceof Menu) + this.populate(item); + } + }, + + // Recurses through the menu setting the visibility of items. Returns true + // if any of the items in this menu were visible + setVisibility: function setVisibility(menu, popupNode, defaultVisibility) { + let anyVisible = false; + + for (let item of internal(menu).children) { + let visible = isItemVisible(item, popupNode, defaultVisibility); + + // Recurse through Menus, if none of the sub-items were visible then the + // menu is hidden too. + if (visible && (item instanceof Menu)) + visible = this.setVisibility(item, popupNode, true); + + let xulNode = this.getXULNodeForItem(item); + xulNode.hidden = !visible; + + anyVisible = anyVisible || visible; + } + + return anyVisible; + }, + + // Works out where to insert a XUL node for an item in a browser window + insertIntoXUL: function insertIntoXUL(item, node, after) { + let menupopup = null; + let before = null; + + let menu = item.parentMenu; + if (menu === this.items) { + // Insert into the overflow popup if it exists, otherwise the normal + // context menu + menupopup = this.overflowPopup; + if (!menupopup) + menupopup = this.contextMenu; + } + else { + let xulNode = this.getXULNodeForItem(menu); + menupopup = xulNode.firstChild; + } + + if (after) { + let afterNode = this.getXULNodeForItem(after); + before = afterNode.nextSibling; + } + else if (menupopup === this.contextMenu) { + let topLevel = this.topLevelItems; + if (topLevel.length > 0) + before = topLevel[topLevel.length - 1].nextSibling; + else + before = this.separator.nextSibling; + } + + menupopup.insertBefore(node, before); + }, + + // Sets the right class for XUL nodes + updateXULClass: function updateXULClass(xulNode) { + if (xulNode.parentNode == this.contextMenu) + xulNode.classList.add(TOPLEVEL_ITEM_CLASS); + else + xulNode.classList.remove(TOPLEVEL_ITEM_CLASS); + + if (xulNode.parentNode == this.overflowPopup) + xulNode.classList.add(OVERFLOW_ITEM_CLASS); + else + xulNode.classList.remove(OVERFLOW_ITEM_CLASS); + }, + + // Creates a XUL node for an item + createItem: function createItem(item, after) { + if (!this.populated) + return; + + // Create the separator if it doesn't already exist + if (!this.separator) { + let separator = this.window.document.createElement("menuseparator"); + separator.setAttribute("class", SEPARATOR_CLASS); + + // Insert before the separator created by the old context-menu if it + // exists to avoid bug 832401 + let oldSeparator = this.window.document.getElementById("jetpack-context-menu-separator"); + if (oldSeparator && oldSeparator.parentNode != this.contextMenu) + oldSeparator = null; + this.contextMenu.insertBefore(separator, oldSeparator); + } + + let type = "menuitem"; + if (item instanceof Menu) + type = "menu"; + else if (item instanceof Separator) + type = "menuseparator"; + + let xulNode = this.window.document.createElement(type); + xulNode.setAttribute("class", ITEM_CLASS); + if (item instanceof LabelledItem) { + xulNode.setAttribute("label", item.label); + if (item.image) { + xulNode.setAttribute("image", item.image); + if (item instanceof Menu) + xulNode.classList.add("menu-iconic"); + else + xulNode.classList.add("menuitem-iconic"); + } + if (item.data) + xulNode.setAttribute("value", item.data); + + let self = this; + xulNode.addEventListener("command", function(event) { + // Only care about clicks directly on this item + if (event.target !== xulNode) + return; + + itemClicked(item, item, self.contextMenu.triggerNode); + }, false); + } + + this.insertIntoXUL(item, xulNode, after); + this.updateXULClass(xulNode); + xulNode.data = item.data; + + if (item instanceof Menu) { + let menupopup = this.window.document.createElement("menupopup"); + xulNode.appendChild(menupopup); + } + + this.menuMap.set(item, xulNode); + }, + + // Updates the XUL node for an item in this window + updateItem: function updateItem(item) { + if (!this.populated) + return; + + let xulNode = this.getXULNodeForItem(item); + + // TODO figure out why this requires setAttribute + xulNode.setAttribute("label", item.label); + + if (item.image) { + xulNode.setAttribute("image", item.image); + if (item instanceof Menu) + xulNode.classList.add("menu-iconic"); + else + xulNode.classList.add("menuitem-iconic"); + } + else { + xulNode.removeAttribute("image"); + xulNode.classList.remove("menu-iconic"); + xulNode.classList.remove("menuitem-iconic"); + } + + if (item.data) + xulNode.setAttribute("value", item.data); + else + xulNode.removeAttribute("value"); + }, + + // Moves the XUL node for an item in this window to its new place in the + // hierarchy + moveItem: function moveItem(item, after) { + if (!this.populated) + return; + + let xulNode = this.getXULNodeForItem(item); + let oldParent = xulNode.parentNode; + + this.insertIntoXUL(item, xulNode, after); + this.updateXULClass(xulNode); + this.onXULRemoved(oldParent); + }, + + // Removes the XUL nodes for an item in every window we've ever populated. + removeItem: function removeItem(item) { + if (!this.populated) + return; + + let xulItem = this.getXULNodeForItem(item); + + let oldParent = xulItem.parentNode; + + oldParent.removeChild(xulItem); + this.menuMap.delete(item); + + this.onXULRemoved(oldParent); + }, + + // Called when any XUL nodes have been removed from a menupopup. This handles + // making sure the separator and overflow are correct + onXULRemoved: function onXULRemoved(parent) { + if (parent == this.contextMenu) { + let toplevel = this.topLevelItems; + + // If there are no more items then remove the separator + if (toplevel.length == 0) { + let separator = this.separator; + if (separator) + separator.parentNode.removeChild(separator); + } + } + else if (parent == this.overflowPopup) { + // If there are no more items then remove the overflow menu and separator + if (parent.childNodes.length == 0) { + let separator = this.separator; + separator.parentNode.removeChild(separator); + this.contextMenu.removeChild(parent.parentNode); + } + } + }, + + // Recurses through all the items owned by this module and sets their hidden + // state + updateItemVisibilities: function updateItemVisibilities(event) { + try { + if (event.type != "popupshowing") + return; + if (event.target != this.contextMenu) + return; + + if (internal(this.items).children.length == 0) + return; + + if (!this.populated) { + this.populated = true; + this.populate(this.items); + } + + let popupNode = event.target.triggerNode; + this.setVisibility(this.items, popupNode, PageContext().isCurrent(popupNode)); + } + catch (e) { + console.exception(e); + } + }, + + // Counts the number of visible items across all modules and makes sure they + // are in the right place between the top level context menu and the overflow + // menu + updateOverflowState: function updateOverflowState(event) { + try { + if (event.type != "popupshowing") + return; + if (event.target != this.contextMenu) + return; + + // The main items will be in either the top level context menu or the + // overflow menu at this point. Count the visible ones and if they are in + // the wrong place move them + let toplevel = this.topLevelItems; + let overflow = this.overflowItems; + let visibleCount = countVisibleItems(toplevel) + + countVisibleItems(overflow); + + if (visibleCount == 0) { + let separator = this.separator; + if (separator) + separator.hidden = true; + let overflowMenu = this.overflowMenu; + if (overflowMenu) + overflowMenu.hidden = true; + } + else if (visibleCount > MenuManager.overflowThreshold) { + this.separator.hidden = false; + let overflowPopup = this.overflowPopup; + if (overflowPopup) + overflowPopup.parentNode.hidden = false; + + if (toplevel.length > 0) { + // The overflow menu shouldn't exist here but let's play it safe + if (!overflowPopup) { + let overflowMenu = this.window.document.createElement("menu"); + overflowMenu.setAttribute("class", OVERFLOW_MENU_CLASS); + overflowMenu.setAttribute("label", OVERFLOW_MENU_LABEL); + this.contextMenu.insertBefore(overflowMenu, this.separator.nextSibling); + + overflowPopup = this.window.document.createElement("menupopup"); + overflowPopup.setAttribute("class", OVERFLOW_POPUP_CLASS); + overflowMenu.appendChild(overflowPopup); + } + + for (let xulNode of toplevel) { + overflowPopup.appendChild(xulNode); + this.updateXULClass(xulNode); + } + } + } + else { + this.separator.hidden = false; + + if (overflow.length > 0) { + // Move all the overflow nodes out of the overflow menu and position + // them immediately before it + for (let xulNode of overflow) { + this.contextMenu.insertBefore(xulNode, xulNode.parentNode.parentNode); + this.updateXULClass(xulNode); + } + this.contextMenu.removeChild(this.overflowMenu); + } + } + } + catch (e) { + console.exception(e); + } + } +}); + +// This wraps every window that we've seen +let WindowWrapper = Class({ + initialize: function initialize(window) { + this.window = window; + this.menus = [ + new MenuWrapper(this, contentContextMenu, window.document.getElementById("contentAreaContextMenu")), + ]; + }, + + destroy: function destroy() { + for (let menuWrapper of this.menus) + menuWrapper.destroy(); + }, + + getMenuWrapperForItem: function getMenuWrapperForItem(item) { + let root = item.parentMenu; + while (root.parentMenu) + root = root.parentMenu; + + for (let wrapper of this.menus) { + if (wrapper.items === root) + return wrapper; + } + + return null; + } +}); + +let MenuManager = { + windowMap: new Map(), + + get overflowThreshold() { + let prefs = require("./preferences/service"); + return prefs.get(OVERFLOW_THRESH_PREF, OVERFLOW_THRESH_DEFAULT); + }, + + // When a new window is added start watching it for context menu shows + onTrack: function onTrack(window) { + if (!isBrowser(window)) + return; + + // Generally shouldn't happen, but just in case + if (this.windowMap.has(window)) { + console.warn("Already seen this window"); + return; + } + + let winWrapper = WindowWrapper(window); + this.windowMap.set(window, winWrapper); + }, + + onUntrack: function onUntrack(window) { + if (!isBrowser(window)) + return; + + let winWrapper = this.windowMap.get(window); + // This shouldn't happen but protect against it anyway + if (!winWrapper) + return; + winWrapper.destroy(); + + this.windowMap.delete(window); + }, + + // Creates a XUL node for an item in every window we've already populated + createItem: function createItem(item, after) { + for (let [window, winWrapper] of this.windowMap) { + let menuWrapper = winWrapper.getMenuWrapperForItem(item); + if (menuWrapper) + menuWrapper.createItem(item, after); + } + }, + + // Updates the XUL node for an item in every window we've already populated + updateItem: function updateItem(item) { + for (let [window, winWrapper] of this.windowMap) { + let menuWrapper = winWrapper.getMenuWrapperForItem(item); + if (menuWrapper) + menuWrapper.updateItem(item); + } + }, + + // Moves the XUL node for an item in every window we've ever populated to its + // new place in the hierarchy + moveItem: function moveItem(item, after) { + for (let [window, winWrapper] of this.windowMap) { + let menuWrapper = winWrapper.getMenuWrapperForItem(item); + if (menuWrapper) + menuWrapper.moveItem(item, after); + } + }, + + // Removes the XUL nodes for an item in every window we've ever populated. + removeItem: function removeItem(item) { + for (let [window, winWrapper] of this.windowMap) { + let menuWrapper = winWrapper.getMenuWrapperForItem(item); + if (menuWrapper) + menuWrapper.removeItem(item); + } + } +}; + +WindowTracker(MenuManager); diff --git a/addon-sdk-1.16/lib/sdk/core/disposable.js b/addon-sdk-1.16/lib/sdk/core/disposable.js new file mode 100644 index 00000000..162fc4f3 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/core/disposable.js @@ -0,0 +1,74 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "experimental" +}; + + +let { Class } = require("./heritage"); +let { on, off } = require('../system/events'); +let unloadSubject = require('@loader/unload'); + +let disposables = WeakMap(); + +function initialize(instance) { + // Create an event handler that will dispose instance on unload. + function handler(event) { + if (event.subject.wrappedJSObject === unloadSubject) { + instance.destroy(); + } + } + + // Form weak reference between disposable instance and an unload event + // handler associated with it. This will make sure that event handler can't + // be garbage collected as long as instance is referenced. Also note that + // system events intentionally hold weak reference to an event handler, this + // will let GC claim both instance and an unload handler before actual add-on + // unload if instance contains no other references. + disposables.set(instance, handler); + on("sdk:loader:destroy", handler); +} +exports.initialize = initialize; + +function dispose(instance) { + // Disposes given instance by removing it from weak map so that handler can + // be GC-ed even if references to instance are kept. Also unregister unload + // handler. + + let handler = disposables.get(instance); + if (handler) off("sdk:loader:destroy", handler); + disposables.delete(instance); +} +exports.dispose = dispose; + +// Base type that takes care of disposing it's instances on add-on unload. +// Also makes sure to remove unload listener if it's already being disposed. +let Disposable = Class({ + initialize: function setupDisposable() { + // First setup instance before initializing it's disposal. If instance + // fails to initialize then there is no instance to be disposed at the + // unload. + this.setup.apply(this, arguments); + initialize(this); + }, + setup: function setup() { + // Implement your initialize logic here. + }, + dispose: function dispose() { + // Implement your cleanup logic here. + }, + + destroy: function destroy() { + // Destroying disposable removes unload handler so that attempt to dispose + // won't be made at unload & delegates to dispose. + if (disposables.has(this)) { + dispose(this); + this.dispose(); + } + } +}); +exports.Disposable = Disposable; diff --git a/addon-sdk-1.16/lib/sdk/core/heritage.js b/addon-sdk-1.16/lib/sdk/core/heritage.js new file mode 100644 index 00000000..3dd98a28 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/core/heritage.js @@ -0,0 +1,183 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +'use strict'; + +module.metadata = { + "stability": "unstable" +}; + +var getPrototypeOf = Object.getPrototypeOf; +var getNames = Object.getOwnPropertyNames; +var getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; +var create = Object.create; +var freeze = Object.freeze; +var unbind = Function.call.bind(Function.bind, Function.call); + +// This shortcut makes sure that we do perform desired operations, even if +// associated methods have being overridden on the used object. +var owns = unbind(Object.prototype.hasOwnProperty); +var apply = unbind(Function.prototype.apply); +var slice = Array.slice || unbind(Array.prototype.slice); +var reduce = Array.reduce || unbind(Array.prototype.reduce); +var map = Array.map || unbind(Array.prototype.map); +var concat = Array.concat || unbind(Array.prototype.concat); + +// Utility function to get own properties descriptor map. +function getOwnPropertyDescriptors(object) { + return reduce(getNames(object), function(descriptor, name) { + descriptor[name] = getOwnPropertyDescriptor(object, name); + return descriptor; + }, {}); +} + +function isDataProperty(property) { + var value = property.value; + var type = typeof(property.value); + return "value" in property && + (type !== "object" || value === null) && + type !== "function"; +} + +function getDataProperties(object) { + var properties = getOwnPropertyDescriptors(object); + return getNames(properties).reduce(function(result, name) { + var property = properties[name]; + if (isDataProperty(property)) { + result[name] = { + value: property.value, + writable: true, + configurable: true, + enumerable: false + }; + } + return result; + }, {}) +} + +/** + * Takes `source` object as an argument and returns identical object + * with the difference that all own properties will be non-enumerable + */ +function obscure(source) { + var descriptor = reduce(getNames(source), function(descriptor, name) { + var property = getOwnPropertyDescriptor(source, name); + property.enumerable = false; + descriptor[name] = property; + return descriptor; + }, {}); + return create(getPrototypeOf(source), descriptor); +} +exports.obscure = obscure; + +/** + * Takes arbitrary number of source objects and returns fresh one, that + * inherits from the same prototype as a first argument and implements all + * own properties of all argument objects. If two or more argument objects + * have own properties with the same name, the property is overridden, with + * precedence from right to left, implying, that properties of the object on + * the left are overridden by a same named property of the object on the right. + */ +var mix = function(source) { + var descriptor = reduce(slice(arguments), function(descriptor, source) { + return reduce(getNames(source), function(descriptor, name) { + descriptor[name] = getOwnPropertyDescriptor(source, name); + return descriptor; + }, descriptor); + }, {}); + + return create(getPrototypeOf(source), descriptor); +}; +exports.mix = mix; + +/** + * Returns a frozen object with that inherits from the given `prototype` and + * implements all own properties of the given `properties` object. + */ +function extend(prototype, properties) { + return create(prototype, getOwnPropertyDescriptors(properties)); +} +exports.extend = extend; + +/** + * Returns a constructor function with a proper `prototype` setup. Returned + * constructor's `prototype` inherits from a given `options.extends` or + * `Class.prototype` if omitted and implements all the properties of the + * given `option`. If `options.implemens` array is passed, it's elements + * will be mixed into prototype as well. Also, `options.extends` can be + * a function or a prototype. If function than it's prototype is used as + * an ancestor of the prototype, if it's an object that it's used directly. + * Also `options.implements` may contain functions or objects, in case of + * functions their prototypes are used for mixing. + */ +var Class = new function() { + function prototypeOf(input) { + return typeof(input) === 'function' ? input.prototype : input; + } + var none = freeze([]); + + return function Class(options) { + // Create descriptor with normalized `options.extends` and + // `options.implements`. + var descriptor = { + // Normalize extends property of `options.extends` to a prototype object + // in case it's constructor. If property is missing that fallback to + // `Type.prototype`. + extends: owns(options, 'extends') ? + prototypeOf(options.extends) : Class.prototype, + // Normalize `options.implements` to make sure that it's array of + // prototype objects instead of constructor functions. + implements: owns(options, 'implements') ? + freeze(map(options.implements, prototypeOf)) : none + }; + + // Create array of property descriptors who's properties will be defined + // on the resulting prototype. Note: Using reflection `concat` instead of + // method as it may be overridden. + var descriptors = concat(descriptor.implements, options, descriptor, { + constructor: constructor + }); + + // Note: we use reflection `apply` in the constructor instead of method + // call since later may be overridden. + function constructor() { + var instance = create(prototype, attributes); + if (initialize) apply(initialize, instance, arguments); + return instance; + } + // Create `prototype` that inherits from given ancestor passed as + // `options.extends`, falling back to `Type.prototype`, implementing all + // properties of given `options.implements` and `options` itself. + var prototype = extend(descriptor.extends, mix.apply(mix, descriptors)); + var initialize = prototype.initialize; + + // Combine ancestor attributes with prototype's attributes so that + // ancestors attributes also become initializeable. + var attributes = mix(descriptor.extends.constructor.attributes || {}, + getDataProperties(prototype)); + + constructor.attributes = attributes; + Object.defineProperty(constructor, 'prototype', { + configurable: false, + writable: false, + value: prototype + }); + return constructor; + }; +} +Class.prototype = extend(null, obscure({ + constructor: function constructor() { + this.initialize.apply(this, arguments); + return this; + }, + initialize: function initialize() { + // Do your initialization logic here + }, + // Copy useful properties from `Object.prototype`. + toString: Object.prototype.toString, + toLocaleString: Object.prototype.toLocaleString, + toSource: Object.prototype.toSource, + valueOf: Object.prototype.valueOf, + isPrototypeOf: Object.prototype.isPrototypeOf +})); +exports.Class = freeze(Class); diff --git a/addon-sdk-1.16/lib/sdk/core/namespace.js b/addon-sdk-1.16/lib/sdk/core/namespace.js new file mode 100644 index 00000000..3ceb73b7 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/core/namespace.js @@ -0,0 +1,43 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "unstable" +}; + +const create = Object.create; +const prototypeOf = Object.getPrototypeOf; + +/** + * Returns a new namespace, function that may can be used to access an + * namespaced object of the argument argument. Namespaced object are associated + * with owner objects via weak references. Namespaced objects inherit from the + * owners ancestor namespaced object. If owner's ancestor is `null` then + * namespaced object inherits from given `prototype`. Namespaces can be used + * to define internal APIs that can be shared via enclosing `namespace` + * function. + * @examples + * const internals = ns(); + * internals(object).secret = secret; + */ +function ns() { + const map = new WeakMap(); + return function namespace(target) { + if (!target) // If `target` is not an object return `target` itself. + return target; + // If target has no namespaced object yet, create one that inherits from + // the target prototype's namespaced object. + if (!map.has(target)) + map.set(target, create(namespace(prototypeOf(target) || null))); + + return map.get(target); + }; +}; + +// `Namespace` is a e4x function in the scope, so we export the function also as +// `ns` as alias to avoid clashing. +exports.ns = ns; +exports.Namespace = ns; diff --git a/addon-sdk-1.16/lib/sdk/core/promise.js b/addon-sdk-1.16/lib/sdk/core/promise.js new file mode 100644 index 00000000..3f62c37e --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/core/promise.js @@ -0,0 +1,290 @@ +;(function(id, factory) { // Module boilerplate :( + if (typeof(define) === 'function') { // RequireJS + define(factory); + } else if (typeof(require) === 'function') { // CommonJS + factory.call(this, require, exports, module); + } else if (String(this).indexOf('BackstagePass') >= 0) { // JSM + this[factory.name] = {}; + try { + this.console = this['Components'].utils + .import('resource://gre/modules/devtools/Console.jsm', {}).console; + } + catch (ex) { + // Avoid failures on different toolkit configurations. + } + factory(function require(uri) { + var imports = {}; + this['Components'].utils.import(uri, imports); + return imports; + }, this[factory.name], { uri: __URI__, id: id }); + this.EXPORTED_SYMBOLS = [factory.name]; + } else if (~String(this).indexOf('Sandbox')) { // Sandbox + factory(function require(uri) {}, this, { id: id }); + } else { // Browser or alike + var globals = this; + factory(function require(id) { + return globals[id]; + }, (globals[id] = {}), { uri: document.location.href + '#' + id, id: id }); + } +}).call(this, 'promise/core', function Promise(require, exports, module) { + +'use strict'; + +module.metadata = { + "stability": "unstable" +}; + +/** + * Internal utility: Wraps given `value` into simplified promise, successfully + * fulfilled to a given `value`. Note the result is not a complete promise + * implementation, as its method `then` does not returns anything. + */ +function fulfilled(value) { + return { then: function then(fulfill) { fulfill(value); } }; +} + +/** + * Internal utility: Wraps given input into simplified promise, pre-rejected + * with a given `reason`. Note the result is not a complete promise + * implementation, as its method `then` does not returns anything. + */ +function rejected(reason) { + return { then: function then(fulfill, reject) { reject(reason); } }; +} + +/** + * Internal utility: Returns `true` if given `value` is a promise. Value is + * assumed to be a promise if it implements method `then`. + */ +function isPromise(value) { + return value && typeof(value.then) === 'function'; +} + +/** + * Creates deferred object containing fresh promise & methods to either resolve + * or reject it. The result is an object with the following properties: + * - `promise` Eventual value representation implementing CommonJS [Promises/A] + * (http://wiki.commonjs.org/wiki/Promises/A) API. + * - `resolve` Single shot function that resolves enclosed `promise` with a + * given `value`. + * - `reject` Single shot function that rejects enclosed `promise` with a given + * `reason`. + * + * An optional `prototype` argument is used as a prototype of the returned + * `promise` allowing one to implement additional API. If prototype is not + * passed then it falls back to `Object.prototype`. + * + * ## Example + * + * function fetchURI(uri, type) { + * var deferred = defer(); + * var request = new XMLHttpRequest(); + * request.open("GET", uri, true); + * request.responseType = type; + * request.onload = function onload() { + * deferred.resolve(request.response); + * } + * request.onerror = function(event) { + * deferred.reject(event); + * } + * request.send(); + * + * return deferred.promise; + * } + */ +function defer(prototype) { + // Define FIFO queue of observer pairs. Once promise is resolved & all queued + // observers are forwarded to `result` and variable is set to `null`. + var observers = []; + + // Promise `result`, which will be assigned a resolution value once promise + // is resolved. Note that result will always be assigned promise (or alike) + // object to take care of propagation through promise chains. If result is + // `null` promise is not resolved yet. + var result = null; + + prototype = (prototype || prototype === null) ? prototype : Object.prototype; + + // Create an object implementing promise API. + var promise = Object.create(prototype, { + then: { value: function then(onFulfill, onError) { + var deferred = defer(prototype); + + function resolve(value) { + // If `onFulfill` handler is provided resolve `deferred.promise` with + // result of invoking it with a resolution value. If handler is not + // provided propagate value through. + try { + deferred.resolve(onFulfill ? onFulfill(value) : value); + } + // `onFulfill` may throw exception in which case resulting promise + // is rejected with thrown exception. + catch(error) { + if (exports._reportErrors && typeof(console) === 'object') + console.error(error); + // Note: Following is equivalent of `deferred.reject(error)`, + // we use this shortcut to reduce a stack. + deferred.resolve(rejected(error)); + } + } + + function reject(reason) { + try { + if (onError) deferred.resolve(onError(reason)); + else deferred.resolve(rejected(reason)); + } + catch(error) { + if (exports._reportErrors && typeof(console) === 'object') + console.error(error); + deferred.resolve(rejected(error)); + } + } + + // If enclosed promise (`this.promise`) observers queue is still alive + // enqueue a new observer pair into it. Note that this does not + // necessary means that promise is pending, it may already be resolved, + // but we still have to queue observers to guarantee an order of + // propagation. + if (observers) { + observers.push({ resolve: resolve, reject: reject }); + } + // Otherwise just forward observer pair right to a `result` promise. + else { + result.then(resolve, reject); + } + + return deferred.promise; + }} + }) + + var deferred = { + promise: promise, + /** + * Resolves associated `promise` to a given `value`, unless it's already + * resolved or rejected. Note that resolved promise is not necessary a + * successfully fulfilled. Promise may be resolved with a promise `value` + * in which case `value` promise's fulfillment / rejection will propagate + * up to a promise resolved with `value`. + */ + resolve: function resolve(value) { + if (!result) { + // Store resolution `value` in a `result` as a promise, so that all + // the subsequent handlers can be simply forwarded to it. Since + // `result` will be a promise all the value / error propagation will + // be uniformly taken care of. + result = isPromise(value) ? value : fulfilled(value); + + // Forward already registered observers to a `result` promise in the + // order they were registered. Note that we intentionally dequeue + // observer at a time until queue is exhausted. This makes sure that + // handlers registered as side effect of observer forwarding are + // queued instead of being invoked immediately, guaranteeing FIFO + // order. + while (observers.length) { + var observer = observers.shift(); + result.then(observer.resolve, observer.reject); + } + + // Once `observers` queue is exhausted we `null`-ify it, so that + // new handlers are forwarded straight to the `result`. + observers = null; + } + }, + /** + * Rejects associated `promise` with a given `reason`, unless it's already + * resolved / rejected. This is just a (better performing) convenience + * shortcut for `deferred.resolve(reject(reason))`. + */ + reject: function reject(reason) { + // Note that if promise is resolved that does not necessary means that it + // is successfully fulfilled. Resolution value may be a promise in which + // case its result propagates. In other words if promise `a` is resolved + // with promise `b`, `a` is either fulfilled or rejected depending + // on weather `b` is fulfilled or rejected. Here `deferred.promise` is + // resolved with a promise pre-rejected with a given `reason`, there for + // `deferred.promise` is rejected with a given `reason`. This may feel + // little awkward first, but doing it this way greatly simplifies + // propagation through promise chains. + deferred.resolve(rejected(reason)); + } + }; + + return deferred; +} +exports.defer = defer; + +/** + * Returns a promise resolved to a given `value`. Optionally a second + * `prototype` argument may be provided to be used as a prototype for the + * returned promise. + */ +function resolve(value, prototype) { + var deferred = defer(prototype); + deferred.resolve(value); + return deferred.promise; +} +exports.resolve = resolve; + +/** + * Returns a promise rejected with a given `reason`. Optionally a second + * `prototype` argument may be provided to be used as a prototype for the + * returned promise. + */ +function reject(reason, prototype) { + var deferred = defer(prototype); + deferred.reject(reason); + return deferred.promise; +} +exports.reject = reject; + +var promised = (function() { + // Note: Define shortcuts and utility functions here in order to avoid + // slower property accesses and unnecessary closure creations on each + // call of this popular function. + + var call = Function.call; + var concat = Array.prototype.concat; + + // Utility function that does following: + // execute([ f, self, args...]) => f.apply(self, args) + function execute(args) { return call.apply(call, args) } + + // Utility function that takes promise of `a` array and maybe promise `b` + // as arguments and returns promise for `a.concat(b)`. + function promisedConcat(promises, unknown) { + return promises.then(function(values) { + return resolve(unknown).then(function(value) { + return values.concat([ value ]); + }); + }); + } + + return function promised(f, prototype) { + /** + Returns a wrapped `f`, which when called returns a promise that resolves to + `f(...)` passing all the given arguments to it, which by the way may be + promises. Optionally second `prototype` argument may be provided to be used + a prototype for a returned promise. + + ## Example + + var promise = promised(Array)(1, promise(2), promise(3)) + promise.then(console.log) // => [ 1, 2, 3 ] + **/ + + return function promised() { + // create array of [ f, this, args... ] + return concat.apply([ f, this ], arguments). + // reduce it via `promisedConcat` to get promised array of fulfillments + reduce(promisedConcat, resolve([], prototype)). + // finally map that to promise of `f.apply(this, args...)` + then(execute); + }; + } +})(); +exports.promised = promised; + +var all = promised(Array); +exports.all = all; + +}); diff --git a/addon-sdk-1.16/lib/sdk/deprecated/api-utils.js b/addon-sdk-1.16/lib/sdk/deprecated/api-utils.js new file mode 100644 index 00000000..b5ce0325 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/deprecated/api-utils.js @@ -0,0 +1,193 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; +module.metadata = { + "stability": "deprecated" +}; + +const memory = require("./memory"); + +const { merge } = require("../util/object"); +const { union } = require("../util/array"); +const { isNil } = require("../lang/type"); + +// The possible return values of getTypeOf. +const VALID_TYPES = [ + "array", + "boolean", + "function", + "null", + "number", + "object", + "string", + "undefined", +]; + +const { isArray } = Array; + +/** + * Returns a validated options dictionary given some requirements. If any of + * the requirements are not met, an exception is thrown. + * + * @param options + * An object, the options dictionary to validate. It's not modified. + * If it's null or otherwise falsey, an empty object is assumed. + * @param requirements + * An object whose keys are the expected keys in options. Any key in + * options that is not present in requirements is ignored. Each value + * in requirements is itself an object describing the requirements of + * its key. There are four optional keys in this object: + * map: A function that's passed the value of the key in options. + * map's return value is taken as the key's value in the final + * validated options, is, and ok. If map throws an exception + * it's caught and discarded, and the key's value is its value in + * options. + * is: An array containing any number of the typeof type names. If + * the key's value is none of these types, it fails validation. + * Arrays and null are identified by the special type names + * "array" and "null"; "object" will not match either. No type + * coercion is done. + * ok: A function that's passed the key's value. If it returns + * false, the value fails validation. + * msg: If the key's value fails validation, an exception is thrown. + * This string will be used as its message. If undefined, a + * generic message is used, unless is is defined, in which case + * the message will state that the value needs to be one of the + * given types. + * @return An object whose keys are those keys in requirements that are also in + * options and whose values are the corresponding return values of map + * or the corresponding values in options. Note that any keys not + * shared by both requirements and options are not in the returned + * object. + */ +exports.validateOptions = function validateOptions(options, requirements) { + options = options || {}; + let validatedOptions = {}; + + for (let key in requirements) { + let isOptional = false; + let mapThrew = false; + let req = requirements[key]; + let [optsVal, keyInOpts] = (key in options) ? + [options[key], true] : + [undefined, false]; + if (req.map) { + try { + optsVal = req.map(optsVal); + } + catch (err) { + if (err instanceof RequirementError) + throw err; + + mapThrew = true; + } + } + if (req.is) { + let types = req.is; + + if (!isArray(types) && isArray(types.is)) + types = types.is; + + if (isArray(types)) { + isOptional = ['undefined', 'null'].every(v => ~types.indexOf(v)); + + // Sanity check the caller's type names. + types.forEach(function (typ) { + if (VALID_TYPES.indexOf(typ) < 0) { + let msg = 'Internal error: invalid requirement type "' + typ + '".'; + throw new Error(msg); + } + }); + if (types.indexOf(getTypeOf(optsVal)) < 0) + throw new RequirementError(key, req); + } + } + + if (req.ok && ((!isOptional || !isNil(optsVal)) && !req.ok(optsVal))) + throw new RequirementError(key, req); + + if (keyInOpts || (req.map && !mapThrew && optsVal !== undefined)) + validatedOptions[key] = optsVal; + } + + return validatedOptions; +}; + +exports.addIterator = function addIterator(obj, keysValsGenerator) { + obj.__iterator__ = function(keysOnly, keysVals) { + let keysValsIterator = keysValsGenerator.call(this); + + // "for (.. in ..)" gets only keys, "for each (.. in ..)" gets values, + // and "for (.. in Iterator(..))" gets [key, value] pairs. + let index = keysOnly ? 0 : 1; + while (true) + yield keysVals ? keysValsIterator.next() : keysValsIterator.next()[index]; + }; +}; + +// Similar to typeof, except arrays and null are identified by "array" and +// "null", not "object". +let getTypeOf = exports.getTypeOf = function getTypeOf(val) { + let typ = typeof(val); + if (typ === "object") { + if (!val) + return "null"; + if (isArray(val)) + return "array"; + } + return typ; +} + +function RequirementError(key, requirement) { + Error.call(this); + + this.name = "RequirementError"; + + let msg = requirement.msg; + if (!msg) { + msg = 'The option "' + key + '" '; + msg += requirement.is ? + "must be one of the following types: " + requirement.is.join(", ") : + "is invalid."; + } + + this.message = msg; +} +RequirementError.prototype = Object.create(Error.prototype); + +let string = { is: ['string', 'undefined', 'null'] }; +exports.string = string; + +let number = { is: ['number', 'undefined', 'null'] }; +exports.number = number; + +let boolean = { is: ['boolean', 'undefined', 'null'] }; +exports.boolean = boolean; + +let object = { is: ['object', 'undefined', 'null'] }; +exports.object = object; + +let isTruthyType = type => !(type === 'undefined' || type === 'null'); +let findTypes = v => { while (!isArray(v) && v.is) v = v.is; return v }; + +function required(req) { + let types = (findTypes(req) || VALID_TYPES).filter(isTruthyType); + + return merge({}, req, {is: types}); +} +exports.required = required; + +function optional(req) { + req = merge({is: []}, req); + req.is = findTypes(req).filter(isTruthyType).concat('undefined', 'null'); + + return req; +} +exports.optional = optional; + +function either(...types) { + return union.apply(null, types.map(findTypes)); +} +exports.either = either; diff --git a/addon-sdk-1.16/lib/sdk/deprecated/cortex.js b/addon-sdk-1.16/lib/sdk/deprecated/cortex.js new file mode 100644 index 00000000..95d4e538 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/deprecated/cortex.js @@ -0,0 +1,112 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "deprecated" +}; + +// `var` is being used in the module in order to make it reusable in +// environments in which `let` and `const` is not yet supported. + +// Returns `object`'s property value, where `name` is a name of the property. +function get(object, name) { + return object[name]; +} + +// Assigns `value` to the `object`'s property, where `name` is the name of the +// property. +function set(object, name, value) { + return object[name] = value; +} + +/** + * Given an `object` containing a property with the given `name`, create + * a property descriptor that can be used to define alias/proxy properties + * on other objects. A change in the value of an alias will propagate + * to the aliased property and vice versa. + */ +function createAliasProperty(object, name) { + // Getting own property descriptor of an `object` for the given `name` as + // we are going to create proxy analog. + var property = Object.getOwnPropertyDescriptor(object, name); + var descriptor = { + configurable: property.configurable, + enumerable: property.enumerable, + alias: true + }; + + // If the original property has a getter and/or setter, bind a + // corresponding getter/setter in the alias descriptor to the original + // object, so the `this` object in the getter/setter is the original object + // rather than the alias. + if ("get" in property && property.get) + descriptor.get = property.get.bind(object); + if ("set" in property && property.set) + descriptor.set = property.set.bind(object); + + // If original property was a value property. + if ("value" in property) { + // If original property is a method using it's `object` bounded copy. + if (typeof property.value === "function") { + descriptor.value = property.value.bind(object); + // Also preserving writability of the original property. + descriptor.writable = property.writable; + } + + // If the original property was just a data property, we create proxy + // accessors using our custom get/set functions to propagate changes to the + // original `object` and vice versa. + else { + descriptor.get = get.bind(null, object, name); + descriptor.set = set.bind(null, object, name); + } + } + return descriptor; +} + +// Defines property on `object` object with a name `alias` if given if not +// defaults to `name` that represents an alias of `source[name]`. If aliased +// property was an assessor or a method `this` pseudo-variable will be `source` +// when invoked. If aliased property was a data property changes on any of the +// aliases will propagate to the `source[name]` and also other way round. +function defineAlias(source, target, name, alias) { + return Object.defineProperty(target, alias || name, + createAliasProperty(source, name)); +} + +/** + * Function takes any `object` and returns a proxy for its own public + * properties. By default properties are considered to be public if they don't + * start with `"_"`, but default behavior can be overridden if needed, by + * passing array of public property `names` as a second argument. By default + * returned object will be direct decedent of the given `object`'s prototype, + * but this can be overridden by passing third optional argument, that will be + * used as `prototype` instead. + * @param {Object} object + * Object to create cortex for. + * @param {String[]} [names] + * Optional array of public property names. + * @param {Object} [prototype] + * Optional argument that will be used as `prototype` of the returned object, + * if not provided `Object.getPrototypeOf(object)` is used instead. + */ +exports.Cortex = function Cortex(object, names, prototype) { + // Creating a cortex object from the given `prototype`, if one was not + // provided then `prototype` of a given `object` is used. This allows + // consumer to define expected behavior `instanceof`. In common case + // `prototype` argument can be omitted to preserve same behavior of + // `instanceof` as on original `object`. + var cortex = Object.create(prototype || Object.getPrototypeOf(object)); + // Creating alias properties on the `cortex` object for all the own + // properties of the original `object` that are contained in `names` array. + // If `names` array is not provided then all the properties that don't + // start with `"_"` are aliased. + Object.getOwnPropertyNames(object).forEach(function (name) { + if ((!names && "_" !== name.charAt(0)) || (names && ~names.indexOf(name))) + defineAlias(object, cortex, name); + }); + return cortex; +} diff --git a/addon-sdk-1.16/lib/sdk/deprecated/errors.js b/addon-sdk-1.16/lib/sdk/deprecated/errors.js new file mode 100644 index 00000000..b640d05c --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/deprecated/errors.js @@ -0,0 +1,64 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "deprecated" +}; + +function logToConsole(e) { + console.exception(e); +} + +var catchAndLog = exports.catchAndLog = function(callback, + defaultResponse, + logException) { + if (!logException) + logException = logToConsole; + + return function() { + try { + return callback.apply(this, arguments); + } catch (e) { + logException(e); + return defaultResponse; + } + }; +}; + +exports.catchAndLogProps = function catchAndLogProps(object, + props, + defaultResponse, + logException) { + if (typeof(props) == "string") + props = [props]; + props.forEach( + function(property) { + object[property] = catchAndLog(object[property], + defaultResponse, + logException); + }); +}; + +/** + * Catch and return an exception while calling the callback. If the callback + * doesn't throw, return the return value of the callback in a way that makes it + * possible to distinguish between a return value and an exception. + * + * This function is useful when you need to pass the result of a call across + * a process boundary (across which exceptions don't propagate). It probably + * doesn't need to be factored out into this module, since it is only used by + * a single caller, but putting it here works around bug 625560. + */ +exports.catchAndReturn = function(callback) { + return function() { + try { + return { returnValue: callback.apply(this, arguments) }; + } + catch (exception) { + return { exception: exception }; + } + }; +}; diff --git a/addon-sdk-1.16/lib/sdk/deprecated/events.js b/addon-sdk-1.16/lib/sdk/deprecated/events.js new file mode 100644 index 00000000..a0b2c6a6 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/deprecated/events.js @@ -0,0 +1,182 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "deprecated" +}; + +const ERROR_TYPE = 'error', + UNCAUGHT_ERROR = 'An error event was dispatched for which there was' + + ' no listener.', + BAD_LISTENER = 'The event listener must be a function.'; +/** + * This object is used to create an `EventEmitter` that, useful for composing + * objects that emit events. It implements an interface like `EventTarget` from + * DOM Level 2, which is implemented by Node objects in implementations that + * support the DOM Event Model. + * @see http://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-EventTarget + * @see http://nodejs.org/api.html#EventEmitter + * @see http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/flash/events/EventDispatcher.html + */ +const eventEmitter = { + /** + * Registers an event `listener` that is called every time events of + * specified `type` are emitted. + * @param {String} type + * The type of event. + * @param {Function} listener + * The listener function that processes the event. + * @example + * worker.on('message', function (data) { + * console.log('data received: ' + data) + * }) + */ + on: function on(type, listener) { + if ('function' !== typeof listener) + throw new Error(BAD_LISTENER); + let listeners = this._listeners(type); + if (0 > listeners.indexOf(listener)) + listeners.push(listener); + // Use of `_public` is required by the legacy traits code that will go away + // once bug-637633 is fixed. + return this._public || this; + }, + + /** + * Registers an event `listener` that is called once the next time an event + * of the specified `type` is emitted. + * @param {String} type + * The type of the event. + * @param {Function} listener + * The listener function that processes the event. + */ + once: function once(type, listener) { + this.on(type, function selfRemovableListener() { + this.removeListener(type, selfRemovableListener); + listener.apply(this, arguments); + }); + }, + + /** + * Unregister `listener` for the specified event type. + * @param {String} type + * The type of event. + * @param {Function} listener + * The listener function that processes the event. + */ + removeListener: function removeListener(type, listener) { + if ('function' !== typeof listener) + throw new Error(BAD_LISTENER); + let listeners = this._listeners(type), + index = listeners.indexOf(listener); + if (0 <= index) + listeners.splice(index, 1); + // Use of `_public` is required by the legacy traits code, that will go away + // once bug-637633 is fixed. + return this._public || this; + }, + + /** + * Hash of listeners on this EventEmitter. + */ + _events: null, + + /** + * Returns an array of listeners for the specified event `type`. This array + * can be manipulated, e.g. to remove listeners. + * @param {String} type + * The type of event. + */ + _listeners: function listeners(type) { + let events = this._events || (this._events = {}); + return (events.hasOwnProperty(type) && events[type]) || (events[type] = []); + }, + + /** + * Execute each of the listeners in order with the supplied arguments. + * Returns `true` if listener for this event was called, `false` if there are + * no listeners for this event `type`. + * + * All the exceptions that are thrown by listeners during the emit + * are caught and can be handled by listeners of 'error' event. Thrown + * exceptions are passed as an argument to an 'error' event listener. + * If no 'error' listener is registered exception will propagate to a + * caller of this method. + * + * **It's recommended to have a default 'error' listener in all the complete + * composition that in worst case may dump errors to the console.** + * + * @param {String} type + * The type of event. + * @params {Object|Number|String|Boolean} + * Arguments that will be passed to listeners. + * @returns {Boolean} + */ + _emit: function _emit(type, event) { + let args = Array.slice(arguments); + // Use of `_public` is required by the legacy traits code that will go away + // once bug-637633 is fixed. + args.unshift(this._public || this); + return this._emitOnObject.apply(this, args); + }, + + /** + * A version of _emit that lets you specify the object on which listeners are + * called. This is a hack that is sometimes necessary when such an object + * (exports, for example) cannot be an EventEmitter for some reason, but other + * object(s) managing events for the object are EventEmitters. Once bug + * 577782 is fixed, this method shouldn't be necessary. + * + * @param {object} targetObj + * The object on which listeners will be called. + * @param {string} type + * The event name. + * @param {value} event + * The first argument to pass to listeners. + * @param {value} ... + * More arguments to pass to listeners. + * @returns {boolean} + */ + _emitOnObject: function _emitOnObject(targetObj, type, event /* , ... */) { + let listeners = this._listeners(type).slice(0); + // If there is no 'error' event listener then throw. + if (type === ERROR_TYPE && !listeners.length) + console.exception(event); + if (!listeners.length) + return false; + let params = Array.slice(arguments, 2); + for each (let listener in listeners) { + try { + listener.apply(targetObj, params); + } catch(e) { + // Bug 726967: Ignore exceptions being throws while notifying the error + // in order to avoid infinite loops. + if (type !== ERROR_TYPE) + this._emit(ERROR_TYPE, e); + else + console.exception("Exception in error event listener " + e); + } + } + return true; + }, + + /** + * Removes all the event listeners for the specified event `type`. + * @param {String} type + * The type of event. + */ + _removeAllListeners: function _removeAllListeners(type) { + if (typeof type == "undefined") { + this._events = null; + return this; + } + + this._listeners(type).splice(0); + return this; + } +}; +exports.EventEmitter = require("./traits").Trait.compose(eventEmitter); +exports.EventEmitterTrait = require('./light-traits').Trait(eventEmitter); diff --git a/addon-sdk-1.16/lib/sdk/deprecated/events/assembler.js b/addon-sdk-1.16/lib/sdk/deprecated/events/assembler.js new file mode 100644 index 00000000..7491aebf --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/deprecated/events/assembler.js @@ -0,0 +1,52 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const { Trait } = require("../light-traits"); +const { removeListener, on } = require("../../dom/events"); + +/** + * Trait may be used for building objects / composing traits that wish to handle + * multiple dom events from multiple event targets in one place. Event targets + * can be added / removed by calling `observe / ignore` methods. Composer should + * provide array of event types it wishes to handle as property + * `supportedEventsTypes` and function for handling all those events as + * `handleEvent` property. + */ +exports.DOMEventAssembler = Trait({ + /** + * Function that is supposed to handle all the supported events (that are + * present in the `supportedEventsTypes`) from all the observed + * `eventTargets`. + * @param {Event} event + * Event being dispatched. + */ + handleEvent: Trait.required, + /** + * Array of supported event names. + * @type {String[]} + */ + supportedEventsTypes: Trait.required, + /** + * Adds `eventTarget` to the list of observed `eventTarget`s. Listeners for + * supported events will be registered on the given `eventTarget`. + * @param {EventTarget} eventTarget + */ + observe: function observe(eventTarget) { + this.supportedEventsTypes.forEach(function(eventType) { + on(eventTarget, eventType, this); + }, this); + }, + /** + * Removes `eventTarget` from the list of observed `eventTarget`s. Listeners + * for all supported events will be unregistered from the given `eventTarget`. + * @param {EventTarget} eventTarget + */ + ignore: function ignore(eventTarget) { + this.supportedEventsTypes.forEach(function(eventType) { + removeListener(eventTarget, eventType, this); + }, this); + } +}); diff --git a/addon-sdk-1.16/lib/sdk/deprecated/light-traits.js b/addon-sdk-1.16/lib/sdk/deprecated/light-traits.js new file mode 100644 index 00000000..68c778a8 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/deprecated/light-traits.js @@ -0,0 +1,599 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "deprecated" +}; + +// `var` is being used in the module in order to make it reusable in +// environments in which `let` is not yet supported. + +// Shortcut to `Object.prototype.hasOwnProperty.call`. +// owns(object, name) would be the same as +// Object.prototype.hasOwnProperty.call(object, name); +var owns = Function.prototype.call.bind(Object.prototype.hasOwnProperty); + +/** + * Whether or not given property descriptors are equivalent. They are + * equivalent either if both are marked as 'conflict' or 'required' property + * or if all the properties of descriptors are equal. + * @param {Object} actual + * @param {Object} expected + */ +function equivalentDescriptors(actual, expected) { + return (actual.conflict && expected.conflict) || + (actual.required && expected.required) || + equalDescriptors(actual, expected); +} +/** + * Whether or not given property descriptors define equal properties. + */ +function equalDescriptors(actual, expected) { + return actual.get === expected.get && + actual.set === expected.set && + actual.value === expected.value && + !!actual.enumerable === !!expected.enumerable && + !!actual.configurable === !!expected.configurable && + !!actual.writable === !!expected.writable; +} + +// Utilities that throwing exceptions for a properties that are marked +// as "required" or "conflict" properties. +function throwConflictPropertyError(name) { + throw new Error("Remaining conflicting property: `" + name + "`"); +} +function throwRequiredPropertyError(name) { + throw new Error("Missing required property: `" + name + "`"); +} + +/** + * Generates custom **required** property descriptor. Descriptor contains + * non-standard property `required` that is equal to `true`. + * @param {String} name + * property name to generate descriptor for. + * @returns {Object} + * custom property descriptor + */ +function RequiredPropertyDescriptor(name) { + // Creating function by binding first argument to a property `name` on the + // `throwConflictPropertyError` function. Created function is used as a + // getter & setter of the created property descriptor. This way we ensure + // that we throw exception late (on property access) if object with + // `required` property was instantiated using built-in `Object.create`. + var accessor = throwRequiredPropertyError.bind(null, name); + return { get: accessor, set: accessor, required: true }; +} + +/** + * Generates custom **conflicting** property descriptor. Descriptor contains + * non-standard property `conflict` that is equal to `true`. + * @param {String} name + * property name to generate descriptor for. + * @returns {Object} + * custom property descriptor + */ +function ConflictPropertyDescriptor(name) { + // For details see `RequiredPropertyDescriptor` since idea is same. + var accessor = throwConflictPropertyError.bind(null, name); + return { get: accessor, set: accessor, conflict: true }; +} + +/** + * Tests if property is marked as `required` property. + */ +function isRequiredProperty(object, name) { + return !!object[name].required; +} + +/** + * Tests if property is marked as `conflict` property. + */ +function isConflictProperty(object, name) { + return !!object[name].conflict; +} + +/** + * Function tests whether or not method of the `source` object with a given + * `name` is inherited from `Object.prototype`. + */ +function isBuiltInMethod(name, source) { + var target = Object.prototype[name]; + + // If methods are equal then we know it's `true`. + return target == source || + // If `source` object comes form a different sandbox `==` will evaluate + // to `false`, in that case we check if functions names and sources match. + (String(target) === String(source) && target.name === source.name); +} + +/** + * Function overrides `toString` and `constructor` methods of a given `target` + * object with a same-named methods of a given `source` if methods of `target` + * object are inherited / copied from `Object.prototype`. + * @see create + */ +function overrideBuiltInMethods(target, source) { + if (isBuiltInMethod("toString", target.toString)) { + Object.defineProperty(target, "toString", { + value: source.toString, + configurable: true, + enumerable: false + }); + } + + if (isBuiltInMethod("constructor", target.constructor)) { + Object.defineProperty(target, "constructor", { + value: source.constructor, + configurable: true, + enumerable: false + }); + } +} + +/** + * Composes new trait with the same own properties as the original trait, + * except that all property names appearing in the first argument are replaced + * by "required" property descriptors. + * @param {String[]} keys + * Array of strings property names. + * @param {Object} trait + * A trait some properties of which should be excluded. + * @returns {Object} + * @example + * var newTrait = exclude(["name", ...], trait) + */ +function exclude(names, trait) { + var map = {}; + + Object.keys(trait).forEach(function(name) { + + // If property is not excluded (the array of names does not contain it), + // or it is a "required" property, copy it to the property descriptor `map` + // that will be used for creation of resulting trait. + if (!~names.indexOf(name) || isRequiredProperty(trait, name)) + map[name] = { value: trait[name], enumerable: true }; + + // For all the `names` in the exclude name array we create required + // property descriptors and copy them to the `map`. + else + map[name] = { value: RequiredPropertyDescriptor(name), enumerable: true }; + }); + + return Object.create(Trait.prototype, map); +} + +/** + * Composes new instance of `Trait` with a properties of a given `trait`, + * except that all properties whose name is an own property of `renames` will + * be renamed to `renames[name]` and a `"required"` property for name will be + * added instead. + * + * For each renamed property, a required property is generated. If + * the `renames` map two properties to the same name, a conflict is generated. + * If the `renames` map a property to an existing unrenamed property, a + * conflict is generated. + * + * @param {Object} renames + * An object whose own properties serve as a mapping from old names to new + * names. + * @param {Object} trait + * A new trait with renamed properties. + * @returns {Object} + * @example + * + * // Return trait with `bar` property equal to `trait.foo` and with + * // `foo` and `baz` "required" properties. + * var renamedTrait = rename({ foo: "bar", baz: null }), trait); + * + * // t1 and t2 are equivalent traits + * var t1 = rename({a: "b"}, t); + * var t2 = compose(exclude(["a"], t), { a: { required: true }, b: t[a] }); + */ +function rename(renames, trait) { + var map = {}; + + // Loop over all the properties of the given `trait` and copy them to a + // property descriptor `map` that will be used for the creation of the + // resulting trait. Also, rename properties in the `map` as specified by + // `renames`. + Object.keys(trait).forEach(function(name) { + var alias; + + // If the property is in the `renames` map, and it isn't a "required" + // property (which should never need to be aliased because "required" + // properties never conflict), then we must try to rename it. + if (owns(renames, name) && !isRequiredProperty(trait, name)) { + alias = renames[name]; + + // If the `map` already has the `alias`, and it isn't a "required" + // property, that means the `alias` conflicts with an existing name for a + // provided trait (that can happen if >=2 properties are aliased to the + // same name). In this case we mark it as a conflicting property. + // Otherwise, everything is fine, and we copy property with an `alias` + // name. + if (owns(map, alias) && !map[alias].value.required) { + map[alias] = { + value: ConflictPropertyDescriptor(alias), + enumerable: true + }; + } + else { + map[alias] = { + value: trait[name], + enumerable: true + }; + } + + // Regardless of whether or not the rename was successful, we check to + // see if the original `name` exists in the map (such a property + // could exist if previous another property was aliased to this `name`). + // If it isn't, we mark it as "required", to make sure the caller + // provides another value for the old name, which methods of the trait + // might continue to reference. + if (!owns(map, name)) { + map[name] = { + value: RequiredPropertyDescriptor(name), + enumerable: true + }; + } + } + + // Otherwise, either the property isn't in the `renames` map (thus the + // caller is not trying to rename it) or it is a "required" property. + // Either way, we don't have to alias the property, we just have to copy it + // to the map. + else { + // The property isn't in the map yet, so we copy it over. + if (!owns(map, name)) { + map[name] = { value: trait[name], enumerable: true }; + } + + // The property is already in the map (that means another property was + // aliased with this `name`, which creates a conflict if the property is + // not marked as "required"), so we have to mark it as a "conflict" + // property. + else if (!isRequiredProperty(trait, name)) { + map[name] = { + value: ConflictPropertyDescriptor(name), + enumerable: true + }; + } + } + }); + return Object.create(Trait.prototype, map); +} + +/** + * Composes new resolved trait, with all the same properties as the original + * `trait`, except that all properties whose name is an own property of + * `resolutions` will be renamed to `resolutions[name]`. + * + * If `resolutions[name]` is `null`, the value is mapped to a property + * descriptor that is marked as a "required" property. + */ +function resolve(resolutions, trait) { + var renames = {}; + var exclusions = []; + + // Go through each mapping in `resolutions` object and distribute it either + // to `renames` or `exclusions`. + Object.keys(resolutions).forEach(function(name) { + + // If `resolutions[name]` is a truthy value then it's a mapping old -> new + // so we copy it to `renames` map. + if (resolutions[name]) + renames[name] = resolutions[name]; + + // Otherwise it's not a mapping but an exclusion instead in which case we + // add it to the `exclusions` array. + else + exclusions.push(name); + }); + + // First `exclude` **then** `rename` and order is important since + // `exclude` and `rename` are not associative. + return rename(renames, exclude(exclusions, trait)); +} + +/** + * Create a Trait (a custom property descriptor map) that represents the given + * `object`'s own properties. Property descriptor map is a "custom", because it + * inherits from `Trait.prototype` and it's property descriptors may contain + * two attributes that is not part of the ES5 specification: + * + * - "required" (this property must be provided by another trait + * before an instance of this trait can be created) + * - "conflict" (when the trait is composed with another trait, + * a unique value for this property is provided by two or more traits) + * + * Data properties bound to the `Trait.required` singleton exported by + * this module will be marked as "required" properties. + * + * @param {Object} object + * Map of properties to compose trait from. + * @returns {Trait} + * Trait / Property descriptor map containing all the own properties of the + * given argument. + */ +function trait(object) { + var map; + var trait = object; + + if (!(object instanceof Trait)) { + // If the passed `object` is not already an instance of `Trait`, we create + // a property descriptor `map` containing descriptors for the own properties + // of the given `object`. `map` is then used to create a `Trait` instance + // after all properties are mapped. Note that we can't create a trait and + // then just copy properties into it since that will fail for inherited + // read-only properties. + map = {}; + + // Each own property of the given `object` is mapped to a data property + // whose value is a property descriptor. + Object.keys(object).forEach(function (name) { + + // If property of an `object` is equal to a `Trait.required`, it means + // that it was marked as "required" property, in which case we map it + // to "required" property. + if (Trait.required == + Object.getOwnPropertyDescriptor(object, name).value) { + map[name] = { + value: RequiredPropertyDescriptor(name), + enumerable: true + }; + } + // Otherwise property is mapped to it's property descriptor. + else { + map[name] = { + value: Object.getOwnPropertyDescriptor(object, name), + enumerable: true + }; + } + }); + + trait = Object.create(Trait.prototype, map); + } + return trait; +} + +/** + * Compose a property descriptor map that inherits from `Trait.prototype` and + * contains property descriptors for all the own properties of the passed + * traits. + * + * If two or more traits have own properties with the same name, the returned + * trait will contain a "conflict" property for that name. Composition is a + * commutative and associative operation, and the order of its arguments is + * irrelevant. + */ +function compose(trait1, trait2/*, ...*/) { + // Create a new property descriptor `map` to which all the own properties + // of the passed traits are copied. This map will be used to create a `Trait` + // instance that will be the result of this composition. + var map = {}; + + // Properties of each passed trait are copied to the composition. + Array.prototype.forEach.call(arguments, function(trait) { + // Copying each property of the given trait. + Object.keys(trait).forEach(function(name) { + + // If `map` already owns a property with the `name` and it is not + // marked "required". + if (owns(map, name) && !map[name].value.required) { + + // If the source trait's property with the `name` is marked as + // "required", we do nothing, as the requirement was already resolved + // by a property in the `map` (because it already contains a + // non-required property with that `name`). But if properties are just + // different, we have a name clash and we substitute it with a property + // that is marked "conflict". + if (!isRequiredProperty(trait, name) && + !equivalentDescriptors(map[name].value, trait[name]) + ) { + map[name] = { + value: ConflictPropertyDescriptor(name), + enumerable: true + }; + } + } + + // Otherwise, the `map` does not have an own property with the `name`, or + // it is marked "required". Either way, the trait's property is copied to + // the map (if the property of the `map` is marked "required", it is going + // to be resolved by the property that is being copied). + else { + map[name] = { value: trait[name], enumerable: true }; + } + }); + }); + + return Object.create(Trait.prototype, map); +} + +/** + * `defineProperties` is like `Object.defineProperties`, except that it + * ensures that: + * - An exception is thrown if any property in a given `properties` map + * is marked as "required" property and same named property is not + * found in a given `prototype`. + * - An exception is thrown if any property in a given `properties` map + * is marked as "conflict" property. + * @param {Object} object + * Object to define properties on. + * @param {Object} properties + * Properties descriptor map. + * @returns {Object} + * `object` that was passed as a first argument. + */ +function defineProperties(object, properties) { + + // Create a map into which we will copy each verified property from the given + // `properties` description map. We use it to verify that none of the + // provided properties is marked as a "conflict" property and that all + // "required" properties are resolved by a property of an `object`, so we + // can throw an exception before mutating object if that isn't the case. + var verifiedProperties = {}; + + // Coping each property from a given `properties` descriptor map to a + // verified map of property descriptors. + Object.keys(properties).forEach(function(name) { + + // If property is marked as "required" property and we don't have a same + // named property in a given `object` we throw an exception. If `object` + // has same named property just skip this property since required property + // is was inherited and there for requirement was satisfied. + if (isRequiredProperty(properties, name)) { + if (!(name in object)) + throwRequiredPropertyError(name); + } + + // If property is marked as "conflict" property we throw an exception. + else if (isConflictProperty(properties, name)) { + throwConflictPropertyError(name); + } + + // If property is not marked neither as "required" nor "conflict" property + // we copy it to verified properties map. + else { + verifiedProperties[name] = properties[name]; + } + }); + + // If no exceptions were thrown yet, we know that our verified property + // descriptor map has no properties marked as "conflict" or "required", + // so we just delegate to the built-in `Object.defineProperties`. + return Object.defineProperties(object, verifiedProperties); +} + +/** + * `create` is like `Object.create`, except that it ensures that: + * - An exception is thrown if any property in a given `properties` map + * is marked as "required" property and same named property is not + * found in a given `prototype`. + * - An exception is thrown if any property in a given `properties` map + * is marked as "conflict" property. + * @param {Object} prototype + * prototype of the composed object + * @param {Object} properties + * Properties descriptor map. + * @returns {Object} + * An object that inherits form a given `prototype` and implements all the + * properties defined by a given `properties` descriptor map. + */ +function create(prototype, properties) { + + // Creating an instance of the given `prototype`. + var object = Object.create(prototype); + + // Overriding `toString`, `constructor` methods if they are just inherited + // from `Object.prototype` with a same named methods of the `Trait.prototype` + // that will have more relevant behavior. + overrideBuiltInMethods(object, Trait.prototype); + + // Trying to define given `properties` on the `object`. We use our custom + // `defineProperties` function instead of build-in `Object.defineProperties` + // that behaves exactly the same, except that it will throw if any + // property in the given `properties` descriptor is marked as "required" or + // "conflict" property. + return defineProperties(object, properties); +} + +/** + * Composes new trait. If two or more traits have own properties with the + * same name, the new trait will contain a "conflict" property for that name. + * "compose" is a commutative and associative operation, and the order of its + * arguments is not significant. + * + * **Note:** Use `Trait.compose` instead of calling this function with more + * than one argument. The multiple-argument functionality is strictly for + * backward compatibility. + * + * @params {Object} trait + * Takes traits as an arguments + * @returns {Object} + * New trait containing the combined own properties of all the traits. + * @example + * var newTrait = compose(trait_1, trait_2, ..., trait_N) + */ +function Trait(trait1, trait2) { + + // If the function was called with one argument, the argument should be + // an object whose properties are mapped to property descriptors on a new + // instance of Trait, so we delegate to the trait function. + // If the function was called with more than one argument, those arguments + // should be instances of Trait or plain property descriptor maps + // whose properties should be mixed into a new instance of Trait, + // so we delegate to the compose function. + + return trait2 === undefined ? trait(trait1) : compose.apply(null, arguments); +} + +Object.freeze(Object.defineProperties(Trait.prototype, { + toString: { + value: function toString() { + return "[object " + this.constructor.name + "]"; + } + }, + + /** + * `create` is like `Object.create`, except that it ensures that: + * - An exception is thrown if this trait defines a property that is + * marked as required property and same named property is not + * found in a given `prototype`. + * - An exception is thrown if this trait contains property that is + * marked as "conflict" property. + * @param {Object} + * prototype of the compared object + * @returns {Object} + * An object with all of the properties described by the trait. + */ + create: { + value: function createTrait(prototype) { + return create(undefined === prototype ? Object.prototype : prototype, + this); + }, + enumerable: true + }, + + /** + * Composes a new resolved trait, with all the same properties as the original + * trait, except that all properties whose name is an own property of + * `resolutions` will be renamed to the value of `resolutions[name]`. If + * `resolutions[name]` is `null`, the property is marked as "required". + * @param {Object} resolutions + * An object whose own properties serve as a mapping from old names to new + * names, or to `null` if the property should be excluded. + * @returns {Object} + * New trait with the same own properties as the original trait but renamed. + */ + resolve: { + value: function resolveTrait(resolutions) { + return resolve(resolutions, this); + }, + enumerable: true + } +})); + +/** + * @see compose + */ +Trait.compose = Object.freeze(compose); +Object.freeze(compose.prototype); + +/** + * Constant singleton, representing placeholder for required properties. + * @type {Object} + */ +Trait.required = Object.freeze(Object.create(Object.prototype, { + toString: { + value: Object.freeze(function toString() { + return ""; + }) + } +})); +Object.freeze(Trait.required.toString.prototype); + +exports.Trait = Object.freeze(Trait); diff --git a/addon-sdk-1.16/lib/sdk/deprecated/list.js b/addon-sdk-1.16/lib/sdk/deprecated/list.js new file mode 100644 index 00000000..a7832bc5 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/deprecated/list.js @@ -0,0 +1,126 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +module.metadata = { + "stability": "experimental" +}; + +const { Trait } = require('../deprecated/traits'); +const { iteratorSymbol } = require('../util/iteration'); + +/** + * @see https://jetpack.mozillalabs.com/sdk/latest/docs/#module/api-utils/list + */ +const Iterable = Trait.compose({ + /** + * Hash map of key-values to iterate over. + * Note: That this property can be a getter if you need dynamic behavior. + * @type {Object} + */ + _keyValueMap: Trait.required, + /** + * Custom iterator providing `Iterable`s enumeration behavior. + * @param {Boolean} onKeys + */ + __iterator__: function __iterator__(onKeys, onKeyValue) { + let map = this._keyValueMap; + for (let key in map) + yield onKeyValue ? [key, map[key]] : onKeys ? key : map[key]; + } +}); +exports.Iterable = Iterable; + +/** + * An ordered collection (also known as a sequence) disallowing duplicate + * elements. List is composed out of `Iterable` there for it provides custom + * enumeration behavior that is similar to array (enumerates only on the + * elements of the list). List is a base trait and is meant to be a part of + * composition, since all of it's API is private except length property. + */ +const listOptions = { + _keyValueMap: null, + /** + * List constructor can take any number of element to populate itself. + * @params {Object|String|Number} element + * @example + * List(1,2,3).length == 3 // true + */ + constructor: function List() { + this._keyValueMap = []; + for (let i = 0, ii = arguments.length; i < ii; i++) + this._add(arguments[i]); + }, + /** + * Number of elements in this list. + * @type {Number} + */ + get length() this._keyValueMap.length, + /** + * Returns a string representing this list. + * @returns {String} + */ + toString: function toString() 'List(' + this._keyValueMap + ')', + /** + * Returns `true` if this list contains the specified `element`. + * @param {Object|Number|String} element + * @returns {Boolean} + */ + _has: function _has(element) 0 <= this._keyValueMap.indexOf(element), + /** + * Appends the specified `element` to the end of this list, if it doesn't + * contains it. Ignores the call if `element` is already contained. + * @param {Object|Number|String} element + */ + _add: function _add(element) { + let list = this._keyValueMap, + index = list.indexOf(element); + if (0 > index) + list.push(this._public[list.length] = element); + }, + /** + * Removes specified `element` from this list, if it contains it. + * Ignores the call if `element` is not contained. + * @param {Object|Number|String} element + */ + _remove: function _remove(element) { + let list = this._keyValueMap, + index = list.indexOf(element); + if (0 <= index) { + delete this._public[list.length - 1]; + list.splice(index, 1); + for (let length = list.length; index < length; index++) + this._public[index] = list[index]; + } + }, + /** + * Removes all of the elements from this list. + */ + _clear: function _clear() { + for (let i = 0, ii = this._keyValueMap.length; i < ii; i ++) + delete this._public[i]; + this._keyValueMap.splice(0); + }, + /** + * Custom iterator providing `List`s enumeration behavior. + * We cant reuse `_iterator` that is defined by `Iterable` since it provides + * iteration in an arbitrary order. + * @see https://developer.mozilla.org/en/JavaScript/Reference/Statements/for...in + * @param {Boolean} onKeys + */ + __iterator__: function __iterator__(onKeys, onKeyValue) { + let array = this._keyValueMap.slice(0), + i = -1; + for (let element of array) + yield onKeyValue ? [++i, element] : onKeys ? ++i : element; + }, +}; +listOptions[iteratorSymbol] = function* iterator() { + let array = this._keyValueMap.slice(0); + + for (let element of array) + yield element; +} +const List = Trait.resolve({ toString: null }).compose(listOptions); +exports.List = List; diff --git a/addon-sdk-1.16/lib/sdk/deprecated/memory.js b/addon-sdk-1.16/lib/sdk/deprecated/memory.js new file mode 100644 index 00000000..b2aa48e8 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/deprecated/memory.js @@ -0,0 +1,129 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +module.metadata = { + "stability": "deprecated" +}; + +const { Cc, Ci, Cu, components } = require("chrome"); +const { when: unload } = require("../system/unload") + +var trackedObjects = {}; +const Compacter = { + notify: function() { + var newTrackedObjects = {}; + + for (let name in trackedObjects) { + let oldBin = trackedObjects[name]; + let newBin = []; + let strongRefs = []; + + for (let i = 0, l = oldBin.length; i < l; i++) { + let strongRef = oldBin[i].weakref.get(); + + if (strongRef && strongRefs.indexOf(strongRef) == -1) { + strongRefs.push(strongRef); + newBin.push(oldBin[i]); + } + } + + if (newBin.length) + newTrackedObjects[name] = newBin; + } + + trackedObjects = newTrackedObjects; + } +}; + +var timer = Cc["@mozilla.org/timer;1"] + .createInstance(Ci.nsITimer); +timer.initWithCallback(Compacter, + 5000, + Ci.nsITimer.TYPE_REPEATING_SLACK); + +function track(object, bin, stackFrameNumber) { + var frame = components.stack.caller; + var weakref = Cu.getWeakReference(object); + + if (!bin && 'constructor' in object) + bin = object.constructor.name; + if (bin == "Object") + bin = frame.name; + if (!bin) + bin = "generic"; + if (!(bin in trackedObjects)) + trackedObjects[bin] = []; + + if (stackFrameNumber > 0) + for (var i = 0; i < stackFrameNumber; i++) + frame = frame.caller; + + trackedObjects[bin].push({weakref: weakref, + created: new Date(), + filename: frame.filename, + lineNo: frame.lineNumber, + bin: bin}); +} +exports.track = track; + +var getBins = exports.getBins = function getBins() { + var names = []; + for (let name in trackedObjects) + names.push(name); + return names; +}; + +function getObjects(bin) { + var results = []; + + function getLiveObjectsInBin(bin) { + for (let i = 0, l = bin.length; i < l; i++) { + let object = bin[i].weakref.get(); + + if (object) { + results.push(bin[i]); + } + } + } + + if (bin) { + if (bin in trackedObjects) + getLiveObjectsInBin(trackedObjects[bin]); + } + else { + for (let name in trackedObjects) + getLiveObjectsInBin(trackedObjects[name]); + } + + return results; +} +exports.getObjects = getObjects; + +function gc() { + // Components.utils.forceGC() doesn't currently perform + // cycle collection, which means that e.g. DOM elements + // won't be collected by it. Fortunately, there are + // other ways... + var test_utils = Cc["@mozilla.org/appshell/appShellService;1"] + .getService(Ci.nsIAppShellService) + .hiddenDOMWindow + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + test_utils.garbageCollect(); + // Clean metadata for dead objects + Compacter.notify(); + // Not sure why, but sometimes it appears that we don't get + // them all with just one CC, so let's do it again. + test_utils.garbageCollect(); +}; +exports.gc = gc; + +unload(_ => { + trackedObjects = {}; + if (timer) { + timer.cancel(); + timer = null; + } +}); diff --git a/addon-sdk-1.16/lib/sdk/deprecated/symbiont.js b/addon-sdk-1.16/lib/sdk/deprecated/symbiont.js new file mode 100644 index 00000000..89447ec4 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/deprecated/symbiont.js @@ -0,0 +1,229 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +module.metadata = { + "stability": "deprecated" +}; + +const { Worker } = require('./traits-worker'); +const { Loader } = require('../content/loader'); +const hiddenFrames = require('../frame/hidden-frame'); +const { on, off } = require('../system/events'); +const unload = require('../system/unload'); +const { getDocShell } = require("../frame/utils"); +const { ignoreWindow } = require('../private-browsing/utils'); + +// Everything coming from add-on's xpi considered an asset. +const assetsURI = require('../self').data.url().replace(/data\/$/, ""); + +/** + * This trait is layered on top of `Worker` and in contrast to symbiont + * Worker constructor requires `content` option that represents content + * that will be loaded in the provided frame, if frame is not provided + * Worker will create hidden one. + */ +const Symbiont = Worker.resolve({ + constructor: '_initWorker', + destroy: '_workerDestroy' + }).compose(Loader, { + + /** + * The constructor requires all the options that are required by + * `require('content').Worker` with the difference that the `frame` option + * is optional. If `frame` is not provided, `contentURL` is expected. + * @param {Object} options + * @param {String} options.contentURL + * URL of a content to load into `this._frame` and create worker for. + * @param {Element} [options.frame] + * iframe element that is used to load `options.contentURL` into. + * if frame is not provided hidden iframe will be created. + */ + constructor: function Symbiont(options) { + options = options || {}; + + if ('contentURL' in options) + this.contentURL = options.contentURL; + if ('contentScriptWhen' in options) + this.contentScriptWhen = options.contentScriptWhen; + if ('contentScriptOptions' in options) + this.contentScriptOptions = options.contentScriptOptions; + if ('contentScriptFile' in options) + this.contentScriptFile = options.contentScriptFile; + if ('contentScript' in options) + this.contentScript = options.contentScript; + if ('allow' in options) + this.allow = options.allow; + if ('onError' in options) + this.on('error', options.onError); + if ('onMessage' in options) + this.on('message', options.onMessage); + if ('frame' in options) { + this._initFrame(options.frame); + } + else { + let self = this; + this._hiddenFrame = hiddenFrames.HiddenFrame({ + onReady: function onFrame() { + self._initFrame(this.element); + }, + onUnload: function onUnload() { + // Bug 751211: Remove reference to _frame when hidden frame is + // automatically removed on unload, otherwise we are going to face + // "dead object" exception + self.destroy(); + } + }); + hiddenFrames.add(this._hiddenFrame); + } + + unload.ensure(this._public, "destroy"); + }, + + destroy: function destroy() { + this._workerDestroy(); + this._unregisterListener(); + this._frame = null; + if (this._hiddenFrame) { + hiddenFrames.remove(this._hiddenFrame); + this._hiddenFrame = null; + } + }, + + /** + * XUL iframe or browser elements with attribute `type` being `content`. + * Used to create `ContentSymbiont` from. + * @type {nsIFrame|nsIBrowser} + */ + _frame: null, + + /** + * Listener to the `'frameReady"` event (emitted when `iframe` is ready). + * Removes listener, sets right permissions to the frame and loads content. + */ + _initFrame: function _initFrame(frame) { + if (this._loadListener) + this._unregisterListener(); + + this._frame = frame; + + if (getDocShell(frame)) { + this._reallyInitFrame(frame); + } + else { + if (this._waitForFrame) { + off('content-document-global-created', this._waitForFrame); + } + this._waitForFrame = this.__waitForFrame.bind(this, frame); + on('content-document-global-created', this._waitForFrame); + } + }, + + __waitForFrame: function _waitForFrame(frame, { subject: win }) { + if (frame.contentWindow == win) { + off('content-document-global-created', this._waitForFrame); + delete this._waitForFrame; + this._reallyInitFrame(frame); + } + }, + + _reallyInitFrame: function _reallyInitFrame(frame) { + getDocShell(frame).allowJavascript = this.allow.script; + frame.setAttribute("src", this._contentURL); + + // Inject `addon` object in document if we load a document from + // one of our addon folder and if no content script are defined. bug 612726 + let isDataResource = + typeof this._contentURL == "string" && + this._contentURL.indexOf(assetsURI) == 0; + let hasContentScript = + (Array.isArray(this.contentScript) ? this.contentScript.length > 0 + : !!this.contentScript) || + (Array.isArray(this.contentScriptFile) ? this.contentScriptFile.length > 0 + : !!this.contentScriptFile); + // If we have to inject `addon` we have to do it before document + // script execution, so during `start`: + this._injectInDocument = isDataResource && !hasContentScript; + if (this._injectInDocument) + this.contentScriptWhen = "start"; + + if ((frame.contentDocument.readyState == "complete" || + (frame.contentDocument.readyState == "interactive" && + this.contentScriptWhen != 'end' )) && + frame.contentDocument.location == this._contentURL) { + // In some cases src doesn't change and document is already ready + // (for ex: when the user moves a widget while customizing toolbars.) + this._onInit(); + return; + } + + let self = this; + + if ('start' == this.contentScriptWhen) { + this._loadEvent = 'start'; + on('document-element-inserted', + this._loadListener = function onStart({ subject: doc }) { + let window = doc.defaultView; + + if (ignoreWindow(window)) { + return; + } + + if (window && window == frame.contentWindow) { + self._unregisterListener(); + self._onInit(); + } + + }); + return; + } + + let eventName = 'end' == this.contentScriptWhen ? 'load' : 'DOMContentLoaded'; + let self = this; + this._loadEvent = eventName; + frame.addEventListener(eventName, + this._loadListener = function _onReady(event) { + + if (event.target != frame.contentDocument) + return; + self._unregisterListener(); + + self._onInit(); + + }, true); + + }, + + /** + * Unregister listener that watchs for document being ready to be injected. + * This listener is registered in `Symbiont._initFrame`. + */ + _unregisterListener: function _unregisterListener() { + if (this._waitForFrame) { + off('content-document-global-created', this._waitForFrame); + delete this._waitForFrame; + } + + if (!this._loadListener) + return; + if (this._loadEvent == "start") { + off('document-element-inserted', this._loadListener); + } + else { + this._frame.removeEventListener(this._loadEvent, this._loadListener, + true); + } + this._loadListener = null; + }, + + /** + * Called by Symbiont itself when the frame is ready to load + * content scripts according to contentScriptWhen. Overloaded by Panel. + */ + _onInit: function () { + this._initWorker({ window: this._frame.contentWindow }); + } + +}); +exports.Symbiont = Symbiont; diff --git a/addon-sdk-1.16/lib/sdk/deprecated/traits-worker.js b/addon-sdk-1.16/lib/sdk/deprecated/traits-worker.js new file mode 100644 index 00000000..8a7e1cc2 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/deprecated/traits-worker.js @@ -0,0 +1,660 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * + * `deprecated/traits-worker` was previously `content/worker` and kept + * only due to `deprecated/symbiont` using it, which is necessary for + * `widget`, until that reaches deprecation EOL. + * + */ + +"use strict"; + +module.metadata = { + "stability": "deprecated" +}; + +const { Trait } = require('./traits'); +const { EventEmitter, EventEmitterTrait } = require('./events'); +const { Ci, Cu, Cc } = require('chrome'); +const timer = require('../timers'); +const { URL } = require('../url'); +const unload = require('../system/unload'); +const observers = require('../system/events'); +const { Cortex } = require('./cortex'); +const { sandbox, evaluate, load } = require("../loader/sandbox"); +const { merge } = require('../util/object'); +const xulApp = require("../system/xul-app"); +const { getInnerId } = require("../window/utils") +const USE_JS_PROXIES = !xulApp.versionInRange(xulApp.platformVersion, + "17.0a2", "*"); +const { getTabForWindow } = require('../tabs/helpers'); +const { getTabForContentWindow } = require('../tabs/utils'); + +/* Trick the linker in order to ensure shipping these files in the XPI. + require('../content/content-worker.js'); + Then, retrieve URL of these files in the XPI: +*/ +let prefix = module.uri.split('deprecated/traits-worker.js')[0]; +const CONTENT_WORKER_URL = prefix + 'content/content-worker.js'; + +// Fetch additional list of domains to authorize access to for each content +// script. It is stored in manifest `metadata` field which contains +// package.json data. This list is originaly defined by authors in +// `permissions` attribute of their package.json addon file. +const permissions = require('@loader/options').metadata['permissions'] || {}; +const EXPANDED_PRINCIPALS = permissions['cross-domain-content'] || []; + +const JS_VERSION = '1.8'; + +const ERR_DESTROYED = + "Couldn't find the worker to receive this message. " + + "The script may not be initialized yet, or may already have been unloaded."; + +const ERR_FROZEN = "The page is currently hidden and can no longer be used " + + "until it is visible again."; + + +const WorkerSandbox = EventEmitter.compose({ + + /** + * Emit a message to the worker content sandbox + */ + emit: function emit() { + // First ensure having a regular array + // (otherwise, `arguments` would be mapped to an object by `stringify`) + let array = Array.slice(arguments); + // JSON.stringify is buggy with cross-sandbox values, + // it may return "{}" on functions. Use a replacer to match them correctly. + function replacer(k, v) { + return typeof v === "function" ? undefined : v; + } + // Ensure having an asynchronous behavior + let self = this; + timer.setTimeout(function () { + self._emitToContent(JSON.stringify(array, replacer)); + }, 0); + }, + + /** + * Synchronous version of `emit`. + * /!\ Should only be used when it is strictly mandatory /!\ + * Doesn't ensure passing only JSON values. + * Mainly used by context-menu in order to avoid breaking it. + */ + emitSync: function emitSync() { + let args = Array.slice(arguments); + return this._emitToContent(args); + }, + + /** + * Tells if content script has at least one listener registered for one event, + * through `self.on('xxx', ...)`. + * /!\ Shouldn't be used. Implemented to avoid breaking context-menu API. + */ + hasListenerFor: function hasListenerFor(name) { + return this._hasListenerFor(name); + }, + + /** + * Method called by the worker sandbox when it needs to send a message + */ + _onContentEvent: function onContentEvent(args) { + // As `emit`, we ensure having an asynchronous behavior + let self = this; + timer.setTimeout(function () { + // We emit event to chrome/addon listeners + self._emit.apply(self, JSON.parse(args)); + }, 0); + }, + + /** + * Configures sandbox and loads content scripts into it. + * @param {Worker} worker + * content worker + */ + constructor: function WorkerSandbox(worker) { + this._addonWorker = worker; + + // Ensure that `emit` has always the right `this` + this.emit = this.emit.bind(this); + this.emitSync = this.emitSync.bind(this); + + // We receive a wrapped window, that may be an xraywrapper if it's content + let window = worker._window; + let proto = window; + + // Eventually use expanded principal sandbox feature, if some are given. + // + // But prevent it when the Worker isn't used for a content script but for + // injecting `addon` object into a Panel, Widget, ... scope. + // That's because: + // 1/ It is useless to use multiple domains as the worker is only used + // to communicate with the addon, + // 2/ By using it it would prevent the document to have access to any JS + // value of the worker. As JS values coming from multiple domain principals + // can't be accessed by "mono-principals" (principal with only one domain). + // Even if this principal is for a domain that is specified in the multiple + // domain principal. + let principals = window; + let wantGlobalProperties = [] + if (EXPANDED_PRINCIPALS.length > 0 && !worker._injectInDocument) { + principals = EXPANDED_PRINCIPALS.concat(window); + // We have to replace XHR constructor of the content document + // with a custom cross origin one, automagically added by platform code: + delete proto.XMLHttpRequest; + wantGlobalProperties.push("XMLHttpRequest"); + } + + // Instantiate trusted code in another Sandbox in order to prevent content + // script from messing with standard classes used by proxy and API code. + let apiSandbox = sandbox(principals, { wantXrays: true, sameZoneAs: window }); + apiSandbox.console = console; + + // Create the sandbox and bind it to window in order for content scripts to + // have access to all standard globals (window, document, ...) + let content = this._sandbox = sandbox(principals, { + sandboxPrototype: proto, + wantXrays: true, + wantGlobalProperties: wantGlobalProperties, + sameZoneAs: window, + metadata: { SDKContentScript: true } + }); + // We have to ensure that window.top and window.parent are the exact same + // object than window object, i.e. the sandbox global object. But not + // always, in case of iframes, top and parent are another window object. + let top = window.top === window ? content : content.top; + let parent = window.parent === window ? content : content.parent; + merge(content, { + // We need "this === window === top" to be true in toplevel scope: + get window() content, + get top() top, + get parent() parent, + // Use the Greasemonkey naming convention to provide access to the + // unwrapped window object so the content script can access document + // JavaScript values. + // NOTE: this functionality is experimental and may change or go away + // at any time! + get unsafeWindow() window.wrappedJSObject + }); + + // Load trusted code that will inject content script API. + // We need to expose JS objects defined in same principal in order to + // avoid having any kind of wrapper. + load(apiSandbox, CONTENT_WORKER_URL); + + // prepare a clean `self.options` + let options = 'contentScriptOptions' in worker ? + JSON.stringify( worker.contentScriptOptions ) : + undefined; + + // Then call `inject` method and communicate with this script + // by trading two methods that allow to send events to the other side: + // - `onEvent` called by content script + // - `result.emitToContent` called by addon script + // Bug 758203: We have to explicitely define `__exposedProps__` in order + // to allow access to these chrome object attributes from this sandbox with + // content priviledges + // https://developer.mozilla.org/en/XPConnect_wrappers#Other_security_wrappers + let chromeAPI = { + timers: { + setTimeout: timer.setTimeout, + setInterval: timer.setInterval, + clearTimeout: timer.clearTimeout, + clearInterval: timer.clearInterval, + __exposedProps__: { + setTimeout: 'r', + setInterval: 'r', + clearTimeout: 'r', + clearInterval: 'r' + } + }, + sandbox: { + evaluate: evaluate, + __exposedProps__: { + evaluate: 'r', + } + }, + __exposedProps__: { + timers: 'r', + sandbox: 'r', + } + }; + let onEvent = this._onContentEvent.bind(this); + // `ContentWorker` is defined in CONTENT_WORKER_URL file + let result = apiSandbox.ContentWorker.inject(content, chromeAPI, onEvent, options); + this._emitToContent = result.emitToContent; + this._hasListenerFor = result.hasListenerFor; + + // Handle messages send by this script: + let self = this; + // console.xxx calls + this.on("console", function consoleListener(kind) { + console[kind].apply(console, Array.slice(arguments, 1)); + }); + + // self.postMessage calls + this.on("message", function postMessage(data) { + // destroyed? + if (self._addonWorker) + self._addonWorker._emit('message', data); + }); + + // self.port.emit calls + this.on("event", function portEmit(name, args) { + // destroyed? + if (self._addonWorker) + self._addonWorker._onContentScriptEvent.apply(self._addonWorker, arguments); + }); + + // unwrap, recreate and propagate async Errors thrown from content-script + this.on("error", function onError({instanceOfError, value}) { + if (self._addonWorker) { + let error = value; + if (instanceOfError) { + error = new Error(value.message, value.fileName, value.lineNumber); + error.stack = value.stack; + error.name = value.name; + } + self._addonWorker._emit('error', error); + } + }); + + // Inject `addon` global into target document if document is trusted, + // `addon` in document is equivalent to `self` in content script. + if (worker._injectInDocument) { + let win = window.wrappedJSObject ? window.wrappedJSObject : window; + Object.defineProperty(win, "addon", { + value: content.self + } + ); + } + + // Inject our `console` into target document if worker doesn't have a tab + // (e.g Panel, PageWorker, Widget). + // `worker.tab` can't be used because bug 804935. + if (!getTabForContentWindow(window)) { + let win = window.wrappedJSObject ? window.wrappedJSObject : window; + + // export our chrome console to content window, using the same approach + // of `ConsoleAPI`: + // http://mxr.mozilla.org/mozilla-central/source/dom/base/ConsoleAPI.js#150 + // + // and described here: + // https://developer.mozilla.org/en-US/docs/Components.utils.createObjectIn + let con = Cu.createObjectIn(win); + + let genPropDesc = function genPropDesc(fun) { + return { enumerable: true, configurable: true, writable: true, + value: console[fun] }; + } + + const properties = { + log: genPropDesc('log'), + info: genPropDesc('info'), + warn: genPropDesc('warn'), + error: genPropDesc('error'), + debug: genPropDesc('debug'), + trace: genPropDesc('trace'), + dir: genPropDesc('dir'), + group: genPropDesc('group'), + groupCollapsed: genPropDesc('groupCollapsed'), + groupEnd: genPropDesc('groupEnd'), + time: genPropDesc('time'), + timeEnd: genPropDesc('timeEnd'), + profile: genPropDesc('profile'), + profileEnd: genPropDesc('profileEnd'), + __noSuchMethod__: { enumerable: true, configurable: true, writable: true, + value: function() {} } + }; + + Object.defineProperties(con, properties); + Cu.makeObjectPropsNormal(con); + + win.console = con; + }; + + // The order of `contentScriptFile` and `contentScript` evaluation is + // intentional, so programs can load libraries like jQuery from script URLs + // and use them in scripts. + let contentScriptFile = ('contentScriptFile' in worker) ? worker.contentScriptFile + : null, + contentScript = ('contentScript' in worker) ? worker.contentScript : null; + + if (contentScriptFile) { + if (Array.isArray(contentScriptFile)) + this._importScripts.apply(this, contentScriptFile); + else + this._importScripts(contentScriptFile); + } + if (contentScript) { + this._evaluate( + Array.isArray(contentScript) ? contentScript.join(';\n') : contentScript + ); + } + }, + destroy: function destroy() { + this.emitSync("detach"); + this._sandbox = null; + this._addonWorker = null; + }, + + /** + * JavaScript sandbox where all the content scripts are evaluated. + * {Sandbox} + */ + _sandbox: null, + + /** + * Reference to the addon side of the worker. + * @type {Worker} + */ + _addonWorker: null, + + /** + * Evaluates code in the sandbox. + * @param {String} code + * JavaScript source to evaluate. + * @param {String} [filename='javascript:' + code] + * Name of the file + */ + _evaluate: function(code, filename) { + try { + evaluate(this._sandbox, code, filename || 'javascript:' + code); + } + catch(e) { + this._addonWorker._emit('error', e); + } + }, + /** + * Imports scripts to the sandbox by reading files under urls and + * evaluating its source. If exception occurs during evaluation + * `"error"` event is emitted on the worker. + * This is actually an analog to the `importScript` method in web + * workers but in our case it's not exposed even though content + * scripts may be able to do it synchronously since IO operation + * takes place in the UI process. + */ + _importScripts: function _importScripts(url) { + let urls = Array.slice(arguments, 0); + for each (let contentScriptFile in urls) { + try { + let uri = URL(contentScriptFile); + if (uri.scheme === 'resource') + load(this._sandbox, String(uri)); + else + throw Error("Unsupported `contentScriptFile` url: " + String(uri)); + } + catch(e) { + this._addonWorker._emit('error', e); + } + } + } +}); + +/** + * Message-passing facility for communication between code running + * in the content and add-on process. + * @see https://addons.mozilla.org/en-US/developers/docs/sdk/latest/modules/sdk/content/worker.html + */ +const Worker = EventEmitter.compose({ + on: Trait.required, + _removeAllListeners: Trait.required, + + // List of messages fired before worker is initialized + get _earlyEvents() { + delete this._earlyEvents; + this._earlyEvents = []; + return this._earlyEvents; + }, + + /** + * Sends a message to the worker's global scope. Method takes single + * argument, which represents data to be sent to the worker. The data may + * be any primitive type value or `JSON`. Call of this method asynchronously + * emits `message` event with data value in the global scope of this + * symbiont. + * + * `message` event listeners can be set either by calling + * `self.on` with a first argument string `"message"` or by + * implementing `onMessage` function in the global scope of this worker. + * @param {Number|String|JSON} data + */ + postMessage: function (data) { + let args = ['message'].concat(Array.slice(arguments)); + if (!this._inited) { + this._earlyEvents.push(args); + return; + } + processMessage.apply(this, args); + }, + + /** + * EventEmitter, that behaves (calls listeners) asynchronously. + * A way to send customized messages to / from the worker. + * Events from in the worker can be observed / emitted via + * worker.on / worker.emit. + */ + get port() { + // We generate dynamically this attribute as it needs to be accessible + // before Worker.constructor gets called. (For ex: Panel) + + // create an event emitter that receive and send events from/to the worker + this._port = EventEmitterTrait.create({ + emit: this._emitEventToContent.bind(this) + }); + + // expose wrapped port, that exposes only public properties: + // We need to destroy this getter in order to be able to set the + // final value. We need to update only public port attribute as we never + // try to access port attribute from private API. + delete this._public.port; + this._public.port = Cortex(this._port); + // Replicate public port to the private object + delete this.port; + this.port = this._public.port; + + return this._port; + }, + + /** + * Same object than this.port but private API. + * Allow access to _emit, in order to send event to port. + */ + _port: null, + + /** + * Emit a custom event to the content script, + * i.e. emit this event on `self.port` + */ + _emitEventToContent: function () { + let args = ['event'].concat(Array.slice(arguments)); + if (!this._inited) { + this._earlyEvents.push(args); + return; + } + processMessage.apply(this, args); + }, + + // Is worker connected to the content worker sandbox ? + _inited: false, + + // Is worker being frozen? i.e related document is frozen in bfcache. + // Content script should not be reachable if frozen. + _frozen: true, + + constructor: function Worker(options) { + options = options || {}; + + if ('contentScriptFile' in options) + this.contentScriptFile = options.contentScriptFile; + if ('contentScriptOptions' in options) + this.contentScriptOptions = options.contentScriptOptions; + if ('contentScript' in options) + this.contentScript = options.contentScript; + + this._setListeners(options); + + unload.ensure(this._public, "destroy"); + + // Ensure that worker._port is initialized for contentWorker to be able + // to send events during worker initialization. + this.port; + + this._documentUnload = this._documentUnload.bind(this); + this._pageShow = this._pageShow.bind(this); + this._pageHide = this._pageHide.bind(this); + + if ("window" in options) this._attach(options.window); + }, + + _setListeners: function(options) { + if ('onError' in options) + this.on('error', options.onError); + if ('onMessage' in options) + this.on('message', options.onMessage); + if ('onDetach' in options) + this.on('detach', options.onDetach); + }, + + _attach: function(window) { + this._window = window; + // Track document unload to destroy this worker. + // We can't watch for unload event on page's window object as it + // prevents bfcache from working: + // https://developer.mozilla.org/En/Working_with_BFCache + this._windowID = getInnerId(this._window); + observers.on("inner-window-destroyed", this._documentUnload); + + // Listen to pagehide event in order to freeze the content script + // while the document is frozen in bfcache: + this._window.addEventListener("pageshow", this._pageShow, true); + this._window.addEventListener("pagehide", this._pageHide, true); + + // will set this._contentWorker pointing to the private API: + this._contentWorker = WorkerSandbox(this); + + // Mainly enable worker.port.emit to send event to the content worker + this._inited = true; + this._frozen = false; + + // Process all events and messages that were fired before the + // worker was initialized. + this._earlyEvents.forEach((function (args) { + processMessage.apply(this, args); + }).bind(this)); + }, + + _documentUnload: function _documentUnload({ subject, data }) { + let innerWinID = subject.QueryInterface(Ci.nsISupportsPRUint64).data; + if (innerWinID != this._windowID) return false; + this._workerCleanup(); + return true; + }, + + _pageShow: function _pageShow() { + this._contentWorker.emitSync("pageshow"); + this._emit("pageshow"); + this._frozen = false; + }, + + _pageHide: function _pageHide() { + this._contentWorker.emitSync("pagehide"); + this._emit("pagehide"); + this._frozen = true; + }, + + get url() { + // this._window will be null after detach + return this._window ? this._window.document.location.href : null; + }, + + get tab() { + // this._window will be null after detach + if (this._window) + return getTabForWindow(this._window); + return null; + }, + + /** + * Tells content worker to unload itself and + * removes all the references from itself. + */ + destroy: function destroy() { + this._workerCleanup(); + this._inited = true; + this._removeAllListeners(); + }, + + /** + * Remove all internal references to the attached document + * Tells _port to unload itself and removes all the references from itself. + */ + _workerCleanup: function _workerCleanup() { + // maybe unloaded before content side is created + // As Symbiont call worker.constructor on document load + if (this._contentWorker) + this._contentWorker.destroy(); + this._contentWorker = null; + if (this._window) { + this._window.removeEventListener("pageshow", this._pageShow, true); + this._window.removeEventListener("pagehide", this._pageHide, true); + } + this._window = null; + // This method may be called multiple times, + // avoid dispatching `detach` event more than once + if (this._windowID) { + this._windowID = null; + observers.off("inner-window-destroyed", this._documentUnload); + this._earlyEvents.length = 0; + this._emit("detach"); + } + this._inited = false; + }, + + /** + * Receive an event from the content script that need to be sent to + * worker.port. Provide a way for composed object to catch all events. + */ + _onContentScriptEvent: function _onContentScriptEvent() { + this._port._emit.apply(this._port, arguments); + }, + + /** + * Reference to the content side of the worker. + * @type {WorkerGlobalScope} + */ + _contentWorker: null, + + /** + * Reference to the window that is accessible from + * the content scripts. + * @type {Object} + */ + _window: null, + + /** + * Flag to enable `addon` object injection in document. (bug 612726) + * @type {Boolean} + */ + _injectInDocument: false +}); + +/** + * Fired from postMessage and _emitEventToContent, or from the _earlyMessage + * queue when fired before the content is loaded. Sends arguments to + * contentWorker if able + */ + +function processMessage () { + if (!this._contentWorker) + throw new Error(ERR_DESTROYED); + if (this._frozen) + throw new Error(ERR_FROZEN); + + this._contentWorker.emit.apply(null, Array.slice(arguments)); +} + +exports.Worker = Worker; diff --git a/addon-sdk-1.16/lib/sdk/deprecated/traits.js b/addon-sdk-1.16/lib/sdk/deprecated/traits.js new file mode 100644 index 00000000..30ae3424 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/deprecated/traits.js @@ -0,0 +1,187 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "deprecated" +}; + +const { + compose: _compose, + override: _override, + resolve: _resolve, + trait: _trait, + //create: _create, + required, +} = require('./traits/core'); + +const defineProperties = Object.defineProperties, + freeze = Object.freeze, + create = Object.create; + +/** + * Work around bug 608959 by defining the _create function here instead of + * importing it from traits/core. For docs on this function, see the create + * function in that module. + * + * FIXME: remove this workaround in favor of importing the function once that + * bug has been fixed. + */ +function _create(proto, trait) { + let properties = {}, + keys = Object.getOwnPropertyNames(trait); + for each(let key in keys) { + let descriptor = trait[key]; + if (descriptor.required && + !Object.prototype.hasOwnProperty.call(proto, key)) + throw new Error('Missing required property: ' + key); + else if (descriptor.conflict) + throw new Error('Remaining conflicting property: ' + key); + else + properties[key] = descriptor; + } + return Object.create(proto, properties); +} + +/** + * Placeholder for `Trait.prototype` + */ +let TraitProto = Object.prototype; + +function Get(key) this[key] +function Set(key, value) this[key] = value + +/** + * Creates anonymous trait descriptor from the passed argument, unless argument + * is a trait constructor. In later case trait's already existing properties + * descriptor is returned. + * This is module's internal function and is used as a gateway to a trait's + * internal properties descriptor. + * @param {Function} $ + * Composed trait's constructor. + * @returns {Object} + * Private trait property of the composition. + */ +function TraitDescriptor(object) + ( + 'function' == typeof object && + (object.prototype == TraitProto || object.prototype instanceof Trait) + ) ? object._trait(TraitDescriptor) : _trait(object) + +function Public(instance, trait) { + let result = {}, + keys = Object.getOwnPropertyNames(trait); + for each (let key in keys) { + if ('_' === key.charAt(0) && '__iterator__' !== key ) + continue; + let property = trait[key], + descriptor = { + configurable: property.configurable, + enumerable: property.enumerable + }; + if (property.get) + descriptor.get = property.get.bind(instance); + if (property.set) + descriptor.set = property.set.bind(instance); + if ('value' in property) { + let value = property.value; + if ('function' === typeof value) { + descriptor.value = property.value.bind(instance); + descriptor.writable = property.writable; + } else { + descriptor.get = Get.bind(instance, key); + descriptor.set = Set.bind(instance, key); + } + } + result[key] = descriptor; + } + return result; +} + +/** + * This is private function that composes new trait with privates. + */ +function Composition(trait) { + function Trait() { + let self = _create({}, trait); + self._public = create(Trait.prototype, Public(self, trait)); + delete self._public.constructor; + if (Object === self.constructor) + self.constructor = Trait; + else + return self.constructor.apply(self, arguments) || self._public; + return self._public; + } + defineProperties(Trait, { + prototype: { value: freeze(create(TraitProto, { + constructor: { value: constructor, writable: true } + }))}, // writable is `true` to avoid getters in custom ES5 + displayName: { value: (trait.constructor || constructor).name }, + compose: { value: compose, enumerable: true }, + override: { value: override, enumerable: true }, + resolve: { value: resolve, enumerable: true }, + required: { value: required, enumerable: true }, + _trait: { value: function _trait(caller) + caller === TraitDescriptor ? trait : undefined + } + }); + return freeze(Trait); +} + +/** + * Composes new trait out of itself and traits / property maps passed as an + * arguments. If two or more traits / property maps have properties with the + * same name, the new trait will contain a "conflict" property for that name. + * This is a commutative and associative operation, and the order of its + * arguments is not significant. + * @params {Object|Function} + * List of Traits or property maps to create traits from. + * @returns {Function} + * New trait containing the combined properties of all the traits. + */ +function compose() { + let traits = Array.slice(arguments, 0); + traits.push(this); + return Composition(_compose.apply(null, traits.map(TraitDescriptor))); +} + +/** + * Composes a new trait with all of the combined properties of `this` and the + * argument traits. In contrast to `compose`, `override` immediately resolves + * all conflicts resulting from this composition by overriding the properties of + * later traits. Trait priority is from left to right. I.e. the properties of + * the leftmost trait are never overridden. + * @params {Object} trait + * @returns {Object} + */ +function override() { + let traits = Array.slice(arguments, 0); + traits.push(this); + return Composition(_override.apply(null, traits.map(TraitDescriptor))); +} + +/** + * Composes new resolved trait, with all the same properties as this + * trait, except that all properties whose name is an own property of + * `resolutions` will be renamed to `resolutions[name]`. If it is + * `resolutions[name]` is `null` value is changed into a required property + * descriptor. + */ +function resolve(resolutions) + Composition(_resolve(resolutions, TraitDescriptor(this))) + +/** + * Base Trait, that all the traits are composed of. + */ +const Trait = Composition({ + /** + * Internal property holding public API of this instance. + */ + _public: { value: null, configurable: true, writable: true }, + toString: { value: function() '[object ' + this.constructor.name + ']' } +}); +TraitProto = Trait.prototype; +exports.Trait = Trait; + diff --git a/addon-sdk-1.16/lib/sdk/deprecated/traits/core.js b/addon-sdk-1.16/lib/sdk/deprecated/traits/core.js new file mode 100644 index 00000000..c08c38f1 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/deprecated/traits/core.js @@ -0,0 +1,322 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "deprecated" +}; + +// Design inspired by: http://www.traitsjs.org/ + +// shortcuts +const getOwnPropertyNames = Object.getOwnPropertyNames, + getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor, + hasOwn = Object.prototype.hasOwnProperty, + _create = Object.create; + +function doPropertiesMatch(object1, object2, name) { + // If `object1` has property with the given `name` + return name in object1 ? + // then `object2` should have it with the same value. + name in object2 && object1[name] === object2[name] : + // otherwise `object2` should not have property with the given `name`. + !(name in object2); +} + +/** + * Compares two trait custom property descriptors if they are the same. If + * both are `conflict` or all the properties of descriptor are equal returned + * value will be `true`, otherwise it will be `false`. + * @param {Object} desc1 + * @param {Object} desc2 + */ +function areSame(desc1, desc2) { + return ('conflict' in desc1 && desc1.conflict && + 'conflict' in desc2 && desc2.conflict) || + (doPropertiesMatch(desc1, desc2, 'get') && + doPropertiesMatch(desc1, desc2, 'set') && + doPropertiesMatch(desc1, desc2, 'value') && + doPropertiesMatch(desc1, desc2, 'enumerable') && + doPropertiesMatch(desc1, desc2, 'required') && + doPropertiesMatch(desc1, desc2, 'conflict')); +} + +/** + * Converts array to an object whose own property names represent + * values of array. + * @param {String[]} names + * @returns {Object} + * @example + * Map(['foo', ...]) => { foo: true, ...} + */ +function Map(names) { + let map = {}; + for each (let name in names) + map[name] = true; + return map; +} + + +const ERR_CONFLICT = 'Remaining conflicting property: ', + ERR_REQUIRED = 'Missing required property: '; +/** + * Constant singleton, representing placeholder for required properties. + * @type {Object} + */ +const required = { toString: function()'' }; +exports.required = required; + +/** + * Generates custom **required** property descriptor. Descriptor contains + * non-standard property `required` that is equal to `true`. + * @param {String} name + * property name to generate descriptor for. + * @returns {Object} + * custom property descriptor + */ +function Required(name) { + function required() { throw new Error(ERR_REQUIRED + name) } + return { + get: required, + set: required, + required: true + }; +} + +/** + * Generates custom **conflicting** property descriptor. Descriptor contains + * non-standard property `conflict` that is equal to `true`. + * @param {String} name + * property name to generate descriptor for. + * @returns {Object} + * custom property descriptor + */ +function Conflict(name) { + function conflict() { throw new Error(ERR_CONFLICT + name) } + return { + get: conflict, + set: conflict, + conflict: true + }; +} + +/** + * Function generates custom properties descriptor of the `object`s own + * properties. All the inherited properties are going to be ignored. + * Properties with values matching `required` singleton will be marked as + * 'required' properties. + * @param {Object} object + * Set of properties to generate trait from. + * @returns {Object} + * Properties descriptor of all of the `object`'s own properties. + */ +function trait(properties) { + let result = {}, + keys = getOwnPropertyNames(properties); + for each (let key in keys) { + let descriptor = getOwnPropertyDescriptor(properties, key); + result[key] = (required === descriptor.value) ? Required(key) : descriptor; + } + return result; +} +exports.Trait = exports.trait = trait; + +/** + * Composes new trait. If two or more traits have own properties with the + * same name, the new trait will contain a 'conflict' property for that name. + * 'compose' is a commutative and associative operation, and the order of its + * arguments is not significant. + * + * @params {Object} trait + * Takes traits as an arguments + * @returns {Object} + * New trait containing the combined own properties of all the traits. + * @example + * var newTrait = compose(trait_1, trait_2, ..., trait_N); + */ +function compose(trait1, trait2) { + let traits = Array.slice(arguments, 0), + result = {}; + for each (let trait in traits) { + let keys = getOwnPropertyNames(trait); + for each (let key in keys) { + let descriptor = trait[key]; + // if property already exists and it's not a requirement + if (hasOwn.call(result, key) && !result[key].required) { + if (descriptor.required) + continue; + if (!areSame(descriptor, result[key])) + result[key] = Conflict(key); + } else { + result[key] = descriptor; + } + } + } + return result; +} +exports.compose = compose; + +/** + * Composes new trait with the same own properties as the original trait, + * except that all property names appearing in the first argument are replaced + * by 'required' property descriptors. + * @param {String[]} keys + * Array of strings property names. + * @param {Object} trait + * A trait some properties of which should be excluded. + * @returns {Object} + * @example + * var newTrait = exclude(['name', ...], trait) + */ +function exclude(keys, trait) { + let exclusions = Map(keys), + result = {}; + + keys = getOwnPropertyNames(trait); + + for each (let key in keys) { + if (!hasOwn.call(exclusions, key) || trait[key].required) + result[key] = trait[key]; + else + result[key] = Required(key); + } + return result; +} + +/** + * Composes a new trait with all of the combined properties of the argument + * traits. In contrast to `compose`, `override` immediately resolves all + * conflicts resulting from this composition by overriding the properties of + * later traits. Trait priority is from left to right. I.e. the properties of + * the leftmost trait are never overridden. + * @params {Object} trait + * @returns {Object} + * @examples + * // override is associative: + * override(t1,t2,t3) + * // is equivalent to + * override(t1, override(t2, t3)) + * // or + * to override(override(t1, t2), t3) + * + * // override is not commutative: + * override(t1,t2) + * // is not equivalent to + * override(t2,t1) + */ +function override() { + let traits = Array.slice(arguments, 0), + result = {}; + for each (let trait in traits) { + let keys = getOwnPropertyNames(trait); + for each(let key in keys) { + let descriptor = trait[key]; + if (!hasOwn.call(result, key) || result[key].required) + result[key] = descriptor; + } + } + return result; +} +exports.override = override; + +/** + * Composes a new trait with the same properties as the original trait, except + * that all properties whose name is an own property of map will be renamed to + * map[name], and a 'required' property for name will be added instead. + * @param {Object} map + * An object whose own properties serve as a mapping from old names to new + * names. + * @param {Object} trait + * A trait object + * @returns {Object} + * @example + * var newTrait = rename(map, trait); + */ +function rename(map, trait) { + let result = {}, + keys = getOwnPropertyNames(trait); + for each(let key in keys) { + // must be renamed & it's not requirement + if (hasOwn.call(map, key) && !trait[key].required) { + let alias = map[key]; + if (hasOwn.call(result, alias) && !result[alias].required) + result[alias] = Conflict(alias); + else + result[alias] = trait[key]; + if (!hasOwn.call(result, key)) + result[key] = Required(key); + } else { // must not be renamed or its a requirement + // property is not in result trait yet + if (!hasOwn.call(result, key)) + result[key] = trait[key]; + // property is already in resulted trait & it's not requirement + else if (!trait[key].required) + result[key] = Conflict(key); + } + } + return result; +} + +/** +* Composes new resolved trait, with all the same properties as the original +* trait, except that all properties whose name is an own property of +* resolutions will be renamed to `resolutions[name]`. If it is +* `resolutions[name]` is `null` value is changed into a required property +* descriptor. +* function can be implemented as `rename(map,exclude(exclusions, trait))` +* where map is the subset of mappings from oldName to newName and exclusions +* is an array of all the keys that map to `null`. +* Note: it's important to **first** `exclude`, **then** `rename`, since +* `exclude` and rename are not associative. +* @param {Object} resolutions +* An object whose own properties serve as a mapping from old names to new +* names, or to `null` if the property should be excluded. +* @param {Object} trait +* A trait object +* @returns {Object} +* Resolved trait with the same own properties as the original trait. +*/ +function resolve(resolutions, trait) { + let renames = {}, + exclusions = [], + keys = getOwnPropertyNames(resolutions); + for each (let key in keys) { // pre-process renamed and excluded properties + if (resolutions[key]) // old name -> new name + renames[key] = resolutions[key]; + else // name -> undefined + exclusions.push(key); + } + return rename(renames, exclude(exclusions, trait)); +} +exports.resolve = resolve; + +/** + * `create` is like `Object.create`, except that it ensures that: + * - an exception is thrown if 'trait' still contains required properties + * - an exception is thrown if 'trait' still contains conflicting + * properties + * @param {Object} + * prototype of the completed object + * @param {Object} trait + * trait object to be turned into a complete object + * @returns {Object} + * An object with all of the properties described by the trait. + */ +function create(proto, trait) { + let properties = {}, + keys = getOwnPropertyNames(trait); + for each(let key in keys) { + let descriptor = trait[key]; + if (descriptor.required && !hasOwn.call(proto, key)) + throw new Error(ERR_REQUIRED + key); + else if (descriptor.conflict) + throw new Error(ERR_CONFLICT + key); + else + properties[key] = descriptor; + } + return _create(proto, properties); +} +exports.create = create; + diff --git a/addon-sdk-1.16/lib/sdk/deprecated/unit-test-finder.js b/addon-sdk-1.16/lib/sdk/deprecated/unit-test-finder.js new file mode 100644 index 00000000..70025259 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/deprecated/unit-test-finder.js @@ -0,0 +1,89 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "deprecated" +}; + +const file = require("../io/file"); +const memory = require('./memory'); +const suites = require('@test/options').allTestModules; +const { Loader } = require("sdk/test/loader"); +const cuddlefish = require("sdk/loader/cuddlefish"); + +let loader = Loader(module); +const NOT_TESTS = ['setup', 'teardown']; + +var TestFinder = exports.TestFinder = function TestFinder(options) { + memory.track(this); + this.filter = options.filter; + this.testInProcess = options.testInProcess === false ? false : true; + this.testOutOfProcess = options.testOutOfProcess === true ? true : false; +}; + +TestFinder.prototype = { + findTests: function findTests(cb) { + var self = this; + var tests = []; + var filter; + // A filter string is {fileNameRegex}[:{testNameRegex}] - ie, a colon + // optionally separates a regex for the test fileName from a regex for the + // testName. + if (this.filter) { + var colonPos = this.filter.indexOf(':'); + var filterFileRegex, filterNameRegex; + if (colonPos === -1) { + filterFileRegex = new RegExp(self.filter); + } else { + filterFileRegex = new RegExp(self.filter.substr(0, colonPos)); + filterNameRegex = new RegExp(self.filter.substr(colonPos + 1)); + } + // This function will first be called with just the filename; if + // it returns true the module will be loaded then the function + // called again with both the filename and the testname. + filter = function(filename, testname) { + return filterFileRegex.test(filename) && + ((testname && filterNameRegex) ? filterNameRegex.test(testname) + : true); + }; + } else + filter = function() {return true}; + + suites.forEach(function(suite) { + // Load each test file as a main module in its own loader instance + // `suite` is defined by cuddlefish/manifest.py:ManifestBuilder.build + + let suiteModule; + + try { + suiteModule = cuddlefish.main(loader, suite); + } + catch (e) { + if (!/^Unsupported Application/.test(e.message)) + throw e; + // If `Unsupported Application` error thrown during test, + // skip the test suite + suiteModule = { + 'test suite skipped': assert => assert.pass(e.message) + }; + } + + if (self.testInProcess) + for each (let name in Object.keys(suiteModule).sort()) { + if(NOT_TESTS.indexOf(name) === -1 && filter(suite, name)) { + tests.push({ + setup: suiteModule.setup, + teardown: suiteModule.teardown, + testFunction: suiteModule[name], + name: suite + "." + name + }); + } + } + }); + + cb(tests); + } +}; diff --git a/addon-sdk-1.16/lib/sdk/deprecated/unit-test.js b/addon-sdk-1.16/lib/sdk/deprecated/unit-test.js new file mode 100644 index 00000000..dc3d3eba --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/deprecated/unit-test.js @@ -0,0 +1,489 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "deprecated" +}; + +const memory = require('./memory'); +var timer = require("../timers"); +var cfxArgs = require("@test/options"); + +exports.findAndRunTests = function findAndRunTests(options) { + var TestFinder = require("./unit-test-finder").TestFinder; + var finder = new TestFinder({ + filter: options.filter, + testInProcess: options.testInProcess, + testOutOfProcess: options.testOutOfProcess + }); + var runner = new TestRunner({fs: options.fs}); + finder.findTests( + function (tests) { + runner.startMany({tests: tests, + stopOnError: options.stopOnError, + onDone: options.onDone}); + }); +}; + +var TestRunner = exports.TestRunner = function TestRunner(options) { + if (options) { + this.fs = options.fs; + } + this.console = (options && "console" in options) ? options.console : console; + memory.track(this); + this.passed = 0; + this.failed = 0; + this.testRunSummary = []; + this.expectFailNesting = 0; +}; + +TestRunner.prototype = { + toString: function toString() "[object TestRunner]", + + DEFAULT_PAUSE_TIMEOUT: 5*60000, + PAUSE_DELAY: 500, + + _logTestFailed: function _logTestFailed(why) { + if (!(why in this.test.errors)) + this.test.errors[why] = 0; + this.test.errors[why]++; + }, + + pass: function pass(message) { + if(!this.expectFailure) { + if ("testMessage" in this.console) + this.console.testMessage(true, true, this.test.name, message); + else + this.console.info("pass:", message); + this.passed++; + this.test.passed++; + } + else { + this.expectFailure = false; + this._logTestFailed("failure"); + if ("testMessage" in this.console) { + this.console.testMessage(true, false, this.test.name, message); + } + else { + this.console.error("fail:", 'Failure Expected: ' + message) + this.console.trace(); + } + this.failed++; + this.test.failed++; + } + }, + + fail: function fail(message) { + if(!this.expectFailure) { + this._logTestFailed("failure"); + if ("testMessage" in this.console) { + this.console.testMessage(false, false, this.test.name, message); + } + else { + this.console.error("fail:", message) + this.console.trace(); + } + this.failed++; + this.test.failed++; + } + else { + this.expectFailure = false; + if ("testMessage" in this.console) + this.console.testMessage(false, true, this.test.name, message); + else + this.console.info("pass:", message); + this.passed++; + this.test.passed++; + } + }, + + expectFail: function(callback) { + this.expectFailure = true; + callback(); + this.expectFailure = false; + }, + + exception: function exception(e) { + this._logTestFailed("exception"); + if (cfxArgs.parseable) + this.console.print("TEST-UNEXPECTED-FAIL | " + this.test.name + " | " + e + "\n"); + this.console.exception(e); + this.failed++; + this.test.failed++; + }, + + assertMatches: function assertMatches(string, regexp, message) { + if (regexp.test(string)) { + if (!message) + message = uneval(string) + " matches " + uneval(regexp); + this.pass(message); + } else { + var no = uneval(string) + " doesn't match " + uneval(regexp); + if (!message) + message = no; + else + message = message + " (" + no + ")"; + this.fail(message); + } + }, + + assertRaises: function assertRaises(func, predicate, message) { + try { + func(); + if (message) + this.fail(message + " (no exception thrown)"); + else + this.fail("function failed to throw exception"); + } catch (e) { + var errorMessage; + if (typeof(e) == "string") + errorMessage = e; + else + errorMessage = e.message; + if (typeof(predicate) == "string") + this.assertEqual(errorMessage, predicate, message); + else + this.assertMatches(errorMessage, predicate, message); + } + }, + + assert: function assert(a, message) { + if (!a) { + if (!message) + message = "assertion failed, value is " + a; + this.fail(message); + } else + this.pass(message || "assertion successful"); + }, + + assertNotEqual: function assertNotEqual(a, b, message) { + if (a != b) { + if (!message) + message = "a != b != " + uneval(a); + this.pass(message); + } else { + var equality = uneval(a) + " == " + uneval(b); + if (!message) + message = equality; + else + message += " (" + equality + ")"; + this.fail(message); + } + }, + + assertEqual: function assertEqual(a, b, message) { + if (a == b) { + if (!message) + message = "a == b == " + uneval(a); + this.pass(message); + } else { + var inequality = uneval(a) + " != " + uneval(b); + if (!message) + message = inequality; + else + message += " (" + inequality + ")"; + this.fail(message); + } + }, + + assertNotStrictEqual: function assertNotStrictEqual(a, b, message) { + if (a !== b) { + if (!message) + message = "a !== b !== " + uneval(a); + this.pass(message); + } else { + var equality = uneval(a) + " === " + uneval(b); + if (!message) + message = equality; + else + message += " (" + equality + ")"; + this.fail(message); + } + }, + + assertStrictEqual: function assertStrictEqual(a, b, message) { + if (a === b) { + if (!message) + message = "a === b === " + uneval(a); + this.pass(message); + } else { + var inequality = uneval(a) + " !== " + uneval(b); + if (!message) + message = inequality; + else + message += " (" + inequality + ")"; + this.fail(message); + } + }, + + assertFunction: function assertFunction(a, message) { + this.assertStrictEqual('function', typeof a, message); + }, + + assertUndefined: function(a, message) { + this.assertStrictEqual('undefined', typeof a, message); + }, + + assertNotUndefined: function(a, message) { + this.assertNotStrictEqual('undefined', typeof a, message); + }, + + assertNull: function(a, message) { + this.assertStrictEqual(null, a, message); + }, + + assertNotNull: function(a, message) { + this.assertNotStrictEqual(null, a, message); + }, + + assertObject: function(a, message) { + this.assertStrictEqual('[object Object]', Object.prototype.toString.apply(a), message); + }, + + assertString: function(a, message) { + this.assertStrictEqual('[object String]', Object.prototype.toString.apply(a), message); + }, + + assertArray: function(a, message) { + this.assertStrictEqual('[object Array]', Object.prototype.toString.apply(a), message); + }, + + assertNumber: function(a, message) { + this.assertStrictEqual('[object Number]', Object.prototype.toString.apply(a), message); + }, + + done: function done() { + if (!this.isDone) { + this.isDone = true; + if(this.test.teardown) { + this.test.teardown(this); + } + if (this.waitTimeout !== null) { + timer.clearTimeout(this.waitTimeout); + this.waitTimeout = null; + } + // Do not leave any callback set when calling to `waitUntil` + this.waitUntilCallback = null; + if (this.test.passed == 0 && this.test.failed == 0) { + this._logTestFailed("empty test"); + if ("testMessage" in this.console) { + this.console.testMessage(false, false, this.test.name, "Empty test"); + } + else { + this.console.error("fail:", "Empty test") + } + this.failed++; + this.test.failed++; + } + + this.testRunSummary.push({ + name: this.test.name, + passed: this.test.passed, + failed: this.test.failed, + errors: [error for (error in this.test.errors)].join(", ") + }); + + if (this.onDone !== null) { + var onDone = this.onDone; + var self = this; + this.onDone = null; + timer.setTimeout(function() { onDone(self); }, 0); + } + } + }, + + // Set of assertion functions to wait for an assertion to become true + // These functions take the same arguments as the TestRunner.assert* methods. + waitUntil: function waitUntil() { + return this._waitUntil(this.assert, arguments); + }, + + waitUntilNotEqual: function waitUntilNotEqual() { + return this._waitUntil(this.assertNotEqual, arguments); + }, + + waitUntilEqual: function waitUntilEqual() { + return this._waitUntil(this.assertEqual, arguments); + }, + + waitUntilMatches: function waitUntilMatches() { + return this._waitUntil(this.assertMatches, arguments); + }, + + /** + * Internal function that waits for an assertion to become true. + * @param {Function} assertionMethod + * Reference to a TestRunner assertion method like test.assert, + * test.assertEqual, ... + * @param {Array} args + * List of arguments to give to the previous assertion method. + * All functions in this list are going to be called to retrieve current + * assertion values. + */ + _waitUntil: function waitUntil(assertionMethod, args) { + let count = 0; + let maxCount = this.DEFAULT_PAUSE_TIMEOUT / this.PAUSE_DELAY; + + // We need to ensure that test is asynchronous + if (!this.waitTimeout) + this.waitUntilDone(this.DEFAULT_PAUSE_TIMEOUT); + + let callback = null; + let finished = false; + + let test = this; + + // capture a traceback before we go async. + let traceback = require("../console/traceback"); + let stack = traceback.get(); + stack.splice(-2, 2); + let currentWaitStack = traceback.format(stack); + let timeout = null; + + function loop(stopIt) { + timeout = null; + + // Build a mockup object to fake TestRunner API and intercept calls to + // pass and fail methods, in order to retrieve nice error messages + // and assertion result + let mock = { + pass: function (msg) { + test.pass(msg); + test.waitUntilCallback = null; + if (callback && !stopIt) + callback(); + finished = true; + }, + fail: function (msg) { + // If we are called on test timeout, we stop the loop + // and print which test keeps failing: + if (stopIt) { + test.console.error("test assertion never became true:\n", + msg + "\n", + currentWaitStack); + if (timeout) + timer.clearTimeout(timeout); + return; + } + timeout = timer.setTimeout(loop, test.PAUSE_DELAY); + } + }; + + // Automatically call args closures in order to build arguments for + // assertion function + let appliedArgs = []; + for (let i = 0, l = args.length; i < l; i++) { + let a = args[i]; + if (typeof a == "function") { + try { + a = a(); + } + catch(e) { + test.fail("Exception when calling asynchronous assertion: " + e + + "\n" + e.stack); + finished = true; + return; + } + } + appliedArgs.push(a); + } + + // Finally call assertion function with current assertion values + assertionMethod.apply(mock, appliedArgs); + } + loop(); + this.waitUntilCallback = loop; + + // Return an object with `then` method, to offer a way to execute + // some code when the assertion passed or failed + return { + then: function (c) { + callback = c; + + // In case of immediate positive result, we need to execute callback + // immediately here: + if (finished) + callback(); + } + }; + }, + + waitUntilDone: function waitUntilDone(ms) { + if (ms === undefined) + ms = this.DEFAULT_PAUSE_TIMEOUT; + + var self = this; + + function tiredOfWaiting() { + self._logTestFailed("timed out"); + if ("testMessage" in self.console) { + self.console.testMessage(false, false, self.test.name, "Timed out"); + } + else { + self.console.error("fail:", "Timed out") + } + if (self.waitUntilCallback) { + self.waitUntilCallback(true); + self.waitUntilCallback = null; + } + self.failed++; + self.test.failed++; + self.done(); + } + + // We may already have registered a timeout callback + if (this.waitTimeout) + timer.clearTimeout(this.waitTimeout); + + this.waitTimeout = timer.setTimeout(tiredOfWaiting, ms); + }, + + startMany: function startMany(options) { + function runNextTest(self) { + var test = options.tests.shift(); + if (options.stopOnError && self.test && self.test.failed) { + self.console.error("aborted: test failed and --stop-on-error was specified"); + options.onDone(self); + } else if (test) { + self.start({test: test, onDone: runNextTest}); + } else { + options.onDone(self); + } + } + runNextTest(this); + }, + + start: function start(options) { + this.test = options.test; + this.test.passed = 0; + this.test.failed = 0; + this.test.errors = {}; + + this.isDone = false; + this.onDone = function(self) { + if (cfxArgs.parseable) + self.console.print("TEST-END | " + self.test.name + "\n"); + options.onDone(self); + } + this.waitTimeout = null; + + try { + if (cfxArgs.parseable) + this.console.print("TEST-START | " + this.test.name + "\n"); + else + this.console.info("executing '" + this.test.name + "'"); + + if(this.test.setup) { + this.test.setup(this); + } + this.test.testFunction(this); + } catch (e) { + this.exception(e); + } + if (this.waitTimeout === null) + this.done(); + } +}; diff --git a/addon-sdk-1.16/lib/sdk/deprecated/window-utils.js b/addon-sdk-1.16/lib/sdk/deprecated/window-utils.js new file mode 100644 index 00000000..47db2026 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/deprecated/window-utils.js @@ -0,0 +1,227 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +'use strict'; + +module.metadata = { + 'stability': 'deprecated' +}; + +const { Cc, Ci } = require('chrome'); +const { EventEmitter } = require('../deprecated/events'); +const { Trait } = require('../deprecated/traits'); +const { when } = require('../system/unload'); +const events = require('../system/events'); +const { getInnerId, getOuterId, windows, isDocumentLoaded, isBrowser, + getMostRecentBrowserWindow, getMostRecentWindow } = require('../window/utils'); +const errors = require('../deprecated/errors'); +const { deprecateFunction } = require('../util/deprecate'); +const { ignoreWindow, isGlobalPBSupported } = require('sdk/private-browsing/utils'); +const { isPrivateBrowsingSupported } = require('../self'); + +const windowWatcher = Cc['@mozilla.org/embedcomp/window-watcher;1']. + getService(Ci.nsIWindowWatcher); +const appShellService = Cc['@mozilla.org/appshell/appShellService;1']. + getService(Ci.nsIAppShellService); + +// Bug 834961: ignore private windows when they are not supported +function getWindows() windows(null, { includePrivate: isPrivateBrowsingSupported || isGlobalPBSupported }); + +/** + * An iterator for XUL windows currently in the application. + * + * @return A generator that yields XUL windows exposing the + * nsIDOMWindow interface. + */ +function windowIterator() { + // Bug 752631: We only pass already loaded window in order to avoid + // breaking XUL windows DOM. DOM is broken when some JS code try + // to access DOM during "uninitialized" state of the related document. + let list = getWindows().filter(isDocumentLoaded); + for (let i = 0, l = list.length; i < l; i++) { + yield list[i]; + } +}; +exports.windowIterator = windowIterator; + +/** + * An iterator for browser windows currently open in the application. + * @returns {Function} + * A generator that yields browser windows exposing the `nsIDOMWindow` + * interface. + */ +function browserWindowIterator() { + for each (let window in windowIterator()) { + if (isBrowser(window)) + yield window; + } +} +exports.browserWindowIterator = browserWindowIterator; + +function WindowTracker(delegate) { + if (!(this instanceof WindowTracker)) { + return new WindowTracker(delegate); + } + + this._delegate = delegate; + this._loadingWindows = []; + + for each (let window in getWindows()) + this._regWindow(window); + windowWatcher.registerNotification(this); + this._onToplevelWindowReady = this._onToplevelWindowReady.bind(this); + events.on('toplevel-window-ready', this._onToplevelWindowReady); + + require('../system/unload').ensure(this); + + return this; +}; + +WindowTracker.prototype = { + _regLoadingWindow: function _regLoadingWindow(window) { + // Bug 834961: ignore private windows when they are not supported + if (ignoreWindow(window)) + return; + + this._loadingWindows.push(window); + window.addEventListener('load', this, true); + }, + + _unregLoadingWindow: function _unregLoadingWindow(window) { + var index = this._loadingWindows.indexOf(window); + + if (index != -1) { + this._loadingWindows.splice(index, 1); + window.removeEventListener('load', this, true); + } + }, + + _regWindow: function _regWindow(window) { + // Bug 834961: ignore private windows when they are not supported + if (ignoreWindow(window)) + return; + + if (window.document.readyState == 'complete') { + this._unregLoadingWindow(window); + this._delegate.onTrack(window); + } else + this._regLoadingWindow(window); + }, + + _unregWindow: function _unregWindow(window) { + if (window.document.readyState == 'complete') { + if (this._delegate.onUntrack) + this._delegate.onUntrack(window); + } else { + this._unregLoadingWindow(window); + } + }, + + unload: function unload() { + windowWatcher.unregisterNotification(this); + events.off('toplevel-window-ready', this._onToplevelWindowReady); + for each (let window in getWindows()) + this._unregWindow(window); + }, + + handleEvent: errors.catchAndLog(function handleEvent(event) { + if (event.type == 'load' && event.target) { + var window = event.target.defaultView; + if (window) + this._regWindow(window); + } + }), + + _onToplevelWindowReady: function _onToplevelWindowReady({subject}) { + let window = subject; + // ignore private windows if they are not supported + if (ignoreWindow(window)) + return; + this._regWindow(window); + }, + + observe: errors.catchAndLog(function observe(subject, topic, data) { + var window = subject.QueryInterface(Ci.nsIDOMWindow); + // ignore private windows if they are not supported + if (ignoreWindow(window)) + return; + if (topic == 'domwindowclosed') + this._unregWindow(window); + }) +}; +exports.WindowTracker = WindowTracker; + +const WindowTrackerTrait = Trait.compose({ + _onTrack: Trait.required, + _onUntrack: Trait.required, + constructor: function WindowTrackerTrait() { + WindowTracker({ + onTrack: this._onTrack.bind(this), + onUntrack: this._onUntrack.bind(this) + }); + } +}); +exports.WindowTrackerTrait = WindowTrackerTrait; + +var gDocsToClose = []; + +function onDocUnload(event) { + var index = gDocsToClose.indexOf(event.target); + if (index == -1) + throw new Error('internal error: unloading document not found'); + var document = gDocsToClose.splice(index, 1)[0]; + // Just in case, let's remove the event listener too. + document.defaultView.removeEventListener('unload', onDocUnload, false); +} + +onDocUnload = require('./errors').catchAndLog(onDocUnload); + +exports.closeOnUnload = function closeOnUnload(window) { + window.addEventListener('unload', onDocUnload, false); + gDocsToClose.push(window.document); +}; + +Object.defineProperties(exports, { + activeWindow: { + enumerable: true, + get: function() { + return getMostRecentWindow(null); + }, + set: function(window) { + try { + window.focus(); + } catch (e) {} + } + }, + activeBrowserWindow: { + enumerable: true, + get: getMostRecentBrowserWindow + } +}); + + +/** + * Returns the ID of the window's current inner window. + */ +exports.getInnerId = deprecateFunction(getInnerId, + 'require("window-utils").getInnerId is deprecated, ' + + 'please use require("sdk/window/utils").getInnerId instead' +); + +exports.getOuterId = deprecateFunction(getOuterId, + 'require("window-utils").getOuterId is deprecated, ' + + 'please use require("sdk/window/utils").getOuterId instead' +); + +exports.isBrowser = deprecateFunction(isBrowser, + 'require("window-utils").isBrowser is deprecated, ' + + 'please use require("sdk/window/utils").isBrowser instead' +); + +exports.hiddenWindow = appShellService.hiddenDOMWindow; + +when( + function() { + gDocsToClose.slice().forEach( + function(doc) { doc.defaultView.close(); }); + }); diff --git a/addon-sdk-1.16/lib/sdk/dom/events.js b/addon-sdk-1.16/lib/sdk/dom/events.js new file mode 100644 index 00000000..2a74e6de --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/dom/events.js @@ -0,0 +1,139 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "unstable" +}; + +// Utility function that returns copy of the given `text` with last character +// removed if it is `"s"`. +function singularify(text) { + return text[text.length - 1] === "s" ? text.substr(0, text.length - 1) : text; +} + +// Utility function that takes event type, argument is passed to +// `document.createEvent` and returns name of the initializer method of the +// given event. Please note that there are some event types whose initializer +// methods can't be guessed by this function. For more details see following +// link: https://developer.mozilla.org/En/DOM/Document.createEvent +function getInitializerName(category) { + return "init" + singularify(category); +} + +/** + * Registers an event `listener` on a given `element`, that will be called + * when events of specified `type` is dispatched on the `element`. + * @param {Element} element + * Dom element to register listener on. + * @param {String} type + * A string representing the + * [event type](https://developer.mozilla.org/en/DOM/event.type) to + * listen for. + * @param {Function} listener + * Function that is called whenever an event of the specified `type` + * occurs. + * @param {Boolean} capture + * If true, indicates that the user wishes to initiate capture. After + * initiating capture, all events of the specified type will be dispatched + * to the registered listener before being dispatched to any `EventTarget`s + * beneath it in the DOM tree. Events which are bubbling upward through + * the tree will not trigger a listener designated to use capture. + * See [DOM Level 3 Events](http://www.w3.org/TR/DOM-Level-3-Events/#event-flow) + * for a detailed explanation. + */ +function on(element, type, listener, capture) { + // `capture` defaults to `false`. + capture = capture || false; + element.addEventListener(type, listener, capture); +} +exports.on = on; + +/** + * Registers an event `listener` on a given `element`, that will be called + * only once, next time event of specified `type` is dispatched on the + * `element`. + * @param {Element} element + * Dom element to register listener on. + * @param {String} type + * A string representing the + * [event type](https://developer.mozilla.org/en/DOM/event.type) to + * listen for. + * @param {Function} listener + * Function that is called whenever an event of the specified `type` + * occurs. + * @param {Boolean} capture + * If true, indicates that the user wishes to initiate capture. After + * initiating capture, all events of the specified type will be dispatched + * to the registered listener before being dispatched to any `EventTarget`s + * beneath it in the DOM tree. Events which are bubbling upward through + * the tree will not trigger a listener designated to use capture. + * See [DOM Level 3 Events](http://www.w3.org/TR/DOM-Level-3-Events/#event-flow) + * for a detailed explanation. + */ +function once(element, type, listener, capture) { + on(element, type, function selfRemovableListener(event) { + removeListener(element, type, selfRemovableListener, capture); + listener.apply(this, arguments); + }, capture); +} +exports.once = once; + +/** + * Unregisters an event `listener` on a given `element` for the events of the + * specified `type`. + * + * @param {Element} element + * Dom element to unregister listener from. + * @param {String} type + * A string representing the + * [event type](https://developer.mozilla.org/en/DOM/event.type) to + * listen for. + * @param {Function} listener + * Function that is called whenever an event of the specified `type` + * occurs. + * @param {Boolean} capture + * If true, indicates that the user wishes to initiate capture. After + * initiating capture, all events of the specified type will be dispatched + * to the registered listener before being dispatched to any `EventTarget`s + * beneath it in the DOM tree. Events which are bubbling upward through + * the tree will not trigger a listener designated to use capture. + * See [DOM Level 3 Events](http://www.w3.org/TR/DOM-Level-3-Events/#event-flow) + * for a detailed explanation. + */ +function removeListener(element, type, listener, capture) { + element.removeEventListener(type, listener, capture); +} +exports.removeListener = removeListener; + +/** + * Emits event of the specified `type` and `category` on the given `element`. + * Specified `settings` are used to initialize event before dispatching it. + * @param {Element} element + * Dom element to dispatch event on. + * @param {String} type + * A string representing the + * [event type](https://developer.mozilla.org/en/DOM/event.type). + * @param {Object} options + * Options object containing following properties: + * - `category`: String passed to the `document.createEvent`. Option is + * optional and defaults to "UIEvents". + * - `initializer`: If passed it will be used as name of the method used + * to initialize event. If omitted name will be generated from the + * `category` field by prefixing it with `"init"` and removing last + * character if it matches `"s"`. + * - `settings`: Array of settings that are forwarded to the event + * initializer after firs `type` argument. + * @see https://developer.mozilla.org/En/DOM/Document.createEvent + */ +function emit(element, type, { category, initializer, settings }) { + category = category || "UIEvents"; + initializer = initializer || getInitializerName(category); + let document = element.ownerDocument; + let event = document.createEvent(category); + event[initializer].apply(event, [type].concat(settings)); + element.dispatchEvent(event); +}; +exports.emit = emit; diff --git a/addon-sdk-1.16/lib/sdk/dom/events/keys.js b/addon-sdk-1.16/lib/sdk/dom/events/keys.js new file mode 100644 index 00000000..e6f1483a --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/dom/events/keys.js @@ -0,0 +1,63 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "unstable" +}; + +const { emit } = require("../events"); +const { getCodeForKey, toJSON } = require("../../keyboard/utils"); +const { has } = require("../../util/array"); +const { isString } = require("../../lang/type"); + +const INITIALIZER = "initKeyEvent"; +const CATEGORY = "KeyboardEvent"; + +function Options(options) { + if (!isString(options)) + return options; + + var { key, modifiers } = toJSON(options); + return { + key: key, + control: has(modifiers, "control"), + alt: has(modifiers, "alt"), + shift: has(modifiers, "shift"), + meta: has(modifiers, "meta") + }; +} + +var keyEvent = exports.keyEvent = function keyEvent(element, type, options) { + + emit(element, type, { + initializer: INITIALIZER, + category: CATEGORY, + settings: [ + !("bubbles" in options) || options.bubbles !== false, + !("cancelable" in options) || options.cancelable !== false, + "window" in options && options.window ? options.window : null, + "control" in options && !!options.control, + "alt" in options && !!options.alt, + "shift" in options && !!options.shift, + "meta" in options && !!options.meta, + getCodeForKey(options.key) || 0, + options.key.length === 1 ? options.key.charCodeAt(0) : 0 + ] + }); +} + +exports.keyDown = function keyDown(element, options) { + keyEvent(element, "keydown", Options(options)); +}; + +exports.keyUp = function keyUp(element, options) { + keyEvent(element, "keyup", Options(options)); +}; + +exports.keyPress = function keyPress(element, options) { + keyEvent(element, "keypress", Options(options)); +}; + diff --git a/addon-sdk-1.16/lib/sdk/event/chrome.js b/addon-sdk-1.16/lib/sdk/event/chrome.js new file mode 100644 index 00000000..86e8a7a0 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/event/chrome.js @@ -0,0 +1,54 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "unstable" +}; + +const { Cc, Ci, Cr } = require("chrome"); +const { emit, on, off } = require("./core"); +const { addObserver } = Cc['@mozilla.org/observer-service;1']. + getService(Ci.nsIObserverService); + +// Simple class that can be used to instantiate event channel that +// implements `nsIObserver` interface. It's will is used by `observe` +// function as observer + event target. It basically proxies observer +// notifications as to it's registered listeners. +function ObserverChannel() {} +Object.freeze(Object.defineProperties(ObserverChannel.prototype, { + QueryInterface: { + value: function(iid) { + if (!iid.equals(Ci.nsIObserver) && + !iid.equals(Ci.nsISupportsWeakReference) && + !iid.equals(Ci.nsISupports)) + throw Cr.NS_ERROR_NO_INTERFACE; + return this; + } + }, + observe: { + value: function(subject, topic, data) { + emit(this, "data", { + type: topic, + target: subject, + data: data + }); + } + } +})); + +function observe(topic) { + let observerChannel = new ObserverChannel(); + + // Note: `nsIObserverService` will not hold a weak reference to a + // observerChannel (since third argument is `true`). There for if it + // will be GC-ed with all it's event listeners once no other references + // will be held. + addObserver(observerChannel, topic, true); + + return observerChannel; +} + +exports.observe = observe; diff --git a/addon-sdk-1.16/lib/sdk/event/core.js b/addon-sdk-1.16/lib/sdk/event/core.js new file mode 100644 index 00000000..114d82fe --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/event/core.js @@ -0,0 +1,172 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "unstable" +}; + +const UNCAUGHT_ERROR = 'An error event was emitted for which there was no listener.'; +const BAD_LISTENER = 'The event listener must be a function.'; + +const { ns } = require('../core/namespace'); + +const event = ns(); + +const EVENT_TYPE_PATTERN = /^on([A-Z]\w+$)/; +exports.EVENT_TYPE_PATTERN = EVENT_TYPE_PATTERN; + +// Utility function to access given event `target` object's event listeners for +// the specific event `type`. If listeners for this type does not exists they +// will be created. +const observers = function observers(target, type) { + if (!target) throw TypeError("Event target must be an object"); + let listeners = event(target); + return type in listeners ? listeners[type] : listeners[type] = []; +}; + +/** + * Registers an event `listener` that is called every time events of + * specified `type` is emitted on the given event `target`. + * @param {Object} target + * Event target object. + * @param {String} type + * The type of event. + * @param {Function} listener + * The listener function that processes the event. + */ +function on(target, type, listener) { + if (typeof(listener) !== 'function') + throw new Error(BAD_LISTENER); + + let listeners = observers(target, type); + if (!~listeners.indexOf(listener)) + listeners.push(listener); +} +exports.on = on; + +/** + * Registers an event `listener` that is called only the next time an event + * of the specified `type` is emitted on the given event `target`. + * @param {Object} target + * Event target object. + * @param {String} type + * The type of the event. + * @param {Function} listener + * The listener function that processes the event. + */ +function once(target, type, listener) { + on(target, type, function observer(...args) { + off(target, type, observer); + listener.apply(target, args); + }); +} +exports.once = once; + +/** + * Execute each of the listeners in order with the supplied arguments. + * All the exceptions that are thrown by listeners during the emit + * are caught and can be handled by listeners of 'error' event. Thrown + * exceptions are passed as an argument to an 'error' event listener. + * If no 'error' listener is registered exception will be logged into an + * error console. + * @param {Object} target + * Event target object. + * @param {String} type + * The type of event. + * @params {Object|Number|String|Boolean} args + * Arguments that will be passed to listeners. + */ +function emit (target, type, ...args) { + let state = observers(target, type); + let listeners = state.slice(); + let count = listeners.length; + let index = 0; + + // If error event and there are no handlers then print error message + // into a console. + if (count === 0 && type === 'error') console.exception(args[0]); + while (index < count) { + try { + let listener = listeners[index]; + // Dispatch only if listener is still registered. + if (~state.indexOf(listener)) + listener.apply(target, args); + } + catch (error) { + // If exception is not thrown by a error listener and error listener is + // registered emit `error` event. Otherwise dump exception to the console. + if (type !== 'error') emit(target, 'error', error); + else console.exception(error); + } + index++; + } + // Also emit on `"*"` so that one could listen for all events. + if (type !== '*') emit(target, '*', type, ...args); +} +exports.emit = emit; + +/** + * Removes an event `listener` for the given event `type` on the given event + * `target`. If no `listener` is passed removes all listeners of the given + * `type`. If `type` is not passed removes all the listeners of the given + * event `target`. + * @param {Object} target + * The event target object. + * @param {String} type + * The type of event. + * @param {Function} listener + * The listener function that processes the event. + */ +function off(target, type, listener) { + let length = arguments.length; + if (length === 3) { + let listeners = observers(target, type); + let index = listeners.indexOf(listener); + if (~index) + listeners.splice(index, 1); + } + else if (length === 2) { + observers(target, type).splice(0); + } + else if (length === 1) { + let listeners = event(target); + Object.keys(listeners).forEach(type => delete listeners[type]); + } +} +exports.off = off; + +/** + * Returns a number of event listeners registered for the given event `type` + * on the given event `target`. + */ +function count(target, type) { + return observers(target, type).length; +} +exports.count = count; + +/** + * Registers listeners on the given event `target` from the given `listeners` + * dictionary. Iterates over the listeners and if property name matches name + * pattern `onEventType` and property is a function, then registers it as + * an `eventType` listener on `target`. + * + * @param {Object} target + * The type of event. + * @param {Object} listeners + * Dictionary of listeners. + */ +function setListeners(target, listeners) { + Object.keys(listeners || {}).forEach(key => { + let match = EVENT_TYPE_PATTERN.exec(key); + let type = match && match[1].toLowerCase(); + if (!type) return; + + let listener = listeners[key]; + if (typeof(listener) === 'function') + on(target, type, listener); + }); +} +exports.setListeners = setListeners; diff --git a/addon-sdk-1.16/lib/sdk/event/dom.js b/addon-sdk-1.16/lib/sdk/event/dom.js new file mode 100644 index 00000000..a9dc8026 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/event/dom.js @@ -0,0 +1,26 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "unstable" +}; + +let { emit, on, off } = require("./core"); + +// Simple utility function takes event target, event type and optional +// `options.capture` and returns node style event stream that emits "data" +// events every time event of that type occurs on the given `target`. +function open(target, type, options) { + let output = {}; + let capture = options && options.capture ? true : false; + + target.addEventListener(type, function(event) { + emit(output, "data", event); + }, capture); + + return output; +} +exports.open = open; diff --git a/addon-sdk-1.16/lib/sdk/event/target.js b/addon-sdk-1.16/lib/sdk/event/target.js new file mode 100644 index 00000000..40ddeaf5 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/event/target.js @@ -0,0 +1,76 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +'use strict'; + +module.metadata = { + "stability": "stable" +}; + +const { on, once, off, setListeners } = require('./core'); +const { method, chainable } = require('../lang/functional'); +const { Class } = require('../core/heritage'); + +/** + * `EventTarget` is an exemplar for creating an objects that can be used to + * add / remove event listeners on them. Events on these objects may be emitted + * via `emit` function exported by 'event/core' module. + */ +const EventTarget = Class({ + /** + * Method initializes `this` event source. It goes through properties of a + * given `options` and registers listeners for the ones that look like an + * event listeners. + */ + /** + * Method initializes `this` event source. It goes through properties of a + * given `options` and registers listeners for the ones that look like an + * event listeners. + */ + initialize: function initialize(options) { + setListeners(this, options); + }, + /** + * Registers an event `listener` that is called every time events of + * specified `type` are emitted. + * @param {String} type + * The type of event. + * @param {Function} listener + * The listener function that processes the event. + * @example + * worker.on('message', function (data) { + * console.log('data received: ' + data) + * }) + */ + on: chainable(method(on)), + /** + * Registers an event `listener` that is called once the next time an event + * of the specified `type` is emitted. + * @param {String} type + * The type of the event. + * @param {Function} listener + * The listener function that processes the event. + */ + once: chainable(method(once)), + /** + * Removes an event `listener` for the given event `type`. + * @param {String} type + * The type of event. + * @param {Function} listener + * The listener function that processes the event. + */ + removeListener: function removeListener(type, listener) { + // Note: We can't just wrap `off` in `method` as we do it for other methods + // cause skipping a second or third argument will behave very differently + // than intended. This way we make sure all arguments are passed and only + // one listener is removed at most. + off(this, type, listener); + return this; + }, + off: function(type, listener) { + off(this, type, listener); + return this; + } +}); +exports.EventTarget = EventTarget; diff --git a/addon-sdk-1.16/lib/sdk/event/utils.js b/addon-sdk-1.16/lib/sdk/event/utils.js new file mode 100644 index 00000000..38b9a75a --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/event/utils.js @@ -0,0 +1,275 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +module.metadata = { + "stability": "unstable" +}; + +let { emit, on, once, off, EVENT_TYPE_PATTERN } = require("./core"); + +// This module provides set of high order function for working with event +// streams (streams in a NodeJS style that dispatch data, end and error +// events). + +// Function takes a `target` object and returns set of implicit references +// (non property references) it keeps. This basically allows defining +// references between objects without storing the explicitly. See transform for +// more details. +let refs = (function() { + let refSets = new WeakMap(); + return function refs(target) { + if (!refSets.has(target)) refSets.set(target, new Set()); + return refSets.get(target); + }; +})(); + +function transform(input, f) { + let output = {}; + + // Since event listeners don't prevent `input` to be GC-ed we wanna presrve + // it until `output` can be GC-ed. There for we add implicit reference which + // is removed once `input` ends. + refs(output).add(input); + + const next = data => receive(output, data); + once(output, "start", () => start(input)); + on(input, "error", error => emit(output, "error", error)); + on(input, "end", function() { + refs(output).delete(input); + end(output); + }); + on(input, "data", data => f(data, next)); + return output; +} + +// High order event transformation function that takes `input` event channel +// and returns transformation containing only events on which `p` predicate +// returns `true`. +function filter(input, predicate) { + return transform(input, function(data, next) { + if (predicate(data)) + next(data); + }); +} +exports.filter = filter; + +// High order function that takes `input` and returns input of it's values +// mapped via given `f` function. +const map = (input, f) => transform(input, (data, next) => next(f(data))); +exports.map = map; + +// High order function that takes `input` stream of streams and merges them +// into single event stream. Like flatten but time based rather than order +// based. +function merge(inputs) { + let output = {}; + let open = 1; + let state = []; + output.state = state; + refs(output).add(inputs); + + function end(input) { + open = open - 1; + refs(output).delete(input); + if (open === 0) emit(output, "end"); + } + const error = e => emit(output, "error", e); + function forward(input) { + state.push(input); + open = open + 1; + on(input, "end", () => end(input)); + on(input, "error", error); + on(input, "data", data => emit(output, "data", data)); + } + + // If `inputs` is an array treat it as a stream. + if (Array.isArray(inputs)) { + inputs.forEach(forward); + end(inputs); + } + else { + on(inputs, "end", () => end(inputs)); + on(inputs, "error", error); + on(inputs, "data", forward); + } + + return output; +} +exports.merge = merge; + +const expand = (inputs, f) => merge(map(inputs, f)); +exports.expand = expand; + +const pipe = (from, to) => on(from, "*", emit.bind(emit, to)); +exports.pipe = pipe; + + +// Shim signal APIs so other modules can be used as is. + +const receive = (input, message) => { + if (input[receive]) + input[receive](input, message); + else + emit(input, "data", message); + + input.value = message; +}; +receive.toString = () => "@@receive"; +exports.receive = receive; +exports.send = receive; + +const end = input => { + if (input[end]) + input[end](input); + else + emit(input, "end", input); +}; +end.toString = () => "@@end"; +exports.end = end; + +const stop = input => { + if (input[stop]) + input[stop](input); + else + emit(input, "stop", input); +}; +stop.toString = () => "@@stop"; +exports.stop = stop; + +const start = input => { + if (input[start]) + input[start](input); + else + emit(input, "start", input); +}; +start.toString = () => "@@start"; +exports.start = start; + +const lift = (step, ...inputs) => { + let args = null; + let opened = inputs.length; + let started = false; + const output = {}; + const init = () => { + args = [...inputs.map(input => input.value)]; + output.value = step(...args); + }; + + inputs.forEach((input, index) => { + on(input, "data", data => { + args[index] = data; + receive(output, step(...args)); + }); + on(input, "end", () => { + opened = opened - 1; + if (opened <= 0) + end(output); + }); + }); + + once(output, "start", () => { + inputs.forEach(start); + init(); + }); + + init(); + + return output; +}; +exports.lift = lift; + +const merges = inputs => { + let opened = inputs.length; + let output = { value: inputs[0].value }; + inputs.forEach((input, index) => { + on(input, "data", data => receive(output, data)); + on(input, "end", () => { + opened = opened - 1; + if (opened <= 0) + end(output); + }); + }); + + once(output, "start", () => { + inputs.forEach(start); + output.value = inputs[0].value; + }); + + return output; +}; +exports.merges = merges; + +const foldp = (step, initial, input) => { + let output = map(input, x => step(output.value, x)); + output.value = initial; + return output; +}; +exports.foldp = foldp; + +const keepIf = (p, base, input) => { + let output = filter(input, p); + output.value = base; + return output; +}; +exports.keepIf = keepIf; + +function Input() {} +Input.start = input => emit(input, "start", input); +Input.prototype.start = Input.start; + +Input.end = input => { + emit(input, "end", input); + stop(input); +}; +Input.prototype[end] = Input.end; + +exports.Input = Input; + +const $source = "@@source"; +const $outputs = "@@outputs"; +exports.outputs = $outputs; + +function Reactor(options={}) { + const {onStep, onStart, onEnd} = options; + if (onStep) + this.onStep = onStep; + if (onStart) + this.onStart = onStart; + if (onEnd) + this.onEnd = onEnd; +} +Reactor.prototype.onStep = _ => void(0); +Reactor.prototype.onStart = _ => void(0); +Reactor.prototype.onEnd = _ => void(0); +Reactor.prototype.onNext = function(present, past) { + this.value = present; + this.onStep(present, past); +}; +Reactor.prototype.run = function(input) { + on(input, "data", message => this.onNext(message, input.value)); + on(input, "end", () => this.onEnd(input.value)); + start(input); + this.value = input.value; + this.onStart(input.value); +}; +exports.Reactor = Reactor; + +/** + * Takes an object used as options with potential keys like 'onMessage', + * used to be called `require('sdk/event/core').setListeners` on. + * This strips all keys that would trigger a listener to be set. + * + * @params {Object} object + * @return {Object} + */ + +function stripListeners (object) { + return Object.keys(object || {}).reduce((agg, key) => { + if (!EVENT_TYPE_PATTERN.test(key)) + agg[key] = object[key]; + return agg; + }, {}); +} +exports.stripListeners = stripListeners; diff --git a/addon-sdk-1.16/lib/sdk/frame/hidden-frame.js b/addon-sdk-1.16/lib/sdk/frame/hidden-frame.js new file mode 100644 index 00000000..a0a285d6 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/frame/hidden-frame.js @@ -0,0 +1,116 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "experimental" +}; + +const { Cc, Ci } = require("chrome"); +const errors = require("../deprecated/errors"); +const { Class } = require("../core/heritage"); +const { List, addListItem, removeListItem } = require("../util/list"); +const { EventTarget } = require("../event/target"); +const { emit } = require("../event/core"); +const { create: makeFrame } = require("./utils"); +const { defer } = require("../core/promise"); +const { when: unload } = require("../system/unload"); +const { validateOptions, getTypeOf } = require("../deprecated/api-utils"); +const { window } = require("../addon/window"); +const { fromIterator } = require("../util/array"); + +// This cache is used to access friend properties between functions +// without exposing them on the public API. +let cache = new Set(); +let elements = new WeakMap(); + +function contentLoaded(target) { + var deferred = defer(); + target.addEventListener("DOMContentLoaded", function DOMContentLoaded(event) { + // "DOMContentLoaded" events from nested frames propagate up to target, + // ignore events unless it's DOMContentLoaded for the given target. + if (event.target === target || event.target === target.contentDocument) { + target.removeEventListener("DOMContentLoaded", DOMContentLoaded, false); + deferred.resolve(target); + } + }, false); + return deferred.promise; +} + +function FrameOptions(options) { + options = options || {} + return validateOptions(options, FrameOptions.validator); +} +FrameOptions.validator = { + onReady: { + is: ["undefined", "function", "array"], + ok: function(v) { + if (getTypeOf(v) === "array") { + // make sure every item is a function + return v.every(function (item) typeof(item) === "function") + } + return true; + } + }, + onUnload: { + is: ["undefined", "function"] + } +}; + +var HiddenFrame = Class({ + extends: EventTarget, + initialize: function initialize(options) { + options = FrameOptions(options); + EventTarget.prototype.initialize.call(this, options); + }, + get element() { + return elements.get(this); + }, + toString: function toString() { + return "[object Frame]" + } +}); +exports.HiddenFrame = HiddenFrame + +function addHidenFrame(frame) { + if (!(frame instanceof HiddenFrame)) + throw Error("The object to be added must be a HiddenFrame."); + + // This instance was already added. + if (cache.has(frame)) return frame; + else cache.add(frame); + + let element = makeFrame(window.document, { + nodeName: "iframe", + type: "content", + allowJavascript: true, + allowPlugins: true, + allowAuth: true, + }); + elements.set(frame, element); + + contentLoaded(element).then(function onFrameReady(element) { + emit(frame, "ready"); + }, console.exception); + + return frame; +} +exports.add = addHidenFrame + +function removeHiddenFrame(frame) { + if (!(frame instanceof HiddenFrame)) + throw Error("The object to be removed must be a HiddenFrame."); + + if (!cache.has(frame)) return; + + // Remove from cache before calling in order to avoid loop + cache.delete(frame); + emit(frame, "unload") + let element = frame.element + if (element) element.parentNode.removeChild(element) +} +exports.remove = removeHiddenFrame; + +unload(function() fromIterator(cache).forEach(removeHiddenFrame)); diff --git a/addon-sdk-1.16/lib/sdk/frame/utils.js b/addon-sdk-1.16/lib/sdk/frame/utils.js new file mode 100644 index 00000000..d5c2a3c4 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/frame/utils.js @@ -0,0 +1,100 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +'use strict'; + +module.metadata = { + "stability": "experimental" +}; + +const { Ci } = require("chrome"); +const XUL = 'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul'; + +function eventTarget(frame) { + return getDocShell(frame).chromeEventHandler; +} +exports.eventTarget = eventTarget; + +function getDocShell(frame) { + let { frameLoader } = frame.QueryInterface(Ci.nsIFrameLoaderOwner); + return frameLoader && frameLoader.docShell; +} +exports.getDocShell = getDocShell; + +/** + * Creates a XUL `browser` element in a privileged document. + * @params {nsIDOMDocument} document + * @params {String} options.type + * By default is 'content' for possible values see: + * https://developer.mozilla.org/en/XUL/iframe#a-browser.type + * @params {String} options.uri + * URI of the document to be loaded into created frame. + * @params {Boolean} options.remote + * If `true` separate process will be used for this frame, also in such + * case all the following options are ignored. + * @params {Boolean} options.allowAuth + * Whether to allow auth dialogs. Defaults to `false`. + * @params {Boolean} options.allowJavascript + * Whether to allow Javascript execution. Defaults to `false`. + * @params {Boolean} options.allowPlugins + * Whether to allow plugin execution. Defaults to `false`. + */ +function create(target, options) { + target = target instanceof Ci.nsIDOMDocument ? target.documentElement : + target instanceof Ci.nsIDOMWindow ? target.document.documentElement : + target; + options = options || {}; + let remote = options.remote || false; + let namespaceURI = options.namespaceURI || XUL; + let isXUL = namespaceURI === XUL; + let nodeName = isXUL && options.browser ? 'browser' : 'iframe'; + let document = target.ownerDocument; + + let frame = document.createElementNS(namespaceURI, nodeName); + // Type="content" is mandatory to enable stuff here: + // http://mxr.mozilla.org/mozilla-central/source/content/base/src/nsFrameLoader.cpp#1776 + frame.setAttribute('type', options.type || 'content'); + frame.setAttribute('src', options.uri || 'about:blank'); + + target.appendChild(frame); + + // Load in separate process if `options.remote` is `true`. + // http://mxr.mozilla.org/mozilla-central/source/content/base/src/nsFrameLoader.cpp#1347 + if (remote) { + if (isXUL) { + // We remove XBL binding to avoid execution of code that is not going to + // work because browser has no docShell attribute in remote mode + // (for example) + frame.setAttribute('style', '-moz-binding: none;'); + frame.setAttribute('remote', 'true'); + } + else { + frame.QueryInterface(Ci.nsIMozBrowserFrame); + frame.createRemoteFrameLoader(null); + } + } + + + + // If browser is remote it won't have a `docShell`. + if (!remote) { + let docShell = getDocShell(frame); + docShell.allowAuth = options.allowAuth || false; + docShell.allowJavascript = options.allowJavascript || false; + docShell.allowPlugins = options.allowPlugins || false; + + // Control whether the document can move/resize the window. Requires + // recently added platform capability, so we test to avoid exceptions + // in cases where capability is not present yet. + if ("allowWindowControl" in docShell && "allowWindowControl" in options) + docShell.allowWindowControl = !!options.allowWindowControl; + } + + return frame; +} +exports.create = create; + +function swapFrameLoaders(from, to) + from.QueryInterface(Ci.nsIFrameLoaderOwner).swapFrameLoaders(to) +exports.swapFrameLoaders = swapFrameLoaders; diff --git a/addon-sdk-1.16/lib/sdk/fs/path.js b/addon-sdk-1.16/lib/sdk/fs/path.js new file mode 100644 index 00000000..4474b2b4 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/fs/path.js @@ -0,0 +1,500 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// Adapted version of: +// https://github.com/joyent/node/blob/v0.11.3/lib/path.js + +// Shim process global from node. +var process = Object.create(require('../system')); +process.cwd = process.pathFor.bind(process, 'CurProcD'); + +// Update original check in node `process.platform === 'win32'` since in SDK it's `winnt`. +var isWindows = process.platform.indexOf('win') === 0; + + + +// resolves . and .. elements in a path array with directory names there +// must be no slashes, empty elements, or device names (c:\) in the array +// (so also no leading and trailing slashes - it does not distinguish +// relative and absolute paths) +function normalizeArray(parts, allowAboveRoot) { + // if the path tries to go above the root, `up` ends up > 0 + var up = 0; + for (var i = parts.length - 1; i >= 0; i--) { + var last = parts[i]; + if (last === '.') { + parts.splice(i, 1); + } else if (last === '..') { + parts.splice(i, 1); + up++; + } else if (up) { + parts.splice(i, 1); + up--; + } + } + + // if the path is allowed to go above the root, restore leading ..s + if (allowAboveRoot) { + for (; up--; up) { + parts.unshift('..'); + } + } + + return parts; +} + + +if (isWindows) { + // Regex to split a windows path into three parts: [*, device, slash, + // tail] windows-only + var splitDeviceRe = + /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/; + + // Regex to split the tail part of the above into [*, dir, basename, ext] + var splitTailRe = + /^([\s\S]*?)((?:\.{1,2}|[^\\\/]+?|)(\.[^.\/\\]*|))(?:[\\\/]*)$/; + + // Function to split a filename into [root, dir, basename, ext] + // windows version + var splitPath = function(filename) { + // Separate device+slash from tail + var result = splitDeviceRe.exec(filename), + device = (result[1] || '') + (result[2] || ''), + tail = result[3] || ''; + // Split the tail into dir, basename and extension + var result2 = splitTailRe.exec(tail), + dir = result2[1], + basename = result2[2], + ext = result2[3]; + return [device, dir, basename, ext]; + }; + + var normalizeUNCRoot = function(device) { + return '\\\\' + device.replace(/^[\\\/]+/, '').replace(/[\\\/]+/g, '\\'); + }; + + // path.resolve([from ...], to) + // windows version + exports.resolve = function() { + var resolvedDevice = '', + resolvedTail = '', + resolvedAbsolute = false; + + for (var i = arguments.length - 1; i >= -1; i--) { + var path; + if (i >= 0) { + path = arguments[i]; + } else if (!resolvedDevice) { + path = process.cwd(); + } else { + // Windows has the concept of drive-specific current working + // directories. If we've resolved a drive letter but not yet an + // absolute path, get cwd for that drive. We're sure the device is not + // an unc path at this points, because unc paths are always absolute. + path = process.env['=' + resolvedDevice]; + // Verify that a drive-local cwd was found and that it actually points + // to our drive. If not, default to the drive's root. + if (!path || path.substr(0, 3).toLowerCase() !== + resolvedDevice.toLowerCase() + '\\') { + path = resolvedDevice + '\\'; + } + } + + // Skip empty and invalid entries + if (typeof path !== 'string') { + throw new TypeError('Arguments to path.resolve must be strings'); + } else if (!path) { + continue; + } + + var result = splitDeviceRe.exec(path), + device = result[1] || '', + isUnc = device && device.charAt(1) !== ':', + isAbsolute = exports.isAbsolute(path), + tail = result[3]; + + if (device && + resolvedDevice && + device.toLowerCase() !== resolvedDevice.toLowerCase()) { + // This path points to another device so it is not applicable + continue; + } + + if (!resolvedDevice) { + resolvedDevice = device; + } + if (!resolvedAbsolute) { + resolvedTail = tail + '\\' + resolvedTail; + resolvedAbsolute = isAbsolute; + } + + if (resolvedDevice && resolvedAbsolute) { + break; + } + } + + // Convert slashes to backslashes when `resolvedDevice` points to an UNC + // root. Also squash multiple slashes into a single one where appropriate. + if (isUnc) { + resolvedDevice = normalizeUNCRoot(resolvedDevice); + } + + // At this point the path should be resolved to a full absolute path, + // but handle relative paths to be safe (might happen when process.cwd() + // fails) + + // Normalize the tail path + + function f(p) { + return !!p; + } + + resolvedTail = normalizeArray(resolvedTail.split(/[\\\/]+/).filter(f), + !resolvedAbsolute).join('\\'); + + return (resolvedDevice + (resolvedAbsolute ? '\\' : '') + resolvedTail) || + '.'; + }; + + // windows version + exports.normalize = function(path) { + var result = splitDeviceRe.exec(path), + device = result[1] || '', + isUnc = device && device.charAt(1) !== ':', + isAbsolute = exports.isAbsolute(path), + tail = result[3], + trailingSlash = /[\\\/]$/.test(tail); + + // If device is a drive letter, we'll normalize to lower case. + if (device && device.charAt(1) === ':') { + device = device[0].toLowerCase() + device.substr(1); + } + + // Normalize the tail path + tail = normalizeArray(tail.split(/[\\\/]+/).filter(function(p) { + return !!p; + }), !isAbsolute).join('\\'); + + if (!tail && !isAbsolute) { + tail = '.'; + } + if (tail && trailingSlash) { + tail += '\\'; + } + + // Convert slashes to backslashes when `device` points to an UNC root. + // Also squash multiple slashes into a single one where appropriate. + if (isUnc) { + device = normalizeUNCRoot(device); + } + + return device + (isAbsolute ? '\\' : '') + tail; + }; + + // windows version + exports.isAbsolute = function(path) { + var result = splitDeviceRe.exec(path), + device = result[1] || '', + isUnc = device && device.charAt(1) !== ':'; + // UNC paths are always absolute + return !!result[2] || isUnc; + }; + + // windows version + exports.join = function() { + function f(p) { + if (typeof p !== 'string') { + throw new TypeError('Arguments to path.join must be strings'); + } + return p; + } + + var paths = Array.prototype.filter.call(arguments, f); + var joined = paths.join('\\'); + + // Make sure that the joined path doesn't start with two slashes, because + // normalize() will mistake it for an UNC path then. + // + // This step is skipped when it is very clear that the user actually + // intended to point at an UNC path. This is assumed when the first + // non-empty string arguments starts with exactly two slashes followed by + // at least one more non-slash character. + // + // Note that for normalize() to treat a path as an UNC path it needs to + // have at least 2 components, so we don't filter for that here. + // This means that the user can use join to construct UNC paths from + // a server name and a share name; for example: + // path.join('//server', 'share') -> '\\\\server\\share\') + if (!/^[\\\/]{2}[^\\\/]/.test(paths[0])) { + joined = joined.replace(/^[\\\/]{2,}/, '\\'); + } + + return exports.normalize(joined); + }; + + // path.relative(from, to) + // it will solve the relative path from 'from' to 'to', for instance: + // from = 'C:\\orandea\\test\\aaa' + // to = 'C:\\orandea\\impl\\bbb' + // The output of the function should be: '..\\..\\impl\\bbb' + // windows version + exports.relative = function(from, to) { + from = exports.resolve(from); + to = exports.resolve(to); + + // windows is not case sensitive + var lowerFrom = from.toLowerCase(); + var lowerTo = to.toLowerCase(); + + function trim(arr) { + var start = 0; + for (; start < arr.length; start++) { + if (arr[start] !== '') break; + } + + var end = arr.length - 1; + for (; end >= 0; end--) { + if (arr[end] !== '') break; + } + + if (start > end) return []; + return arr.slice(start, end - start + 1); + } + + var toParts = trim(to.split('\\')); + + var lowerFromParts = trim(lowerFrom.split('\\')); + var lowerToParts = trim(lowerTo.split('\\')); + + var length = Math.min(lowerFromParts.length, lowerToParts.length); + var samePartsLength = length; + for (var i = 0; i < length; i++) { + if (lowerFromParts[i] !== lowerToParts[i]) { + samePartsLength = i; + break; + } + } + + if (samePartsLength == 0) { + return to; + } + + var outputParts = []; + for (var i = samePartsLength; i < lowerFromParts.length; i++) { + outputParts.push('..'); + } + + outputParts = outputParts.concat(toParts.slice(samePartsLength)); + + return outputParts.join('\\'); + }; + + exports.sep = '\\'; + exports.delimiter = ';'; + +} else /* posix */ { + + // Split a filename into [root, dir, basename, ext], unix version + // 'root' is just a slash, or nothing. + var splitPathRe = + /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; + var splitPath = function(filename) { + return splitPathRe.exec(filename).slice(1); + }; + + // path.resolve([from ...], to) + // posix version + exports.resolve = function() { + var resolvedPath = '', + resolvedAbsolute = false; + + for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { + var path = (i >= 0) ? arguments[i] : process.cwd(); + + // Skip empty and invalid entries + if (typeof path !== 'string') { + throw new TypeError('Arguments to path.resolve must be strings'); + } else if (!path) { + continue; + } + + resolvedPath = path + '/' + resolvedPath; + resolvedAbsolute = path.charAt(0) === '/'; + } + + // At this point the path should be resolved to a full absolute path, but + // handle relative paths to be safe (might happen when process.cwd() fails) + + // Normalize the path + resolvedPath = normalizeArray(resolvedPath.split('/').filter(function(p) { + return !!p; + }), !resolvedAbsolute).join('/'); + + return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.'; + }; + + // path.normalize(path) + // posix version + exports.normalize = function(path) { + var isAbsolute = exports.isAbsolute(path), + trailingSlash = path.substr(-1) === '/'; + + // Normalize the path + path = normalizeArray(path.split('/').filter(function(p) { + return !!p; + }), !isAbsolute).join('/'); + + if (!path && !isAbsolute) { + path = '.'; + } + if (path && trailingSlash) { + path += '/'; + } + + return (isAbsolute ? '/' : '') + path; + }; + + // posix version + exports.isAbsolute = function(path) { + return path.charAt(0) === '/'; + }; + + // posix version + exports.join = function() { + var paths = Array.prototype.slice.call(arguments, 0); + return exports.normalize(paths.filter(function(p, index) { + if (typeof p !== 'string') { + throw new TypeError('Arguments to path.join must be strings'); + } + return p; + }).join('/')); + }; + + + // path.relative(from, to) + // posix version + exports.relative = function(from, to) { + from = exports.resolve(from).substr(1); + to = exports.resolve(to).substr(1); + + function trim(arr) { + var start = 0; + for (; start < arr.length; start++) { + if (arr[start] !== '') break; + } + + var end = arr.length - 1; + for (; end >= 0; end--) { + if (arr[end] !== '') break; + } + + if (start > end) return []; + return arr.slice(start, end - start + 1); + } + + var fromParts = trim(from.split('/')); + var toParts = trim(to.split('/')); + + var length = Math.min(fromParts.length, toParts.length); + var samePartsLength = length; + for (var i = 0; i < length; i++) { + if (fromParts[i] !== toParts[i]) { + samePartsLength = i; + break; + } + } + + var outputParts = []; + for (var i = samePartsLength; i < fromParts.length; i++) { + outputParts.push('..'); + } + + outputParts = outputParts.concat(toParts.slice(samePartsLength)); + + return outputParts.join('/'); + }; + + exports.sep = '/'; + exports.delimiter = ':'; +} + +exports.dirname = function(path) { + var result = splitPath(path), + root = result[0], + dir = result[1]; + + if (!root && !dir) { + // No dirname whatsoever + return '.'; + } + + if (dir) { + // It has a dirname, strip trailing slash + dir = dir.substr(0, dir.length - 1); + } + + return root + dir; +}; + + +exports.basename = function(path, ext) { + var f = splitPath(path)[2]; + // TODO: make this comparison case-insensitive on windows? + if (ext && f.substr(-1 * ext.length) === ext) { + f = f.substr(0, f.length - ext.length); + } + return f; +}; + + +exports.extname = function(path) { + return splitPath(path)[3]; +}; + +if (isWindows) { + exports._makeLong = function(path) { + // Note: this will *probably* throw somewhere. + if (typeof path !== 'string') + return path; + + if (!path) { + return ''; + } + + var resolvedPath = exports.resolve(path); + + if (/^[a-zA-Z]\:\\/.test(resolvedPath)) { + // path is local filesystem path, which needs to be converted + // to long UNC path. + return '\\\\?\\' + resolvedPath; + } else if (/^\\\\[^?.]/.test(resolvedPath)) { + // path is network UNC path, which needs to be converted + // to long UNC path. + return '\\\\?\\UNC\\' + resolvedPath.substring(2); + } + + return path; + }; +} else { + exports._makeLong = function(path) { + return path; + }; +} \ No newline at end of file diff --git a/addon-sdk-1.16/lib/sdk/hotkeys.js b/addon-sdk-1.16/lib/sdk/hotkeys.js new file mode 100644 index 00000000..00081455 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/hotkeys.js @@ -0,0 +1,40 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "stable" +}; + +const INVALID_HOTKEY = "Hotkey must have at least one modifier."; + +const { toJSON: jsonify, toString: stringify, + isFunctionKey } = require("./keyboard/utils"); +const { register, unregister } = require("./keyboard/hotkeys"); + +const Hotkey = exports.Hotkey = function Hotkey(options) { + if (!(this instanceof Hotkey)) + return new Hotkey(options); + + // Parsing key combination string. + let hotkey = jsonify(options.combo); + if (!isFunctionKey(hotkey.key) && !hotkey.modifiers.length) { + throw new TypeError(INVALID_HOTKEY); + } + + this.onPress = options.onPress && options.onPress.bind(this); + this.toString = stringify.bind(null, hotkey); + // Registering listener on keyboard combination enclosed by this hotkey. + // Please note that `this.toString()` is a normalized version of + // `options.combination` where order of modifiers is sorted and `accel` is + // replaced with platform specific key. + register(this.toString(), this.onPress); + // We freeze instance before returning it in order to make it's properties + // read-only. + return Object.freeze(this); +}; +Hotkey.prototype.destroy = function destroy() { + unregister(this.toString(), this.onPress); +}; diff --git a/addon-sdk-1.16/lib/sdk/indexed-db.js b/addon-sdk-1.16/lib/sdk/indexed-db.js new file mode 100644 index 00000000..73a4e820 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/indexed-db.js @@ -0,0 +1,58 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "experimental" +}; + +const { Cc, Ci } = require("chrome"); +const { id } = require("./self"); + +// placeholder, copied from bootstrap.js +let sanitizeId = function(id){ + let uuidRe = + /^\{([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\}$/; + + let domain = id. + toLowerCase(). + replace(/@/g, "-at-"). + replace(/\./g, "-dot-"). + replace(uuidRe, "$1"); + + return domain +}; + +const PSEUDOURI = "indexeddb://" + sanitizeId(id) // https://bugzilla.mozilla.org/show_bug.cgi?id=779197 + +// Firefox 26 and earlier releases don't support `indexedDB` in sandboxes +// automatically, so we need to inject `indexedDB` to `this` scope ourselves. +if (typeof(indexedDB) === "undefined") { + Cc["@mozilla.org/dom/indexeddb/manager;1"]. + getService(Ci.nsIIndexedDatabaseManager). + initWindowless(this); + + // Firefox 14 gets this with a prefix + if (typeof(indexedDB) === "undefined") + this.indexedDB = mozIndexedDB; +} + +// Use XPCOM because `require("./url").URL` doesn't expose the raw uri object. +let principaluri = Cc["@mozilla.org/network/io-service;1"]. + getService(Ci.nsIIOService). + newURI(PSEUDOURI, null, null); + +let principal = Cc["@mozilla.org/scriptsecuritymanager;1"]. + getService(Ci.nsIScriptSecurityManager). + getCodebasePrincipal(principaluri); + +exports.indexedDB = Object.freeze({ + open: indexedDB.openForPrincipal.bind(indexedDB, principal), + deleteDatabase: indexedDB.deleteForPrincipal.bind(indexedDB, principal), + cmp: indexedDB.cmp +}); + +exports.IDBKeyRange = IDBKeyRange; +exports.DOMException = Ci.nsIDOMDOMException; diff --git a/addon-sdk-1.16/lib/sdk/input/browser.js b/addon-sdk-1.16/lib/sdk/input/browser.js new file mode 100644 index 00000000..8118b73c --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/input/browser.js @@ -0,0 +1,73 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const { windows, isBrowser, isInteractive, isDocumentLoaded, + getOuterId } = require("../window/utils"); +const { InputPort } = require("./system"); +const { lift, merges, foldp, keepIf, start, Input } = require("../event/utils"); +const { patch } = require("diffpatcher/index"); +const { Sequence, seq, filter, object, pairs } = require("../util/sequence"); + + +// Create lazy iterators from the regular arrays, although +// once https://github.com/mozilla/addon-sdk/pull/1314 lands +// `windows` will be transforme to lazy iterators. +// When iterated over belowe sequences items will represent +// state of windows at the time of iteration. +const opened = seq(function*() { + const items = windows("navigator:browser", {includePrivates: true}); + for (let item of items) { + yield [getOuterId(item), item]; + } +}); +const interactive = filter(([_, window]) => isInteractive(window), opened); +const loaded = filter(([_, window]) => isDocumentLoaded(window), opened); + +// Helper function that converts given argument to a delta. +const Update = window => window && object([getOuterId(window), window]); +const Delete = window => window && object([getOuterId(window), null]); + + +// Signal represents delta for last top level window close. +const LastClosed = lift(Delete, + keepIf(isBrowser, null, + new InputPort({topic: "domwindowclosed"}))); +exports.LastClosed = LastClosed; + +const windowFor = document => document && document.defaultView; + +// Signal represent delta for last top level window document becoming interactive. +const InteractiveDoc = new InputPort({topic: "chrome-document-interactive"}); +const InteractiveWin = lift(windowFor, InteractiveDoc); +const LastInteractive = lift(Update, keepIf(isBrowser, null, InteractiveWin)); +exports.LastInteractive = LastInteractive; + +// Signal represent delta for last top level window loaded. +const LoadedDoc = new InputPort({topic: "chrome-document-loaded"}); +const LoadedWin = lift(windowFor, LoadedDoc); +const LastLoaded = lift(Update, keepIf(isBrowser, null, LoadedWin)); +exports.LastLoaded = LastLoaded; + + +const initialize = input => { + if (!input.initialized) { + input.value = object(...input.value); + Input.start(input); + input.initialized = true; + } +}; + +// Signal represents set of top level interactive windows, updated any +// time new window becomes interactive or one get's closed. +const Interactive = foldp(patch, interactive, merges([LastInteractive, + LastClosed])); +Interactive[start] = initialize; +exports.Interactive = Interactive; + +// Signal represents set of top level loaded window, updated any time +// new window becomes interactive or one get's closed. +const Loaded = foldp(patch, loaded, merges([LastLoaded, LastClosed])); +Loaded[start] = initialize; +exports.Loaded = Loaded; diff --git a/addon-sdk-1.16/lib/sdk/input/customizable-ui.js b/addon-sdk-1.16/lib/sdk/input/customizable-ui.js new file mode 100644 index 00000000..07c483ac --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/input/customizable-ui.js @@ -0,0 +1,40 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const { Cu } = require("chrome"); + +// Because Firefox Holly, we still need to check if `CustomizableUI` is +// available. Once Australis will officially land, we can safely remove it. +// See Bug 959142 +try { + Cu.import("resource:///modules/CustomizableUI.jsm", {}); +} +catch (e) { + throw Error("Unsupported Application: The module" + module.id + + " does not support this application."); +} + +const { CustomizableUI } = Cu.import('resource:///modules/CustomizableUI.jsm', {}); +const { receive } = require("../event/utils"); +const { InputPort } = require("./system"); +const { object} = require("../util/sequence"); +const { getOuterId } = require("../window/utils"); + +const Input = function() {}; +Input.prototype = Object.create(InputPort.prototype); + +Input.prototype.onCustomizeStart = function (window) { + receive(this, object([getOuterId(window), true])); +} + +Input.prototype.onCustomizeEnd = function (window) { + receive(this, object([getOuterId(window), null])); +} + +Input.prototype.addListener = input => CustomizableUI.addListener(input); + +Input.prototype.removeListener = input => CustomizableUI.removeListener(input); + +exports.CustomizationInput = Input; diff --git a/addon-sdk-1.16/lib/sdk/input/frame.js b/addon-sdk-1.16/lib/sdk/input/frame.js new file mode 100644 index 00000000..50efaa74 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/input/frame.js @@ -0,0 +1,85 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const { Ci } = require("chrome"); +const { InputPort } = require("./system"); +const { getFrameElement, getOuterId, + getOwnerBrowserWindow } = require("../window/utils"); +const { isnt } = require("../lang/functional"); +const { foldp, lift, merges, keepIf } = require("../event/utils"); +const { object } = require("../util/sequence"); +const { compose } = require("../lang/functional"); +const { LastClosed } = require("./browser"); +const { patch } = require("diffpatcher/index"); + +const Document = Ci.nsIDOMDocument; + +const isntNull = isnt(null); + +const frameID = frame => frame.id; +const browserID = compose(getOuterId, getOwnerBrowserWindow); + +const isInnerFrame = frame => + frame && frame.hasAttribute("data-is-sdk-inner-frame"); + +// Utility function that given content window loaded in our frame views returns +// an actual frame. This basically takes care of fact that actual frame document +// is loaded in the nested iframe. If content window is not loaded in the nested +// frame of the frame view it returs null. +const getFrame = document => + document && document.defaultView && getFrameElement(document.defaultView); + +const FrameInput = function(options) { + const input = keepIf(isInnerFrame, null, + lift(getFrame, new InputPort(options))); + return lift(frame => { + if (!frame) return frame; + const [id, owner] = [frameID(frame), browserID(frame)]; + return object([id, {owners: object([owner, options.update])}]); + }, input); +}; + +const LastLoading = new FrameInput({topic: "document-element-inserted", + update: {readyState: "loading"}}); +exports.LastLoading = LastLoading; + +const LastInteractive = new FrameInput({topic: "content-document-interactive", + update: {readyState: "interactive"}}); +exports.LastInteractive = LastInteractive; + +const LastLoaded = new FrameInput({topic: "content-document-loaded", + update: {readyState: "complete"}}); +exports.LastLoaded = LastLoaded; + +const LastUnloaded = new FrameInput({topic: "content-page-hidden", + update: null}); +exports.LastUnloaded = LastUnloaded; + +// Represents state of SDK frames in form of data structure: +// {"frame#1": {"id": "frame#1", +// "inbox": {"data": "ping", +// "target": {"id": "frame#1", "owner": "outerWindowID#2"}, +// "source": {"id": "frame#1"}} +// "url": "resource://addon-1/data/index.html", +// "owners": {"outerWindowID#1": {"readyState": "loading"}, +// "outerWindowID#2": {"readyState": "complete"}} +// +// +// frame#2: {"id": "frame#2", +// "url": "resource://addon-1/data/main.html", +// "outbox": {"data": "pong", +// "source": {"id": "frame#2", "owner": "outerWindowID#1"} +// "target": {"id": "frame#2"}} +// "owners": {outerWindowID#1: {readyState: "interacitve"}}}} +const Frames = foldp(patch, {}, merges([ + LastLoading, + LastInteractive, + LastLoaded, + LastUnloaded, + new InputPort({ id: "frame-mailbox" }), + new InputPort({ id: "frame-change" }), + new InputPort({ id: "frame-changed" }) +])); +exports.Frames = Frames; diff --git a/addon-sdk-1.16/lib/sdk/input/system.js b/addon-sdk-1.16/lib/sdk/input/system.js new file mode 100644 index 00000000..7ce07aab --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/input/system.js @@ -0,0 +1,109 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const { Cc, Ci, Cr, Cu } = require("chrome"); +const { Input, start, stop, end, receive, outputs } = require("../event/utils"); +const { once, off } = require("../event/core"); +const { id: addonID } = require("../self"); + +const unloadMessage = require("@loader/unload"); +const { addObserver, removeObserver } = Cc['@mozilla.org/observer-service;1']. + getService(Ci.nsIObserverService); + +const addonUnloadTopic = "sdk:loader:destroy"; + +const isXrayWrapper = Cu.isXrayWrapper; +// In the past SDK used to double-wrap notifications dispatched, which +// made them awkward to use outside of SDK. At present they no longer +// do that, although we still supported for legacy reasons. +const isLegacyWrapper = x => + x && x.wrappedJSObject && + "observersModuleSubjectWrapper" in x.wrappedJSObject; + +const unwrapLegacy = x => x.wrappedJSObject.object; + +// `InputPort` provides a way to create a signal out of the observer +// notification subject's for the given `topic`. If `options.initial` +// is provided it is used as initial value otherwise `null` is used. +// Constructor can be given `options.id` that will be used to create +// a `topic` which is namespaced to an add-on (this avoids conflicts +// when multiple add-on are used, although in a future host probably +// should just be shared across add-ons). It is also possible to +// specify a specific `topic` via `options.topic` which is used as +// without namespacing. Created signal ends whenever add-on is +// unloaded. +const InputPort = function InputPort({id, topic, initial}) { + this.id = id || topic; + this.topic = topic || "sdk:" + addonID + ":" + id; + this.value = initial === void(0) ? null : initial; + this.observing = false; + this[outputs] = []; +}; + +// InputPort type implements `Input` signal interface. +InputPort.prototype = new Input(); +InputPort.prototype.constructor = InputPort; + +// When port is started (which is when it's subgraph get's +// first subscriber) actual observer is registered. +InputPort.start = input => { + input.addListener(input); + // Also register add-on unload observer to end this signal + // when that happens. + addObserver(input, addonUnloadTopic, false); +}; +InputPort.prototype[start] = InputPort.start; + +InputPort.addListener = input => addObserver(input, input.topic, false); +InputPort.prototype.addListener = InputPort.addListener; + +// When port is stopped (which is when it's subgraph has no +// no subcribers left) an actual observer unregistered. +// Note that port stopped once it ends as well (which is when +// add-on is unloaded). +InputPort.stop = input => { + input.removeListener(input); + removeObserver(input, addonUnloadTopic); +}; +InputPort.prototype[stop] = InputPort.stop; + +InputPort.removeListener = input => removeObserver(input, input.topic); +InputPort.prototype.removeListener = InputPort.removeListener; + +// `InputPort` also implements `nsIObserver` interface and +// `nsISupportsWeakReference` interfaces as it's going to be used as such. +InputPort.prototype.QueryInterface = function(iid) { + if (!iid.equals(Ci.nsIObserver) && !iid.equals(Ci.nsISupportsWeakReference)) + throw Cr.NS_ERROR_NO_INTERFACE; + + return this; +}; + +// `InputPort` instances implement `observe` method, which is invoked when +// observer notifications are dispatched. The `subject` of that notification +// are received on this signal. +InputPort.prototype.observe = function(subject, topic, data) { + // Unwrap message from the subject. SDK used to have it's own version of + // wrappedJSObjects which take precedence, if subject has `wrappedJSObject` + // and it's not an XrayWrapper use it as message. Otherwise use subject as + // is. + const message = subject === null ? null : + isLegacyWrapper(subject) ? unwrapLegacy(subject) : + isXrayWrapper(subject) ? subject : + subject.wrappedJSObject ? subject.wrappedJSObject : + subject; + + // If observer topic matches topic of the input port receive a message. + if (topic === this.topic) { + receive(this, message); + } + + // If observe topic is add-on unload topic we create an end message. + if (topic === addonUnloadTopic && message === unloadMessage) { + end(this); + } +}; + +exports.InputPort = InputPort; diff --git a/addon-sdk-1.16/lib/sdk/input/window.js b/addon-sdk-1.16/lib/sdk/input/window.js new file mode 100644 index 00000000..fc273eed --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/input/window.js @@ -0,0 +1,80 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const { windows, isInteractive, isDocumentLoaded, + getOuterId, isTopLevel } = require("../window/utils"); +const { InputPort } = require("./system"); +const { lift, merges, foldp, keepIf, start, Input } = require("../event/utils"); +const { patch } = require("diffpatcher/index"); +const { on } = require("../event/core"); +const { Sequence, seq, filter, object, pairs } = require("../util/sequence"); + + +// Create lazy iterators from the regular arrays, although +// once https://github.com/mozilla/addon-sdk/pull/1314 lands +// `windows` will be transforme to lazy iterators. +// When iterated over belowe sequences items will represent +// state of windows at the time of iteration. +const opened = seq(function*() { + const items = windows(null, {includePrivates: true}); + for (let item of items) + yield [getOuterId(item), item]; +}); +const interactive = filter(([_, window]) => isInteractive(window), opened); +const loaded = filter(([_, window]) => isDocumentLoaded(window), opened); + +// Helper function that converts given argument to a delta. +const Update = window => window && object([getOuterId(window), window]); +const Delete = window => window && object([getOuterId(window), null]); + +// Signal represents delta for last opened top level window. +const LastOpened = lift(Update, new InputPort({topic: "domwindowopened"})); +exports.LastOpened = LastOpened; + +// Signal represents delta for last top level window close. +const LastClosed = lift(Delete, new InputPort({topic: "domwindowclosed"})); +exports.LastClosed = LastClosed; + +const windowFor = document => document && document.defaultView; + +// Signal represent delta for last top level window document becoming interactive. +const InteractiveDoc = new InputPort({topic: "chrome-document-interactive"}); +const InteractiveWin = lift(windowFor, InteractiveDoc); +const LastInteractive = lift(Update, keepIf(isTopLevel, null, InteractiveWin)); +exports.LastInteractive = LastInteractive; + +// Signal represent delta for last top level window loaded. +const LoadedDoc = new InputPort({topic: "chrome-document-loaded"}); +const LoadedWin = lift(windowFor, LoadedDoc); +const LastLoaded = lift(Update, keepIf(isTopLevel, null, LoadedWin)); +exports.LastLoaded = LastLoaded; + + +const initialize = input => { + if (!input.initialized) { + input.value = object(...input.value); + Input.start(input); + input.initialized = true; + } +}; + +// Signal represents set of currently opened top level windows, updated +// to new set any time window is opened or closed. +const Opened = foldp(patch, opened, merges([LastOpened, LastClosed])); +Opened[start] = initialize; +exports.Opened = Opened; + +// Signal represents set of top level interactive windows, updated any +// time new window becomes interactive or one get's closed. +const Interactive = foldp(patch, interactive, merges([LastInteractive, + LastClosed])); +Interactive[start] = initialize; +exports.Interactive = Interactive; + +// Signal represents set of top level loaded window, updated any time +// new window becomes interactive or one get's closed. +const Loaded = foldp(patch, loaded, merges([LastLoaded, LastClosed])); +Loaded[start] = initialize; +exports.Loaded = Loaded; diff --git a/addon-sdk-1.16/lib/sdk/io/buffer.js b/addon-sdk-1.16/lib/sdk/io/buffer.js new file mode 100644 index 00000000..838a66af --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/io/buffer.js @@ -0,0 +1,346 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +'use strict'; + +module.metadata = { + 'stability': 'experimental' +}; + +/* + * Encodings supported by TextEncoder/Decoder: + * utf-8, utf-16le, utf-16be + * http://encoding.spec.whatwg.org/#interface-textencoder + * + * Node however supports the following encodings: + * ascii, utf-8, utf-16le, usc2, base64, hex + */ + +const { Cu } = require('chrome'); +const { isNumber } = require('sdk/lang/type'); +const { TextEncoder, TextDecoder } = Cu.import('resource://gre/modules/commonjs/toolkit/loader.js', {}); + +exports.TextEncoder = TextEncoder; +exports.TextDecoder = TextDecoder; + +/** + * Use WeakMaps to work around Bug 929146, which prevents us from adding + * getters or values to typed arrays + * https://bugzilla.mozilla.org/show_bug.cgi?id=929146 + */ +const parents = new WeakMap(); +const views = new WeakMap(); + +function Buffer(subject, encoding /*, bufferLength */) { + + // Allow invocation without `new` constructor + if (!(this instanceof Buffer)) + return new Buffer(subject, encoding, arguments[2]); + + var type = typeof(subject); + + switch (type) { + case 'number': + // Create typed array of the given size if number. + try { + let buffer = Uint8Array(subject > 0 ? Math.floor(subject) : 0); + return buffer; + } catch (e) { + if (/size and count too large/.test(e.message) || + /invalid arguments/.test(e.message)) + throw new RangeError('Could not instantiate buffer: size of buffer may be too large'); + else + throw new Error('Could not instantiate buffer'); + } + break; + case 'string': + // If string encode it and use buffer for the returned Uint8Array + // to create a local patched version that acts like node buffer. + encoding = encoding || 'utf8'; + return Uint8Array(TextEncoder(encoding).encode(subject).buffer); + case 'object': + // This form of the constructor uses the form of + // Uint8Array(buffer, offset, length); + // So we can instantiate a typed array within the constructor + // to inherit the appropriate properties, where both the + // `subject` and newly instantiated buffer share the same underlying + // data structure. + if (arguments.length === 3) + return Uint8Array(subject, encoding, arguments[2]); + // If array or alike just make a copy with a local patched prototype. + else + return Uint8Array(subject); + default: + throw new TypeError('must start with number, buffer, array or string'); + } +} +exports.Buffer = Buffer; + +// Tests if `value` is a Buffer. +Buffer.isBuffer = value => value instanceof Buffer + +// Returns true if the encoding is a valid encoding argument & false otherwise +Buffer.isEncoding = function (encoding) { + if (!encoding) return false; + try { + TextDecoder(encoding); + } catch(e) { + return false; + } + return true; +} + +// Gives the actual byte length of a string. encoding defaults to 'utf8'. +// This is not the same as String.prototype.length since that returns the +// number of characters in a string. +Buffer.byteLength = (value, encoding = 'utf8') => + TextEncoder(encoding).encode(value).byteLength + +// Direct copy of the nodejs's buffer implementation: +// https://github.com/joyent/node/blob/b255f4c10a80343f9ce1cee56d0288361429e214/lib/buffer.js#L146-L177 +Buffer.concat = function(list, length) { + if (!Array.isArray(list)) + throw new TypeError('Usage: Buffer.concat(list[, length])'); + + if (typeof length === 'undefined') { + length = 0; + for (var i = 0; i < list.length; i++) + length += list[i].length; + } else { + length = ~~length; + } + + if (length < 0) + length = 0; + + if (list.length === 0) + return new Buffer(0); + else if (list.length === 1) + return list[0]; + + if (length < 0) + throw new RangeError('length is not a positive number'); + + var buffer = new Buffer(length); + var pos = 0; + for (var i = 0; i < list.length; i++) { + var buf = list[i]; + buf.copy(buffer, pos); + pos += buf.length; + } + + return buffer; +}; + +// Node buffer is very much like Uint8Array although it has bunch of methods +// that typically can be used in combination with `DataView` while preserving +// access by index. Since in SDK each module has it's own set of bult-ins it +// ok to patch ours to make it nodejs Buffer compatible. +Buffer.prototype = Uint8Array.prototype; +Object.defineProperties(Buffer.prototype, { + parent: { + get: function() { return parents.get(this, undefined); } + }, + view: { + get: function () { + let view = views.get(this, undefined); + if (view) return view; + view = DataView(this.buffer); + views.set(this, view); + return view; + } + }, + toString: { + value: function(encoding, start, end) { + encoding = !!encoding ? (encoding + '').toLowerCase() : 'utf8'; + start = Math.max(0, ~~start); + end = Math.min(this.length, end === void(0) ? this.length : ~~end); + return TextDecoder(encoding).decode(this.subarray(start, end)); + } + }, + toJSON: { + value: function() { + return { type: 'Buffer', data: Array.slice(this, 0) }; + } + }, + get: { + value: function(offset) { + return this[offset]; + } + }, + set: { + value: function(offset, value) { this[offset] = value; } + }, + copy: { + value: function(target, offset, start, end) { + let length = this.length; + let targetLength = target.length; + offset = isNumber(offset) ? offset : 0; + start = isNumber(start) ? start : 0; + + if (start < 0) + throw new RangeError('sourceStart is outside of valid range'); + if (end < 0) + throw new RangeError('sourceEnd is outside of valid range'); + + // If sourceStart > sourceEnd, or targetStart > targetLength, + // zero bytes copied + if (start > end || + offset > targetLength + ) + return 0; + + // If `end` is not defined, or if it is defined + // but would overflow `target`, redefine `end` + // so we can copy as much as we can + if (end - start > targetLength - offset || + end == null) { + let remainingTarget = targetLength - offset; + let remainingSource = length - start; + if (remainingSource <= remainingTarget) + end = length; + else + end = start + remainingTarget; + } + + Uint8Array.set(target, this.subarray(start, end), offset); + return end - start; + } + }, + slice: { + value: function(start, end) { + let length = this.length; + start = ~~start; + end = end != null ? end : length; + + if (start < 0) { + start += length; + if (start < 0) start = 0; + } else if (start > length) + start = length; + + if (end < 0) { + end += length; + if (end < 0) end = 0; + } else if (end > length) + end = length; + + if (end < start) + end = start; + + // This instantiation uses the Uint8Array(buffer, offset, length) version + // of construction to share the same underling data structure + let buffer = new Buffer(this.buffer, start, end - start); + + // If buffer has a value, assign its parent value to the + // buffer it shares its underlying structure with. If a slice of + // a slice, then use the root structure + if (buffer.length > 0) + parents.set(buffer, this.parent || this); + + return buffer; + } + }, + write: { + value: function(string, offset, length, encoding = 'utf8') { + // write(string, encoding); + if (typeof(offset) === 'string' && Number.isNaN(parseInt(offset))) { + ([offset, length, encoding]) = [0, null, offset]; + } + // write(string, offset, encoding); + else if (typeof(length) === 'string') + ([length, encoding]) = [null, length]; + + if (offset < 0 || offset > this.length) + throw new RangeError('offset is outside of valid range'); + + offset = ~~offset; + + // Clamp length if it would overflow buffer, or if its + // undefined + if (length == null || length + offset > this.length) + length = this.length - offset; + + let buffer = TextEncoder(encoding).encode(string); + let result = Math.min(buffer.length, length); + if (buffer.length !== length) + buffer = buffer.subarray(0, length); + + Uint8Array.set(this, buffer, offset); + return result; + } + }, + fill: { + value: function fill(value, start, end) { + let length = this.length; + value = value || 0; + start = start || 0; + end = end || length; + + if (typeof(value) === 'string') + value = value.charCodeAt(0); + if (typeof(value) !== 'number' || isNaN(value)) + throw TypeError('value is not a number'); + if (end < start) + throw new RangeError('end < start'); + + // Fill 0 bytes; we're done + if (end === start) + return 0; + if (length == 0) + return 0; + + if (start < 0 || start >= length) + throw RangeError('start out of bounds'); + + if (end < 0 || end > length) + throw RangeError('end out of bounds'); + + let index = start; + while (index < end) this[index++] = value; + } + } +}); + +// Define nodejs Buffer's getter and setter functions that just proxy +// to internal DataView's equivalent methods. + +// TODO do we need to check architecture to see if it's default big/little endian? +[['readUInt16LE', 'getUint16', true], + ['readUInt16BE', 'getUint16', false], + ['readInt16LE', 'getInt16', true], + ['readInt16BE', 'getInt16', false], + ['readUInt32LE', 'getUint32', true], + ['readUInt32BE', 'getUint32', false], + ['readInt32LE', 'getInt32', true], + ['readInt32BE', 'getInt32', false], + ['readFloatLE', 'getFloat32', true], + ['readFloatBE', 'getFloat32', false], + ['readDoubleLE', 'getFloat64', true], + ['readDoubleBE', 'getFloat64', false], + ['readUInt8', 'getUint8'], + ['readInt8', 'getInt8']].forEach(([alias, name, littleEndian]) => { + Object.defineProperty(Buffer.prototype, alias, { + value: function(offset) this.view[name](offset, littleEndian) + }); +}); + +[['writeUInt16LE', 'setUint16', true], + ['writeUInt16BE', 'setUint16', false], + ['writeInt16LE', 'setInt16', true], + ['writeInt16BE', 'setInt16', false], + ['writeUInt32LE', 'setUint32', true], + ['writeUInt32BE', 'setUint32', false], + ['writeInt32LE', 'setInt32', true], + ['writeInt32BE', 'setInt32', false], + ['writeFloatLE', 'setFloat32', true], + ['writeFloatBE', 'setFloat32', false], + ['writeDoubleLE', 'setFloat64', true], + ['writeDoubleBE', 'setFloat64', false], + ['writeUInt8', 'setUint8'], + ['writeInt8', 'setInt8']].forEach(([alias, name, littleEndian]) => { + Object.defineProperty(Buffer.prototype, alias, { + value: function(value, offset) this.view[name](offset, value, littleEndian) + }); +}); diff --git a/addon-sdk-1.16/lib/sdk/io/byte-streams.js b/addon-sdk-1.16/lib/sdk/io/byte-streams.js new file mode 100644 index 00000000..6afab436 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/io/byte-streams.js @@ -0,0 +1,104 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "experimental" +}; + +exports.ByteReader = ByteReader; +exports.ByteWriter = ByteWriter; + +const {Cc, Ci} = require("chrome"); + +// This just controls the maximum number of bytes we read in at one time. +const BUFFER_BYTE_LEN = 0x8000; + +function ByteReader(inputStream) { + const self = this; + + let stream = Cc["@mozilla.org/binaryinputstream;1"]. + createInstance(Ci.nsIBinaryInputStream); + stream.setInputStream(inputStream); + + let manager = new StreamManager(this, stream); + + this.read = function ByteReader_read(numBytes) { + manager.ensureOpened(); + if (typeof(numBytes) !== "number") + numBytes = Infinity; + + let data = ""; + let read = 0; + try { + while (true) { + let avail = stream.available(); + let toRead = Math.min(numBytes - read, avail, BUFFER_BYTE_LEN); + if (toRead <= 0) + break; + data += stream.readBytes(toRead); + read += toRead; + } + } + catch (err) { + throw new Error("Error reading from stream: " + err); + } + + return data; + }; +} + +function ByteWriter(outputStream) { + const self = this; + + let stream = Cc["@mozilla.org/binaryoutputstream;1"]. + createInstance(Ci.nsIBinaryOutputStream); + stream.setOutputStream(outputStream); + + let manager = new StreamManager(this, stream); + + this.write = function ByteWriter_write(str) { + manager.ensureOpened(); + try { + stream.writeBytes(str, str.length); + } + catch (err) { + throw new Error("Error writing to stream: " + err); + } + }; +} + + +// This manages the lifetime of stream, a ByteReader or ByteWriter. It defines +// closed and close() on stream and registers an unload listener that closes +// rawStream if it's still opened. It also provides ensureOpened(), which +// throws an exception if the stream is closed. +function StreamManager(stream, rawStream) { + const self = this; + this.rawStream = rawStream; + this.opened = true; + + stream.__defineGetter__("closed", function stream_closed() { + return !self.opened; + }); + + stream.close = function stream_close() { + self.ensureOpened(); + self.unload(); + }; + + require("../system/unload").ensure(this); +} + +StreamManager.prototype = { + ensureOpened: function StreamManager_ensureOpened() { + if (!this.opened) + throw new Error("The stream is closed and cannot be used."); + }, + unload: function StreamManager_unload() { + this.rawStream.close(); + this.opened = false; + } +}; diff --git a/addon-sdk-1.16/lib/sdk/io/data.js b/addon-sdk-1.16/lib/sdk/io/data.js new file mode 100644 index 00000000..fcb8b5cf --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/io/data.js @@ -0,0 +1,82 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "unstable" +}; + +const { Cc, Ci, Cu } = require("chrome"); +const base64 = require("../base64"); +const IOService = Cc["@mozilla.org/network/io-service;1"]. + getService(Ci.nsIIOService); + +const { deprecateFunction } = require('../util/deprecate'); +const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm"); +const FaviconService = Cc["@mozilla.org/browser/favicon-service;1"]. + getService(Ci.nsIFaviconService); + +const PNG_B64 = "data:image/png;base64,"; +const DEF_FAVICON_URI = "chrome://mozapps/skin/places/defaultFavicon.png"; +let DEF_FAVICON = null; + +/** + * Takes URI of the page and returns associated favicon URI. + * If page under passed uri has no favicon then base64 encoded data URI of + * default faveicon is returned. + * @param {String} uri + * @returns {String} + */ +function getFaviconURIForLocation(uri) { + let pageURI = NetUtil.newURI(uri); + try { + return FaviconService.getFaviconDataAsDataURL( + FaviconService.getFaviconForPage(pageURI)); + } + catch(e) { + if (!DEF_FAVICON) { + DEF_FAVICON = PNG_B64 + + base64.encode(getChromeURIContent(DEF_FAVICON_URI)); + } + return DEF_FAVICON; + } +} +exports.getFaviconURIForLocation = getFaviconURIForLocation; + +/** + * Takes chrome URI and returns content under that URI. + * @param {String} chromeURI + * @returns {String} + */ +function getChromeURIContent(chromeURI) { + let channel = IOService.newChannel(chromeURI, null, null); + let input = channel.open(); + let stream = Cc["@mozilla.org/binaryinputstream;1"]. + createInstance(Ci.nsIBinaryInputStream); + stream.setInputStream(input); + let content = stream.readBytes(input.available()); + stream.close(); + input.close(); + return content; +} +exports.getChromeURIContent = deprecateFunction(getChromeURIContent, + 'getChromeURIContent is deprecated, ' + + 'please use require("sdk/net/url").readURI instead.' +); + +/** + * Creates a base-64 encoded ASCII string from a string of binary data. + */ +exports.base64Encode = deprecateFunction(base64.encode, + 'base64Encode is deprecated, ' + + 'please use require("sdk/base64").encode instead.' +); +/** + * Decodes a string of data which has been encoded using base-64 encoding. + */ +exports.base64Decode = deprecateFunction(base64.decode, + 'base64Dencode is deprecated, ' + + 'please use require("sdk/base64").decode instead.' +); diff --git a/addon-sdk-1.16/lib/sdk/io/file.js b/addon-sdk-1.16/lib/sdk/io/file.js new file mode 100644 index 00000000..d7bb4bb1 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/io/file.js @@ -0,0 +1,196 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "experimental" +}; + +const {Cc,Ci,Cr} = require("chrome"); +const byteStreams = require("./byte-streams"); +const textStreams = require("./text-streams"); + +// Flags passed when opening a file. See nsprpub/pr/include/prio.h. +const OPEN_FLAGS = { + RDONLY: parseInt("0x01"), + WRONLY: parseInt("0x02"), + CREATE_FILE: parseInt("0x08"), + APPEND: parseInt("0x10"), + TRUNCATE: parseInt("0x20"), + EXCL: parseInt("0x80") +}; + +var dirsvc = Cc["@mozilla.org/file/directory_service;1"] + .getService(Ci.nsIProperties); + +function MozFile(path) { + var file = Cc['@mozilla.org/file/local;1'] + .createInstance(Ci.nsILocalFile); + file.initWithPath(path); + return file; +} + +function ensureReadable(file) { + if (!file.isReadable()) + throw new Error("path is not readable: " + file.path); +} + +function ensureDir(file) { + ensureExists(file); + if (!file.isDirectory()) + throw new Error("path is not a directory: " + file.path); +} + +function ensureFile(file) { + ensureExists(file); + if (!file.isFile()) + throw new Error("path is not a file: " + file.path); +} + +function ensureExists(file) { + if (!file.exists()) + throw friendlyError(Cr.NS_ERROR_FILE_NOT_FOUND, file.path); +} + +function friendlyError(errOrResult, filename) { + var isResult = typeof(errOrResult) === "number"; + var result = isResult ? errOrResult : errOrResult.result; + switch (result) { + case Cr.NS_ERROR_FILE_NOT_FOUND: + return new Error("path does not exist: " + filename); + } + return isResult ? new Error("XPCOM error code: " + errOrResult) : errOrResult; +} + +exports.exists = function exists(filename) { + return MozFile(filename).exists(); +}; + +exports.isFile = function isFile(filename) { + return MozFile(filename).isFile(); +}; + +exports.read = function read(filename, mode) { + if (typeof(mode) !== "string") + mode = ""; + + // Ensure mode is read-only. + mode = /b/.test(mode) ? "b" : ""; + + var stream = exports.open(filename, mode); + try { + var str = stream.read(); + } + finally { + stream.close(); + } + + return str; +}; + +exports.join = function join(base) { + if (arguments.length < 2) + throw new Error("need at least 2 args"); + base = MozFile(base); + for (var i = 1; i < arguments.length; i++) + base.append(arguments[i]); + return base.path; +}; + +exports.dirname = function dirname(path) { + var parent = MozFile(path).parent; + return parent ? parent.path : ""; +}; + +exports.basename = function basename(path) { + var leafName = MozFile(path).leafName; + + // On Windows, leafName when the path is a volume letter and colon ("c:") is + // the path itself. But such a path has no basename, so we want the empty + // string. + return leafName == path ? "" : leafName; +}; + +exports.list = function list(path) { + var file = MozFile(path); + ensureDir(file); + ensureReadable(file); + + var entries = file.directoryEntries; + var entryNames = []; + while(entries.hasMoreElements()) { + var entry = entries.getNext(); + entry.QueryInterface(Ci.nsIFile); + entryNames.push(entry.leafName); + } + return entryNames; +}; + +exports.open = function open(filename, mode) { + var file = MozFile(filename); + if (typeof(mode) !== "string") + mode = ""; + + // File opened for write only. + if (/w/.test(mode)) { + if (file.exists()) + ensureFile(file); + var stream = Cc['@mozilla.org/network/file-output-stream;1']. + createInstance(Ci.nsIFileOutputStream); + var openFlags = OPEN_FLAGS.WRONLY | + OPEN_FLAGS.CREATE_FILE | + OPEN_FLAGS.TRUNCATE; + var permFlags = parseInt("0644", 8); // u+rw go+r + try { + stream.init(file, openFlags, permFlags, 0); + } + catch (err) { + throw friendlyError(err, filename); + } + return /b/.test(mode) ? + new byteStreams.ByteWriter(stream) : + new textStreams.TextWriter(stream); + } + + // File opened for read only, the default. + ensureFile(file); + stream = Cc['@mozilla.org/network/file-input-stream;1']. + createInstance(Ci.nsIFileInputStream); + try { + stream.init(file, OPEN_FLAGS.RDONLY, 0, 0); + } + catch (err) { + throw friendlyError(err, filename); + } + return /b/.test(mode) ? + new byteStreams.ByteReader(stream) : + new textStreams.TextReader(stream); +}; + +exports.remove = function remove(path) { + var file = MozFile(path); + ensureFile(file); + file.remove(false); +}; + +exports.mkpath = function mkpath(path) { + var file = MozFile(path); + if (!file.exists()) + file.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("0755", 8)); // u+rwx go+rx + else if (!file.isDirectory()) + throw new Error("The path already exists and is not a directory: " + path); +}; + +exports.rmdir = function rmdir(path) { + var file = MozFile(path); + ensureDir(file); + try { + file.remove(false); + } + catch (err) { + // Bug 566950 explains why we're not catching a specific exception here. + throw new Error("The directory is not empty: " + path); + } +}; diff --git a/addon-sdk-1.16/lib/sdk/io/fs.js b/addon-sdk-1.16/lib/sdk/io/fs.js new file mode 100644 index 00000000..57878dbf --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/io/fs.js @@ -0,0 +1,940 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +module.metadata = { + "stability": "experimental" +}; + +const { Cc, Ci, CC } = require("chrome"); + +const { setTimeout } = require("../timers"); +const { Stream, InputStream, OutputStream } = require("./stream"); +const { emit, on } = require("../event/core"); +const { Buffer } = require("./buffer"); +const { ns } = require("../core/namespace"); +const { Class } = require("../core/heritage"); + + +const nsILocalFile = CC("@mozilla.org/file/local;1", "nsILocalFile", + "initWithPath"); +const FileOutputStream = CC("@mozilla.org/network/file-output-stream;1", + "nsIFileOutputStream", "init"); +const FileInputStream = CC("@mozilla.org/network/file-input-stream;1", + "nsIFileInputStream", "init"); +const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", "setInputStream"); +const BinaryOutputStream = CC("@mozilla.org/binaryoutputstream;1", + "nsIBinaryOutputStream", "setOutputStream"); +const StreamPump = CC("@mozilla.org/network/input-stream-pump;1", + "nsIInputStreamPump", "init"); + +const { createOutputTransport, createInputTransport } = + Cc["@mozilla.org/network/stream-transport-service;1"]. + getService(Ci.nsIStreamTransportService); + +const { OPEN_UNBUFFERED } = Ci.nsITransport; + + +const { REOPEN_ON_REWIND, DEFER_OPEN } = Ci.nsIFileInputStream; +const { DIRECTORY_TYPE, NORMAL_FILE_TYPE } = Ci.nsIFile; +const { NS_SEEK_SET, NS_SEEK_CUR, NS_SEEK_END } = Ci.nsISeekableStream; + +const FILE_PERMISSION = parseInt("0666", 8); +const PR_UINT32_MAX = 0xfffffff; +// Values taken from: +// http://mxr.mozilla.org/mozilla-central/source/nsprpub/pr/include/prio.h#615 +const PR_RDONLY = 0x01; +const PR_WRONLY = 0x02; +const PR_RDWR = 0x04; +const PR_CREATE_FILE = 0x08; +const PR_APPEND = 0x10; +const PR_TRUNCATE = 0x20; +const PR_SYNC = 0x40; +const PR_EXCL = 0x80; + +const FLAGS = { + "r": PR_RDONLY, + "r+": PR_RDWR, + "w": PR_CREATE_FILE | PR_TRUNCATE | PR_WRONLY, + "w+": PR_CREATE_FILE | PR_TRUNCATE | PR_RDWR, + "a": PR_APPEND | PR_CREATE_FILE | PR_WRONLY, + "a+": PR_APPEND | PR_CREATE_FILE | PR_RDWR +}; + +function accessor() { + let map = new WeakMap(); + return function(fd, value) { + if (value === null) map.delete(fd); + if (value !== undefined) map.set(fd, value); + return map.get(fd); + } +} + +let nsIFile = accessor(); +let nsIFileInputStream = accessor(); +let nsIFileOutputStream = accessor(); +let nsIBinaryInputStream = accessor(); +let nsIBinaryOutputStream = accessor(); + +// Just a contstant object used to signal that all of the file +// needs to be read. +const ALL = new String("Read all of the file"); + +function isWritable(mode) !!(mode & PR_WRONLY || mode & PR_RDWR) +function isReadable(mode) !!(mode & PR_RDONLY || mode & PR_RDWR) + +function isString(value) typeof(value) === "string" +function isFunction(value) typeof(value) === "function" + +function toArray(enumerator) { + let value = []; + while(enumerator.hasMoreElements()) + value.push(enumerator.getNext()) + return value +} + +function getFileName(file) file.QueryInterface(Ci.nsIFile).leafName + + +function remove(path, recursive) { + let fd = new nsILocalFile(path) + if (fd.exists()) { + fd.remove(recursive || false); + } + else { + throw FSError("remove", "ENOENT", 34, path); + } +} + +/** + * Utility function to convert either an octal number or string + * into an octal number + * 0777 => 0777 + * "0644" => 0644 + */ +function Mode(mode, fallback) { + return isString(mode) ? parseInt(mode, 8) : mode || fallback; +} +function Flags(flag) { + return !isString(flag) ? flag : + FLAGS[flag] || Error("Unknown file open flag: " + flag); +} + + +function FSError(op, code, errno, path, file, line) { + let error = Error(code + ", " + op + " " + path, file, line); + error.code = code; + error.path = path; + error.errno = errno; + return error; +} + +const ReadStream = Class({ + extends: InputStream, + initialize: function initialize(path, options) { + this.position = -1; + this.length = -1; + this.flags = "r"; + this.mode = FILE_PERMISSION; + this.bufferSize = 64 * 1024; + + options = options || {}; + + if ("flags" in options && options.flags) + this.flags = options.flags; + if ("bufferSize" in options && options.bufferSize) + this.bufferSize = options.bufferSize; + if ("length" in options && options.length) + this.length = options.length; + if ("position" in options && options.position !== undefined) + this.position = options.position; + + let { flags, mode, position, length } = this; + let fd = isString(path) ? openSync(path, flags, mode) : path; + this.fd = fd; + + let input = nsIFileInputStream(fd); + // Setting a stream position, unless it"s `-1` which means current position. + if (position >= 0) + input.QueryInterface(Ci.nsISeekableStream).seek(NS_SEEK_SET, position); + // We use `nsIStreamTransportService` service to transform blocking + // file input stream into a fully asynchronous stream that can be written + // without blocking the main thread. + let transport = createInputTransport(input, position, length, false); + // Open an input stream on a transport. We don"t pass flags to guarantee + // non-blocking stream semantics. Also we use defaults for segment size & + // count. + InputStream.prototype.initialize.call(this, { + asyncInputStream: transport.openInputStream(null, 0, 0) + }); + + // Close file descriptor on end and destroy the stream. + on(this, "end", _ => { + this.destroy(); + emit(this, "close"); + }); + + this.read(); + }, + destroy: function() { + closeSync(this.fd); + InputStream.prototype.destroy.call(this); + } +}); +exports.ReadStream = ReadStream; +exports.createReadStream = function createReadStream(path, options) { + return new ReadStream(path, options); +}; + +const WriteStream = Class({ + extends: OutputStream, + initialize: function initialize(path, options) { + this.drainable = true; + this.flags = "w"; + this.position = -1; + this.mode = FILE_PERMISSION; + + options = options || {}; + + if ("flags" in options && options.flags) + this.flags = options.flags; + if ("mode" in options && options.mode) + this.mode = options.mode; + if ("position" in options && options.position !== undefined) + this.position = options.position; + + let { position, flags, mode } = this; + // If pass was passed we create a file descriptor out of it. Otherwise + // we just use given file descriptor. + let fd = isString(path) ? openSync(path, flags, mode) : path; + this.fd = fd; + + let output = nsIFileOutputStream(fd); + // Setting a stream position, unless it"s `-1` which means current position. + if (position >= 0) + output.QueryInterface(Ci.nsISeekableStream).seek(NS_SEEK_SET, position); + // We use `nsIStreamTransportService` service to transform blocking + // file output stream into a fully asynchronous stream that can be written + // without blocking the main thread. + let transport = createOutputTransport(output, position, -1, false); + // Open an output stream on a transport. We don"t pass flags to guarantee + // non-blocking stream semantics. Also we use defaults for segment size & + // count. + OutputStream.prototype.initialize.call(this, { + asyncOutputStream: transport.openOutputStream(OPEN_UNBUFFERED, 0, 0), + output: output + }); + + // For write streams "finish" basically means close. + on(this, "finish", _ => { + this.destroy(); + emit(this, "close"); + }); + }, + destroy: function() { + OutputStream.prototype.destroy.call(this); + closeSync(this.fd); + } +}); +exports.WriteStream = WriteStream; +exports.createWriteStream = function createWriteStream(path, options) { + return new WriteStream(path, options); +}; + +const Stats = Class({ + initialize: function initialize(path) { + let file = new nsILocalFile(path); + if (!file.exists()) throw FSError("stat", "ENOENT", 34, path); + nsIFile(this, file); + }, + isDirectory: function() nsIFile(this).isDirectory(), + isFile: function() nsIFile(this).isFile(), + isSymbolicLink: function() nsIFile(this).isSymlink(), + get mode() nsIFile(this).permissions, + get size() nsIFile(this).fileSize, + get mtime() nsIFile(this).lastModifiedTime, + isBlockDevice: function() nsIFile(this).isSpecial(), + isCharacterDevice: function() nsIFile(this).isSpecial(), + isFIFO: function() nsIFile(this).isSpecial(), + isSocket: function() nsIFile(this).isSpecial(), + // non standard + get exists() nsIFile(this).exists(), + get hidden() nsIFile(this).isHidden(), + get writable() nsIFile(this).isWritable(), + get readable() nsIFile(this).isReadable() +}); +exports.Stats = Stats; + +const LStats = Class({ + extends: Stats, + get size() this.isSymbolicLink() ? nsIFile(this).fileSizeOfLink : + nsIFile(this).fileSize, + get mtime() this.isSymbolicLink() ? nsIFile(this).lastModifiedTimeOfLink : + nsIFile(this).lastModifiedTime, + // non standard + get permissions() this.isSymbolicLink() ? nsIFile(this).permissionsOfLink : + nsIFile(this).permissions +}); + +const FStat = Class({ + extends: Stats, + initialize: function initialize(fd) { + nsIFile(this, nsIFile(fd)); + } +}); + +function noop() {} +function Async(wrapped) { + return function (path, callback) { + let args = Array.slice(arguments); + callback = args.pop(); + // If node is not given a callback argument + // it just does not calls it. + if (typeof(callback) !== "function") { + args.push(callback); + callback = noop; + } + setTimeout(function() { + try { + var result = wrapped.apply(this, args); + if (result === undefined) callback(null); + else callback(null, result); + } catch (error) { + callback(error); + } + }, 0); + } +} + + +/** + * Synchronous rename(2) + */ +function renameSync(oldPath, newPath) { + let source = new nsILocalFile(oldPath); + let target = new nsILocalFile(newPath); + if (!source.exists()) throw FSError("rename", "ENOENT", 34, oldPath); + return source.moveTo(target.parent, target.leafName); +}; +exports.renameSync = renameSync; + +/** + * Asynchronous rename(2). No arguments other than a possible exception are + * given to the completion callback. + */ +let rename = Async(renameSync); +exports.rename = rename; + +/** + * Test whether or not the given path exists by checking with the file system. + */ +function existsSync(path) { + return new nsILocalFile(path).exists(); +} +exports.existsSync = existsSync; + +let exists = Async(existsSync); +exports.exists = exists; + +/** + * Synchronous ftruncate(2). + */ +function truncateSync(path, length) { + let fd = openSync(path, "w"); + ftruncateSync(fd, length); + closeSync(fd); +} +exports.truncateSync = truncateSync; + +/** + * Asynchronous ftruncate(2). No arguments other than a possible exception are + * given to the completion callback. + */ +function truncate(path, length, callback) { + open(path, "w", function(error, fd) { + if (error) return callback(error); + ftruncate(fd, length, function(error) { + if (error) { + closeSync(fd); + callback(error); + } + else { + close(fd, callback); + } + }); + }); +} +exports.truncate = truncate; + +function ftruncate(fd, length, callback) { + write(fd, new Buffer(length), 0, length, 0, function(error) { + callback(error); + }); +} +exports.ftruncate = ftruncate; + +function ftruncateSync(fd, length = 0) { + writeSync(fd, new Buffer(length), 0, length, 0); +} +exports.ftruncateSync = ftruncateSync; + +function chownSync(path, uid, gid) { + throw Error("Not implemented yet!!"); +} +exports.chownSync = chownSync; + +let chown = Async(chownSync); +exports.chown = chown; + +function lchownSync(path, uid, gid) { + throw Error("Not implemented yet!!"); +} +exports.lchownSync = chownSync; + +let lchown = Async(lchown); +exports.lchown = lchown; + +/** + * Synchronous chmod(2). + */ +function chmodSync (path, mode) { + let file; + try { + file = new nsILocalFile(path); + } catch(e) { + throw FSError("chmod", "ENOENT", 34, path); + } + + file.permissions = Mode(mode); +} +exports.chmodSync = chmodSync; +/** + * Asynchronous chmod(2). No arguments other than a possible exception are + * given to the completion callback. + */ +let chmod = Async(chmodSync); +exports.chmod = chmod; + +/** + * Synchronous chmod(2). + */ +function fchmodSync(fd, mode) { + throw Error("Not implemented yet!!"); +}; +exports.fchmodSync = fchmodSync; +/** + * Asynchronous chmod(2). No arguments other than a possible exception are + * given to the completion callback. + */ +let fchmod = Async(fchmodSync); +exports.fchmod = fchmod; + + +/** + * Synchronous stat(2). Returns an instance of `fs.Stats` + */ +function statSync(path) { + return new Stats(path); +}; +exports.statSync = statSync; + +/** + * Asynchronous stat(2). The callback gets two arguments (err, stats) where + * stats is a `fs.Stats` object. It looks like this: + */ +let stat = Async(statSync); +exports.stat = stat; + +/** + * Synchronous lstat(2). Returns an instance of `fs.Stats`. + */ +function lstatSync(path) { + return new LStats(path); +}; +exports.lstatSync = lstatSync; + +/** + * Asynchronous lstat(2). The callback gets two arguments (err, stats) where + * stats is a fs.Stats object. lstat() is identical to stat(), except that if + * path is a symbolic link, then the link itself is stat-ed, not the file that + * it refers to. + */ +let lstat = Async(lstatSync); +exports.lstat = lstat; + +/** + * Synchronous fstat(2). Returns an instance of `fs.Stats`. + */ +function fstatSync(fd) { + return new FStat(fd); +}; +exports.fstatSync = fstatSync; + +/** + * Asynchronous fstat(2). The callback gets two arguments (err, stats) where + * stats is a fs.Stats object. + */ +let fstat = Async(fstatSync); +exports.fstat = fstat; + +/** + * Synchronous link(2). + */ +function linkSync(source, target) { + throw Error("Not implemented yet!!"); +}; +exports.linkSync = linkSync; + +/** + * Asynchronous link(2). No arguments other than a possible exception are given + * to the completion callback. + */ +let link = Async(linkSync); +exports.link = link; + +/** + * Synchronous symlink(2). + */ +function symlinkSync(source, target) { + throw Error("Not implemented yet!!"); +}; +exports.symlinkSync = symlinkSync; + +/** + * Asynchronous symlink(2). No arguments other than a possible exception are + * given to the completion callback. + */ +let symlink = Async(symlinkSync); +exports.symlink = symlink; + +/** + * Synchronous readlink(2). Returns the resolved path. + */ +function readlinkSync(path) { + return new nsILocalFile(path).target; +}; +exports.readlinkSync = readlinkSync; + +/** + * Asynchronous readlink(2). The callback gets two arguments + * `(error, resolvedPath)`. + */ +let readlink = Async(readlinkSync); +exports.readlink = readlink; + +/** + * Synchronous realpath(2). Returns the resolved path. + */ +function realpathSync(path) { + return new nsILocalFile(path).path; +}; +exports.realpathSync = realpathSync; + +/** + * Asynchronous realpath(2). The callback gets two arguments + * `(err, resolvedPath)`. + */ +let realpath = Async(realpathSync); +exports.realpath = realpath; + +/** + * Synchronous unlink(2). + */ +let unlinkSync = remove; +exports.unlinkSync = unlinkSync; + +/** + * Asynchronous unlink(2). No arguments other than a possible exception are + * given to the completion callback. + */ +let unlink = Async(remove); +exports.unlink = unlink; + +/** + * Synchronous rmdir(2). + */ +let rmdirSync = remove; +exports.rmdirSync = rmdirSync; + +/** + * Asynchronous rmdir(2). No arguments other than a possible exception are + * given to the completion callback. + */ +let rmdir = Async(rmdirSync); +exports.rmdir = rmdir; + +/** + * Synchronous mkdir(2). + */ +function mkdirSync(path, mode) { + try { + return nsILocalFile(path).create(DIRECTORY_TYPE, Mode(mode)); + } catch (error) { + // Adjust exception thorw to match ones thrown by node. + if (error.name === "NS_ERROR_FILE_ALREADY_EXISTS") { + let { fileName, lineNumber } = error; + error = FSError("mkdir", "EEXIST", 47, path, fileName, lineNumber); + } + throw error; + } +}; +exports.mkdirSync = mkdirSync; + +/** + * Asynchronous mkdir(2). No arguments other than a possible exception are + * given to the completion callback. + */ +let mkdir = Async(mkdirSync); +exports.mkdir = mkdir; + +/** + * Synchronous readdir(3). Returns an array of filenames excluding `"."` and + * `".."`. + */ +function readdirSync(path) { + try { + return toArray(new nsILocalFile(path).directoryEntries).map(getFileName); + } + catch (error) { + // Adjust exception thorw to match ones thrown by node. + if (error.name === "NS_ERROR_FILE_TARGET_DOES_NOT_EXIST" || + error.name === "NS_ERROR_FILE_NOT_FOUND") + { + let { fileName, lineNumber } = error; + error = FSError("readdir", "ENOENT", 34, path, fileName, lineNumber); + } + throw error; + } +}; +exports.readdirSync = readdirSync; + +/** + * Asynchronous readdir(3). Reads the contents of a directory. The callback + * gets two arguments `(error, files)` where `files` is an array of the names + * of the files in the directory excluding `"."` and `".."`. + */ +let readdir = Async(readdirSync); +exports.readdir = readdir; + +/** + * Synchronous close(2). + */ + function closeSync(fd) { + let input = nsIFileInputStream(fd); + let output = nsIFileOutputStream(fd); + + // Closing input stream and removing reference. + if (input) input.close(); + // Closing output stream and removing reference. + if (output) output.close(); + + nsIFile(fd, null); + nsIFileInputStream(fd, null); + nsIFileOutputStream(fd, null); + nsIBinaryInputStream(fd, null); + nsIBinaryOutputStream(fd, null); +}; +exports.closeSync = closeSync; +/** + * Asynchronous close(2). No arguments other than a possible exception are + * given to the completion callback. + */ +let close = Async(closeSync); +exports.close = close; + +/** + * Synchronous open(2). + */ +function openSync(path, flags, mode) { + let [ fd, flags, mode, file ] = + [ { path: path }, Flags(flags), Mode(mode), nsILocalFile(path) ]; + + nsIFile(fd, file); + + // If trying to open file for just read that does not exists + // need to throw exception as node does. + if (!file.exists() && !isWritable(flags)) + throw FSError("open", "ENOENT", 34, path); + + // If we want to open file in read mode we initialize input stream. + if (isReadable(flags)) { + let input = FileInputStream(file, flags, mode, DEFER_OPEN); + nsIFileInputStream(fd, input); + } + + // If we want to open file in write mode we initialize output stream for it. + if (isWritable(flags)) { + let output = FileOutputStream(file, flags, mode, DEFER_OPEN); + nsIFileOutputStream(fd, output); + } + + return fd; +} +exports.openSync = openSync; +/** + * Asynchronous file open. See open(2). Flags can be + * `"r", "r+", "w", "w+", "a"`, or `"a+"`. mode defaults to `0666`. + * The callback gets two arguments `(error, fd). + */ +let open = Async(openSync); +exports.open = open; + +/** + * Synchronous version of buffer-based fs.write(). Returns the number of bytes + * written. + */ +function writeSync(fd, buffer, offset, length, position) { + if (length + offset > buffer.length) { + throw Error("Length is extends beyond buffer"); + } + else if (length + offset !== buffer.length) { + buffer = buffer.slice(offset, offset + length); + } + let writeStream = new WriteStream(fd, { position: position, + length: length }); + + let output = BinaryOutputStream(nsIFileOutputStream(fd)); + nsIBinaryOutputStream(fd, output); + // We write content as a byte array as this will avoid any transcoding + // if content was a buffer. + output.writeByteArray(buffer.valueOf(), buffer.length); + output.flush(); +}; +exports.writeSync = writeSync; + +/** + * Write buffer to the file specified by fd. + * + * `offset` and `length` determine the part of the buffer to be written. + * + * `position` refers to the offset from the beginning of the file where this + * data should be written. If `position` is `null`, the data will be written + * at the current position. See pwrite(2). + * + * The callback will be given three arguments `(error, written, buffer)` where + * written specifies how many bytes were written into buffer. + * + * Note that it is unsafe to use `fs.write` multiple times on the same file + * without waiting for the callback. + */ +function write(fd, buffer, offset, length, position, callback) { + if (!Buffer.isBuffer(buffer)) { + // (fd, data, position, encoding, callback) + let encoding = null; + [ position, encoding, callback ] = Array.slice(arguments, 1); + buffer = new Buffer(String(buffer), encoding); + offset = 0; + } else if (length + offset > buffer.length) { + throw Error("Length is extends beyond buffer"); + } else if (length + offset !== buffer.length) { + buffer = buffer.slice(offset, offset + length); + } + + let writeStream = new WriteStream(fd, { position: position, + length: length }); + writeStream.on("error", callback); + writeStream.write(buffer, function onEnd() { + writeStream.destroy(); + if (callback) + callback(null, buffer.length, buffer); + }); +}; +exports.write = write; + +/** + * Synchronous version of string-based fs.read. Returns the number of + * bytes read. + */ +function readSync(fd, buffer, offset, length, position) { + let input = nsIFileInputStream(fd); + // Setting a stream position, unless it"s `-1` which means current position. + if (position >= 0) + input.QueryInterface(Ci.nsISeekableStream).seek(NS_SEEK_SET, position); + // We use `nsIStreamTransportService` service to transform blocking + // file input stream into a fully asynchronous stream that can be written + // without blocking the main thread. + let binaryInputStream = BinaryInputStream(input); + let count = length === ALL ? binaryInputStream.available() : length; + if (offset === 0) binaryInputStream.readArrayBuffer(count, buffer.buffer); + else { + let chunk = new Buffer(count); + binaryInputStream.readArrayBuffer(count, chunk.buffer); + chunk.copy(buffer, offset); + } + + return buffer.slice(offset, offset + count); +}; +exports.readSync = readSync; + +/** + * Read data from the file specified by `fd`. + * + * `buffer` is the buffer that the data will be written to. + * `offset` is offset within the buffer where writing will start. + * + * `length` is an integer specifying the number of bytes to read. + * + * `position` is an integer specifying where to begin reading from in the file. + * If `position` is `null`, data will be read from the current file position. + * + * The callback is given the three arguments, `(error, bytesRead, buffer)`. + */ +function read(fd, buffer, offset, length, position, callback) { + let bytesRead = 0; + let readStream = new ReadStream(fd, { position: position, length: length }); + readStream.on("data", function onData(data) { + data.copy(buffer, offset + bytesRead); + bytesRead += data.length; + }); + readStream.on("end", function onEnd() { + callback(null, bytesRead, buffer); + readStream.destroy(); + }); +}; +exports.read = read; + +/** + * Asynchronously reads the entire contents of a file. + * The callback is passed two arguments `(error, data)`, where data is the + * contents of the file. + */ +function readFile(path, encoding, callback) { + if (isFunction(encoding)) { + callback = encoding + encoding = null + } + + let buffer = null; + try { + let readStream = new ReadStream(path); + readStream.on("data", function(data) { + if (!buffer) buffer = data; + else buffer = Buffer.concat([buffer, data], 2); + }); + readStream.on("error", function onError(error) { + callback(error); + }); + readStream.on("end", function onEnd() { + // Note: Need to destroy before invoking a callback + // so that file descriptor is released. + readStream.destroy(); + callback(null, buffer); + }); + } catch (error) { + setTimeout(callback, 0, error); + } +}; +exports.readFile = readFile; + +/** + * Synchronous version of `fs.readFile`. Returns the contents of the path. + * If encoding is specified then this function returns a string. + * Otherwise it returns a buffer. + */ +function readFileSync(path, encoding) { + let fd = openSync(path, "r"); + let size = fstatSync(fd).size; + let buffer = new Buffer(size); + try { + readSync(fd, buffer, 0, ALL, 0); + } + finally { + closeSync(fd); + } + return buffer; +}; +exports.readFileSync = readFileSync; + +/** + * Asynchronously writes data to a file, replacing the file if it already + * exists. data can be a string or a buffer. + */ +function writeFile(path, content, encoding, callback) { + if (!isString(path)) + throw new TypeError('path must be a string'); + + try { + if (isFunction(encoding)) { + callback = encoding + encoding = null + } + if (isString(content)) + content = new Buffer(content, encoding); + + let writeStream = new WriteStream(path); + let error = null; + + writeStream.end(content, function() { + writeStream.destroy(); + callback(error); + }); + + writeStream.on("error", function onError(reason) { + error = reason; + writeStream.destroy(); + }); + } catch (error) { + callback(error); + } +}; +exports.writeFile = writeFile; + +/** + * The synchronous version of `fs.writeFile`. + */ +function writeFileSync(filename, data, encoding) { + throw Error("Not implemented"); +}; +exports.writeFileSync = writeFileSync; + + +function utimesSync(path, atime, mtime) { + throw Error("Not implemented"); +} +exports.utimesSync = utimesSync; + +let utimes = Async(utimesSync); +exports.utimes = utimes; + +function futimesSync(fd, atime, mtime, callback) { + throw Error("Not implemented"); +} +exports.futimesSync = futimesSync; + +let futimes = Async(futimesSync); +exports.futimes = futimes; + +function fsyncSync(fd, atime, mtime, callback) { + throw Error("Not implemented"); +} +exports.fsyncSync = fsyncSync; + +let fsync = Async(fsyncSync); +exports.fsync = fsync; + + +/** + * Watch for changes on filename. The callback listener will be called each + * time the file is accessed. + * + * The second argument is optional. The options if provided should be an object + * containing two members a boolean, persistent, and interval, a polling value + * in milliseconds. The default is { persistent: true, interval: 0 }. + */ +function watchFile(path, options, listener) { + throw Error("Not implemented"); +}; +exports.watchFile = watchFile; + + +function unwatchFile(path, listener) { + throw Error("Not implemented"); +} +exports.unwatchFile = unwatchFile; + +function watch(path, options, listener) { + throw Error("Not implemented"); +} +exports.watch = watch; diff --git a/addon-sdk-1.16/lib/sdk/io/stream.js b/addon-sdk-1.16/lib/sdk/io/stream.js new file mode 100644 index 00000000..92bfc212 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/io/stream.js @@ -0,0 +1,436 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +module.metadata = { + "stability": "experimental" +}; + +const { CC, Cc, Ci, Cu, Cr, components } = require("chrome"); +const { EventTarget } = require("../event/target"); +const { emit } = require("../event/core"); +const { Buffer } = require("./buffer"); +const { Class } = require("../core/heritage"); +const { setTimeout } = require("../timers"); + + +const MultiplexInputStream = CC("@mozilla.org/io/multiplex-input-stream;1", + "nsIMultiplexInputStream"); +const AsyncStreamCopier = CC("@mozilla.org/network/async-stream-copier;1", + "nsIAsyncStreamCopier", "init"); +const StringInputStream = CC("@mozilla.org/io/string-input-stream;1", + "nsIStringInputStream"); +const ArrayBufferInputStream = CC("@mozilla.org/io/arraybuffer-input-stream;1", + "nsIArrayBufferInputStream"); + +const BinaryInputStream = CC("@mozilla.org/binaryinputstream;1", + "nsIBinaryInputStream", "setInputStream"); +const InputStreamPump = CC("@mozilla.org/network/input-stream-pump;1", + "nsIInputStreamPump", "init"); + +const threadManager = Cc["@mozilla.org/thread-manager;1"]. + getService(Ci.nsIThreadManager); + +const eventTarget = Cc["@mozilla.org/network/socket-transport-service;1"]. + getService(Ci.nsIEventTarget); + +let isFunction = value => typeof(value) === "function" + +function accessor() { + let map = new WeakMap(); + return function(target, value) { + if (value) + map.set(target, value); + return map.get(target); + } +} + +const Stream = Class({ + extends: EventTarget, + initialize: function() { + this.readable = false; + this.writable = false; + this.encoding = null; + }, + setEncoding: function setEncoding(encoding) { + this.encoding = String(encoding).toUpperCase(); + }, + pipe: function pipe(target, options) { + let source = this; + function onData(chunk) { + if (target.writable) { + if (false === target.write(chunk)) + source.pause(); + } + } + function onDrain() { + if (source.readable) + source.resume(); + } + function onEnd() { + target.end(); + } + function onPause() { + source.pause(); + } + function onResume() { + if (source.readable) + source.resume(); + } + + function cleanup() { + source.removeListener("data", onData); + target.removeListener("drain", onDrain); + source.removeListener("end", onEnd); + + target.removeListener("pause", onPause); + target.removeListener("resume", onResume); + + source.removeListener("end", cleanup); + source.removeListener("close", cleanup); + + target.removeListener("end", cleanup); + target.removeListener("close", cleanup); + } + + if (!options || options.end !== false) + target.on("end", onEnd); + + source.on("data", onData); + target.on("drain", onDrain); + target.on("resume", onResume); + target.on("pause", onPause); + + source.on("end", cleanup); + source.on("close", cleanup); + + target.on("end", cleanup); + target.on("close", cleanup); + + emit(target, "pipe", source); + }, + pause: function pause() { + emit(this, "pause"); + }, + resume: function resume() { + emit(this, "resume"); + }, + destroySoon: function destroySoon() { + this.destroy(); + } +}); +exports.Stream = Stream; + + +let nsIStreamListener = accessor(); +let nsIInputStreamPump = accessor(); +let nsIAsyncInputStream = accessor(); +let nsIBinaryInputStream = accessor(); + +const StreamListener = Class({ + initialize: function(stream) { + this.stream = stream; + }, + + // Next three methods are part of `nsIStreamListener` interface and are + // invoked by `nsIInputStreamPump.asyncRead`. + onDataAvailable: function(request, context, input, offset, count) { + let stream = this.stream; + let buffer = new ArrayBuffer(count); + nsIBinaryInputStream(stream).readArrayBuffer(count, buffer); + emit(stream, "data", new Buffer(buffer)); + }, + + // Next two methods implement `nsIRequestObserver` interface and are invoked + // by `nsIInputStreamPump.asyncRead`. + onStartRequest: function() {}, + // Called to signify the end of an asynchronous request. We only care to + // discover errors. + onStopRequest: function(request, context, status) { + let stream = this.stream; + stream.readable = false; + if (!components.isSuccessCode(status)) + emit(stream, "error", status); + else + emit(stream, "end"); + } +}); + + +const InputStream = Class({ + extends: Stream, + readable: false, + paused: false, + initialize: function initialize(options) { + let { asyncInputStream } = options; + + this.readable = true; + + let binaryInputStream = new BinaryInputStream(asyncInputStream); + let inputStreamPump = new InputStreamPump(asyncInputStream, + -1, -1, 0, 0, false); + let streamListener = new StreamListener(this); + + nsIAsyncInputStream(this, asyncInputStream); + nsIInputStreamPump(this, inputStreamPump); + nsIBinaryInputStream(this, binaryInputStream); + nsIStreamListener(this, streamListener); + + this.asyncInputStream = asyncInputStream; + this.inputStreamPump = inputStreamPump; + this.binaryInputStream = binaryInputStream; + }, + get status() nsIInputStreamPump(this).status, + read: function() { + nsIInputStreamPump(this).asyncRead(nsIStreamListener(this), null); + }, + pause: function pause() { + this.paused = true; + nsIInputStreamPump(this).suspend(); + emit(this, "paused"); + }, + resume: function resume() { + this.paused = false; + nsIInputStreamPump(this).resume(); + emit(this, "resume"); + }, + close: function close() { + this.readable = false; + nsIInputStreamPump(this).cancel(Cr.NS_OK); + nsIBinaryInputStream(this).close(); + nsIAsyncInputStream(this).close(); + }, + destroy: function destroy() { + this.close(); + + nsIInputStreamPump(this); + nsIAsyncInputStream(this); + nsIBinaryInputStream(this); + nsIStreamListener(this); + } +}); +exports.InputStream = InputStream; + + + +let nsIRequestObserver = accessor(); +let nsIAsyncOutputStream = accessor(); +let nsIAsyncStreamCopier = accessor(); +let nsIMultiplexInputStream = accessor(); + +const RequestObserver = Class({ + initialize: function(stream) { + this.stream = stream; + }, + // Method is part of `nsIRequestObserver` interface that is + // invoked by `nsIAsyncStreamCopier.asyncCopy`. + onStartRequest: function() {}, + // Method is part of `nsIRequestObserver` interface that is + // invoked by `nsIAsyncStreamCopier.asyncCopy`. + onStopRequest: function(request, context, status) { + let stream = this.stream; + stream.drained = true; + + // Remove copied chunk. + let multiplexInputStream = nsIMultiplexInputStream(stream); + multiplexInputStream.removeStream(0); + + // If there was an error report. + if (!components.isSuccessCode(status)) + emit(stream, "error", status); + + // If there more chunks in queue then flush them. + else if (multiplexInputStream.count) + stream.flush(); + + // If stream is still writable notify that queue has drained. + else if (stream.writable) + emit(stream, "drain"); + + // If stream is no longer writable close it. + else { + nsIAsyncStreamCopier(stream).cancel(Cr.NS_OK); + nsIMultiplexInputStream(stream).close(); + nsIAsyncOutputStream(stream).close(); + nsIAsyncOutputStream(stream).flush(); + } + } +}); + +const OutputStreamCallback = Class({ + initialize: function(stream) { + this.stream = stream; + }, + // Method is part of `nsIOutputStreamCallback` interface that + // is invoked by `nsIAsyncOutputStream.asyncWait`. It is registered + // with `WAIT_CLOSURE_ONLY` flag that overrides the default behavior, + // causing the `onOutputStreamReady` notification to be suppressed until + // the stream becomes closed. + onOutputStreamReady: function(nsIAsyncOutputStream) { + emit(this.stream, "finish"); + } +}); + +const OutputStream = Class({ + extends: Stream, + writable: false, + drained: true, + get bufferSize() { + let multiplexInputStream = nsIMultiplexInputStream(this); + return multiplexInputStream && multiplexInputStream.available(); + }, + initialize: function initialize(options) { + let { asyncOutputStream, output } = options; + this.writable = true; + + // Ensure that `nsIAsyncOutputStream` was provided. + asyncOutputStream.QueryInterface(Ci.nsIAsyncOutputStream); + + // Create a `nsIMultiplexInputStream` and `nsIAsyncStreamCopier`. Former + // is used to queue written data chunks that `asyncStreamCopier` will + // asynchronously drain into `asyncOutputStream`. + let multiplexInputStream = MultiplexInputStream(); + let asyncStreamCopier = AsyncStreamCopier(multiplexInputStream, + output || asyncOutputStream, + eventTarget, + // nsIMultiplexInputStream + // implemnts .readSegments() + true, + // nsIOutputStream may or + // may not implemnet + // .writeSegments(). + false, + // Use default buffer size. + null, + // Should not close an input. + false, + // Should not close an output. + false); + + // Create `requestObserver` implementing `nsIRequestObserver` interface + // in the constructor that's gonna be reused across several flushes. + let requestObserver = RequestObserver(this); + + + // Create observer that implements `nsIOutputStreamCallback` and register + // using `WAIT_CLOSURE_ONLY` flag. That way it will be notfied once + // `nsIAsyncOutputStream` is closed. + asyncOutputStream.asyncWait(OutputStreamCallback(this), + asyncOutputStream.WAIT_CLOSURE_ONLY, + 0, + threadManager.currentThread); + + nsIRequestObserver(this, requestObserver); + nsIAsyncOutputStream(this, asyncOutputStream); + nsIMultiplexInputStream(this, multiplexInputStream); + nsIAsyncStreamCopier(this, asyncStreamCopier); + + this.asyncOutputStream = asyncOutputStream; + this.multiplexInputStream = multiplexInputStream; + this.asyncStreamCopier = asyncStreamCopier; + }, + write: function write(content, encoding, callback) { + if (isFunction(encoding)) { + callback = encoding; + encoding = callback; + } + + // If stream is not writable we throw an error. + if (!this.writable) throw Error("stream is not writable"); + + let chunk = null; + + // If content is not a buffer then we create one out of it. + if (Buffer.isBuffer(content)) { + chunk = new ArrayBufferInputStream(); + chunk.setData(content.buffer, 0, content.length); + } + else { + chunk = new StringInputStream(); + chunk.setData(content, content.length); + } + + if (callback) + this.once("drain", callback); + + // Queue up chunk to be copied to output sync. + nsIMultiplexInputStream(this).appendStream(chunk); + this.flush(); + + return this.drained; + }, + flush: function() { + if (this.drained) { + this.drained = false; + nsIAsyncStreamCopier(this).asyncCopy(nsIRequestObserver(this), null); + } + }, + end: function end(content, encoding, callback) { + if (isFunction(content)) { + callback = content + content = callback + } + if (isFunction(encoding)) { + callback = encoding + encoding = callback + } + + // Setting a listener to "finish" event if passed. + if (isFunction(callback)) + this.once("finish", callback); + + + if (content) + this.write(content, encoding); + this.writable = false; + + // Close `asyncOutputStream` only if output has drained. If it's + // not drained than `asyncStreamCopier` is busy writing, so let + // it finish. Note that since `this.writable` is false copier will + // close `asyncOutputStream` once output drains. + if (this.drained) + nsIAsyncOutputStream(this).close(); + }, + destroy: function destroy() { + nsIAsyncOutputStream(this).close(); + nsIAsyncOutputStream(this); + nsIMultiplexInputStream(this); + nsIAsyncStreamCopier(this); + nsIRequestObserver(this); + } +}); +exports.OutputStream = OutputStream; + +const DuplexStream = Class({ + extends: Stream, + implements: [InputStream, OutputStream], + allowHalfOpen: true, + initialize: function initialize(options) { + options = options || {}; + let { readable, writable, allowHalfOpen } = options; + + InputStream.prototype.initialize.call(this, options); + OutputStream.prototype.initialize.call(this, options); + + if (readable === false) + this.readable = false; + + if (writable === false) + this.writable = false; + + if (allowHalfOpen === false) + this.allowHalfOpen = false; + + // If in a half open state and it's disabled enforce end. + this.once("end", () => { + if (!this.allowHalfOpen && (!this.readable || !this.writable)) + this.end(); + }); + }, + destroy: function destroy(error) { + InputStream.prototype.destroy.call(this); + OutputStream.prototype.destroy.call(this); + } +}); +exports.DuplexStream = DuplexStream; diff --git a/addon-sdk-1.16/lib/sdk/io/text-streams.js b/addon-sdk-1.16/lib/sdk/io/text-streams.js new file mode 100644 index 00000000..7a385cf0 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/io/text-streams.js @@ -0,0 +1,242 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "experimental" +}; + +const {Cc,Ci,Cu,components} = require("chrome"); +var NetUtil = {}; +Cu.import("resource://gre/modules/NetUtil.jsm", NetUtil); +NetUtil = NetUtil.NetUtil; + +// NetUtil.asyncCopy() uses this buffer length, and since we call it, for best +// performance we use it, too. +const BUFFER_BYTE_LEN = 0x8000; +const PR_UINT32_MAX = 0xffffffff; +const DEFAULT_CHARSET = "UTF-8"; + +exports.TextReader = TextReader; +exports.TextWriter = TextWriter; + +/** + * An input stream that reads text from a backing stream using a given text + * encoding. + * + * @param inputStream + * The stream is backed by this nsIInputStream. It must already be + * opened. + * @param charset + * Text in inputStream is expected to be in this character encoding. If + * not given, "UTF-8" is assumed. See nsICharsetConverterManager.idl for + * documentation on how to determine other valid values for this. + */ +function TextReader(inputStream, charset) { + const self = this; + charset = checkCharset(charset); + + let stream = Cc["@mozilla.org/intl/converter-input-stream;1"]. + createInstance(Ci.nsIConverterInputStream); + stream.init(inputStream, charset, BUFFER_BYTE_LEN, + Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER); + + let manager = new StreamManager(this, stream); + + /** + * Reads a string from the stream. If the stream is closed, an exception is + * thrown. + * + * @param numChars + * The number of characters to read. If not given, the remainder of + * the stream is read. + * @return The string read. If the stream is already at EOS, returns the + * empty string. + */ + this.read = function TextReader_read(numChars) { + manager.ensureOpened(); + + let readAll = false; + if (typeof(numChars) === "number") + numChars = Math.max(numChars, 0); + else + readAll = true; + + let str = ""; + let totalRead = 0; + let chunkRead = 1; + + // Read in numChars or until EOS, whichever comes first. Note that the + // units here are characters, not bytes. + while (true) { + let chunk = {}; + let toRead = readAll ? + PR_UINT32_MAX : + Math.min(numChars - totalRead, PR_UINT32_MAX); + if (toRead <= 0 || chunkRead <= 0) + break; + + // The converter stream reads in at most BUFFER_BYTE_LEN bytes in a call + // to readString, enough to fill its byte buffer. chunkRead will be the + // number of characters encoded by the bytes in that buffer. + chunkRead = stream.readString(toRead, chunk); + str += chunk.value; + totalRead += chunkRead; + } + + return str; + }; +} + +/** + * A buffered output stream that writes text to a backing stream using a given + * text encoding. + * + * @param outputStream + * The stream is backed by this nsIOutputStream. It must already be + * opened. + * @param charset + * Text will be written to outputStream using this character encoding. + * If not given, "UTF-8" is assumed. See nsICharsetConverterManager.idl + * for documentation on how to determine other valid values for this. + */ +function TextWriter(outputStream, charset) { + const self = this; + charset = checkCharset(charset); + + let stream = outputStream; + + // Buffer outputStream if it's not already. + let ioUtils = Cc["@mozilla.org/io-util;1"].getService(Ci.nsIIOUtil); + if (!ioUtils.outputStreamIsBuffered(outputStream)) { + stream = Cc["@mozilla.org/network/buffered-output-stream;1"]. + createInstance(Ci.nsIBufferedOutputStream); + stream.init(outputStream, BUFFER_BYTE_LEN); + } + + // I'd like to use nsIConverterOutputStream. But NetUtil.asyncCopy(), which + // we use below in writeAsync(), naturally expects its sink to be an instance + // of nsIOutputStream, which nsIConverterOutputStream's only implementation is + // not. So we use uconv and manually convert all strings before writing to + // outputStream. + let uconv = Cc["@mozilla.org/intl/scriptableunicodeconverter"]. + createInstance(Ci.nsIScriptableUnicodeConverter); + uconv.charset = charset; + + let manager = new StreamManager(this, stream); + + /** + * Flushes the backing stream's buffer. + */ + this.flush = function TextWriter_flush() { + manager.ensureOpened(); + stream.flush(); + }; + + /** + * Writes a string to the stream. If the stream is closed, an exception is + * thrown. + * + * @param str + * The string to write. + */ + this.write = function TextWriter_write(str) { + manager.ensureOpened(); + let istream = uconv.convertToInputStream(str); + let len = istream.available(); + while (len > 0) { + stream.writeFrom(istream, len); + len = istream.available(); + } + istream.close(); + }; + + /** + * Writes a string on a background thread. After the write completes, the + * backing stream's buffer is flushed, and both the stream and the backing + * stream are closed, also on the background thread. If the stream is already + * closed, an exception is thrown immediately. + * + * @param str + * The string to write. + * @param callback + * An optional function. If given, it's called as callback(error) when + * the write completes. error is an Error object or undefined if there + * was no error. Inside callback, |this| is the stream object. + */ + this.writeAsync = function TextWriter_writeAsync(str, callback) { + manager.ensureOpened(); + let istream = uconv.convertToInputStream(str); + NetUtil.asyncCopy(istream, stream, function (result) { + let err = components.isSuccessCode(result) ? undefined : + new Error("An error occured while writing to the stream: " + result); + if (err) + console.error(err); + + // asyncCopy() closes its output (and input) stream. + manager.opened = false; + + if (typeof(callback) === "function") { + try { + callback.call(self, err); + } + catch (exc) { + console.exception(exc); + } + } + }); + }; +} + +// This manages the lifetime of stream, a TextReader or TextWriter. It defines +// closed and close() on stream and registers an unload listener that closes +// rawStream if it's still opened. It also provides ensureOpened(), which +// throws an exception if the stream is closed. +function StreamManager(stream, rawStream) { + const self = this; + this.rawStream = rawStream; + this.opened = true; + + /** + * True iff the stream is closed. + */ + stream.__defineGetter__("closed", function stream_closed() { + return !self.opened; + }); + + /** + * Closes both the stream and its backing stream. If the stream is already + * closed, an exception is thrown. For TextWriters, this first flushes the + * backing stream's buffer. + */ + stream.close = function stream_close() { + self.ensureOpened(); + self.unload(); + }; + + require("../system/unload").ensure(this); +} + +StreamManager.prototype = { + ensureOpened: function StreamManager_ensureOpened() { + if (!this.opened) + throw new Error("The stream is closed and cannot be used."); + }, + unload: function StreamManager_unload() { + // TextWriter.writeAsync() causes rawStream to close and therefore sets + // opened to false, so check that we're still opened. + if (this.opened) { + // Calling close() on both an nsIUnicharInputStream and + // nsIBufferedOutputStream closes their backing streams. It also forces + // nsIOutputStreams to flush first. + this.rawStream.close(); + this.opened = false; + } + } +}; + +function checkCharset(charset) { + return typeof(charset) === "string" ? charset : DEFAULT_CHARSET; +} diff --git a/addon-sdk-1.16/lib/sdk/keyboard/hotkeys.js b/addon-sdk-1.16/lib/sdk/keyboard/hotkeys.js new file mode 100644 index 00000000..696182d4 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/keyboard/hotkeys.js @@ -0,0 +1,110 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "unstable" +}; + +const { observer: keyboardObserver } = require("./observer"); +const { getKeyForCode, normalize, isFunctionKey, + MODIFIERS } = require("./utils"); + +/** + * Register a global `hotkey` that executes `listener` when the key combination + * in `hotkey` is pressed. If more then one `listener` is registered on the same + * key combination only last one will be executed. + * + * @param {string} hotkey + * Key combination in the format of 'modifier key'. + * + * Examples: + * + * "accel s" + * "meta shift i" + * "control alt d" + * + * Modifier keynames: + * + * - **shift**: The Shift key. + * - **alt**: The Alt key. On the Macintosh, this is the Option key. On + * Macintosh this can only be used in conjunction with another modifier, + * since `Alt+Letter` combinations are reserved for entering special + * characters in text. + * - **meta**: The Meta key. On the Macintosh, this is the Command key. + * - **control**: The Control key. + * - **accel**: The key used for keyboard shortcuts on the user's platform, + * which is Control on Windows and Linux, and Command on Mac. Usually, this + * would be the value you would use. + * + * @param {function} listener + * Function to execute when the `hotkey` is executed. + */ +exports.register = function register(hotkey, listener) { + hotkey = normalize(hotkey); + hotkeys[hotkey] = listener; +}; + +/** + * Unregister a global `hotkey`. If passed `listener` is not the one registered + * for the given `hotkey`, the call to this function will be ignored. + * + * @param {string} hotkey + * Key combination in the format of 'modifier key'. + * @param {function} listener + * Function that will be invoked when the `hotkey` is pressed. + */ +exports.unregister = function unregister(hotkey, listener) { + hotkey = normalize(hotkey); + if (hotkeys[hotkey] === listener) + delete hotkeys[hotkey]; +}; + +/** + * Map of hotkeys and associated functions. + */ +const hotkeys = exports.hotkeys = {}; + +keyboardObserver.on("keydown", function onKeypress(event, window) { + let key, modifiers = []; + let isChar = "isChar" in event && event.isChar; + let which = "which" in event ? event.which : null; + let keyCode = "keyCode" in event ? event.keyCode : null; + + if ("shiftKey" in event && event.shiftKey) + modifiers.push("shift"); + if ("altKey" in event && event.altKey) + modifiers.push("alt"); + if ("ctrlKey" in event && event.ctrlKey) + modifiers.push("control"); + if ("metaKey" in event && event.metaKey) + modifiers.push("meta"); + + // If it's not a printable character then we fall back to a human readable + // equivalent of one of the following constants. + // http://mxr.mozilla.org/mozilla-central/source/dom/interfaces/events/nsIDOMKeyEvent.idl + key = getKeyForCode(keyCode); + + // If only non-function (f1 - f24) key or only modifiers are pressed we don't + // have a valid combination so we return immediately (Also, sometimes + // `keyCode` may be one for the modifier which means we do not have a + // modifier). + if (!key || (!isFunctionKey(key) && !modifiers.length) || key in MODIFIERS) + return; + + let combination = normalize({ key: key, modifiers: modifiers }); + let hotkey = hotkeys[combination]; + + if (hotkey) { + try { + hotkey(); + } catch (exception) { + console.exception(exception); + } finally { + // Work around bug 582052 by preventing the (nonexistent) default action. + event.preventDefault(); + } + } +}); diff --git a/addon-sdk-1.16/lib/sdk/keyboard/observer.js b/addon-sdk-1.16/lib/sdk/keyboard/observer.js new file mode 100644 index 00000000..5535178a --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/keyboard/observer.js @@ -0,0 +1,57 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "unstable" +}; + +const { Trait } = require("../deprecated/light-traits"); +const { EventEmitterTrait: EventEmitter } = require("../deprecated/events"); +const { DOMEventAssembler } = require("../deprecated/events/assembler"); +const { browserWindowIterator } = require('../deprecated/window-utils'); +const { isBrowser } = require('../window/utils'); +const { observer: windowObserver } = require("../windows/observer"); + +// Event emitter objects used to register listeners and emit events on them +// when they occur. +const observer = Trait.compose(DOMEventAssembler, EventEmitter).create({ + /** + * Method is implemented by `EventEmitter` and is used just for emitting + * events on registered listeners. + */ + _emit: Trait.required, + /** + * Events that are supported and emitted by the module. + */ + supportedEventsTypes: [ "keydown", "keyup", "keypress" ], + /** + * Function handles all the supported events on all the windows that are + * observed. Method is used to proxy events to the listeners registered on + * this event emitter. + * @param {Event} event + * Keyboard event being emitted. + */ + handleEvent: function handleEvent(event) { + this._emit(event.type, event, event.target.ownerDocument.defaultView); + } +}); + +// Adding each opened window to a list of observed windows. +windowObserver.on("open", function onOpen(window) { + if (isBrowser(window)) + observer.observe(window); +}); +// Removing each closed window form the list of observed windows. +windowObserver.on("close", function onClose(window) { + if (isBrowser(window)) + observer.ignore(window); +}); + +// Making observer aware of already opened windows. +for each (let window in browserWindowIterator()) + observer.observe(window); + +exports.observer = observer; diff --git a/addon-sdk-1.16/lib/sdk/keyboard/utils.js b/addon-sdk-1.16/lib/sdk/keyboard/utils.js new file mode 100644 index 00000000..c4ff3dd7 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/keyboard/utils.js @@ -0,0 +1,189 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "unstable" +}; + +const { Cc, Ci } = require("chrome"); +const runtime = require("../system/runtime"); +const { isString } = require("../lang/type"); +const array = require("../util/array"); + + +const SWP = "{{SEPARATOR}}"; +const SEPARATOR = "-" +const INVALID_COMBINATION = "Hotkey key combination must contain one or more " + + "modifiers and only one key"; + +// Map of modifier key mappings. +const MODIFIERS = exports.MODIFIERS = { + 'accel': runtime.OS === "Darwin" ? 'meta' : 'control', + 'meta': 'meta', + 'control': 'control', + 'ctrl': 'control', + 'option': 'alt', + 'command': 'meta', + 'alt': 'alt', + 'shift': 'shift' +}; + +// Hash of key:code pairs for all the chars supported by `nsIDOMKeyEvent`. +// This is just a copy of the `nsIDOMKeyEvent` hash with normalized names. +// @See: http://mxr.mozilla.org/mozilla-central/source/dom/interfaces/events/nsIDOMKeyEvent.idl +const CODES = exports.CODES = new function Codes() { + let nsIDOMKeyEvent = Ci.nsIDOMKeyEvent; + // Names that will be substituted with a shorter analogs. + let aliases = { + 'subtract': '-', + 'add': '+', + 'equals': '=', + 'slash': '/', + 'backslash': '\\', + 'openbracket': '[', + 'closebracket': ']', + 'quote': '\'', + 'backquote': '`', + 'period': '.', + 'semicolon': ';', + 'comma': ',' + }; + + // Normalizing keys and copying values to `this` object. + Object.keys(nsIDOMKeyEvent).filter(function(key) { + // Filter out only key codes. + return key.indexOf('DOM_VK') === 0; + }).map(function(key) { + // Map to key:values + return [ key, nsIDOMKeyEvent[key] ]; + }).map(function([key, value]) { + return [ key.replace('DOM_VK_', '').replace('_', '').toLowerCase(), value ]; + }).forEach(function ([ key, value ]) { + this[aliases[key] || key] = value; + }, this); +}; + +// Inverted `CODES` hash of `code:key`. +const KEYS = exports.KEYS = new function Keys() { + Object.keys(CODES).forEach(function(key) { + this[CODES[key]] = key; + }, this) +} + +exports.getKeyForCode = function getKeyForCode(code) { + return (code in KEYS) && KEYS[code]; +}; +exports.getCodeForKey = function getCodeForKey(key) { + return (key in CODES) && CODES[key]; +}; + +/** + * Utility function that takes string or JSON that defines a `hotkey` and + * returns normalized string version of it. + * @param {JSON|String} hotkey + * @param {String} [separator=" "] + * Optional string that represents separator used to concatenate keys in the + * given `hotkey`. + * @returns {String} + * @examples + * + * require("keyboard/hotkeys").normalize("b Shift accel"); + * // 'control shift b' -> on windows & linux + * // 'meta shift b' -> on mac + * require("keyboard/hotkeys").normalize("alt-d-shift", "-"); + * // 'alt shift d' + */ +var normalize = exports.normalize = function normalize(hotkey, separator) { + if (!isString(hotkey)) + hotkey = toString(hotkey, separator); + return toString(toJSON(hotkey, separator), separator); +}; + +/* + * Utility function that splits a string of characters that defines a `hotkey` + * into modifier keys and the defining key. + * @param {String} hotkey + * @param {String} [separator=" "] + * Optional string that represents separator used to concatenate keys in the + * given `hotkey`. + * @returns {JSON} + * @examples + * + * require("keyboard/hotkeys").toJSON("accel shift b"); + * // { key: 'b', modifiers: [ 'control', 'shift' ] } -> on windows & linux + * // { key: 'b', modifiers: [ 'meta', 'shift' ] } -> on mac + * + * require("keyboard/hotkeys").normalize("alt-d-shift", "-"); + * // { key: 'd', modifiers: [ 'alt', 'shift' ] } + */ +var toJSON = exports.toJSON = function toJSON(hotkey, separator) { + separator = separator || SEPARATOR; + // Since default separator is `-`, combination may take form of `alt--`. To + // avoid misbehavior we replace `--` with `-{{SEPARATOR}}` where + // `{{SEPARATOR}}` can be swapped later. + hotkey = hotkey.toLowerCase().replace(separator + separator, separator + SWP); + + let value = {}; + let modifiers = []; + let keys = hotkey.split(separator); + keys.forEach(function(name) { + // If name is `SEPARATOR` than we swap it back. + if (name === SWP) + name = separator; + if (name in MODIFIERS) { + array.add(modifiers, MODIFIERS[name]); + } else { + if (!value.key) + value.key = name; + else + throw new TypeError(INVALID_COMBINATION); + } + }); + + if (!value.key) + throw new TypeError(INVALID_COMBINATION); + + value.modifiers = modifiers.sort(); + return value; +}; + +/** + * Utility function that takes object that defines a `hotkey` and returns + * string representation of it. + * + * _Please note that this function does not validates data neither it normalizes + * it, if you are unsure that data is well formed use `normalize` function + * instead. + * + * @param {JSON} hotkey + * @param {String} [separator=" "] + * Optional string that represents separator used to concatenate keys in the + * given `hotkey`. + * @returns {String} + * @examples + * + * require("keyboard/hotkeys").toString({ + * key: 'b', + * modifiers: [ 'control', 'shift' ] + * }, '+'); + * // 'control+shift+b + * + */ +var toString = exports.toString = function toString(hotkey, separator) { + let keys = hotkey.modifiers.slice(); + keys.push(hotkey.key); + return keys.join(separator || SEPARATOR); +}; + +/** + * Utility function takes `key` name and returns `true` if it's function key + * (F1, ..., F24) and `false` if it's not. + */ +var isFunctionKey = exports.isFunctionKey = function isFunctionKey(key) { + var $ + return key[0].toLowerCase() === 'f' && + ($ = parseInt(key.substr(1)), 0 < $ && $ < 25); +}; diff --git a/addon-sdk-1.16/lib/sdk/l10n.js b/addon-sdk-1.16/lib/sdk/l10n.js new file mode 100644 index 00000000..40f219cd --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/l10n.js @@ -0,0 +1,83 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +module.metadata = { + "stability": "stable" +}; + +const core = require("./l10n/core"); +const { getRulesForLocale } = require("./l10n/plural-rules"); + +// Retrieve the plural mapping function +let pluralMappingFunction = getRulesForLocale(core.language()) || + getRulesForLocale("en"); + +exports.get = function get(k) { + // For now, we only accept a "string" as first argument + // TODO: handle plural forms in gettext pattern + if (typeof k !== "string") + throw new Error("First argument of localization method should be a string"); + + // Get translation from big hashmap or default to hard coded string: + let localized = core.get(k) || k; + + // # Simplest usecase: + // // String hard coded in source code: + // _("Hello world") + // // Identifier of a key stored in properties file + // _("helloString") + if (arguments.length <= 1) + return localized; + + let args = arguments; + + if (typeof localized == "object" && "other" in localized) { + // # Plural form: + // // Strings hard coded in source code: + // _(["One download", "%d downloads"], 10); + // // Identifier of a key stored in properties file + // _("downloadNumber", 0); + let n = arguments[1]; + + // First handle simple universal forms that may not be mandatory + // for each language, (i.e. not different than 'other' form, + // but still usefull for better phrasing) + // For example 0 in english is the same form than 'other' + // but we accept 'zero' form if specified in localization file + if (n === 0 && "zero" in localized) + localized = localized["zero"]; + else if (n === 1 && "one" in localized) + localized = localized["one"]; + else if (n === 2 && "two" in localized) + localized = localized["two"]; + else { + let pluralForm = pluralMappingFunction(n); + if (pluralForm in localized) + localized = localized[pluralForm]; + else // Fallback in case of error: missing plural form + localized = localized["other"]; + } + + // Simulate a string with one placeholder: + args = [null, n]; + } + + // # String with placeholders: + // // Strings hard coded in source code: + // _("Hello %s", username) + // // Identifier of a key stored in properties file + // _("helloString", username) + // * We supports `%1s`, `%2s`, ... pattern in order to change arguments order + // in translation. + // * In case of plural form, we has `%d` instead of `%s`. + let offset = 1; + localized = localized.replace(/%(\d*)(s|d)/g, function (v, n) { + let rv = args[n != "" ? n : offset]; + offset++; + return rv; + }); + + return localized; +} diff --git a/addon-sdk-1.16/lib/sdk/l10n/core.js b/addon-sdk-1.16/lib/sdk/l10n/core.js new file mode 100644 index 00000000..50472e96 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/l10n/core.js @@ -0,0 +1,35 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Following pseudo module is set by `api-utils/addon/runner` and its load +// method needs to be called before loading `core` module. But it may have +// failed, so that this pseudo won't be available + +module.metadata = { + "stability": "unstable" +}; + + +let hash = {}, bestMatchingLocale = null; +try { + let data = require("@l10n/data"); + hash = data.hash; + bestMatchingLocale = data.bestMatchingLocale; +} +catch(e) {} + +// Returns the translation for a given key, if available. +exports.get = function get(k) { + return k in hash ? hash[k] : null; +} + +// Returns the full length locale code: ja-JP-mac, en-US or fr +exports.locale = function locale() { + return bestMatchingLocale; +} +// Returns the short locale code: ja, en, fr +exports.language = function language() { + return bestMatchingLocale ? bestMatchingLocale.split("-")[0].toLowerCase() + : null; +} diff --git a/addon-sdk-1.16/lib/sdk/l10n/html.js b/addon-sdk-1.16/lib/sdk/l10n/html.js new file mode 100644 index 00000000..7a0b0a25 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/l10n/html.js @@ -0,0 +1,110 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "unstable" +}; + +const { Ci, Cu } = require("chrome"); +const events = require("../system/events"); +const core = require("./core"); + +const assetsURI = require('../self').data.url(); +const { Services } = Cu.import("resource://gre/modules/Services.jsm"); + +const hideContentStyle = "data:text/css,:root {visibility: hidden !important;}"; +const hideSheetUri = Services.io.newURI(hideContentStyle, null, null); + +// Taken from Gaia: +// https://github.com/andreasgal/gaia/blob/04fde2640a7f40314643016a5a6c98bf3755f5fd/webapi.js#L1470 +function translateElement(element) { + element = element || document; + + // check all translatable children (= w/ a `data-l10n-id' attribute) + var children = element.querySelectorAll('*[data-l10n-id]'); + var elementCount = children.length; + for (var i = 0; i < elementCount; i++) { + var child = children[i]; + + // translate the child + var key = child.dataset.l10nId; + var data = core.get(key); + if (data) + child.textContent = data; + } +} +exports.translateElement = translateElement; + +function onDocumentReady2Translate(event) { + let document = event.target; + document.removeEventListener("DOMContentLoaded", onDocumentReady2Translate, + false); + + translateElement(document); + + try { + // Finally display document when we finished replacing all text content + let winUtils = document.defaultView.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + winUtils.removeSheet(hideSheetUri, winUtils.USER_SHEET); + } + catch(e) { + console.exception(e); + } +} + +function onContentWindow(event) { + let document = event.subject; + + // Accept only HTML documents + if (!(document instanceof Ci.nsIDOMHTMLDocument)) + return; + + // Bug 769483: data:URI documents instanciated with nsIDOMParser + // have a null `location` attribute at this time + if (!document.location) + return; + + // Accept only document from this addon + if (document.location.href.indexOf(assetsURI) !== 0) + return; + + try { + // First hide content of the document in order to have content blinking + // between untranslated and translated states + let winUtils = document.defaultView.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + winUtils.loadSheet(hideSheetUri, winUtils.USER_SHEET); + } + catch(e) { + console.exception(e); + } + // Wait for DOM tree to be built before applying localization + document.addEventListener("DOMContentLoaded", onDocumentReady2Translate, + false); +} + +// Listen to creation of content documents in order to translate them as soon +// as possible in their loading process +const ON_CONTENT = "document-element-inserted"; +let enabled = false; +function enable() { + if (!enabled) { + events.on(ON_CONTENT, onContentWindow); + enabled = true; + } +} +exports.enable = enable; + +function disable() { + if (enabled) { + events.off(ON_CONTENT, onContentWindow); + enabled = false; + } +} +exports.disable = disable; + +require("sdk/system/unload").when(disable); diff --git a/addon-sdk-1.16/lib/sdk/l10n/loader.js b/addon-sdk-1.16/lib/sdk/l10n/loader.js new file mode 100644 index 00000000..9af6c5c7 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/l10n/loader.js @@ -0,0 +1,71 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "unstable" +}; + +const { Cc, Ci } = require("chrome"); +const { getPreferedLocales, findClosestLocale } = require("./locale"); +const { readURI } = require("../net/url"); +const { resolve } = require("../core/promise"); + +function parseJsonURI(uri) { + return readURI(uri). + then(JSON.parse). + then(null, function (error) { + throw Error("Failed to parse locale file:\n" + uri + "\n" + error); + }); +} + +// Returns the array stored in `locales.json` manifest that list available +// locales files +function getAvailableLocales(rootURI) { + let uri = rootURI + "locales.json"; + return parseJsonURI(uri).then(function (manifest) { + return "locales" in manifest && + Array.isArray(manifest.locales) ? + manifest.locales : []; + }); +} + +// Returns URI of the best locales file to use from the XPI +function getBestLocale(rootURI) { + // Read localization manifest file that contains list of available languages + return getAvailableLocales(rootURI).then(function (availableLocales) { + // Retrieve list of prefered locales to use + let preferedLocales = getPreferedLocales(); + + // Compute the most preferable locale to use by using these two lists + return findClosestLocale(availableLocales, preferedLocales); + }); +} + +/** + * Read localization files and returns a promise of data to put in `@l10n/data` + * pseudo module, in order to allow l10n/core to fetch it. + */ +exports.load = function load(rootURI) { + // First, search for a locale file: + return getBestLocale(rootURI).then(function (bestMatchingLocale) { + // It may be null if the addon doesn't have any locale file + if (!bestMatchingLocale) + return resolve(null); + + let localeURI = rootURI + "locale/" + bestMatchingLocale + ".json"; + + // Locale files only contains one big JSON object that is used as + // an hashtable of: "key to translate" => "translated key" + // TODO: We are likely to change this in order to be able to overload + // a specific key translation. For a specific package, module or line? + return parseJsonURI(localeURI).then(function (json) { + return { + hash: json, + bestMatchingLocale: bestMatchingLocale + }; + }); + }); +} diff --git a/addon-sdk-1.16/lib/sdk/l10n/locale.js b/addon-sdk-1.16/lib/sdk/l10n/locale.js new file mode 100644 index 00000000..0e59a641 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/l10n/locale.js @@ -0,0 +1,126 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +module.metadata = { + "stability": "unstable" +}; + +const prefs = require("../preferences/service"); +const { Cu, Cc, Ci } = require("chrome"); +const { Services } = Cu.import("resource://gre/modules/Services.jsm"); + + +/** + * Gets the currently selected locale for display. + * Gets all usable locale that we can use sorted by priority of relevance + * @return Array of locales, begins with highest priority + */ +const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS"; +const PREF_SELECTED_LOCALE = "general.useragent.locale"; +const PREF_ACCEPT_LANGUAGES = "intl.accept_languages"; +exports.getPreferedLocales = function getPreferedLocales() { + let locales = []; + + function addLocale(locale) { + locale = locale.toLowerCase(); + if (locales.indexOf(locale) === -1) + locales.push(locale); + } + + // Most important locale is OS one. But we use it, only if + // "intl.locale.matchOS" pref is set to `true`. + // Currently only used for multi-locales mobile builds. + // http://mxr.mozilla.org/mozilla-central/source/mobile/android/installer/Makefile.in#46 + if (prefs.get(PREF_MATCH_OS_LOCALE, false)) { + let localeService = Cc["@mozilla.org/intl/nslocaleservice;1"]. + getService(Ci.nsILocaleService); + let osLocale = localeService.getLocaleComponentForUserAgent(); + addLocale(osLocale); + } + + // In some cases, mainly on Fennec and on Linux version, + // `general.useragent.locale` is a special 'localized' value, like: + // "chrome://global/locale/intl.properties" + let browserUiLocale = prefs.getLocalized(PREF_SELECTED_LOCALE, "") || + prefs.get(PREF_SELECTED_LOCALE, ""); + if (browserUiLocale) + addLocale(browserUiLocale); + + + // Third priority is the list of locales used for web content + let contentLocales = prefs.get(PREF_ACCEPT_LANGUAGES, ""); + if (contentLocales) { + // This list is a string of locales seperated by commas. + // There is spaces after commas, so strip each item + for each(let locale in contentLocales.split(",")) + addLocale(locale.replace(/(^\s+)|(\s+$)/g, "")); + } + + // Finally, we ensure that en-US is the final fallback if it wasn't added + addLocale("en-US"); + + return locales; +} + +/** + * Selects the closest matching locale from a list of locales. + * + * @param aLocales + * An array of available locales + * @param aMatchLocales + * An array of prefered locales, ordered by priority. Most wanted first. + * Locales have to be in lowercase. + * If null, uses getPreferedLocales() results + * @return the best match for the currently selected locale + * + * Stolen from http://mxr.mozilla.org/mozilla-central/source/toolkit/mozapps/extensions/XPIProvider.jsm + */ +exports.findClosestLocale = function findClosestLocale(aLocales, aMatchLocales) { + + aMatchLocales = aMatchLocales || exports.getPreferedLocales(); + + // Holds the best matching localized resource + let bestmatch = null; + // The number of locale parts it matched with + let bestmatchcount = 0; + // The number of locale parts in the match + let bestpartcount = 0; + + for each (let locale in aMatchLocales) { + let lparts = locale.split("-"); + for each (let localized in aLocales) { + let found = localized.toLowerCase(); + // Exact match is returned immediately + if (locale == found) + return localized; + + let fparts = found.split("-"); + /* If we have found a possible match and this one isn't any longer + then we dont need to check further. */ + if (bestmatch && fparts.length < bestmatchcount) + continue; + + // Count the number of parts that match + let maxmatchcount = Math.min(fparts.length, lparts.length); + let matchcount = 0; + while (matchcount < maxmatchcount && + fparts[matchcount] == lparts[matchcount]) + matchcount++; + + /* If we matched more than the last best match or matched the same and + this locale is less specific than the last best match. */ + if (matchcount > bestmatchcount || + (matchcount == bestmatchcount && fparts.length < bestpartcount)) { + bestmatch = localized; + bestmatchcount = matchcount; + bestpartcount = fparts.length; + } + } + // If we found a valid match for this locale return it + if (bestmatch) + return bestmatch; + } + return null; +} diff --git a/addon-sdk-1.16/lib/sdk/l10n/plural-rules.js b/addon-sdk-1.16/lib/sdk/l10n/plural-rules.js new file mode 100644 index 00000000..22e5b8b7 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/l10n/plural-rules.js @@ -0,0 +1,403 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// This file is automatically generated with /python-lib/plural-rules-generator.py +// Fetching data from: http://unicode.org/repos/cldr/trunk/common/supplemental/plurals.xml + +// Mapping of short locale name == to == > rule index in following list + +module.metadata = { + "stability": "unstable" +}; + +const LOCALES_TO_RULES = { + "af": 3, + "ak": 4, + "am": 4, + "ar": 1, + "asa": 3, + "az": 0, + "be": 11, + "bem": 3, + "bez": 3, + "bg": 3, + "bh": 4, + "bm": 0, + "bn": 3, + "bo": 0, + "br": 20, + "brx": 3, + "bs": 11, + "ca": 3, + "cgg": 3, + "chr": 3, + "cs": 12, + "cy": 17, + "da": 3, + "de": 3, + "dv": 3, + "dz": 0, + "ee": 3, + "el": 3, + "en": 3, + "eo": 3, + "es": 3, + "et": 3, + "eu": 3, + "fa": 0, + "ff": 5, + "fi": 3, + "fil": 4, + "fo": 3, + "fr": 5, + "fur": 3, + "fy": 3, + "ga": 8, + "gd": 24, + "gl": 3, + "gsw": 3, + "gu": 3, + "guw": 4, + "gv": 23, + "ha": 3, + "haw": 3, + "he": 2, + "hi": 4, + "hr": 11, + "hu": 0, + "id": 0, + "ig": 0, + "ii": 0, + "is": 3, + "it": 3, + "iu": 7, + "ja": 0, + "jmc": 3, + "jv": 0, + "ka": 0, + "kab": 5, + "kaj": 3, + "kcg": 3, + "kde": 0, + "kea": 0, + "kk": 3, + "kl": 3, + "km": 0, + "kn": 0, + "ko": 0, + "ksb": 3, + "ksh": 21, + "ku": 3, + "kw": 7, + "lag": 18, + "lb": 3, + "lg": 3, + "ln": 4, + "lo": 0, + "lt": 10, + "lv": 6, + "mas": 3, + "mg": 4, + "mk": 16, + "ml": 3, + "mn": 3, + "mo": 9, + "mr": 3, + "ms": 0, + "mt": 15, + "my": 0, + "nah": 3, + "naq": 7, + "nb": 3, + "nd": 3, + "ne": 3, + "nl": 3, + "nn": 3, + "no": 3, + "nr": 3, + "nso": 4, + "ny": 3, + "nyn": 3, + "om": 3, + "or": 3, + "pa": 3, + "pap": 3, + "pl": 13, + "ps": 3, + "pt": 3, + "rm": 3, + "ro": 9, + "rof": 3, + "ru": 11, + "rwk": 3, + "sah": 0, + "saq": 3, + "se": 7, + "seh": 3, + "ses": 0, + "sg": 0, + "sh": 11, + "shi": 19, + "sk": 12, + "sl": 14, + "sma": 7, + "smi": 7, + "smj": 7, + "smn": 7, + "sms": 7, + "sn": 3, + "so": 3, + "sq": 3, + "sr": 11, + "ss": 3, + "ssy": 3, + "st": 3, + "sv": 3, + "sw": 3, + "syr": 3, + "ta": 3, + "te": 3, + "teo": 3, + "th": 0, + "ti": 4, + "tig": 3, + "tk": 3, + "tl": 4, + "tn": 3, + "to": 0, + "tr": 0, + "ts": 3, + "tzm": 22, + "uk": 11, + "ur": 3, + "ve": 3, + "vi": 0, + "vun": 3, + "wa": 4, + "wae": 3, + "wo": 0, + "xh": 3, + "xog": 3, + "yo": 0, + "zh": 0, + "zu": 3 +}; + +// Utility functions for plural rules methods +function isIn(n, list) list.indexOf(n) !== -1; +function isBetween(n, start, end) start <= n && n <= end; + +// List of all plural rules methods, that maps an integer to the plural form name to use +const RULES = { + "0": function (n) { + + return "other" + }, + "1": function (n) { + if ((isBetween((n % 100), 3, 10))) + return "few"; + if (n == 0) + return "zero"; + if ((isBetween((n % 100), 11, 99))) + return "many"; + if (n == 2) + return "two"; + if (n == 1) + return "one"; + return "other" + }, + "2": function (n) { + if (n != 0 && (n % 10) == 0) + return "many"; + if (n == 2) + return "two"; + if (n == 1) + return "one"; + return "other" + }, + "3": function (n) { + if (n == 1) + return "one"; + return "other" + }, + "4": function (n) { + if ((isBetween(n, 0, 1))) + return "one"; + return "other" + }, + "5": function (n) { + if ((isBetween(n, 0, 2)) && n != 2) + return "one"; + return "other" + }, + "6": function (n) { + if (n == 0) + return "zero"; + if ((n % 10) == 1 && (n % 100) != 11) + return "one"; + return "other" + }, + "7": function (n) { + if (n == 2) + return "two"; + if (n == 1) + return "one"; + return "other" + }, + "8": function (n) { + if ((isBetween(n, 3, 6))) + return "few"; + if ((isBetween(n, 7, 10))) + return "many"; + if (n == 2) + return "two"; + if (n == 1) + return "one"; + return "other" + }, + "9": function (n) { + if (n == 0 || n != 1 && (isBetween((n % 100), 1, 19))) + return "few"; + if (n == 1) + return "one"; + return "other" + }, + "10": function (n) { + if ((isBetween((n % 10), 2, 9)) && !(isBetween((n % 100), 11, 19))) + return "few"; + if ((n % 10) == 1 && !(isBetween((n % 100), 11, 19))) + return "one"; + return "other" + }, + "11": function (n) { + if ((isBetween((n % 10), 2, 4)) && !(isBetween((n % 100), 12, 14))) + return "few"; + if ((n % 10) == 0 || (isBetween((n % 10), 5, 9)) || (isBetween((n % 100), 11, 14))) + return "many"; + if ((n % 10) == 1 && (n % 100) != 11) + return "one"; + return "other" + }, + "12": function (n) { + if ((isBetween(n, 2, 4))) + return "few"; + if (n == 1) + return "one"; + return "other" + }, + "13": function (n) { + if ((isBetween((n % 10), 2, 4)) && !(isBetween((n % 100), 12, 14))) + return "few"; + if (n != 1 && (isBetween((n % 10), 0, 1)) || (isBetween((n % 10), 5, 9)) || (isBetween((n % 100), 12, 14))) + return "many"; + if (n == 1) + return "one"; + return "other" + }, + "14": function (n) { + if ((isBetween((n % 100), 3, 4))) + return "few"; + if ((n % 100) == 2) + return "two"; + if ((n % 100) == 1) + return "one"; + return "other" + }, + "15": function (n) { + if (n == 0 || (isBetween((n % 100), 2, 10))) + return "few"; + if ((isBetween((n % 100), 11, 19))) + return "many"; + if (n == 1) + return "one"; + return "other" + }, + "16": function (n) { + if ((n % 10) == 1 && n != 11) + return "one"; + return "other" + }, + "17": function (n) { + if (n == 3) + return "few"; + if (n == 0) + return "zero"; + if (n == 6) + return "many"; + if (n == 2) + return "two"; + if (n == 1) + return "one"; + return "other" + }, + "18": function (n) { + if (n == 0) + return "zero"; + if ((isBetween(n, 0, 2)) && n != 0 && n != 2) + return "one"; + return "other" + }, + "19": function (n) { + if ((isBetween(n, 2, 10))) + return "few"; + if ((isBetween(n, 0, 1))) + return "one"; + return "other" + }, + "20": function (n) { + if ((isBetween((n % 10), 3, 4) || ((n % 10) == 9)) && !(isBetween((n % 100), 10, 19) || isBetween((n % 100), 70, 79) || isBetween((n % 100), 90, 99))) + return "few"; + if ((n % 1000000) == 0 && n != 0) + return "many"; + if ((n % 10) == 2 && !isIn((n % 100), [12, 72, 92])) + return "two"; + if ((n % 10) == 1 && !isIn((n % 100), [11, 71, 91])) + return "one"; + return "other" + }, + "21": function (n) { + if (n == 0) + return "zero"; + if (n == 1) + return "one"; + return "other" + }, + "22": function (n) { + if ((isBetween(n, 0, 1)) || (isBetween(n, 11, 99))) + return "one"; + return "other" + }, + "23": function (n) { + if ((isBetween((n % 10), 1, 2)) || (n % 20) == 0) + return "one"; + return "other" + }, + "24": function (n) { + if ((isBetween(n, 3, 10) || isBetween(n, 13, 19))) + return "few"; + if (isIn(n, [2, 12])) + return "two"; + if (isIn(n, [1, 11])) + return "one"; + return "other" + }, +}; + +/** + * Return a function that gives the plural form name for a given integer + * for the specified `locale` + * let fun = getRulesForLocale('en'); + * fun(1) -> 'one' + * fun(0) -> 'other' + * fun(1000) -> 'other' + */ +exports.getRulesForLocale = function getRulesForLocale(locale) { + let index = LOCALES_TO_RULES[locale]; + if (!(index in RULES)) { + console.warn('Plural form unknown for locale "' + locale + '"'); + return function () { return "other"; }; + } + return RULES[index]; +} + diff --git a/addon-sdk-1.16/lib/sdk/l10n/prefs.js b/addon-sdk-1.16/lib/sdk/l10n/prefs.js new file mode 100644 index 00000000..f83bdc98 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/l10n/prefs.js @@ -0,0 +1,42 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const { on } = require("../system/events"); +const core = require("./core"); +const { id: jetpackId} = require('../self'); + +const OPTIONS_DISPLAYED = "addon-options-displayed"; + +function onOptionsDisplayed({ subject: document, data: addonId }) { + if (addonId !== jetpackId) + return; + let query = 'setting[data-jetpack-id="' + jetpackId + '"][pref-name], ' + + 'button[data-jetpack-id="' + jetpackId + '"][pref-name]'; + let nodes = document.querySelectorAll(query); + for (let node of nodes) { + let name = node.getAttribute("pref-name"); + if (node.tagName == "setting") { + let desc = core.get(name + "_description"); + if (desc) + node.setAttribute("desc", desc); + let title = core.get(name + "_title"); + if (title) + node.setAttribute("title", title); + + for (let item of node.querySelectorAll("menuitem, radio")) { + let key = name + "_options." + item.getAttribute("label"); + let label = core.get(key); + if (label) + item.setAttribute("label", label); + } + } + else if (node.tagName == "button") { + let label = core.get(name + "_label"); + if (label) + node.setAttribute("label", label); + } + } +} +on(OPTIONS_DISPLAYED, onOptionsDisplayed); diff --git a/addon-sdk-1.16/lib/sdk/lang/functional.js b/addon-sdk-1.16/lib/sdk/lang/functional.js new file mode 100644 index 00000000..27036de1 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/lang/functional.js @@ -0,0 +1,330 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// Disclaimer: Some of the functions in this module implement APIs from +// Jeremy Ashkenas's http://underscorejs.org/ library and all credits for +// those goes to him. + +"use strict"; + +module.metadata = { + "stability": "unstable" +}; + +const { deprecateFunction } = require("../util/deprecate"); +const { setImmediate, setTimeout } = require("../timers"); + +const arity = f => f.arity || f.length; + +const name = f => f.displayName || f.name; + +const derive = (f, source) => { + f.displayName = name(source); + f.arity = arity(source); + return f; +}; + +/** + * Takes variadic numeber of functions and returns composed one. + * Returned function pushes `this` pseudo-variable to the head + * of the passed arguments and invokes all the functions from + * left to right passing same arguments to them. Composite function + * returns return value of the right most funciton. + */ +const method = (...lambdas) => { + return function method(...args) { + args.unshift(this); + return lambdas.reduce((_, lambda) => lambda.apply(this, args), + void(0)); + }; +}; +exports.method = method; + +/** + * Takes a function and returns a wrapped one instead, calling which will call + * original function in the next turn of event loop. This is basically utility + * to do `setImmediate(function() { ... })`, with a difference that returned + * function is reused, instead of creating a new one each time. This also allows + * to use this functions as event listeners. + */ +const defer = f => derive(function(...args) { + setImmediate(invoke, f, args, this); +}, f); +exports.defer = defer; +// Exporting `remit` alias as `defer` may conflict with promises. +exports.remit = defer; + +/** + * Invokes `callee` by passing `params` as an arguments and `self` as `this` + * pseudo-variable. Returns value that is returned by a callee. + * @param {Function} callee + * Function to invoke. + * @param {Array} params + * Arguments to invoke function with. + * @param {Object} self + * Object to be passed as a `this` pseudo variable. + */ +const invoke = (callee, params, self) => callee.apply(self, params); +exports.invoke = invoke; + +/** + * Takes a function and bind values to one or more arguments, returning a new + * function of smaller arity. + * + * @param {Function} fn + * The function to partial + * + * @returns The new function with binded values + */ +const partial = (f, ...curried) => { + if (typeof(f) !== "function") + throw new TypeError(String(f) + " is not a function"); + + let fn = derive(function(...args) { + return f.apply(this, curried.concat(args)); + }, f); + fn.arity = arity(f) - curried.length; + return fn; +}; +exports.partial = partial; + +/** + * Returns function with implicit currying, which will continue currying until + * expected number of argument is collected. Expected number of arguments is + * determined by `fn.length`. Using this with variadic functions is stupid, + * so don't do it. + * + * @examples + * + * var sum = curry(function(a, b) { + * return a + b + * }) + * console.log(sum(2, 2)) // 4 + * console.log(sum(2)(4)) // 6 + */ +const curry = new function() { + const currier = (fn, arity, params) => { + // Function either continues to curry arguments or executes function + // if desired arguments have being collected. + const curried = function(...input) { + // Prepend all curried arguments to the given arguments. + if (params) input.unshift.apply(input, params); + // If expected number of arguments has being collected invoke fn, + // othrewise return curried version Otherwise continue curried. + return (input.length >= arity) ? fn.apply(this, input) : + currier(fn, arity, input); + }; + curried.arity = arity - (params ? params.length : 0); + + return curried; + }; + + return fn => currier(fn, arity(fn)); +}; +exports.curry = curry; + +/** + * Returns the composition of a list of functions, where each function consumes + * the return value of the function that follows. In math terms, composing the + * functions `f()`, `g()`, and `h()` produces `f(g(h()))`. + * @example + * + * var greet = function(name) { return "hi: " + name; }; + * var exclaim = function(statement) { return statement + "!"; }; + * var welcome = compose(exclaim, greet); + * + * welcome('moe'); // => 'hi: moe!' + */ +function compose(...lambdas) { + return function composed(...args) { + let index = lambdas.length; + while (0 <= --index) + args = [lambdas[index].apply(this, args)]; + + return args[0]; + }; +} +exports.compose = compose; + +/* + * Returns the first function passed as an argument to the second, + * allowing you to adjust arguments, run code before and after, and + * conditionally execute the original function. + * @example + * + * var hello = function(name) { return "hello: " + name; }; + * hello = wrap(hello, function(f) { + * return "before, " + f("moe") + ", after"; + * }); + * + * hello(); // => 'before, hello: moe, after' + */ +const wrap = (f, wrapper) => derive(function wrapped(...args) { + return wrapper.apply(this, [f].concat(args)); +}, f); +exports.wrap = wrap; + +/** + * Returns the same value that is used as the argument. In math: f(x) = x + */ +const identity = value => value; +exports.identity = identity; + +/** + * Memoizes a given function by caching the computed result. Useful for + * speeding up slow-running computations. If passed an optional hashFunction, + * it will be used to compute the hash key for storing the result, based on + * the arguments to the original function. The default hashFunction just uses + * the first argument to the memoized function as the key. + */ +const memoize = (f, hasher) => { + let memo = Object.create(null); + let cache = new WeakMap(); + hasher = hasher || identity; + return derive(function memoizer(...args) { + const key = hasher.apply(this, args); + const type = typeof(key); + if (key && (type === "object" || type === "function")) { + if (!cache.has(key)) + cache.set(key, f.apply(this, args)); + return cache.get(key); + } + else { + if (!(key in memo)) + memo[key] = f.apply(this, args); + return memo[key]; + } + }, f); +}; +exports.memoize = memoize; + +/** + * Much like setTimeout, invokes function after wait milliseconds. If you pass + * the optional arguments, they will be forwarded on to the function when it is + * invoked. + */ +const delay = function delay(f, ms, ...args) { + setTimeout(() => f.apply(this, args), ms); +}; +exports.delay = delay; + +/** + * Creates a version of the function that can only be called one time. Repeated + * calls to the modified function will have no effect, returning the value from + * the original call. Useful for initialization functions, instead of having to + * set a boolean flag and then check it later. + */ +const once = f => { + let ran = false, cache; + return derive(function(...args) { + return ran ? cache : (ran = true, cache = f.apply(this, args)); + }, f); +}; +exports.once = once; +// export cache as once will may be conflicting with event once a lot. +exports.cache = once; + +// Takes a `f` function and returns a function that takes the same +// arguments as `f`, has the same effects, if any, and returns the +// opposite truth value. +const complement = f => derive(function(...args) { + return args.length < arity(f) ? complement(partial(f, ...args)) : + !f.apply(this, args); +}, f); +exports.complement = complement; + +// Constructs function that returns `x` no matter what is it +// invoked with. +const constant = x => _ => x; +exports.constant = constant; + +// Takes `p` predicate, `consequent` function and an optional +// `alternate` function and composes function that returns +// application of arguments over `consequent` if application over +// `p` is `true` otherwise returns application over `alternate`. +// If `alternate` is not a function returns `undefined`. +const when = (p, consequent, alternate) => { + if (typeof(alternate) !== "function" && alternate !== void(0)) + throw TypeError("alternate must be a function"); + if (typeof(consequent) !== "function") + throw TypeError("consequent must be a function"); + + return function(...args) { + return p.apply(this, args) ? + consequent.apply(this, args) : + alternate && alternate.apply(this, args); + }; +}; +exports.when = when; + +// Apply function that behaves as `apply` does in lisp: +// apply(f, x, [y, z]) => f.apply(f, [x, y, z]) +// apply(f, x) => f.apply(f, [x]) +const apply = (f, ...rest) => f.apply(f, rest.concat(rest.pop())); +exports.apply = apply; + +// Returns function identical to given `f` but with flipped order +// of arguments. +const flip = f => derive(function(...args) { + return f.apply(this, args.reverse()); +}, f); +exports.flip = flip; + + +// Takes field `name` and `target` and returns value of that field. +// If `target` is `null` or `undefined` it would be returned back +// instead of attempt to access it's field. Function is implicitly +// curried, this allows accessor function generation by calling it +// with only `name` argument. +const field = curry((name, target) => + // Note: Permisive `==` is intentional. + target == null ? target : target[name]); +exports.field = field; + +// Takes `.` delimited string representing `path` to a nested field +// and a `target` to get it from. For convinience function is +// implicitly curried, there for accessors can be created by invoking +// it with just a `path` argument. +const query = curry((path, target) => { + const names = path.split("."); + const count = names.length; + let index = 0; + let result = target; + // Note: Permisive `!=` is intentional. + while (result != null && index < count) { + result = result[names[index]]; + index = index + 1; + } + return result; +}); +exports.query = query; + +// Takes `Type` (constructor function) and a `value` and returns +// `true` if `value` is instance of the given `Type`. Function is +// implicitly curried this allows predicate generation by calling +// function with just first argument. +const isInstance = curry((Type, value) => value instanceof Type); +exports.isInstance = isInstance; + +/* + * Takes a funtion and returns a wrapped function that returns `this` + */ +const chainable = f => derive(function(...args) { + f.apply(this, args); + return this; +}, f); +exports.chainable = chainable; +exports.chain = + deprecateFunction(chainable, "Function `chain` was renamed to `chainable`"); + +// Functions takes `expected` and `actual` values and returns `true` if +// `expected === actual`. Returns curried function if called with less then +// two arguments. +// +// [ 1, 0, 1, 0, 1 ].map(is(1)) // => [ true, false, true, false, true ] +const is = curry((expected, actual) => actual === expected); +exports.is = is; + +const isnt = complement(is); +exports.isnt = isnt; diff --git a/addon-sdk-1.16/lib/sdk/lang/type.js b/addon-sdk-1.16/lib/sdk/lang/type.js new file mode 100644 index 00000000..8d2feea4 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/lang/type.js @@ -0,0 +1,364 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "unstable" +}; + +/** + * Returns `true` if `value` is `undefined`. + * @examples + * var foo; isUndefined(foo); // true + * isUndefined(0); // false + */ +function isUndefined(value) { + return value === undefined; +} +exports.isUndefined = isUndefined; + +/** + * Returns `true` if value is `null`. + * @examples + * isNull(null); // true + * isNull(undefined); // false + */ +function isNull(value) { + return value === null; +} +exports.isNull = isNull; + +/** + * Returns `true` if value is `null` or `undefined`. + * It's equivalent to `== null`, but resolve the ambiguity of the writer + * intention, makes clear that he's clearly checking both `null` and `undefined` + * values, and it's not a typo for `=== null`. + */ +function isNil(value) { + return value === null || value === undefined; +} +exports.isNil = isNil; + +function isBoolean(value) { + return typeof value === "boolean"; +} +exports.isBoolean = isBoolean; +/** + * Returns `true` if value is a string. + * @examples + * isString("moe"); // true + */ +function isString(value) { + return typeof value === "string"; +} +exports.isString = isString; + +/** + * Returns `true` if `value` is a number. + * @examples + * isNumber(8.4 * 5); // true + */ +function isNumber(value) { + return typeof value === "number"; +} +exports.isNumber = isNumber; + +/** + * Returns `true` if `value` is a `RegExp`. + * @examples + * isRegExp(/moe/); // true + */ +function isRegExp(value) { + return isObject(value) && instanceOf(value, RegExp); +} +exports.isRegExp = isRegExp; + +/** + * Returns true if `value` is a `Date`. + * @examples + * isDate(new Date()); // true + */ +function isDate(value) { + return isObject(value) && instanceOf(value, Date); +} +exports.isDate = isDate; + +/** + * Returns true if object is a Function. + * @examples + * isFunction(function foo(){}) // true + */ +function isFunction(value) { + return typeof value === "function"; +} +exports.isFunction = isFunction; + +/** + * Returns `true` if `value` is an object (please note that `null` is considered + * to be an atom and not an object). + * @examples + * isObject({}) // true + * isObject(null) // false + */ +function isObject(value) { + return typeof value === "object" && value !== null; +} +exports.isObject = isObject; + +/** + * Returns true if `value` is an Array. + * @examples + * isArray([1, 2, 3]) // true + * isArray({ 0: 'foo', length: 1 }) // false + */ +var isArray = Array.isArray || function isArray(value) { + Object.prototype.toString.call(value) === "[object Array]"; +} +exports.isArray = isArray; + +/** + * Returns `true` if `value` is an Arguments object. + * @examples + * (function(){ return isArguments(arguments); })(1, 2, 3); // true + * isArguments([1,2,3]); // false + */ +function isArguments(value) { + Object.prototype.toString.call(value) === "[object Arguments]"; +} +exports.isArguments = isArguments; + +let isMap = value => Object.prototype.toString.call(value) === "[object Map]" +exports.isMap = isMap; + +let isSet = value => Object.prototype.toString.call(value) === "[object Set]" +exports.isSet = isSet; + +/** + * Returns true if it is a primitive `value`. (null, undefined, number, + * boolean, string) + * @examples + * isPrimitive(3) // true + * isPrimitive('foo') // true + * isPrimitive({ bar: 3 }) // false + */ +function isPrimitive(value) { + return !isFunction(value) && !isObject(value); +} +exports.isPrimitive = isPrimitive; + +/** + * Returns `true` if given `object` is flat (it is direct decedent of + * `Object.prototype` or `null`). + * @examples + * isFlat({}) // true + * isFlat(new Type()) // false + */ +function isFlat(object) { + return isObject(object) && (isNull(Object.getPrototypeOf(object)) || + isNull(Object.getPrototypeOf( + Object.getPrototypeOf(object)))); +} +exports.isFlat = isFlat; + +/** + * Returns `true` if object contains no values. + */ +function isEmpty(object) { + if (isObject(object)) { + for (var key in object) + return false; + return true; + } + return false; +} +exports.isEmpty = isEmpty; + +/** + * Returns `true` if `value` is an array / flat object containing only atomic + * values and other flat objects. + */ +function isJSON(value, visited) { + // Adding value to array of visited values. + (visited || (visited = [])).push(value); + // If `value` is an atom return `true` cause it's valid JSON. + return isPrimitive(value) || + // If `value` is an array of JSON values that has not been visited + // yet. + (isArray(value) && value.every(function(element) { + return isJSON(element, visited); + })) || + // If `value` is a plain object containing properties with a JSON + // values it's a valid JSON. + (isFlat(value) && Object.keys(value).every(function(key) { + var $ = Object.getOwnPropertyDescriptor(value, key); + // Check every proprety of a plain object to verify that + // it's neither getter nor setter, but a JSON value, that + // has not been visited yet. + return ((!isObject($.value) || !~visited.indexOf($.value)) && + !('get' in $) && !('set' in $) && + isJSON($.value, visited)); + })); +} +exports.isJSON = function (value) { + return isJSON(value); +}; + +/** + * Returns if `value` is an instance of a given `Type`. This is exactly same as + * `value instanceof Type` with a difference that `Type` can be from a scope + * that has a different top level object. (Like in case where `Type` is a + * function from different iframe / jetpack module / sandbox). + */ +function instanceOf(value, Type) { + var isConstructorNameSame; + var isConstructorSourceSame; + + // If `instanceof` returned `true` we know result right away. + var isInstanceOf = value instanceof Type; + + // If `instanceof` returned `false` we do ducktype check since `Type` may be + // from a different sandbox. If a constructor of the `value` or a constructor + // of the value's prototype has same name and source we assume that it's an + // instance of the Type. + if (!isInstanceOf && value) { + isConstructorNameSame = value.constructor.name === Type.name; + isConstructorSourceSame = String(value.constructor) == String(Type); + isInstanceOf = (isConstructorNameSame && isConstructorSourceSame) || + instanceOf(Object.getPrototypeOf(value), Type); + } + return isInstanceOf; +} +exports.instanceOf = instanceOf; + +/** + * Function returns textual representation of a value passed to it. Function + * takes additional `indent` argument that is used for indentation. Also + * optional `limit` argument may be passed to limit amount of detail returned. + * @param {Object} value + * @param {String} [indent=" "] + * @param {Number} [limit] + */ +function source(value, indent, limit, offset, visited) { + var result; + var names; + var nestingIndex; + var isCompact = !isUndefined(limit); + + indent = indent || " "; + offset = (offset || ""); + result = ""; + visited = visited || []; + + if (isUndefined(value)) { + result += "undefined"; + } + else if (isNull(value)) { + result += "null"; + } + else if (isString(value)) { + result += '"' + value + '"'; + } + else if (isFunction(value)) { + value = String(value).split("\n"); + if (isCompact && value.length > 2) { + value = value.splice(0, 2); + value.push("...}"); + } + result += value.join("\n" + offset); + } + else if (isArray(value)) { + if ((nestingIndex = (visited.indexOf(value) + 1))) { + result = "#" + nestingIndex + "#"; + } + else { + visited.push(value); + + if (isCompact) + value = value.slice(0, limit); + + result += "[\n"; + result += value.map(function(value) { + return offset + indent + source(value, indent, limit, offset + indent, + visited); + }).join(",\n"); + result += isCompact && value.length > limit ? + ",\n" + offset + "...]" : "\n" + offset + "]"; + } + } + else if (isObject(value)) { + if ((nestingIndex = (visited.indexOf(value) + 1))) { + result = "#" + nestingIndex + "#" + } + else { + visited.push(value) + + names = Object.keys(value); + + result += "{ // " + value + "\n"; + result += (isCompact ? names.slice(0, limit) : names).map(function(name) { + var _limit = isCompact ? limit - 1 : limit; + var descriptor = Object.getOwnPropertyDescriptor(value, name); + var result = offset + indent + "// "; + var accessor; + if (0 <= name.indexOf(" ")) + name = '"' + name + '"'; + + if (descriptor.writable) + result += "writable "; + if (descriptor.configurable) + result += "configurable "; + if (descriptor.enumerable) + result += "enumerable "; + + result += "\n"; + if ("value" in descriptor) { + result += offset + indent + name + ": "; + result += source(descriptor.value, indent, _limit, indent + offset, + visited); + } + else { + + if (descriptor.get) { + result += offset + indent + "get " + name + " "; + accessor = source(descriptor.get, indent, _limit, indent + offset, + visited); + result += accessor.substr(accessor.indexOf("{")); + } + + if (descriptor.set) { + result += offset + indent + "set " + name + " "; + accessor = source(descriptor.set, indent, _limit, indent + offset, + visited); + result += accessor.substr(accessor.indexOf("{")); + } + } + return result; + }).join(",\n"); + + if (isCompact) { + if (names.length > limit && limit > 0) { + result += ",\n" + offset + indent + "//..."; + } + } + else { + if (names.length) + result += ","; + + result += "\n" + offset + indent + '"__proto__": '; + result += source(Object.getPrototypeOf(value), indent, 0, + offset + indent); + } + + result += "\n" + offset + "}"; + } + } + else { + result += String(value); + } + return result; +} +exports.source = function (value, indentation, limit) { + return source(value, indentation, limit); +}; diff --git a/addon-sdk-1.16/lib/sdk/lang/weak-set.js b/addon-sdk-1.16/lib/sdk/lang/weak-set.js new file mode 100644 index 00000000..57e4ae6e --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/lang/weak-set.js @@ -0,0 +1,70 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +module.metadata = { + "stability": "experimental" +}; + +"use strict"; + +const { Cu } = require("chrome"); + +function makeGetterFor(Type) { + let cache = new WeakMap(); + + return function getFor(target) { + if (!cache.has(target)) + cache.set(target, new Type()); + + return cache.get(target); + } +} + +let getLookupFor = makeGetterFor(WeakMap); +let getRefsFor = makeGetterFor(Set); + +function add(target, value) { + if (has(target, value)) + return; + + getLookupFor(target).set(value, true); + getRefsFor(target).add(Cu.getWeakReference(value)); +} +exports.add = add; + +function remove(target, value) { + getLookupFor(target).delete(value); +} +exports.remove = remove; + +function has(target, value) { + return getLookupFor(target).has(value); +} +exports.has = has; + +function clear(target) { + getLookupFor(target).clear(); + getRefsFor(target).clear(); +} +exports.clear = clear; + +function iterator(target) { + let refs = getRefsFor(target); + + for (let ref of refs) { + let value = ref.get(); + + // If `value` is already gc'ed, it would be `null`. + // The `has` function is using a WeakMap as lookup table, so passing `null` + // would raise an exception because WeakMap accepts as value only non-null + // object. + // Plus, if `value` is already gc'ed, we do not have to take it in account + // during the iteration, and remove it from the references. + if (value !== null && has(target, value)) + yield value; + else + refs.delete(ref); + } +} +exports.iterator = iterator; diff --git a/addon-sdk-1.16/lib/sdk/loader/cuddlefish.js b/addon-sdk-1.16/lib/sdk/loader/cuddlefish.js new file mode 100644 index 00000000..f6b8b45c --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/loader/cuddlefish.js @@ -0,0 +1,152 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +'use strict'; + +module.metadata = { + "stability": "unstable" +}; + +// This module is manually loaded by bootstrap.js in a sandbox and immediatly +// put in module cache so that it is never loaded in any other way. + +/* Workarounds to include dependencies in the manifest +require('chrome') // Otherwise CFX will complain about Components +require('toolkit/loader') // Otherwise CFX will stip out loader.js +require('sdk/addon/runner') // Otherwise CFX will stip out addon/runner.js +require('sdk/system/xul-app') // Otherwise CFX will stip out sdk/system/xul-app +*/ + +const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components; + +// `loadSandbox` is exposed by bootstrap.js +const loaderURI = module.uri.replace("sdk/loader/cuddlefish.js", + "toolkit/loader.js"); +const xulappURI = module.uri.replace("loader/cuddlefish.js", + "system/xul-app.js"); +// We need to keep a reference to the sandbox in order to unload it in +// bootstrap.js + +const loaderSandbox = loadSandbox(loaderURI); +const loaderModule = loaderSandbox.exports; + +const xulappSandbox = loadSandbox(xulappURI); +const xulappModule = xulappSandbox.exports; + +const { override, load } = loaderModule; + +/** + * Ensure the current application satisfied the requirements specified in the + * module given. If not, an exception related to the incompatibility is + * returned; `null` otherwise. + * + * @param {Object} module + * The module to check + * @returns {Error} + */ +function incompatibility(module) { + let { metadata, id } = module; + + // if metadata or engines are not specified we assume compatibility is not + // an issue. + if (!metadata || !("engines" in metadata)) + return null; + + let { engines } = metadata; + + if (engines === null || typeof(engines) !== "object") + return new Error("Malformed engines' property in metadata"); + + let applications = Object.keys(engines); + + let versionRange; + applications.forEach(function(name) { + if (xulappModule.is(name)) { + versionRange = engines[name]; + // Continue iteration. We want to ensure the module doesn't + // contain a typo in the applications' name or some unknown + // application - `is` function throws an exception in that case. + } + }); + + if (typeof(versionRange) === "string") { + if (xulappModule.satisfiesVersion(versionRange)) + return null; + + return new Error("Unsupported Application version: The module " + id + + " currently supports only version " + versionRange + " of " + + xulappModule.name + "."); + } + + return new Error("Unsupported Application: The module " + id + + " currently supports only " + applications.join(", ") + ".") +} + +function CuddlefishLoader(options) { + let { manifest } = options; + + options = override(options, { + // Put `api-utils/loader` and `api-utils/cuddlefish` loaded as JSM to module + // cache to avoid subsequent loads via `require`. + modules: override({ + 'toolkit/loader': loaderModule, + 'sdk/loader/cuddlefish': exports, + 'sdk/system/xul-app': xulappModule + }, options.modules), + resolve: function resolve(id, requirer) { + let entry = requirer && requirer in manifest && manifest[requirer]; + let uri = null; + + // If manifest entry for this requirement is present we follow manifest. + // Note: Standard library modules like 'panel' will be present in + // manifest unless they were moved to platform. + if (entry) { + let requirement = entry.requirements[id]; + // If requirer entry is in manifest and it's requirement is not, than + // it has no authority to load since linker was not able to find it. + if (!requirement) + throw Error('Module: ' + requirer + ' has no authority to load: ' + + id, requirer); + + uri = requirement; + } else { + // If requirer is off manifest than it's a system module and we allow it + // to go off manifest by resolving a relative path. + uri = loaderModule.resolve(id, requirer); + } + return uri; + }, + load: function(loader, module) { + let result; + let error; + + // In order to get the module's metadata, we need to load the module. + // if an exception is raised here, it could be that is due to application + // incompatibility. Therefore the exception is stored, and thrown again + // only if the module seems be compatible with the application currently + // running. Otherwise the incompatibility message takes the precedence. + try { + result = load(loader, module); + } + catch (e) { + error = e; + } + + error = incompatibility(module) || error; + + if (error) + throw error; + + return result; + } + }); + + let loader = loaderModule.Loader(options); + // Hack to allow loading from `toolkit/loader`. + loader.modules[loaderURI] = loaderSandbox; + return loader; +} + +exports = override(loaderModule, { + Loader: CuddlefishLoader +}); diff --git a/addon-sdk-1.16/lib/sdk/loader/sandbox.js b/addon-sdk-1.16/lib/sdk/loader/sandbox.js new file mode 100644 index 00000000..3d4996e8 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/loader/sandbox.js @@ -0,0 +1,54 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +module.metadata = { + "stability": "experimental" +}; + +const { Cc, Ci, CC, Cu } = require('chrome'); +const systemPrincipal = CC('@mozilla.org/systemprincipal;1', 'nsIPrincipal')(); +const scriptLoader = Cc['@mozilla.org/moz/jssubscript-loader;1']. + getService(Ci.mozIJSSubScriptLoader); +const self = require('sdk/self'); + +/** + * Make a new sandbox that inherits given `source`'s principals. Source can be + * URI string, DOMWindow or `null` for system principals. + */ +function sandbox(target, options) { + options = options || {}; + options.metadata = options.metadata ? options.metadata : {}; + options.metadata.addonID = options.metadata.addonID ? + options.metadata.addonID : self.id; + + return Cu.Sandbox(target || systemPrincipal, options); +} +exports.sandbox = sandbox; + +/** + * Evaluates given `source` in a given `sandbox` and returns result. + */ +function evaluate(sandbox, code, uri, line, version) { + return Cu.evalInSandbox(code, sandbox, version || '1.8', uri || '', line || 1); +} +exports.evaluate = evaluate; + +/** + * Evaluates code under the given `uri` in the given `sandbox`. + * + * @param {String} uri + * The URL pointing to the script to load. + * It must be a local chrome:, resource:, file: or data: URL. + */ +function load(sandbox, uri) { + if (uri.indexOf('data:') === 0) { + let source = uri.substr(uri.indexOf(',') + 1); + + return evaluate(sandbox, decodeURIComponent(source), '1.8', uri, 0); + } else { + return scriptLoader.loadSubScript(uri, sandbox, 'UTF-8'); + } +} +exports.load = load; diff --git a/addon-sdk-1.16/lib/sdk/net/url.js b/addon-sdk-1.16/lib/sdk/net/url.js new file mode 100644 index 00000000..0fb847ac --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/net/url.js @@ -0,0 +1,119 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "experimental" +}; + +const { Cu, components } = require("chrome"); +const { defer } = require("../core/promise"); +const { merge } = require("../util/object"); + +const { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {}); + +/** + * Open a channel synchronously for the URI given, with an optional charset, and + * returns a resolved promise if succeed; rejected promise otherwise. + */ +function readSync(uri, charset) { + let { promise, resolve, reject } = defer(); + + try { + resolve(readURISync(uri, charset)); + } + catch (e) { + reject("Failed to read: '" + uri + "' (Error Code: " + e.result + ")"); + } + + return promise; +} + +/** + * Open a channel synchronously for the URI given, with an optional charset, and + * returns a promise. + */ +function readAsync(uri, charset) { + let channel = NetUtil.newChannel(uri, charset, null); + + let { promise, resolve, reject } = defer(); + + try { + NetUtil.asyncFetch(channel, function (stream, result) { + if (components.isSuccessCode(result)) { + let count = stream.available(); + let data = NetUtil.readInputStreamToString(stream, count, { charset : charset }); + + resolve(data); + } else { + reject("Failed to read: '" + uri + "' (Error Code: " + result + ")"); + } + }); + } + catch (e) { + reject("Failed to read: '" + uri + "' (Error: " + e.message + ")"); + } + + return promise; +} + +/** + * Reads a URI and returns a promise. If the `sync` option is set to `true`, the + * promise will be resolved synchronously. + * + * @param uri {string} The URI to read + * @param [options] {object} This parameter can have any or all of the following + * fields: `sync`, `charset`. By default the `charset` is set to 'UTF-8'. + * + * @returns {promise} The promise that will be resolved with the content of the + * URL given. + * + * @example + * let promise = readURI('resource://gre/modules/NetUtil.jsm', { + * sync: true, + * charset: 'US-ASCII' + }); + */ +function readURI(uri, options) { + options = merge({ + charset: "UTF-8", + sync: false + }, options); + + return options.sync + ? readSync(uri, options.charset) + : readAsync(uri, options.charset); +} + +exports.readURI = readURI; + +/** + * Reads a URI synchronously. + * This function is intentionally undocumented to favorites the `readURI` usage. + * + * @param uri {string} The URI to read + * @param [charset] {string} The character set to use when read the content of + * the `uri` given. By default is set to 'UTF-8'. + * + * @returns {string} The content of the URI given. + * + * @example + * let data = readURISync('resource://gre/modules/NetUtil.jsm'); + */ +function readURISync(uri, charset) { + charset = typeof charset === "string" ? charset : "UTF-8"; + + let channel = NetUtil.newChannel(uri, charset, null); + let stream = channel.open(); + + let count = stream.available(); + let data = NetUtil.readInputStreamToString(stream, count, { charset : charset }); + + stream.close(); + + return data; +} + +exports.readURISync = readURISync; diff --git a/addon-sdk-1.16/lib/sdk/net/xhr.js b/addon-sdk-1.16/lib/sdk/net/xhr.js new file mode 100644 index 00000000..415b9cbf --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/net/xhr.js @@ -0,0 +1,36 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +module.metadata = { + "stability": "stable" +}; + +const { deprecateFunction } = require("../util/deprecate"); +const { Cc, Ci } = require("chrome"); +const XMLHttpRequest = require("../addon/window").window.XMLHttpRequest; + +Object.defineProperties(XMLHttpRequest.prototype, { + mozBackgroundRequest: { + value: true, + }, + forceAllowThirdPartyCookie: { + configurable: true, + value: deprecateFunction(function() { + forceAllowThirdPartyCookie(this); + + }, "`xhr.forceAllowThirdPartyCookie()` is deprecated, please use" + + "`require('sdk/net/xhr').forceAllowThirdPartyCookie(request)` instead") + } +}); +exports.XMLHttpRequest = XMLHttpRequest; + +function forceAllowThirdPartyCookie(xhr) { + if (xhr.channel instanceof Ci.nsIHttpChannelInternal) + xhr.channel.forceAllowThirdPartyCookie = true; +} +exports.forceAllowThirdPartyCookie = forceAllowThirdPartyCookie; + +// No need to handle add-on unloads as addon/window is closed at unload +// and it will take down all the associated requests. diff --git a/addon-sdk-1.16/lib/sdk/notifications.js b/addon-sdk-1.16/lib/sdk/notifications.js new file mode 100644 index 00000000..362f2858 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/notifications.js @@ -0,0 +1,102 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "stable" +}; + +const { Cc, Ci, Cr } = require("chrome"); +const apiUtils = require("./deprecated/api-utils"); +const errors = require("./deprecated/errors"); +const { isString, isUndefined, instanceOf } = require('./lang/type'); +const { URL } = require('./url'); + +const NOTIFICATION_DIRECTIONS = ["auto", "ltr", "rtl"]; + +try { + let alertServ = Cc["@mozilla.org/alerts-service;1"]. + getService(Ci.nsIAlertsService); + + // The unit test sets this to a mock notification function. + var notify = alertServ.showAlertNotification.bind(alertServ); +} +catch (err) { + // An exception will be thrown if the platform doesn't provide an alert + // service, e.g., if Growl is not installed on OS X. In that case, use a + // mock notification function that just logs to the console. + notify = notifyUsingConsole; +} + +exports.notify = function notifications_notify(options) { + let valOpts = validateOptions(options); + let clickObserver = !valOpts.onClick ? null : { + observe: function notificationClickObserved(subject, topic, data) { + if (topic === "alertclickcallback") + errors.catchAndLog(valOpts.onClick).call(exports, valOpts.data); + } + }; + function notifyWithOpts(notifyFn) { + notifyFn(valOpts.iconURL, valOpts.title, valOpts.text, !!clickObserver, + valOpts.data, clickObserver, valOpts.tag, valOpts.dir, valOpts.lang); + } + try { + notifyWithOpts(notify); + } + catch (err if err instanceof Ci.nsIException && + err.result == Cr.NS_ERROR_FILE_NOT_FOUND) { + console.warn("The notification icon named by " + valOpts.iconURL + + " does not exist. A default icon will be used instead."); + delete valOpts.iconURL; + notifyWithOpts(notify); + } + catch (err) { + notifyWithOpts(notifyUsingConsole); + } +}; + +function notifyUsingConsole(iconURL, title, text) { + title = title ? "[" + title + "]" : ""; + text = text || ""; + let str = [title, text].filter(function (s) s).join(" "); + console.log(str); +} + +function validateOptions(options) { + return apiUtils.validateOptions(options, { + data: { + is: ["string", "undefined"] + }, + iconURL: { + is: ["string", "undefined", "object"], + ok: function(value) { + return isUndefined(value) || isString(value) || (value instanceof URL); + }, + msg: "`iconURL` must be a string or an URL instance." + }, + onClick: { + is: ["function", "undefined"] + }, + text: { + is: ["string", "undefined", "number"] + }, + title: { + is: ["string", "undefined", "number"] + }, + tag: { + is: ["string", "undefined", "number"] + }, + dir: { + is: ["string", "undefined"], + ok: function(value) { + return isUndefined(value) || ~NOTIFICATION_DIRECTIONS.indexOf(value); + }, + msg: '`dir` option must be one of: "auto", "ltr" or "rtl".' + }, + lang: { + is: ["string", "undefined"] + } + }); +} diff --git a/addon-sdk-1.16/lib/sdk/output/system.js b/addon-sdk-1.16/lib/sdk/output/system.js new file mode 100644 index 00000000..4fb16dcd --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/output/system.js @@ -0,0 +1,71 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const { Cc, Ci, Cr } = require("chrome"); +const { Input, start, stop, receive, outputs } = require("../event/utils"); +const { id: addonID } = require("../self"); +const { setImmediate } = require("../timers"); +const { notifyObservers } = Cc['@mozilla.org/observer-service;1']. + getService(Ci.nsIObserverService); + +const NOT_AN_INPUT = "OutputPort can be used only for sending messages"; + +// `OutputPort` creates a port to which messages can be send. Those +// messages are actually disptached as `subject`'s of the observer +// notifications. This is handy for communicating between different +// components of the SDK. By default messages are dispatched +// asynchronously, although `options.sync` can be used to make them +// synchronous. If `options.id` is given `topic` for observer +// notifications is generated by namespacing it, to avoid spamming +// other SDK add-ons. It's also possible to provide `options.topic` +// to use excat `topic` without namespacing it. +// +// Note: Symmetric `new InputPort({ id: "x" })` instances can be used to +// receive messages send to the instances of `new OutputPort({ id: "x" })`. +const OutputPort = function({id, topic, sync}) { + this.id = id || topic; + this.sync = !!sync; + this.topic = topic || "sdk:" + addonID + ":" + id; +}; +// OutputPort extends base signal type to implement same message +// receiving interface. +OutputPort.prototype = new Input(); +OutputPort.constructor = OutputPort; + +// OutputPort can not be consumed there for starting or stopping it +// is not supported. +OutputPort.prototype[start] = _ => { throw TypeError(NOT_AN_INPUT); }; +OutputPort.prototype[stop] = _ => { throw TypeError(NOT_AN_INPUT); }; + +// Port reecives message send to it, which will be dispatched via +// observer notification service. +OutputPort.receive = ({topic, sync}, message) => { + const type = typeof(message); + const supported = message === null || + type === "object" || + type === "function"; + + // There is no sensible way to wrap JS primitives that would make sense + // for general observer notification users. It's also probably not very + // useful to dispatch JS primitives as subject of observer service, there + // for we do not support those use cases. + if (!supported) + throw new TypeError("Unsupproted message type: `" + type + "`"); + + // Normalize `message` to create a valid observer notification `subject`. + // If `message` is `null`, implements `nsISupports` interface or already + // represents wrapped JS object use it as is. Otherwise create a wrapped + // object so that observers could receive it. + const subject = message === null ? null : + message instanceof Ci.nsISupports ? message : + message.wrappedJSObject ? message : + {wrappedJSObject: message}; + if (sync) + notifyObservers(subject, topic, null); + else + setImmediate(notifyObservers, subject, topic, null); +}; +OutputPort.prototype[receive] = OutputPort.receive; +exports.OutputPort = OutputPort; diff --git a/addon-sdk-1.16/lib/sdk/page-mod.js b/addon-sdk-1.16/lib/sdk/page-mod.js new file mode 100644 index 00000000..1a8b59a1 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/page-mod.js @@ -0,0 +1,273 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +module.metadata = { + "stability": "stable" +}; + +const observers = require('./system/events'); +const { Loader, validationAttributes } = require('./content/loader'); +const { Worker } = require('./content/worker'); +const { Registry } = require('./util/registry'); +const { EventEmitter } = require('./deprecated/events'); +const { on, emit } = require('./event/core'); +const { validateOptions : validate } = require('./deprecated/api-utils'); +const { Cc, Ci } = require('chrome'); +const { merge } = require('./util/object'); +const { readURISync } = require('./net/url'); +const { windowIterator } = require('./deprecated/window-utils'); +const { isBrowser, getFrames } = require('./window/utils'); +const { getTabs, getTabContentWindow, getTabForContentWindow, + getURI: getTabURI } = require('./tabs/utils'); +const { ignoreWindow } = require('sdk/private-browsing/utils'); +const { Style } = require("./stylesheet/style"); +const { attach, detach } = require("./content/mod"); +const { has, hasAny } = require("./util/array"); +const { Rules } = require("./util/rules"); + +// Valid values for `attachTo` option +const VALID_ATTACHTO_OPTIONS = ['existing', 'top', 'frame']; + +const mods = new WeakMap(); + +// contentStyle* / contentScript* are sharing the same validation constraints, +// so they can be mostly reused, except for the messages. +const validStyleOptions = { + contentStyle: merge(Object.create(validationAttributes.contentScript), { + msg: 'The `contentStyle` option must be a string or an array of strings.' + }), + contentStyleFile: merge(Object.create(validationAttributes.contentScriptFile), { + msg: 'The `contentStyleFile` option must be a local URL or an array of URLs' + }) +}; + +/** + * PageMod constructor (exported below). + * @constructor + */ +const PageMod = Loader.compose(EventEmitter, { + on: EventEmitter.required, + _listeners: EventEmitter.required, + attachTo: [], + contentScript: Loader.required, + contentScriptFile: Loader.required, + contentScriptWhen: Loader.required, + contentScriptOptions: Loader.required, + include: null, + constructor: function PageMod(options) { + this._onContent = this._onContent.bind(this); + options = options || {}; + + let { contentStyle, contentStyleFile } = validate(options, validStyleOptions); + + if ('contentScript' in options) + this.contentScript = options.contentScript; + if ('contentScriptFile' in options) + this.contentScriptFile = options.contentScriptFile; + if ('contentScriptOptions' in options) + this.contentScriptOptions = options.contentScriptOptions; + if ('contentScriptWhen' in options) + this.contentScriptWhen = options.contentScriptWhen; + if ('onAttach' in options) + this.on('attach', options.onAttach); + if ('onError' in options) + this.on('error', options.onError); + if ('attachTo' in options) { + if (typeof options.attachTo == 'string') + this.attachTo = [options.attachTo]; + else if (Array.isArray(options.attachTo)) + this.attachTo = options.attachTo; + else + throw new Error('The `attachTo` option must be a string or an array ' + + 'of strings.'); + + let isValidAttachToItem = function isValidAttachToItem(item) { + return typeof item === 'string' && + VALID_ATTACHTO_OPTIONS.indexOf(item) !== -1; + } + if (!this.attachTo.every(isValidAttachToItem)) + throw new Error('The `attachTo` option valid accept only following ' + + 'values: '+ VALID_ATTACHTO_OPTIONS.join(', ')); + if (!hasAny(this.attachTo, ["top", "frame"])) + throw new Error('The `attachTo` option must always contain at least' + + ' `top` or `frame` value'); + } + else { + this.attachTo = ["top", "frame"]; + } + + let include = options.include; + let rules = this.include = Rules(); + + if (!include) + throw new Error('The `include` option must always contain atleast one rule'); + + rules.add.apply(rules, [].concat(include)); + + if (contentStyle || contentStyleFile) { + this._style = Style({ + uri: contentStyleFile, + source: contentStyle + }); + } + + this.on('error', this._onUncaughtError = this._onUncaughtError.bind(this)); + pageModManager.add(this._public); + mods.set(this._public, this); + + // `_applyOnExistingDocuments` has to be called after `pageModManager.add()` + // otherwise its calls to `_onContent` method won't do anything. + if ('attachTo' in options && has(options.attachTo, 'existing')) + this._applyOnExistingDocuments(); + }, + + destroy: function destroy() { + if (this._style) + detach(this._style); + + for (let i in this.include) + this.include.remove(this.include[i]); + + mods.delete(this._public); + pageModManager.remove(this._public); + }, + + _applyOnExistingDocuments: function _applyOnExistingDocuments() { + let mod = this; + let tabs = getAllTabs(); + + tabs.forEach(function (tab) { + // Fake a newly created document + let window = getTabContentWindow(tab); + if (has(mod.attachTo, "top") && mod.include.matchesAny(getTabURI(tab))) + mod._onContent(window); + if (has(mod.attachTo, "frame")) { + getFrames(window). + filter((iframe) => mod.include.matchesAny(iframe.location.href)). + forEach(mod._onContent); + } + }); + }, + + _onContent: function _onContent(window) { + // not registered yet + if (!pageModManager.has(this)) + return; + + let isTopDocument = window.top === window; + // Is a top level document and `top` is not set, ignore + if (isTopDocument && !has(this.attachTo, "top")) + return; + // Is a frame document and `frame` is not set, ignore + if (!isTopDocument && !has(this.attachTo, "frame")) + return; + + if (this._style) + attach(this._style, window); + + // Immediatly evaluate content script if the document state is already + // matching contentScriptWhen expectations + let state = window.document.readyState; + if ('start' === this.contentScriptWhen || + // Is `load` event already dispatched? + 'complete' === state || + // Is DOMContentLoaded already dispatched and waiting for it? + ('ready' === this.contentScriptWhen && state === 'interactive') ) { + this._createWorker(window); + return; + } + + let eventName = 'end' == this.contentScriptWhen ? 'load' : 'DOMContentLoaded'; + let self = this; + window.addEventListener(eventName, function onReady(event) { + if (event.target.defaultView != window) + return; + window.removeEventListener(eventName, onReady, true); + + self._createWorker(window); + }, true); + }, + _createWorker: function _createWorker(window) { + let worker = Worker({ + window: window, + contentScript: this.contentScript, + contentScriptFile: this.contentScriptFile, + contentScriptOptions: this.contentScriptOptions, + onError: this._onUncaughtError + }); + this._emit('attach', worker); + let self = this; + worker.once('detach', function detach() { + worker.destroy(); + }); + }, + _onUncaughtError: function _onUncaughtError(e) { + if (this._listeners('error').length == 1) + console.exception(e); + } +}); +exports.PageMod = function(options) PageMod(options) +exports.PageMod.prototype = PageMod.prototype; + +const PageModManager = Registry.resolve({ + constructor: '_init', + _destructor: '_registryDestructor' +}).compose({ + constructor: function PageModRegistry(constructor) { + this._init(PageMod); + observers.on( + 'document-element-inserted', + this._onContentWindow = this._onContentWindow.bind(this) + ); + }, + _destructor: function _destructor() { + observers.off('document-element-inserted', this._onContentWindow); + this._removeAllListeners(); + + // We need to do some cleaning er PageMods, like unregistering any + // `contentStyle*` + this._registry.forEach(function(pageMod) { + pageMod.destroy(); + }); + + this._registryDestructor(); + }, + _onContentWindow: function _onContentWindow({ subject: document }) { + let window = document.defaultView; + // XML documents don't have windows, and we don't yet support them. + if (!window) + return; + // We apply only on documents in tabs of Firefox + if (!getTabForContentWindow(window)) + return; + + // When the tab is private, only addons with 'private-browsing' flag in + // their package.json can apply content script to private documents + if (ignoreWindow(window)) { + return; + } + + this._registry.forEach(function(mod) { + if (mod.include.matchesAny(document.URL)) + mods.get(mod)._onContent(window); + }); + }, + off: function off(topic, listener) { + this.removeListener(topic, listener); + } +}); +const pageModManager = PageModManager(); + +// Returns all tabs on all currently opened windows +function getAllTabs() { + let tabs = []; + // Iterate over all chrome windows + for (let window in windowIterator()) { + if (!isBrowser(window)) + continue; + tabs = tabs.concat(getTabs(window)); + } + return tabs; +} diff --git a/addon-sdk-1.16/lib/sdk/page-mod/match-pattern.js b/addon-sdk-1.16/lib/sdk/page-mod/match-pattern.js new file mode 100644 index 00000000..37b7bfb8 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/page-mod/match-pattern.js @@ -0,0 +1,5 @@ +let { deprecateUsage } = require("../util/deprecate"); + +deprecateUsage("Module 'sdk/page-mod/match-pattern' is deprecated use 'sdk/util/match-pattern' instead"); + +module.exports = require("../util/match-pattern"); diff --git a/addon-sdk-1.16/lib/sdk/page-worker.js b/addon-sdk-1.16/lib/sdk/page-worker.js new file mode 100644 index 00000000..a5a7bf60 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/page-worker.js @@ -0,0 +1,170 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +module.metadata = { + "stability": "stable" +}; + +const { Class } = require('./core/heritage'); +const { on, emit, off, setListeners } = require('./event/core'); +const { filter, pipe, map, merge: streamMerge, stripListeners } = require('./event/utils'); +const { detach, attach, destroy, WorkerHost } = require('./content/utils'); +const { Worker } = require('./content/worker'); +const { Disposable } = require('./core/disposable'); +const { EventTarget } = require('./event/target'); +const { unload } = require('./system/unload'); +const { events, streamEventsFrom } = require('./content/events'); +const { getAttachEventType } = require('./content/utils'); +const { window } = require('./addon/window'); +const { getParentWindow } = require('./window/utils'); +const { create: makeFrame, getDocShell } = require('./frame/utils'); +const { contract } = require('./util/contract'); +const { contract: loaderContract } = require('./content/loader'); +const { has } = require('./util/array'); +const { Rules } = require('./util/rules'); +const { merge } = require('./util/object'); + +const views = WeakMap(); +const workers = WeakMap(); +const pages = WeakMap(); + +const readyEventNames = [ + 'DOMContentLoaded', + 'document-element-inserted', + 'load' +]; + +function workerFor(page) workers.get(page) +function pageFor(view) pages.get(view) +function viewFor(page) views.get(page) +function isDisposed (page) !views.get(page, false) + +let pageContract = contract(merge({ + allow: { + is: ['object', 'undefined', 'null'], + map: function (allow) { return { script: !allow || allow.script !== false }} + }, + onMessage: { + is: ['function', 'undefined'] + }, + include: { + is: ['string', 'array', 'undefined'] + }, + contentScriptWhen: { + is: ['string', 'undefined'] + } +}, loaderContract.rules)); + +function enableScript (page) { + getDocShell(viewFor(page)).allowJavascript = true; +} + +function disableScript (page) { + getDocShell(viewFor(page)).allowJavascript = false; +} + +function Allow (page) { + return { + get script() { return getDocShell(viewFor(page)).allowJavascript; }, + set script(value) { return value ? enableScript(page) : disableScript(page); } + }; +} + +function injectWorker ({page}) { + let worker = workerFor(page); + let view = viewFor(page); + if (isValidURL(page, view.contentDocument.URL)) + attach(worker, view.contentWindow); +} + +function isValidURL(page, url) !page.rules || page.rules.matchesAny(url) + +const Page = Class({ + implements: [ + EventTarget, + Disposable + ], + extends: WorkerHost(workerFor), + setup: function Page(options) { + let page = this; + options = pageContract(options); + let view = makeFrame(window.document, { + nodeName: 'iframe', + type: 'content', + uri: options.contentURL, + allowJavascript: options.allow.script, + allowPlugins: true, + allowAuth: true + }); + + ['contentScriptFile', 'contentScript', 'contentScriptWhen'] + .forEach(prop => page[prop] = options[prop]); + + views.set(this, view); + pages.set(view, this); + + // Set listeners on the {Page} object itself, not the underlying worker, + // like `onMessage`, as it gets piped + setListeners(this, options); + let worker = new Worker(stripListeners(options)); + workers.set(this, worker); + pipe(worker, this); + + if (this.include || options.include) { + this.rules = Rules(); + this.rules.add.apply(this.rules, [].concat(this.include || options.include)); + } + }, + get allow() { return Allow(this); }, + set allow(value) { + let allowJavascript = pageContract({ allow: value }).allow.script; + return allowJavascript ? enableScript(this) : disableScript(this); + }, + get contentURL() { return viewFor(this).getAttribute('src'); }, + set contentURL(value) { + if (!isValidURL(this, value)) return; + let view = viewFor(this); + let contentURL = pageContract({ contentURL: value }).contentURL; + view.setAttribute('src', contentURL); + }, + dispose: function () { + if (isDisposed(this)) return; + let view = viewFor(this); + if (view.parentNode) view.parentNode.removeChild(view); + views.delete(this); + destroy(workers.get(this)); + }, + toString: function () { return '[object Page]' } +}); + +exports.Page = Page; + +let pageEvents = streamMerge([events, streamEventsFrom(window)]); +let readyEvents = filter(pageEvents, isReadyEvent); +let formattedEvents = map(readyEvents, function({target, type}) { + return { type: type, page: pageFromDoc(target) }; +}); +let pageReadyEvents = filter(formattedEvents, function({page, type}) { + return getAttachEventType(page) === type}); +on(pageReadyEvents, 'data', injectWorker); + +function isReadyEvent ({type}) { + return has(readyEventNames, type); +} + +/* + * Takes a document, finds its doc shell tree root and returns the + * matching Page instance if found + */ +function pageFromDoc(doc) { + let parentWindow = getParentWindow(doc.defaultView), page; + if (!parentWindow) return; + + let frames = parentWindow.document.getElementsByTagName('iframe'); + for (let i = frames.length; i--;) + if (frames[i].contentDocument === doc && (page = pageFor(frames[i]))) + return page; + return null; +} diff --git a/addon-sdk-1.16/lib/sdk/panel.js b/addon-sdk-1.16/lib/sdk/panel.js new file mode 100644 index 00000000..19b0ee6a --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/panel.js @@ -0,0 +1,264 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +// The panel module currently supports only Firefox. +// See: https://bugzilla.mozilla.org/show_bug.cgi?id=jetpack-panel-apps +module.metadata = { + "stability": "stable", + "engines": { + "Firefox": "*" + } +}; + +const { Ci } = require("chrome"); +const { validateOptions: valid } = require('./deprecated/api-utils'); +const { setTimeout } = require('./timers'); +const { isPrivateBrowsingSupported } = require('./self'); +const { isWindowPBSupported } = require('./private-browsing/utils'); +const { Class } = require("./core/heritage"); +const { merge } = require("./util/object"); +const { WorkerHost, detach, attach, destroy } = require("./content/utils"); +const { Worker } = require("./content/worker"); +const { Disposable } = require("./core/disposable"); +const { contract: loaderContract } = require("./content/loader"); +const { contract } = require("./util/contract"); +const { on, off, emit, setListeners } = require("./event/core"); +const { EventTarget } = require("./event/target"); +const domPanel = require("./panel/utils"); +const { events } = require("./panel/events"); +const systemEvents = require("./system/events"); +const { filter, pipe, stripListeners } = require("./event/utils"); +const { getNodeView, getActiveView } = require("./view/core"); +const { isNil, isObject } = require("./lang/type"); +const { getAttachEventType } = require("./content/utils"); + +let number = { is: ['number', 'undefined', 'null'] }; +let boolean = { is: ['boolean', 'undefined', 'null'] }; + +let rectContract = contract({ + top: number, + right: number, + bottom: number, + left: number +}); + +let rect = { + is: ['object', 'undefined', 'null'], + map: function(v) isNil(v) || !isObject(v) ? v : rectContract(v) +} + +let displayContract = contract({ + width: number, + height: number, + focus: boolean, + position: rect +}); + +let panelContract = contract(merge({}, displayContract.rules, loaderContract.rules)); + + +function isDisposed(panel) !views.has(panel); + +let panels = new WeakMap(); +let models = new WeakMap(); +let views = new WeakMap(); +let workers = new WeakMap(); + +function viewFor(panel) views.get(panel) +function modelFor(panel) models.get(panel) +function panelFor(view) panels.get(view) +function workerFor(panel) workers.get(panel) + + +// Utility function takes `panel` instance and makes sure it will be +// automatically hidden as soon as other panel is shown. +let setupAutoHide = new function() { + let refs = new WeakMap(); + + return function setupAutoHide(panel) { + // Create system event listener that reacts to any panel showing and + // hides given `panel` if it's not the one being shown. + function listener({subject}) { + // It could be that listener is not GC-ed in the same cycle as + // panel in such case we remove listener manually. + let view = viewFor(panel); + if (!view) systemEvents.off("popupshowing", listener); + else if (subject !== view) panel.hide(); + } + + // system event listener is intentionally weak this way we'll allow GC + // to claim panel if it's no longer referenced by an add-on code. This also + // helps minimizing cleanup required on unload. + systemEvents.on("popupshowing", listener); + // To make sure listener is not claimed by GC earlier than necessary we + // associate it with `panel` it's associated with. This way it won't be + // GC-ed earlier than `panel` itself. + refs.set(panel, listener); + } +} + +const Panel = Class({ + implements: [ + // Generate accessors for the validated properties that update model on + // set and return values from model on get. + panelContract.properties(modelFor), + EventTarget, + Disposable + ], + extends: WorkerHost(workerFor), + setup: function setup(options) { + let model = merge({ + defaultWidth: 320, + defaultHeight: 240, + focus: true, + position: Object.freeze({}), + }, panelContract(options)); + models.set(this, model); + + + // Setup view + let view = domPanel.make(); + panels.set(view, this); + views.set(this, view); + + // Load panel content. + domPanel.setURL(view, model.contentURL); + + setupAutoHide(this); + + // Setup listeners. + setListeners(this, options); + let worker = new Worker(stripListeners(options)); + workers.set(this, worker); + + // pipe events from worker to a panel. + pipe(worker, this); + }, + dispose: function dispose() { + this.hide(); + off(this); + + destroy(workerFor(this)); + + domPanel.dispose(viewFor(this)); + + // Release circular reference between view and panel instance. This + // way view will be GC-ed. And panel as well once all the other refs + // will be removed from it. + views.delete(this); + }, + /* Public API: Panel.width */ + get width() modelFor(this).width, + set width(value) this.resize(value, this.height), + /* Public API: Panel.height */ + get height() modelFor(this).height, + set height(value) this.resize(this.width, value), + + /* Public API: Panel.focus */ + get focus() modelFor(this).focus, + + /* Public API: Panel.position */ + get position() modelFor(this).position, + + get contentURL() modelFor(this).contentURL, + set contentURL(value) { + let model = modelFor(this); + model.contentURL = panelContract({ contentURL: value }).contentURL; + domPanel.setURL(viewFor(this), model.contentURL); + // Detach worker so that messages send will be queued until it's + // reatached once panel content is ready. + detach(workerFor(this)); + }, + + /* Public API: Panel.isShowing */ + get isShowing() !isDisposed(this) && domPanel.isOpen(viewFor(this)), + + /* Public API: Panel.show */ + show: function show(options, anchor) { + if (options instanceof Ci.nsIDOMElement) { + [anchor, options] = [options, null]; + } + + if (anchor instanceof Ci.nsIDOMElement) { + console.warn( + "Passing a DOM node to Panel.show() method is an unsupported " + + "feature that will be soon replaced. " + + "See: https://bugzilla.mozilla.org/show_bug.cgi?id=878877" + ); + } + + let model = modelFor(this); + let view = viewFor(this); + let anchorView = getNodeView(anchor); + + options = merge({ + position: model.position, + width: model.width, + height: model.height, + defaultWidth: model.defaultWidth, + defaultHeight: model.defaultHeight, + focus: model.focus + }, displayContract(options)); + + if (!isDisposed(this)) + domPanel.show(view, options, anchorView); + + return this; + }, + + /* Public API: Panel.hide */ + hide: function hide() { + // Quit immediately if panel is disposed or there is no state change. + domPanel.close(viewFor(this)); + + return this; + }, + + /* Public API: Panel.resize */ + resize: function resize(width, height) { + let model = modelFor(this); + let view = viewFor(this); + let change = panelContract({ + width: width || model.width || model.defaultWidth, + height: height || model.height || model.defaultHeight + }); + + model.width = change.width + model.height = change.height + + domPanel.resize(view, model.width, model.height); + + return this; + } +}); +exports.Panel = Panel; + +// Note must be defined only after value to `Panel` is assigned. +getActiveView.define(Panel, viewFor); + +// Filter panel events to only panels that are create by this module. +let panelEvents = filter(events, function({target}) panelFor(target)); + +// Panel events emitted after panel has being shown. +let shows = filter(panelEvents, function({type}) type === "popupshown"); + +// Panel events emitted after panel became hidden. +let hides = filter(panelEvents, function({type}) type === "popuphidden"); + +// Panel events emitted after content inside panel is ready. For different +// panels ready may mean different state based on `contentScriptWhen` attribute. +// Weather given event represents readyness is detected by `getAttachEventType` +// helper function. +let ready = filter(panelEvents, function({type, target}) + getAttachEventType(modelFor(panelFor(target))) === type); + +// Forward panel show / hide events to panel's own event listeners. +on(shows, "data", function({target}) emit(panelFor(target), "show")); +on(hides, "data", function({target}) emit(panelFor(target), "hide")); + +on(ready, "data", function({target}) { + let worker = workerFor(panelFor(target)); + attach(worker, domPanel.getContentDocument(target).defaultView); +}); diff --git a/addon-sdk-1.16/lib/sdk/panel/events.js b/addon-sdk-1.16/lib/sdk/panel/events.js new file mode 100644 index 00000000..f67af175 --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/panel/events.js @@ -0,0 +1,26 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +// This module basically translates system/events to a SDK standard events +// so that `map`, `filter` and other utilities could be used with them. + +module.metadata = { + "stability": "experimental" +}; + +const events = require("../system/events"); +const { emit } = require("../event/core"); + +let channel = {}; + +function forward({ subject, type, data }) + emit(channel, "data", { target: subject, type: type, data: data }); + +["popupshowing", "popuphiding", "popupshown", "popuphidden", +"document-element-inserted", "DOMContentLoaded", "load" +].forEach(function(type) events.on(type, forward)); + +exports.events = channel; diff --git a/addon-sdk-1.16/lib/sdk/panel/utils.js b/addon-sdk-1.16/lib/sdk/panel/utils.js new file mode 100644 index 00000000..6dd63f4e --- /dev/null +++ b/addon-sdk-1.16/lib/sdk/panel/utils.js @@ -0,0 +1,380 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + "stability": "unstable" +}; + +const { Cc, Ci } = require("chrome"); +const { setTimeout } = require("../timers"); +const { platform } = require("../system"); +const { getMostRecentBrowserWindow, getOwnerBrowserWindow, + getHiddenWindow, getScreenPixelsPerCSSPixel } = require("../window/utils"); + +const { create: createFrame, swapFrameLoaders } = require("../frame/utils"); +const { window: addonWindow } = require("../addon/window"); +const { isNil } = require("../lang/type"); +const events = require("../system/events"); + + +const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; + +function calculateRegion({ position, width, height, defaultWidth, defaultHeight }, rect) { + let x, y; + + let hasTop = !isNil(position.top); + let hasRight = !isNil(position.right); + let hasBottom = !isNil(position.bottom); + let hasLeft = !isNil(position.left); + let hasWidth = !isNil(width); + let hasHeight = !isNil(height); + + // if width is not specified by constructor or show's options, then get + // the default width + if (!hasWidth) + width = defaultWidth; + + // if height is not specified by constructor or show's options, then get + // the default height + if (!hasHeight) + height = defaultHeight; + + // default position is centered + x = (rect.right - width) / 2; + y = (rect.top + rect.bottom - height) / 2; + + if (hasTop) { + y = rect.top + position.top; + + if (hasBottom && !hasHeight) + height = rect.bottom - position.bottom - y; + } + else if (hasBottom) { + y = rect.bottom - position.bottom - height; + } + + if (hasLeft) { + x = position.left; + + if (hasRight && !hasWidth) + width = rect.right - position.right - x; + } + else if (hasRight) { + x = rect.right - width - position.right; + } + + return {x: x, y: y, width: width, height: height}; +} + +function open(panel, options, anchor) { + // Wait for the XBL binding to be constructed + if (!panel.openPopup) setTimeout(open, 50, panel, options, anchor); + else display(panel, options, anchor); +} +exports.open = open; + +function isOpen(panel) { + return panel.state === "open" +} +exports.isOpen = isOpen; + +function isOpening(panel) { + return panel.state === "showing" +} +exports.isOpening = isOpening + +function close(panel) { + // Sometimes "TypeError: panel.hidePopup is not a function" is thrown + // when quitting the host application while a panel is visible. To suppress + // these errors, check for "hidePopup" in panel before calling it. + // It's not clear if there's an issue or it's expected behavior. + + return panel.hidePopup && panel.hidePopup(); +} +exports.close = close + + +function resize(panel, width, height) { + // Resize the iframe instead of using panel.sizeTo + // because sizeTo doesn't work with arrow panels + panel.firstChild.style.width = width + "px"; + panel.firstChild.style.height = height + "px"; +} +exports.resize = resize + +function display(panel, options, anchor) { + let document = panel.ownerDocument; + + let x, y; + let { width, height, defaultWidth, defaultHeight } = options; + + let popupPosition = null; + + // Panel XBL has some SDK incompatible styling decisions. We shim panel + // instances until proper fix for Bug 859504 is shipped. + shimDefaultStyle(panel); + + if (!anchor) { + // The XUL Panel doesn't have an arrow, so the margin needs to be reset + // in order to, be positioned properly + panel.style.margin = "0"; + + let viewportRect = document.defaultView.gBrowser.getBoundingClientRect(); + + ({x, y, width, height}) = calculateRegion(options, viewportRect); + } + else { + width = width || defaultWidth; + height = height || defaultHeight; + + // Open the popup by the anchor. + let rect = anchor.getBoundingClientRect(); + + let window = anchor.ownerDocument.defaultView; + + let zoom = getScreenPixelsPerCSSPixel(window); + let screenX = rect.left + window.mozInnerScreenX * zoom; + let screenY = rect.top + window.mozInnerScreenY * zoom; + + // Set up the vertical position of the popup relative to the anchor + // (always display the arrow on anchor center) + let horizontal, vertical; + if (screenY > window.screen.availHeight / 2 + height) + vertical = "top"; + else + vertical = "bottom"; + + if (screenY > window.screen.availWidth / 2 + width) + horizontal = "left"; + else + horizontal = "right"; + + let verticalInverse = vertical == "top" ? "bottom" : "top"; + popupPosition = vertical + "center " + verticalInverse + horizontal; + + // Allow panel to flip itself if the panel can't be displayed at the + // specified position (useful if we compute a bad position or if the + // user moves the window and panel remains visible) + panel.setAttribute("flip", "both"); + } + + // Resize the iframe instead of using panel.sizeTo + // because sizeTo doesn't work with arrow panels + panel.firstChild.style.width = width + "px"; + panel.firstChild.style.height = height + "px"; + + panel.openPopup(anchor, popupPosition, x, y); +} +exports.display = display; + +// This utility function is just a workaround until Bug 859504 has shipped. +function shimDefaultStyle(panel) { + let document = panel.ownerDocument; + // Please note that `panel` needs to be part of document in order to reach + // it's anonymous nodes. One of the anonymous node has a big padding which + // doesn't work well since panel frame needs to fill all of the panel. + // XBL binding is a not the best option as it's applied asynchronously, and + // makes injected frames behave in strange way. Also this feels a lot + // cheaper to do. + ["panel-inner-arrowcontent", "panel-arrowcontent"].forEach(function(value) { + let node = document.getAnonymousElementByAttribute(panel, "class", value); + if (node) node.style.padding = 0; + }); +} + +function show(panel, options, anchor) { + // Prevent the panel from getting focus when showing up + // if focus is set to false + panel.setAttribute("noautofocus", !options.focus); + + let window = anchor && getOwnerBrowserWindow(anchor); + let { document } = window ? window : getMostRecentBrowserWindow(); + attach(panel, document); + + open(panel, options, anchor); +} +exports.show = show + +function setupPanelFrame(frame) { + frame.setAttribute("flex", 1); + frame.setAttribute("transparent", "transparent"); + frame.setAttribute("showcaret", true); + frame.setAttribute("autocompleteenabled", true); + if (platform === "darwin") { + frame.style.borderRadius = "6px"; + frame.style.padding = "1px"; + } +} + +function make(document) { + document = document || getMostRecentBrowserWindow().document; + let panel = document.createElementNS(XUL_NS, "panel"); + panel.setAttribute("type", "arrow"); + + // Note that panel is a parent of `viewFrame` who's `docShell` will be + // configured at creation time. If `panel` and there for `viewFrame` won't + // have an owner document attempt to access `docShell` will throw. There + // for we attach panel to a document. + attach(panel, document); + + let frameOptions = { + allowJavascript: true, + allowPlugins: true, + allowAuth: true, + allowWindowControl: false, + // Need to override `nodeName` to use `iframe` as `browsers` save session + // history and in consequence do not dispatch "inner-window-destroyed" + // notifications. + browser: false, + // Note that use of this URL let's use swap frame loaders earlier + // than if we used default "about:blank". + uri: "data:text/plain;charset=utf-8," + }; + + let backgroundFrame = createFrame(addonWindow, frameOptions); + setupPanelFrame(backgroundFrame); + + let viewFrame = createFrame(panel, frameOptions); + setupPanelFrame(viewFrame); + + function onDisplayChange({type, target}) { + // Events from child element like \ + \ +"; + +const URL = "data:text/html;charset=utf-8," + encodeURIComponent(HTML); + +const FRAME_HTML = " + + diff --git a/addon-sdk-1.16/test/fixtures/test-iframe.js b/addon-sdk-1.16/test/fixtures/test-iframe.js new file mode 100644 index 00000000..c7f83451 --- /dev/null +++ b/addon-sdk-1.16/test/fixtures/test-iframe.js @@ -0,0 +1,13 @@ + +var count = 0; + +setTimeout(function() { + window.addEventListener("message", function(msg) { + if (++count > 1) { + self.postMessage(msg.data); + } + else msg.source.postMessage(msg.data, '*'); + }); + + document.getElementById('inner').src = iframePath; +}, 0); diff --git a/addon-sdk-1.16/test/fixtures/test-message-manager.js b/addon-sdk-1.16/test/fixtures/test-message-manager.js new file mode 100644 index 00000000..d647bd8f --- /dev/null +++ b/addon-sdk-1.16/test/fixtures/test-message-manager.js @@ -0,0 +1,6 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const TEST_VALUE = 11; + diff --git a/addon-sdk-1.16/test/fixtures/test-net-url.txt b/addon-sdk-1.16/test/fixtures/test-net-url.txt new file mode 100644 index 00000000..9f8166e6 --- /dev/null +++ b/addon-sdk-1.16/test/fixtures/test-net-url.txt @@ -0,0 +1 @@ +Hello, ゼロ! \ No newline at end of file diff --git a/addon-sdk-1.16/test/fixtures/test-page-mod.html b/addon-sdk-1.16/test/fixtures/test-page-mod.html new file mode 100644 index 00000000..901abefc --- /dev/null +++ b/addon-sdk-1.16/test/fixtures/test-page-mod.html @@ -0,0 +1,12 @@ + + + + + Page Mod test + + +

Lorem ipsum dolor sit amet.

+ + diff --git a/addon-sdk-1.16/test/fixtures/test-page-worker.html b/addon-sdk-1.16/test/fixtures/test-page-worker.html new file mode 100644 index 00000000..85264034 --- /dev/null +++ b/addon-sdk-1.16/test/fixtures/test-page-worker.html @@ -0,0 +1,13 @@ + + + + + + Page Worker test + + +

Lorem ipsum dolor sit amet.

+ + diff --git a/addon-sdk-1.16/test/fixtures/test-page-worker.js b/addon-sdk-1.16/test/fixtures/test-page-worker.js new file mode 100644 index 00000000..11108702 --- /dev/null +++ b/addon-sdk-1.16/test/fixtures/test-page-worker.js @@ -0,0 +1,29 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +// get title directly +self.postMessage(["equal", document.title, "Page Worker test", + "Correct page title accessed directly"]); + +// get

directly +let p = document.getElementById("paragraph"); +self.postMessage(["ok", !!p, "

can be accessed directly"]); +self.postMessage(["equal", p.firstChild.nodeValue, + "Lorem ipsum dolor sit amet.", + "Correct text node expected"]); + +// Modify page +let div = document.createElement("div"); +div.setAttribute("id", "block"); +div.appendChild(document.createTextNode("Test text created")); +document.body.appendChild(div); + +// Check back the modification +div = document.getElementById("block"); +self.postMessage(["ok", !!div, "

can be accessed directly"]); +self.postMessage(["equal", div.firstChild.nodeValue, + "Test text created", "Correct text node expected"]); +self.postMessage(["done"]); + diff --git a/addon-sdk-1.16/test/fixtures/test-sidebar-addon-global.html b/addon-sdk-1.16/test/fixtures/test-sidebar-addon-global.html new file mode 100644 index 00000000..cfa07395 --- /dev/null +++ b/addon-sdk-1.16/test/fixtures/test-sidebar-addon-global.html @@ -0,0 +1,10 @@ + +SIDEBAR TEST diff --git a/addon-sdk-1.16/test/fixtures/test-trusted-document.html b/addon-sdk-1.16/test/fixtures/test-trusted-document.html new file mode 100644 index 00000000..c31e055c --- /dev/null +++ b/addon-sdk-1.16/test/fixtures/test-trusted-document.html @@ -0,0 +1,20 @@ + + + + + + Worker test + + +

Lorem ipsum dolor sit amet.

+ + + diff --git a/addon-sdk-1.16/test/fixtures/test.html b/addon-sdk-1.16/test/fixtures/test.html new file mode 100644 index 00000000..181e85f9 --- /dev/null +++ b/addon-sdk-1.16/test/fixtures/test.html @@ -0,0 +1,13 @@ + + + + + + foo + + +

bar

+ + diff --git a/addon-sdk-1.16/test/fixtures/testLocalXhr.json b/addon-sdk-1.16/test/fixtures/testLocalXhr.json new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/addon-sdk-1.16/test/fixtures/testLocalXhr.json @@ -0,0 +1 @@ +{} diff --git a/addon-sdk-1.16/test/loader/fixture.js b/addon-sdk-1.16/test/loader/fixture.js new file mode 100644 index 00000000..ebf91abb --- /dev/null +++ b/addon-sdk-1.16/test/loader/fixture.js @@ -0,0 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +exports.foo = foo; +exports.bar = 2; +print('testing'); diff --git a/addon-sdk-1.16/test/modules/add.js b/addon-sdk-1.16/test/modules/add.js new file mode 100644 index 00000000..54729340 --- /dev/null +++ b/addon-sdk-1.16/test/modules/add.js @@ -0,0 +1,9 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +define('modules/add', function () { + return function (a, b) { + return a + b; + }; +}); diff --git a/addon-sdk-1.16/test/modules/async1.js b/addon-sdk-1.16/test/modules/async1.js new file mode 100644 index 00000000..b7b60fdc --- /dev/null +++ b/addon-sdk-1.16/test/modules/async1.js @@ -0,0 +1,14 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +define(['./traditional2', './async2'], function () { + var traditional2 = require('./traditional2'); + return { + name: 'async1', + traditional1Name: traditional2.traditional1Name, + traditional2Name: traditional2.name, + async2Name: require('./async2').name, + async2Traditional2Name: require('./async2').traditional2Name + }; +}); diff --git a/addon-sdk-1.16/test/modules/async2.js b/addon-sdk-1.16/test/modules/async2.js new file mode 100644 index 00000000..802fb250 --- /dev/null +++ b/addon-sdk-1.16/test/modules/async2.js @@ -0,0 +1,8 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +define(['./traditional2', 'exports'], function (traditional2, exports) { + exports.name = 'async2'; + exports.traditional2Name = traditional2.name; +}); diff --git a/addon-sdk-1.16/test/modules/badExportAndReturn.js b/addon-sdk-1.16/test/modules/badExportAndReturn.js new file mode 100644 index 00000000..35a5359f --- /dev/null +++ b/addon-sdk-1.16/test/modules/badExportAndReturn.js @@ -0,0 +1,10 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +// This is a bad module, it asks for exports but also returns a value from +// the define defintion function. +define(['exports'], function (exports) { + return 'badExportAndReturn'; +}); + diff --git a/addon-sdk-1.16/test/modules/badFirst.js b/addon-sdk-1.16/test/modules/badFirst.js new file mode 100644 index 00000000..fdb03bd4 --- /dev/null +++ b/addon-sdk-1.16/test/modules/badFirst.js @@ -0,0 +1,9 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +define(['./badSecond'], function (badSecond) { + return { + name: 'badFirst' + }; +}); diff --git a/addon-sdk-1.16/test/modules/badSecond.js b/addon-sdk-1.16/test/modules/badSecond.js new file mode 100644 index 00000000..8321a854 --- /dev/null +++ b/addon-sdk-1.16/test/modules/badSecond.js @@ -0,0 +1,8 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +var first = require('./badFirst'); + +exports.name = 'badSecond'; +exports.badFirstName = first.name; diff --git a/addon-sdk-1.16/test/modules/blue.js b/addon-sdk-1.16/test/modules/blue.js new file mode 100644 index 00000000..14ab1493 --- /dev/null +++ b/addon-sdk-1.16/test/modules/blue.js @@ -0,0 +1,9 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +define(function () { + return { + name: 'blue' + }; +}); diff --git a/addon-sdk-1.16/test/modules/castor.js b/addon-sdk-1.16/test/modules/castor.js new file mode 100644 index 00000000..9209623b --- /dev/null +++ b/addon-sdk-1.16/test/modules/castor.js @@ -0,0 +1,10 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +define(['exports', './pollux'], function(exports, pollux) { + exports.name = 'castor'; + exports.getPolluxName = function () { + return pollux.name; + }; +}); diff --git a/addon-sdk-1.16/test/modules/cheetah.js b/addon-sdk-1.16/test/modules/cheetah.js new file mode 100644 index 00000000..37d12bfc --- /dev/null +++ b/addon-sdk-1.16/test/modules/cheetah.js @@ -0,0 +1,9 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +define(function () { + return function () { + return 'cheetah'; + }; +}); diff --git a/addon-sdk-1.16/test/modules/color.js b/addon-sdk-1.16/test/modules/color.js new file mode 100644 index 00000000..0d946bd9 --- /dev/null +++ b/addon-sdk-1.16/test/modules/color.js @@ -0,0 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +define({ + type: 'color' +}); diff --git a/addon-sdk-1.16/test/modules/dupe.js b/addon-sdk-1.16/test/modules/dupe.js new file mode 100644 index 00000000..f87fa555 --- /dev/null +++ b/addon-sdk-1.16/test/modules/dupe.js @@ -0,0 +1,15 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +define({ + name: 'dupe' +}); + +// This is wrong and should not be allowed. Only one call to +// define per file. +define([], function () { + return { + name: 'dupe2' + }; +}); diff --git a/addon-sdk-1.16/test/modules/dupeNested.js b/addon-sdk-1.16/test/modules/dupeNested.js new file mode 100644 index 00000000..703948ca --- /dev/null +++ b/addon-sdk-1.16/test/modules/dupeNested.js @@ -0,0 +1,15 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +define(function () { + // This is wrong and should not be allowed. + define('dupeNested2', { + name: 'dupeNested2' + }); + + return { + name: 'dupeNested' + }; +}); diff --git a/addon-sdk-1.16/test/modules/dupeSetExports.js b/addon-sdk-1.16/test/modules/dupeSetExports.js new file mode 100644 index 00000000..12a1be22 --- /dev/null +++ b/addon-sdk-1.16/test/modules/dupeSetExports.js @@ -0,0 +1,8 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +define({name: "dupeSetExports"}); + +// so this should cause a failure +module.setExports("no no no"); diff --git a/addon-sdk-1.16/test/modules/exportsEquals.js b/addon-sdk-1.16/test/modules/exportsEquals.js new file mode 100644 index 00000000..e176316f --- /dev/null +++ b/addon-sdk-1.16/test/modules/exportsEquals.js @@ -0,0 +1,5 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +module.exports = 4; diff --git a/addon-sdk-1.16/test/modules/green.js b/addon-sdk-1.16/test/modules/green.js new file mode 100644 index 00000000..ce2d134b --- /dev/null +++ b/addon-sdk-1.16/test/modules/green.js @@ -0,0 +1,10 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +define('modules/green', ['./color'], function (color) { + return { + name: 'green', + parentType: color.type + }; +}); diff --git a/addon-sdk-1.16/test/modules/lion.js b/addon-sdk-1.16/test/modules/lion.js new file mode 100644 index 00000000..9c3ac3bf --- /dev/null +++ b/addon-sdk-1.16/test/modules/lion.js @@ -0,0 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +define(function(require) { + return 'lion'; +}); diff --git a/addon-sdk-1.16/test/modules/orange.js b/addon-sdk-1.16/test/modules/orange.js new file mode 100644 index 00000000..900a32b0 --- /dev/null +++ b/addon-sdk-1.16/test/modules/orange.js @@ -0,0 +1,10 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +define(['./color'], function (color) { + return { + name: 'orange', + parentType: color.type + }; +}); diff --git a/addon-sdk-1.16/test/modules/pollux.js b/addon-sdk-1.16/test/modules/pollux.js new file mode 100644 index 00000000..61cf66f9 --- /dev/null +++ b/addon-sdk-1.16/test/modules/pollux.js @@ -0,0 +1,10 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +define(['exports', './castor'], function(exports, castor) { + exports.name = 'pollux'; + exports.getCastorName = function () { + return castor.name; + }; +}); diff --git a/addon-sdk-1.16/test/modules/red.js b/addon-sdk-1.16/test/modules/red.js new file mode 100644 index 00000000..c47b8e92 --- /dev/null +++ b/addon-sdk-1.16/test/modules/red.js @@ -0,0 +1,16 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +define(function (require) { + // comment fake-outs for require finding. + // require('bad1'); + return { + name: 'red', + parentType: require('./color').type + }; + + /* + require('bad2'); + */ +}); diff --git a/addon-sdk-1.16/test/modules/setExports.js b/addon-sdk-1.16/test/modules/setExports.js new file mode 100644 index 00000000..495c301c --- /dev/null +++ b/addon-sdk-1.16/test/modules/setExports.js @@ -0,0 +1,5 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +module.setExports(5); diff --git a/addon-sdk-1.16/test/modules/subtract.js b/addon-sdk-1.16/test/modules/subtract.js new file mode 100644 index 00000000..06e1053a --- /dev/null +++ b/addon-sdk-1.16/test/modules/subtract.js @@ -0,0 +1,9 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +define(function () { + return function (a, b) { + return a - b; + } +}); diff --git a/addon-sdk-1.16/test/modules/tiger.js b/addon-sdk-1.16/test/modules/tiger.js new file mode 100644 index 00000000..80479d01 --- /dev/null +++ b/addon-sdk-1.16/test/modules/tiger.js @@ -0,0 +1,8 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +define(function (require, exports) { + exports.name = 'tiger'; + exports.type = require('./types/cat').type; +}); diff --git a/addon-sdk-1.16/test/modules/traditional1.js b/addon-sdk-1.16/test/modules/traditional1.js new file mode 100644 index 00000000..28af8ef3 --- /dev/null +++ b/addon-sdk-1.16/test/modules/traditional1.js @@ -0,0 +1,12 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +exports.name = 'traditional1' + +var async1 = require('./async1'); + +exports.traditional2Name = async1.traditional2Name; +exports.traditional1Name = async1.traditional1Name; +exports.async2Name = async1.async2Name; +exports.async2Traditional2Name = async1.async2Traditional2Name; diff --git a/addon-sdk-1.16/test/modules/traditional2.js b/addon-sdk-1.16/test/modules/traditional2.js new file mode 100644 index 00000000..67b64eec --- /dev/null +++ b/addon-sdk-1.16/test/modules/traditional2.js @@ -0,0 +1,6 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +exports.name = 'traditional2'; +exports.traditional1Name = require('./traditional1').name; diff --git a/addon-sdk-1.16/test/modules/types/cat.js b/addon-sdk-1.16/test/modules/types/cat.js new file mode 100644 index 00000000..41513d68 --- /dev/null +++ b/addon-sdk-1.16/test/modules/types/cat.js @@ -0,0 +1,5 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +exports.type = 'cat'; diff --git a/addon-sdk-1.16/test/pagemod-test-helpers.js b/addon-sdk-1.16/test/pagemod-test-helpers.js new file mode 100644 index 00000000..8bcd3ebd --- /dev/null +++ b/addon-sdk-1.16/test/pagemod-test-helpers.js @@ -0,0 +1,64 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const {Cc,Ci} = require("chrome"); +const timer = require("sdk/timers"); +const xulApp = require("sdk/system/xul-app"); +const { Loader } = require("sdk/test/loader"); +const { openTab, getBrowserForTab, closeTab } = require("sdk/tabs/utils"); + +/** + * A helper function that creates a PageMod, then opens the specified URL + * and checks the effect of the page mod on 'onload' event via testCallback. + */ +exports.testPageMod = function testPageMod(assert, done, testURL, pageModOptions, + testCallback, timeout) { + if (!xulApp.versionInRange(xulApp.platformVersion, "1.9.3a3", "*") && + !xulApp.versionInRange(xulApp.platformVersion, "1.9.2.7", "1.9.2.*")) { + assert.pass("Note: not testing PageMod, as it doesn't work on this platform version"); + return null; + } + + var wm = Cc['@mozilla.org/appshell/window-mediator;1'] + .getService(Ci.nsIWindowMediator); + var browserWindow = wm.getMostRecentWindow("navigator:browser"); + if (!browserWindow) { + assert.pass("page-mod tests: could not find the browser window, so " + + "will not run. Use -a firefox to run the pagemod tests.") + return null; + } + + let loader = Loader(module); + let pageMod = loader.require("sdk/page-mod"); + + var pageMods = [new pageMod.PageMod(opts) for each(opts in pageModOptions)]; + + let newTab = openTab(browserWindow, testURL, { + inBackground: false + }); + var b = getBrowserForTab(newTab); + + function onPageLoad() { + b.removeEventListener("load", onPageLoad, true); + // Delay callback execute as page-mod content scripts may be executed on + // load event. So page-mod actions may not be already done. + // If we delay even more contentScriptWhen:'end', we may want to modify + // this code again. + timer.setTimeout(testCallback, 0, + b.contentWindow.wrappedJSObject, + function () { + pageMods.forEach(function(mod) mod.destroy()); + // XXX leaks reported if we don't close the tab? + closeTab(newTab); + loader.unload(); + done(); + } + ); + } + b.addEventListener("load", onPageLoad, true); + + return pageMods; +} diff --git a/addon-sdk-1.16/test/private-browsing/global.js b/addon-sdk-1.16/test/private-browsing/global.js new file mode 100644 index 00000000..89d7da4e --- /dev/null +++ b/addon-sdk-1.16/test/private-browsing/global.js @@ -0,0 +1,239 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +'use strict'; + +const timer = require("sdk/timers"); +const { LoaderWithHookedConsole, deactivate, pb, pbUtils } = require("./helper"); +const tabs = require("sdk/tabs"); +const { getMostRecentBrowserWindow, isWindowPrivate } = require('sdk/window/utils'); +const { set: setPref } = require("sdk/preferences/service"); +const DEPRECATE_PREF = "devtools.errorconsole.deprecation_warnings"; + +exports["test activate private mode via handler"] = function(assert, done) { + function onReady(tab) { + if (tab.url == "about:robots") + tab.close(function() pb.activate()); + } + function cleanup(tab) { + if (tab.url == "about:") { + tabs.removeListener("ready", cleanup); + tab.close(function onClose() { + done(); + }); + } + } + + tabs.on("ready", onReady); + pb.once("start", function onStart() { + assert.pass("private mode was activated"); + pb.deactivate(); + }); + pb.once("stop", function onStop() { + assert.pass("private mode was deactivated"); + tabs.removeListener("ready", onReady); + tabs.on("ready", cleanup); + }); + tabs.once("open", function onOpen() { + tabs.open("about:robots"); + }); + tabs.open("about:"); +}; + +// tests that isActive has the same value as the private browsing service +// expects +exports.testGetIsActive = function (assert) { + assert.equal(pb.isActive, false, + "private-browsing.isActive is correct without modifying PB service"); + assert.equal(pb.isPrivate(), false, + "private-browsing.sPrivate() is correct without modifying PB service"); + + pb.once("start", function() { + assert.ok(pb.isActive, + "private-browsing.isActive is correct after modifying PB service"); + assert.ok(pb.isPrivate(), + "private-browsing.sPrivate() is correct after modifying PB service"); + // Switch back to normal mode. + pb.deactivate(); + }); + pb.activate(); + + pb.once("stop", function() { + assert.ok(!pb.isActive, + "private-browsing.isActive is correct after modifying PB service"); + assert.ok(!pb.isPrivate(), + "private-browsing.sPrivate() is correct after modifying PB service"); + test.done(); + }); +}; + +exports.testStart = function(assert, done) { + pb.on("start", function onStart() { + assert.equal(this, pb, "`this` should be private-browsing module"); + assert.ok(pbUtils.getMode(), + 'private mode is active when "start" event is emitted'); + assert.ok(pb.isActive, + '`isActive` is `true` when "start" event is emitted'); + assert.ok(pb.isPrivate(), + '`isPrivate` is `true` when "start" event is emitted'); + pb.removeListener("start", onStart); + deactivate(done); + }); + pb.activate(); +}; + +exports.testStop = function(assert, done) { + pb.once("stop", function onStop() { + assert.equal(this, pb, "`this` should be private-browsing module"); + assert.equal(pbUtils.getMode(), false, + "private mode is disabled when stop event is emitted"); + assert.equal(pb.isActive, false, + "`isActive` is `false` when stop event is emitted"); + assert.equal(pb.isPrivate(), false, + "`isPrivate()` is `false` when stop event is emitted"); + done(); + }); + pb.activate(); + pb.once("start", function() { + pb.deactivate(); + }); +}; + +exports.testBothListeners = function(assert, done) { + let stop = false; + let start = false; + + function onStop() { + assert.equal(stop, false, + "stop callback must be called only once"); + assert.equal(pbUtils.getMode(), false, + "private mode is disabled when stop event is emitted"); + assert.equal(pb.isActive, false, + "`isActive` is `false` when stop event is emitted"); + assert.equal(pb.isPrivate(), false, + "`isPrivate()` is `false` when stop event is emitted"); + + pb.on("start", finish); + pb.removeListener("start", onStart); + pb.removeListener("start", onStart2); + pb.activate(); + stop = true; + } + + function onStart() { + assert.equal(false, start, + "stop callback must be called only once"); + assert.ok(pbUtils.getMode(), + "private mode is active when start event is emitted"); + assert.ok(pb.isActive, + "`isActive` is `true` when start event is emitted"); + assert.ok(pb.isPrivate(), + "`isPrivate()` is `true` when start event is emitted"); + + pb.on("stop", onStop); + pb.deactivate(); + start = true; + } + + function onStart2() { + assert.ok(start, "start listener must be called already"); + assert.equal(false, stop, "stop callback must not be called yet"); + } + + function finish() { + assert.ok(pbUtils.getMode(), true, + "private mode is active when start event is emitted"); + assert.ok(pb.isActive, + "`isActive` is `true` when start event is emitted"); + assert.ok(pb.isPrivate(), + "`isPrivate()` is `true` when start event is emitted"); + + pb.removeListener("start", finish); + pb.removeListener("stop", onStop); + + pb.deactivate(); + pb.once("stop", function () { + assert.equal(pbUtils.getMode(), false); + assert.equal(pb.isActive, false); + assert.equal(pb.isPrivate(), false); + + done(); + }); + } + + pb.on("start", onStart); + pb.on("start", onStart2); + pb.activate(); +}; + +exports.testAutomaticUnload = function(assert, done) { + setPref(DEPRECATE_PREF, true); + + // Create another private browsing instance and unload it + let { loader, errors } = LoaderWithHookedConsole(module); + let pb2 = loader.require("sdk/private-browsing"); + let called = false; + pb2.on("start", function onStart() { + called = true; + assert.fail("should not be called:x"); + }); + loader.unload(); + + // Then switch to private mode in order to check that the previous instance + // is correctly destroyed + pb.once("start", function onStart() { + timer.setTimeout(function () { + assert.ok(!called, + "First private browsing instance is destroyed and inactive"); + // Must reset to normal mode, so that next test starts with it. + deactivate(function() { + assert.ok(errors.length, 0, "should have been 1 deprecation error"); + done(); + }); + }, 0); + }); + + pb.activate(); +}; + +exports.testUnloadWhileActive = function(assert, done) { + let called = false; + let { loader, errors } = LoaderWithHookedConsole(module); + let pb2 = loader.require("sdk/private-browsing"); + let ul = loader.require("sdk/system/unload"); + + let unloadHappened = false; + ul.when(function() { + unloadHappened = true; + timer.setTimeout(function() { + pb.deactivate(); + }); + }); + pb2.once("start", function() { + loader.unload(); + }); + pb2.once("stop", function() { + called = true; + assert.ok(unloadHappened, "the unload event should have already occurred."); + assert.fail("stop should not have been fired"); + }); + pb.once("stop", function() { + assert.ok(!called, "stop was not called on unload"); + assert.ok(errors.length, 2, "should have been 2 deprecation errors"); + done(); + }); + + pb.activate(); +}; + +exports.testIgnoreWindow = function(assert, done) { + let window = getMostRecentBrowserWindow(); + + pb.once('start', function() { + assert.ok(isWindowPrivate(window), 'window is private'); + assert.ok(!pbUtils.ignoreWindow(window), 'window is not ignored'); + pb.once('stop', done); + pb.deactivate(); + }); + pb.activate(); +}; diff --git a/addon-sdk-1.16/test/private-browsing/helper.js b/addon-sdk-1.16/test/private-browsing/helper.js new file mode 100644 index 00000000..cf4d5a01 --- /dev/null +++ b/addon-sdk-1.16/test/private-browsing/helper.js @@ -0,0 +1,100 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +'use strict'; + +const { Loader } = require('sdk/test/loader'); + +const { loader } = LoaderWithHookedConsole(module); + +const pb = loader.require('sdk/private-browsing'); +const pbUtils = loader.require('sdk/private-browsing/utils'); +const xulApp = require("sdk/system/xul-app"); +const { open: openWindow, getMostRecentBrowserWindow } = require('sdk/window/utils'); +const { openTab, getTabContentWindow, getActiveTab, setTabURL, closeTab } = require('sdk/tabs/utils'); +const promise = require("sdk/core/promise"); +const windowHelpers = require('sdk/window/helpers'); +const events = require("sdk/system/events"); + +function LoaderWithHookedConsole(module) { + let globals = {}; + let errors = []; + + globals.console = Object.create(console, { + error: { + value: function(e) { + errors.push(e); + if (!/DEPRECATED:/.test(e)) { + console.error(e); + } + } + } + }); + + let loader = Loader(module, globals); + + return { + loader: loader, + errors: errors + } +} + +function deactivate(callback) { + if (pbUtils.isGlobalPBSupported) { + if (callback) + pb.once('stop', callback); + pb.deactivate(); + } +} +exports.deactivate = deactivate; + +exports.pb = pb; +exports.pbUtils = pbUtils; +exports.LoaderWithHookedConsole = LoaderWithHookedConsole; + +exports.openWebpage = function openWebpage(url, enablePrivate) { + if (xulApp.is("Fennec")) { + let chromeWindow = getMostRecentBrowserWindow(); + let rawTab = openTab(chromeWindow, url, { + isPrivate: enablePrivate + }); + + return { + ready: promise.resolve(getTabContentWindow(rawTab)), + close: function () { + closeTab(rawTab); + // Returns a resolved promise as there is no need to wait + return promise.resolve(); + } + }; + } + else { + let win = openWindow(null, { + features: { + private: enablePrivate + } + }); + let deferred = promise.defer(); + + // Wait for delayed startup code to be executed, in order to ensure + // that the window is really ready + events.on("browser-delayed-startup-finished", function onReady({subject}) { + if (subject == win) { + events.off("browser-delayed-startup-finished", onReady); + deferred.resolve(win); + + let rawTab = getActiveTab(win); + setTabURL(rawTab, url); + deferred.resolve(getTabContentWindow(rawTab)); + } + }, true); + + return { + ready: deferred.promise, + close: function () { + return windowHelpers.close(win); + } + }; + } + return null; +} diff --git a/addon-sdk-1.16/test/private-browsing/tabs.js b/addon-sdk-1.16/test/private-browsing/tabs.js new file mode 100644 index 00000000..726a370a --- /dev/null +++ b/addon-sdk-1.16/test/private-browsing/tabs.js @@ -0,0 +1,27 @@ +'use strict'; + +const { Ci } = require('chrome'); +const { openTab, closeTab } = require('sdk/tabs/utils'); +const { browserWindows } = require('sdk/windows'); +const { getOwnerWindow } = require('sdk/private-browsing/window/utils'); +const { isPrivate } = require('sdk/private-browsing'); + +exports.testIsPrivateOnTab = function(assert) { + let window = browserWindows.activeWindow; + + let chromeWindow = getOwnerWindow(window); + + assert.ok(chromeWindow instanceof Ci.nsIDOMWindow, 'associated window is found'); + assert.ok(!isPrivate(chromeWindow), 'the top level window is not private'); + + let rawTab = openTab(chromeWindow, 'data:text/html,

Hi!

', { + isPrivate: true + }); + + // test that the tab is private + assert.ok(rawTab.browser.docShell.QueryInterface(Ci.nsILoadContext).usePrivateBrowsing); + assert.ok(isPrivate(rawTab.browser.contentWindow)); + assert.ok(isPrivate(rawTab.browser)); + + closeTab(rawTab); +}; diff --git a/addon-sdk-1.16/test/private-browsing/windows.js b/addon-sdk-1.16/test/private-browsing/windows.js new file mode 100644 index 00000000..a302643a --- /dev/null +++ b/addon-sdk-1.16/test/private-browsing/windows.js @@ -0,0 +1,139 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +'use strict'; + +const { pb, pbUtils } = require('./helper'); +const { onFocus, openDialog, open } = require('sdk/window/utils'); +const { open: openPromise, close, focus, promise } = require('sdk/window/helpers'); +const { isPrivate } = require('sdk/private-browsing'); +const { browserWindows: windows } = require('sdk/windows'); +const { defer } = require('sdk/core/promise'); +const tabs = require('sdk/tabs'); + +// test openDialog() from window/utils with private option +// test isActive state in pwpb case +// test isPrivate on ChromeWindow +exports.testPerWindowPrivateBrowsingGetter = function(assert, done) { + let win = openDialog({ + private: true + }); + + promise(win, 'DOMContentLoaded').then(function onload() { + assert.equal(pbUtils.getMode(win), + true, 'Newly opened window is in PB mode'); + assert.ok(isPrivate(win), 'isPrivate(window) is true'); + assert.equal(pb.isActive, false, 'PB mode is not active'); + + close(win).then(function() { + assert.equal(pb.isActive, false, 'PB mode is not active'); + done(); + }); + }); +} + +// test open() from window/utils with private feature +// test isActive state in pwpb case +// test isPrivate on ChromeWindow +exports.testPerWindowPrivateBrowsingGetter = function(assert, done) { + let win = open('chrome://browser/content/browser.xul', { + features: { + private: true + } + }); + + promise(win, 'DOMContentLoaded').then(function onload() { + assert.equal(pbUtils.getMode(win), + true, 'Newly opened window is in PB mode'); + assert.ok(isPrivate(win), 'isPrivate(window) is true'); + assert.equal(pb.isActive, false, 'PB mode is not active'); + + close(win).then(function() { + assert.equal(pb.isActive, false, 'PB mode is not active'); + done(); + }); + }); +} + +exports.testIsPrivateOnWindowOpen = function(assert, done) { + windows.open({ + isPrivate: true, + onOpen: function(window) { + assert.equal(isPrivate(window), false, 'isPrivate for a window is true when it should be'); + assert.equal(isPrivate(window.tabs[0]), false, 'isPrivate for a tab is false when it should be'); + window.close(done); + } + }); +} + +exports.testIsPrivateOnWindowOpenFromPrivate = function(assert, done) { + // open a private window + openPromise(null, { + features: { + private: true, + chrome: true, + titlebar: true, + toolbar: true + } + }).then(focus).then(function(window) { + let { promise, resolve } = defer(); + + assert.equal(isPrivate(window), true, 'the only open window is private'); + + windows.open({ + url: 'about:blank', + onOpen: function(w) { + assert.equal(isPrivate(w), false, 'new test window is not private'); + w.close(function() resolve(window)); + } + }); + + return promise; + }).then(close). + then(done, assert.fail); +}; + +exports.testOpenTabWithPrivateWindow = function(assert, done) { + function start() { + openPromise(null, { + features: { + private: true, + toolbar: true + } + }).then(focus).then(function(window) { + let { promise, resolve } = defer(); + assert.equal(isPrivate(window), true, 'the focused window is private'); + + tabs.open({ + url: 'about:blank', + onOpen: function(tab) { + assert.equal(isPrivate(tab), false, 'the opened tab is not private'); + // not closing this tab on purpose.. for now... + // we keep this tab open because we closed all windows + // and must keep a non-private window open at end of this test for next ones. + resolve(window); + } + }); + + return promise; + }).then(close).then(done, assert.fail); + } + + (function closeWindows() { + if (windows.length > 0) { + return windows.activeWindow.close(closeWindows); + } + assert.pass('all pre test windows have been closed'); + return start(); + })() +}; + +exports.testIsPrivateOnWindowOff = function(assert, done) { + windows.open({ + onOpen: function(window) { + assert.equal(isPrivate(window), false, 'isPrivate for a window is false when it should be'); + assert.equal(isPrivate(window.tabs[0]), false, 'isPrivate for a tab is false when it should be'); + window.close(done); + } + }) +} diff --git a/addon-sdk-1.16/test/sidebar/utils.js b/addon-sdk-1.16/test/sidebar/utils.js new file mode 100644 index 00000000..80c3af91 --- /dev/null +++ b/addon-sdk-1.16/test/sidebar/utils.js @@ -0,0 +1,73 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +'use strict'; + +module.metadata = { + 'engines': { + 'Firefox': '*' + } +}; + +const { Cu } = require('chrome'); +const { getMostRecentBrowserWindow } = require('sdk/window/utils'); +const { fromIterator } = require('sdk/util/array'); + +const BUILTIN_SIDEBAR_MENUITEMS = exports.BUILTIN_SIDEBAR_MENUITEMS = [ + 'menu_socialSidebar', + 'menu_historySidebar', + 'menu_bookmarksSidebar' +]; + +function isSidebarShowing(window) { + window = window || getMostRecentBrowserWindow(); + let sidebar = window.document.getElementById('sidebar-box'); + return !sidebar.hidden; +} +exports.isSidebarShowing = isSidebarShowing; + +function getSidebarMenuitems(window) { + window = window || getMostRecentBrowserWindow(); + return fromIterator(window.document.querySelectorAll('#viewSidebarMenu menuitem')); +} +exports.getSidebarMenuitems = getSidebarMenuitems; + +function getExtraSidebarMenuitems() { + let menuitems = getSidebarMenuitems(); + return menuitems.filter(function(mi) { + return BUILTIN_SIDEBAR_MENUITEMS.indexOf(mi.getAttribute('id')) < 0; + }); +} +exports.getExtraSidebarMenuitems = getExtraSidebarMenuitems; + +function makeID(id) { + return 'jetpack-sidebar-' + id; +} +exports.makeID = makeID; + +function simulateCommand(ele) { + let window = ele.ownerDocument.defaultView; + let { document } = window; + var evt = document.createEvent('XULCommandEvent'); + evt.initCommandEvent('command', true, true, window, + 0, false, false, false, false, null); + ele.dispatchEvent(evt); +} +exports.simulateCommand = simulateCommand; + +function simulateClick(ele) { + let window = ele.ownerDocument.defaultView; + let { document } = window; + let evt = document.createEvent('MouseEvents'); + evt.initMouseEvent('click', true, true, window, + 0, 0, 0, 0, 0, false, false, false, false, 0, null); + ele.dispatchEvent(evt); +} +exports.simulateClick = simulateClick; + +// OSX and Windows exhibit different behaviors when 'checked' is false, +// so compare against the consistent 'true'. See bug 894809. +function isChecked(node) { + return node.getAttribute('checked') === 'true'; +}; +exports.isChecked = isChecked; diff --git a/addon-sdk-1.16/test/tabs/test-fennec-tabs.js b/addon-sdk-1.16/test/tabs/test-fennec-tabs.js new file mode 100644 index 00000000..e579ebba --- /dev/null +++ b/addon-sdk-1.16/test/tabs/test-fennec-tabs.js @@ -0,0 +1,584 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +'use strict'; + +const { Cc, Ci } = require('chrome'); +const { Loader, LoaderWithHookedConsole } = require('sdk/test/loader'); +const timer = require('sdk/timers'); +const tabs = require('sdk/tabs'); +const windows = require('sdk/windows'); +const { set: setPref } = require("sdk/preferences/service"); +const DEPRECATE_PREF = "devtools.errorconsole.deprecation_warnings"; + +const tabsLen = tabs.length; +const URL = 'data:text/html;charset=utf-8,#title#'; + +// Fennec error message dispatched on all currently unimplement tab features, +// that match LoaderWithHookedConsole messages object pattern +const ERR_FENNEC_MSG = { + type: "error", + msg: "This method is not yet supported by Fennec" +}; + +// TEST: tab unloader +exports.testAutomaticDestroy = function(assert, done) { + let called = false; + + let loader2 = Loader(module); + let loader3 = Loader(module); + let tabs2 = loader2.require('sdk/tabs'); + let tabs3 = loader3.require('sdk/tabs'); + let tabs2Len = tabs2.length; + + tabs2.on('open', function onOpen(tab) { + assert.fail("an onOpen listener was called that should not have been"); + called = true; + }); + tabs2.on('ready', function onReady(tab) { + assert.fail("an onReady listener was called that should not have been"); + called = true; + }); + tabs2.on('select', function onSelect(tab) { + assert.fail("an onSelect listener was called that should not have been"); + called = true; + }); + tabs2.on('close', function onClose(tab) { + assert.fail("an onClose listener was called that should not have been"); + called = true; + }); + loader2.unload(); + + tabs3.on('open', function onOpen(tab) { + assert.pass("an onOpen listener was called for tabs3"); + + tab.on('ready', function onReady(tab) { + assert.fail("an onReady listener was called that should not have been"); + called = true; + }); + tab.on('select', function onSelect(tab) { + assert.fail("an onSelect listener was called that should not have been"); + called = true; + }); + tab.on('close', function onClose(tab) { + assert.fail("an onClose listener was called that should not have been"); + called = true; + }); + }); + tabs3.open(URL.replace(/#title#/, 'tabs3')); + loader3.unload(); + + // Fire a tab event and ensure that the destroyed tab is inactive + tabs.once('open', function(tab) { + assert.pass('tabs.once("open") works!'); + + assert.equal(tabs2Len, tabs2.length, "tabs2 length was not changed"); + assert.equal(tabs.length, (tabs2.length+2), "tabs.length > tabs2.length"); + + tab.once('ready', function() { + assert.pass('tab.once("ready") works!'); + + tab.once('close', function() { + assert.pass('tab.once("close") works!'); + + timer.setTimeout(function () { + assert.ok(!called, "Unloaded tab module is destroyed and inactive"); + + // end test + done(); + }); + }); + + tab.close(); + }); + }); + + tabs.open('data:text/html;charset=utf-8,foo'); +}; + +// TEST: tab properties +exports.testTabProperties = function(assert, done) { + setPref(DEPRECATE_PREF, true); + let { loader, messages } = LoaderWithHookedConsole(); + let tabs = loader.require('sdk/tabs'); + + let url = "data:text/html;charset=utf-8,foofoo"; + let tabsLen = tabs.length; + tabs.open({ + url: url, + onReady: function(tab) { + assert.equal(tab.title, "foo", "title of the new tab matches"); + assert.equal(tab.url, url, "URL of the new tab matches"); + assert.ok(tab.favicon, "favicon of the new tab is not empty"); + // TODO: remove need for this test by implementing the favicon feature + assert.equal(messages[0].msg, + "tab.favicon is deprecated, and " + + "currently favicon helpers are not yet supported " + + "by Fennec", + "favicon logs an error for now"); + assert.equal(tab.style, null, "style of the new tab matches"); + assert.equal(tab.index, tabsLen, "index of the new tab matches"); + assert.notEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches"); + assert.notEqual(tab.id, null, "a tab object always has an id property"); + + tab.close(function() { + loader.unload(); + + // end test + done(); + }); + } + }); +}; + +// TEST: tabs iterator and length property +exports.testTabsIteratorAndLength = function(assert, done) { + let newTabs = []; + let startCount = 0; + for each (let t in tabs) startCount++; + + assert.equal(startCount, tabs.length, "length property is correct"); + + let url = "data:text/html;charset=utf-8,testTabsIteratorAndLength"; + tabs.open({url: url, onOpen: function(tab) newTabs.push(tab)}); + tabs.open({url: url, onOpen: function(tab) newTabs.push(tab)}); + tabs.open({ + url: url, + onOpen: function(tab) { + let count = 0; + for each (let t in tabs) count++; + assert.equal(count, startCount + 3, "iterated tab count matches"); + assert.equal(startCount + 3, tabs.length, "iterated tab count matches length property"); + + let newTabsLength = newTabs.length; + newTabs.forEach(function(t) t.close(function() { + if (--newTabsLength > 0) return; + + tab.close(done); + })); + } + }); +}; + +// TEST: tab.url setter +exports.testTabLocation = function(assert, done) { + let url1 = "data:text/html;charset=utf-8,foo"; + let url2 = "data:text/html;charset=utf-8,bar"; + + tabs.on('ready', function onReady(tab) { + if (tab.url != url2) + return; + + tabs.removeListener('ready', onReady); + assert.pass("tab loaded the correct url"); + + tab.close(done); + }); + + tabs.open({ + url: url1, + onOpen: function(tab) { + tab.url = url2; + } + }); +}; + +// TEST: tab.move() +exports.testTabMove = function(assert, done) { + let { loader, messages } = LoaderWithHookedConsole(); + let tabs = loader.require('sdk/tabs'); + + let url = "data:text/html;charset=utf-8,testTabMove"; + + tabs.open({ + url: url, + onOpen: function(tab1) { + assert.ok(tab1.index >= 0, "opening a tab returns a tab w/ valid index"); + + tabs.open({ + url: url, + onOpen: function(tab) { + let i = tab.index; + assert.ok(tab.index > tab1.index, "2nd tab has valid index"); + tab.index = 0; + assert.equal(tab.index, i, "tab index after move matches"); + assert.equal(JSON.stringify(messages), + JSON.stringify([ERR_FENNEC_MSG]), + "setting tab.index logs error"); + // end test + tab1.close(function() tab.close(function() { + loader.unload(); + done(); + })); + } + }); + } + }); +}; + +// TEST: open tab with default options +exports.testTabsOpen_alt = function(assert, done) { + let { loader, messages } = LoaderWithHookedConsole(); + let tabs = loader.require('sdk/tabs'); + let url = "data:text/html;charset=utf-8,default"; + + tabs.open({ + url: url, + onReady: function(tab) { + assert.equal(tab.url, url, "URL of the new tab matches"); + assert.equal(tabs.activeTab, tab, "URL of active tab in the current window matches"); + assert.equal(tab.isPinned, false, "The new tab is not pinned"); + assert.equal(messages.length, 1, "isPinned logs error"); + + // end test + tab.close(function() { + loader.unload(); + done(); + }); + } + }); +}; + +// TEST: open pinned tab +exports.testOpenPinned_alt = function(assert, done) { + let { loader, messages } = LoaderWithHookedConsole(); + let tabs = loader.require('sdk/tabs'); + let url = "about:blank"; + + tabs.open({ + url: url, + isPinned: true, + onOpen: function(tab) { + assert.equal(tab.isPinned, false, "The new tab is pinned"); + // We get two error message: one for tabs.open's isPinned argument + // and another one for tab.isPinned + assert.equal(JSON.stringify(messages), + JSON.stringify([ERR_FENNEC_MSG, ERR_FENNEC_MSG]), + "isPinned logs error"); + + // end test + tab.close(function() { + loader.unload(); + done(); + }); + } + }); +}; + +// TEST: pin/unpin opened tab +exports.testPinUnpin_alt = function(assert, done) { + let { loader, messages } = LoaderWithHookedConsole(); + let tabs = loader.require('sdk/tabs'); + let url = "data:text/html;charset=utf-8,default"; + + tabs.open({ + url: url, + onOpen: function(tab) { + tab.pin(); + assert.equal(tab.isPinned, false, "The tab was pinned correctly"); + assert.equal(JSON.stringify(messages), + JSON.stringify([ERR_FENNEC_MSG, ERR_FENNEC_MSG]), + "tab.pin() logs error"); + + // Clear console messages for the following test + messages.length = 0; + + tab.unpin(); + assert.equal(tab.isPinned, false, "The tab was unpinned correctly"); + assert.equal(JSON.stringify(messages), + JSON.stringify([ERR_FENNEC_MSG, ERR_FENNEC_MSG]), + "tab.unpin() logs error"); + + // end test + tab.close(function() { + loader.unload(); + done(); + }); + } + }); +}; + +// TEST: open tab in background +exports.testInBackground = function(assert, done) { + let activeUrl = tabs.activeTab.url; + let url = "data:text/html;charset=utf-8,background"; + let window = windows.browserWindows.activeWindow; + tabs.once('ready', function onReady(tab) { + assert.equal(tabs.activeTab.url, activeUrl, "URL of active tab has not changed"); + assert.equal(tab.url, url, "URL of the new background tab matches"); + assert.equal(windows.browserWindows.activeWindow, window, "a new window was not opened"); + assert.notEqual(tabs.activeTab.url, url, "URL of active tab is not the new URL"); + + // end test + tab.close(done); + }); + + tabs.open({ + url: url, + inBackground: true + }); +}; + +// TEST: open tab in new window +exports.testOpenInNewWindow = function(assert, done) { + let url = "data:text/html;charset=utf-8,newwindow"; + let window = windows.browserWindows.activeWindow; + + tabs.open({ + url: url, + inNewWindow: true, + onReady: function(tab) { + assert.equal(windows.browserWindows.length, 1, "a new window was not opened"); + assert.equal(windows.browserWindows.activeWindow, window, "old window is active"); + assert.equal(tab.url, url, "URL of the new tab matches"); + assert.equal(tabs.activeTab, tab, "tab is the activeTab"); + + tab.close(done); + } + }); +}; + +// TEST: onOpen event handler +exports.testTabsEvent_onOpen = function(assert, done) { + let url = URL.replace('#title#', 'testTabsEvent_onOpen'); + let eventCount = 0; + + // add listener via property assignment + function listener1(tab) { + eventCount++; + }; + tabs.on('open', listener1); + + // add listener via collection add + tabs.on('open', function listener2(tab) { + assert.equal(++eventCount, 2, "both listeners notified"); + tabs.removeListener('open', listener1); + tabs.removeListener('open', listener2); + + // ends test + tab.close(done); + }); + + tabs.open(url); +}; + +// TEST: onClose event handler +exports.testTabsEvent_onClose = function(assert, done) { + let url = "data:text/html;charset=utf-8,onclose"; + let eventCount = 0; + + // add listener via property assignment + function listener1(tab) { + eventCount++; + } + tabs.on('close', listener1); + + // add listener via collection add + tabs.on('close', function listener2(tab) { + assert.equal(++eventCount, 2, "both listeners notified"); + tabs.removeListener('close', listener1); + tabs.removeListener('close', listener2); + + // end test + done(); + }); + + tabs.on('ready', function onReady(tab) { + tabs.removeListener('ready', onReady); + tab.close(); + }); + + tabs.open(url); +}; + +// TEST: onClose event handler when a window is closed +exports.testTabsEvent_onCloseWindow = function(assert, done) { + let closeCount = 0, individualCloseCount = 0; + function listener() { + closeCount++; + } + tabs.on('close', listener); + + // One tab is already open with the window + let openTabs = 0; + function testCasePossiblyLoaded(tab) { + tab.close(function() { + if (++openTabs == 3) { + tabs.removeListener("close", listener); + + assert.equal(closeCount, 3, "Correct number of close events received"); + assert.equal(individualCloseCount, 3, + "Each tab with an attached onClose listener received a close " + + "event when the window was closed"); + + done(); + } + }); + } + + tabs.open({ + url: "data:text/html;charset=utf-8,tab2", + onOpen: testCasePossiblyLoaded, + onClose: function() individualCloseCount++ + }); + + tabs.open({ + url: "data:text/html;charset=utf-8,tab3", + onOpen: testCasePossiblyLoaded, + onClose: function() individualCloseCount++ + }); + + tabs.open({ + url: "data:text/html;charset=utf-8,tab4", + onOpen: testCasePossiblyLoaded, + onClose: function() individualCloseCount++ + }); +}; + +// TEST: onReady event handler +exports.testTabsEvent_onReady = function(assert, done) { + let url = "data:text/html;charset=utf-8,onready"; + let eventCount = 0; + + // add listener via property assignment + function listener1(tab) { + eventCount++; + }; + tabs.on('ready', listener1); + + // add listener via collection add + tabs.on('ready', function listener2(tab) { + assert.equal(++eventCount, 2, "both listeners notified"); + tabs.removeListener('ready', listener1); + tabs.removeListener('ready', listener2); + + // end test + tab.close(done); + }); + + tabs.open(url); +}; + +// TEST: onActivate event handler +exports.testTabsEvent_onActivate = function(assert, done) { + let url = "data:text/html;charset=utf-8,onactivate"; + let eventCount = 0; + + // add listener via property assignment + function listener1(tab) { + eventCount++; + }; + tabs.on('activate', listener1); + + // add listener via collection add + tabs.on('activate', function listener2(tab) { + assert.equal(++eventCount, 2, "both listeners notified"); + assert.equal(tab, tabs.activeTab, 'the active tab is correct'); + tabs.removeListener('activate', listener1); + tabs.removeListener('activate', listener2); + + // end test + tab.close(done); + }); + + tabs.open(url); +}; + +// TEST: onDeactivate event handler +exports.testTabsEvent_onDeactivate = function(assert, done) { + let url = "data:text/html;charset=utf-8,ondeactivate"; + let eventCount = 0; + + // add listener via property assignment + function listener1(tab) { + eventCount++; + }; + tabs.on('deactivate', listener1); + + // add listener via collection add + tabs.on('deactivate', function listener2(tab) { + assert.equal(++eventCount, 2, 'both listeners notified'); + assert.notEqual(tab, tabs.activeTab, 'the active tab is not the deactivated tab'); + tabs.removeListener('deactivate', listener1); + tabs.removeListener('deactivate', listener2); + + // end test + tab.close(done); + }); + + tabs.on('activate', function onActivate(tab) { + tabs.removeListener('activate', onActivate); + tabs.open("data:text/html;charset=utf-8,foo"); + tab.close(); + }); + + tabs.open(url); +}; + +// TEST: per-tab event handlers +exports.testPerTabEvents = function(assert, done) { + let eventCount = 0; + + tabs.open({ + url: "data:text/html;charset=utf-8,foo", + onOpen: function(tab) { + // add listener via property assignment + function listener1() { + eventCount++; + }; + tab.on('ready', listener1); + + // add listener via collection add + tab.on('ready', function listener2() { + assert.equal(eventCount, 1, "both listeners notified"); + tab.removeListener('ready', listener1); + tab.removeListener('ready', listener2); + + // end test + tab.close(done); + }); + } + }); +}; + +exports.testUniqueTabIds = function(assert, done) { + var tabs = require('sdk/tabs'); + var tabIds = {}; + var steps = [ + function (index) { + tabs.open({ + url: "data:text/html;charset=utf-8,foo", + onOpen: function(tab) { + tabIds['tab1'] = tab.id; + next(index); + } + }); + }, + function (index) { + tabs.open({ + url: "data:text/html;charset=utf-8,bar", + onOpen: function(tab) { + tabIds['tab2'] = tab.id; + next(index); + } + }); + }, + function (index) { + assert.notEqual(tabIds.tab1, tabIds.tab2, "Tab ids should be unique."); + done(); + } + ]; + + function next(index) { + if (index === steps.length) { + return; + } + let fn = steps[index]; + index++; + fn(index); + } + + next(0); +} + +require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/tabs/test-firefox-tabs.js b/addon-sdk-1.16/test/tabs/test-firefox-tabs.js new file mode 100644 index 00000000..18c7e557 --- /dev/null +++ b/addon-sdk-1.16/test/tabs/test-firefox-tabs.js @@ -0,0 +1,993 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +'use strict'; + +const { Cc, Ci } = require('chrome'); +const { Loader } = require('sdk/test/loader'); +const timer = require('sdk/timers'); +const { getOwnerWindow } = require('sdk/private-browsing/window/utils'); +const { windows, onFocus, getMostRecentBrowserWindow } = require('sdk/window/utils'); +const { open, focus, close } = require('sdk/window/helpers'); +const tabs = require('sdk/tabs'); +const { browserWindows } = require('sdk/windows'); +const { set: setPref } = require("sdk/preferences/service"); +const DEPRECATE_PREF = "devtools.errorconsole.deprecation_warnings"; + +const base64png = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAA"; + +// Bug 682681 - tab.title should never be empty +exports.testBug682681_aboutURI = function(assert, done) { + let url = 'chrome://browser/locale/tabbrowser.properties'; + let stringBundle = Cc["@mozilla.org/intl/stringbundle;1"]. + getService(Ci.nsIStringBundleService). + createBundle(url); + let emptyTabTitle = stringBundle.GetStringFromName('tabs.emptyTabTitle'); + + tabs.on('ready', function onReady(tab) { + tabs.removeListener('ready', onReady); + + assert.equal(tab.title, + emptyTabTitle, + "title of about: tab is not blank"); + + tab.close(done); + }); + + // open a about: url + tabs.open({ + url: "about:blank", + inBackground: true + }); +}; + +// related to Bug 682681 +exports.testTitleForDataURI = function(assert, done) { + tabs.open({ + url: "data:text/html;charset=utf-8,tab", + inBackground: true, + onReady: function(tab) { + assert.equal(tab.title, "tab", "data: title is not Connecting..."); + tab.close(done); + } + }); +}; + +// TEST: 'BrowserWindow' instance creation on tab 'activate' event +// See bug 648244: there was a infinite loop. +exports.testBrowserWindowCreationOnActivate = function(assert, done) { + let windows = require("sdk/windows").browserWindows; + let gotActivate = false; + + tabs.once('activate', function onActivate(eventTab) { + assert.ok(windows.activeWindow, "Is able to fetch activeWindow"); + gotActivate = true; + }); + + open().then(function(window) { + assert.ok(gotActivate, "Received activate event before openBrowserWindow's callback is called"); + close(window).then(done); + }); +} + +// TEST: tab unloader +exports.testAutomaticDestroy = function(assert, done) { + // Create a second tab instance that we will destroy + let called = false; + + let loader = Loader(module); + let tabs2 = loader.require("sdk/tabs"); + tabs2.on('open', function onOpen(tab) { + called = true; + }); + + loader.unload(); + + // Fire a tab event and ensure that the destroyed tab is inactive + tabs.once('open', function (tab) { + timer.setTimeout(function () { + assert.ok(!called, "Unloaded tab module is destroyed and inactive"); + tab.close(done); + }); + }); + tabs.open("data:text/html;charset=utf-8,foo"); +}; + +exports.testTabPropertiesInNewWindow = function(assert, done) { + let warning = "DEPRECATED: tab.favicon is deprecated, please use require(\"sdk/places/favicon\").getFavicon instead.\n" + const { LoaderWithFilteredConsole } = require("sdk/test/loader"); + let loader = LoaderWithFilteredConsole(module, function(type, message) { + if (type == "error" && message.substring(0, warning.length) == warning) + return false; + return true; + }); + + let tabs = loader.require('sdk/tabs'); + let { getOwnerWindow } = loader.require('sdk/private-browsing/window/utils'); + + let count = 0; + function onReadyOrLoad (tab) { + if (count++) { + close(getOwnerWindow(tab)).then(done); + } + } + + let url = "data:text/html;charset=utf-8,foofoo"; + tabs.open({ + inNewWindow: true, + url: url, + onReady: function(tab) { + assert.equal(tab.title, "foo", "title of the new tab matches"); + assert.equal(tab.url, url, "URL of the new tab matches"); + assert.ok(tab.favicon, "favicon of the new tab is not empty"); + assert.equal(tab.style, null, "style of the new tab matches"); + assert.equal(tab.index, 0, "index of the new tab matches"); + assert.notEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches"); + assert.notEqual(tab.id, null, "a tab object always has an id property."); + + onReadyOrLoad(tab); + }, + onLoad: function(tab) { + assert.equal(tab.title, "foo", "title of the new tab matches"); + assert.equal(tab.url, url, "URL of the new tab matches"); + assert.ok(tab.favicon, "favicon of the new tab is not empty"); + assert.equal(tab.style, null, "style of the new tab matches"); + assert.equal(tab.index, 0, "index of the new tab matches"); + assert.notEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches"); + assert.notEqual(tab.id, null, "a tab object always has an id property."); + + onReadyOrLoad(tab); + } + }); +}; + +exports.testTabPropertiesInSameWindow = function(assert, done) { + let warning = "DEPRECATED: tab.favicon is deprecated, please use require(\"sdk/places/favicon\").getFavicon instead.\n" + const { LoaderWithFilteredConsole } = require("sdk/test/loader"); + let loader = LoaderWithFilteredConsole(module, function(type, message) { + if (type == "error" && message.substring(0, warning.length) == warning) + return false; + return true; + }); + + let tabs = loader.require('sdk/tabs'); + + // Get current count of tabs so we know the index of the + // new tab, bug 893846 + let tabCount = tabs.length; + let count = 0; + function onReadyOrLoad (tab) { + if (count++) { + tab.close(done); + } + } + + let url = "data:text/html;charset=utf-8,foofoo"; + tabs.open({ + url: url, + onReady: function(tab) { + assert.equal(tab.title, "foo", "title of the new tab matches"); + assert.equal(tab.url, url, "URL of the new tab matches"); + assert.ok(tab.favicon, "favicon of the new tab is not empty"); + assert.equal(tab.style, null, "style of the new tab matches"); + assert.equal(tab.index, tabCount, "index of the new tab matches"); + assert.notEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches"); + assert.notEqual(tab.id, null, "a tab object always has an id property."); + + onReadyOrLoad(tab); + }, + onLoad: function(tab) { + assert.equal(tab.title, "foo", "title of the new tab matches"); + assert.equal(tab.url, url, "URL of the new tab matches"); + assert.ok(tab.favicon, "favicon of the new tab is not empty"); + assert.equal(tab.style, null, "style of the new tab matches"); + assert.equal(tab.index, tabCount, "index of the new tab matches"); + assert.notEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches"); + assert.notEqual(tab.id, null, "a tab object always has an id property."); + + onReadyOrLoad(tab); + } + }); +}; + +// TEST: tab properties +exports.testTabContentTypeAndReload = function(assert, done) { + open().then(focus).then(function(window) { + let url = "data:text/html;charset=utf-8,foofoo"; + let urlXML = "data:text/xml;charset=utf-8,bar"; + tabs.open({ + url: url, + onReady: function(tab) { + if (tab.url === url) { + assert.equal(tab.contentType, "text/html"); + tab.url = urlXML; + } + else { + assert.equal(tab.contentType, "text/xml"); + close(window).then(done); + } + } + }); + }); +}; + +// TEST: tabs iterator and length property +exports.testTabsIteratorAndLength = function(assert, done) { + open(null, { features: { chrome: true, toolbar: true } }).then(focus).then(function(window) { + let startCount = 0; + for each (let t in tabs) startCount++; + assert.equal(startCount, tabs.length, "length property is correct"); + let url = "data:text/html;charset=utf-8,default"; + + tabs.open(url); + tabs.open(url); + tabs.open({ + url: url, + onOpen: function(tab) { + let count = 0; + for each (let t in tabs) count++; + assert.equal(count, startCount + 3, "iterated tab count matches"); + assert.equal(startCount + 3, tabs.length, "iterated tab count matches length property"); + + close(window).then(done); + } + }); + }); +}; + +// TEST: tab.url setter +exports.testTabLocation = function(assert, done) { + open().then(focus).then(function(window) { + let url1 = "data:text/html;charset=utf-8,foo"; + let url2 = "data:text/html;charset=utf-8,bar"; + + tabs.on('ready', function onReady(tab) { + if (tab.url != url2) + return; + tabs.removeListener('ready', onReady); + assert.pass("tab.load() loaded the correct url"); + close(window).then(done); + }); + + tabs.open({ + url: url1, + onOpen: function(tab) { + tab.url = url2 + } + }); + }); +}; + +// TEST: tab.close() +exports.testTabClose = function(assert, done) { + let url = "data:text/html;charset=utf-8,foo"; + + assert.notEqual(tabs.activeTab.url, url, "tab is not the active tab"); + tabs.on('ready', function onReady(tab) { + tabs.removeListener('ready', onReady); + assert.equal(tabs.activeTab.url, tab.url, "tab is now the active tab"); + let secondOnCloseCalled = false; + + // Bug 699450: Multiple calls to tab.close should not throw + tab.close(function() secondOnCloseCalled = true); + try { + tab.close(function () { + assert.ok(secondOnCloseCalled, + "The immediate second call to tab.close gots its callback fired"); + assert.notEqual(tabs.activeTab.url, url, "tab is no longer the active tab"); + + done(); + }); + } + catch(e) { + assert.fail("second call to tab.close() thrown an exception: " + e); + } + assert.notEqual(tabs.activeTab.url, url, "tab is no longer the active tab"); + }); + + tabs.open(url); +}; + +// TEST: tab.move() +exports.testTabMove = function(assert, done) { + open().then(focus).then(function(window) { + let url = "data:text/html;charset=utf-8,foo"; + + tabs.open({ + url: url, + onOpen: function(tab) { + assert.equal(tab.index, 1, "tab index before move matches"); + tab.index = 0; + assert.equal(tab.index, 0, "tab index after move matches"); + close(window).then(done); + } + }); + }); +}; + +// TEST: open tab with default options +exports.testOpen = function(assert, done) { + let url = "data:text/html;charset=utf-8,default"; + tabs.open({ + url: url, + onReady: function(tab) { + assert.equal(tab.url, url, "URL of the new tab matches"); + assert.equal(tab.isPinned, false, "The new tab is not pinned"); + + tab.close(done); + } + }); +}; + +// TEST: opening a pinned tab +exports.testOpenPinned = function(assert, done) { + let url = "data:text/html;charset=utf-8,default"; + tabs.open({ + url: url, + isPinned: true, + onOpen: function(tab) { + assert.equal(tab.isPinned, true, "The new tab is pinned"); + tab.close(done); + } + }); +}; + +// TEST: pin/unpin opened tab +exports.testPinUnpin = function(assert, done) { + let url = "data:text/html;charset=utf-8,default"; + tabs.open({ + url: url, + inBackground: true, + onOpen: function(tab) { + tab.pin(); + assert.equal(tab.isPinned, true, "The tab was pinned correctly"); + tab.unpin(); + assert.equal(tab.isPinned, false, "The tab was unpinned correctly"); + tab.close(done); + } + }); +} + +// TEST: open tab in background +exports.testInBackground = function(assert, done) { + let window = getMostRecentBrowserWindow(); + let activeUrl = tabs.activeTab.url; + let url = "data:text/html;charset=utf-8,background"; + assert.equal(activeWindow, window, "activeWindow matches this window"); + tabs.on('ready', function onReady(tab) { + tabs.removeListener('ready', onReady); + assert.equal(tabs.activeTab.url, activeUrl, "URL of active tab has not changed"); + assert.equal(tab.url, url, "URL of the new background tab matches"); + assert.equal(activeWindow, window, "a new window was not opened"); + assert.notEqual(tabs.activeTab.url, url, "URL of active tab is not the new URL"); + tab.close(done); + }); + + tabs.open({ + url: url, + inBackground: true + }); +} + +// TEST: open tab in new window +exports.testOpenInNewWindow = function(assert, done) { + let startWindowCount = windows().length; + + let url = "data:text/html;charset=utf-8,testOpenInNewWindow"; + tabs.open({ + url: url, + inNewWindow: true, + onReady: function(tab) { + let newWindow = getOwnerWindow(tab); + assert.equal(windows().length, startWindowCount + 1, "a new window was opened"); + + onFocus(newWindow).then(function() { + assert.equal(activeWindow, newWindow, "new window is active"); + assert.equal(tab.url, url, "URL of the new tab matches"); + assert.equal(newWindow.content.location, url, "URL of new tab in new window matches"); + assert.equal(tabs.activeTab.url, url, "URL of activeTab matches"); + + close(newWindow).then(done); + }, assert.fail).then(null, assert.fail); + } + }); + +} + +// Test tab.open inNewWindow + onOpen combination +exports.testOpenInNewWindowOnOpen = function(assert, done) { + let startWindowCount = windows().length; + + let url = "data:text/html;charset=utf-8,newwindow"; + tabs.open({ + url: url, + inNewWindow: true, + onOpen: function(tab) { + let newWindow = getOwnerWindow(tab); + + onFocus(newWindow).then(function() { + assert.equal(windows().length, startWindowCount + 1, "a new window was opened"); + assert.equal(activeWindow, newWindow, "new window is active"); + + close(newWindow).then(done); + }); + } + }); +}; + +// TEST: onOpen event handler +exports.testTabsEvent_onOpen = function(assert, done) { + openBrowserWindow(function(window, browser) { + let url = "data:text/html;charset=utf-8,1"; + let eventCount = 0; + + // add listener via property assignment + function listener1(tab) { + eventCount++; + }; + tabs.on('open', listener1); + + // add listener via collection add + tabs.on('open', function listener2(tab) { + assert.equal(++eventCount, 2, "both listeners notified"); + tabs.removeListener('open', listener1); + tabs.removeListener('open', listener2); + close(window).then(done); + }); + + tabs.open(url); + }); +}; + +// TEST: onClose event handler +exports.testTabsEvent_onClose = function(assert, done) { + openBrowserWindow(function(window, browser) { + let url = "data:text/html;charset=utf-8,onclose"; + let eventCount = 0; + + // add listener via property assignment + function listener1(tab) { + eventCount++; + } + tabs.on('close', listener1); + + // add listener via collection add + tabs.on('close', function listener2(tab) { + assert.equal(++eventCount, 2, "both listeners notified"); + tabs.removeListener('close', listener1); + tabs.removeListener('close', listener2); + close(window).then(done); + }); + + tabs.on('ready', function onReady(tab) { + tabs.removeListener('ready', onReady); + tab.close(); + }); + + tabs.open(url); + }); +}; + +// TEST: onClose event handler when a window is closed +exports.testTabsEvent_onCloseWindow = function(assert, done) { + let closeCount = 0; + let individualCloseCount = 0; + + openBrowserWindow(function(window) { + tabs.on("close", function listener() { + if (++closeCount == 4) { + tabs.removeListener("close", listener); + } + }); + + function endTest() { + if (++individualCloseCount < 3) { + return; + } + + assert.equal(closeCount, 4, "Correct number of close events received"); + assert.equal(individualCloseCount, 3, + "Each tab with an attached onClose listener received a close " + + "event when the window was closed"); + + done(); + } + + // One tab is already open with the window + let openTabs = 1; + function testCasePossiblyLoaded() { + if (++openTabs == 4) { + window.close(); + } + } + + tabs.open({ + url: "data:text/html;charset=utf-8,tab2", + onOpen: testCasePossiblyLoaded, + onClose: endTest + }); + + tabs.open({ + url: "data:text/html;charset=utf-8,tab3", + onOpen: testCasePossiblyLoaded, + onClose: endTest + }); + + tabs.open({ + url: "data:text/html;charset=utf-8,tab4", + onOpen: testCasePossiblyLoaded, + onClose: endTest + }); + }); +} + +// TEST: onReady event handler +exports.testTabsEvent_onReady = function(assert, done) { + openBrowserWindow(function(window, browser) { + let url = "data:text/html;charset=utf-8,onready"; + let eventCount = 0; + + // add listener via property assignment + function listener1(tab) { + eventCount++; + }; + tabs.on('ready', listener1); + + // add listener via collection add + tabs.on('ready', function listener2(tab) { + assert.equal(++eventCount, 2, "both listeners notified"); + tabs.removeListener('ready', listener1); + tabs.removeListener('ready', listener2); + close(window).then(done); + }); + + tabs.open(url); + }); +}; + +// TEST: onActivate event handler +exports.testTabsEvent_onActivate = function(assert, done) { + openBrowserWindow(function(window, browser) { + let url = "data:text/html;charset=utf-8,onactivate"; + let eventCount = 0; + + // add listener via property assignment + function listener1(tab) { + eventCount++; + }; + tabs.on('activate', listener1); + + // add listener via collection add + tabs.on('activate', function listener2(tab) { + assert.equal(++eventCount, 2, "both listeners notified"); + tabs.removeListener('activate', listener1); + tabs.removeListener('activate', listener2); + close(window).then(done); + }); + + tabs.open(url); + }); +}; + +// onDeactivate event handler +exports.testTabsEvent_onDeactivate = function(assert, done) { + openBrowserWindow(function(window, browser) { + let url = "data:text/html;charset=utf-8,ondeactivate"; + let eventCount = 0; + + // add listener via property assignment + function listener1(tab) { + eventCount++; + }; + tabs.on('deactivate', listener1); + + // add listener via collection add + tabs.on('deactivate', function listener2(tab) { + assert.equal(++eventCount, 2, "both listeners notified"); + tabs.removeListener('deactivate', listener1); + tabs.removeListener('deactivate', listener2); + close(window).then(done); + }); + + tabs.on('open', function onOpen(tab) { + tabs.removeListener('open', onOpen); + tabs.open("data:text/html;charset=utf-8,foo"); + }); + + tabs.open(url); + }); +}; + +// pinning +exports.testTabsEvent_pinning = function(assert, done) { + openBrowserWindow(function(window, browser) { + let url = "data:text/html;charset=utf-8,1"; + + tabs.on('open', function onOpen(tab) { + tabs.removeListener('open', onOpen); + tab.pin(); + }); + + tabs.on('pinned', function onPinned(tab) { + tabs.removeListener('pinned', onPinned); + assert.ok(tab.isPinned, "notified tab is pinned"); + tab.unpin(); + }); + + tabs.on('unpinned', function onUnpinned(tab) { + tabs.removeListener('unpinned', onUnpinned); + assert.ok(!tab.isPinned, "notified tab is not pinned"); + close(window).then(done); + }); + + tabs.open(url); + }); +}; + +// TEST: per-tab event handlers +exports.testPerTabEvents = function(assert, done) { + openBrowserWindow(function(window, browser) { + let eventCount = 0; + + tabs.open({ + url: "data:text/html;charset=utf-8,foo", + onOpen: function(tab) { + // add listener via property assignment + function listener1() { + eventCount++; + }; + tab.on('ready', listener1); + + // add listener via collection add + tab.on('ready', function listener2() { + assert.equal(eventCount, 1, "both listeners notified"); + tab.removeListener('ready', listener1); + tab.removeListener('ready', listener2); + close(window).then(done); + }); + } + }); + }); +}; + +exports.testAttachOnOpen = function (assert, done) { + // Take care that attach has to be called on tab ready and not on tab open. + openBrowserWindow(function(window, browser) { + tabs.open({ + url: "data:text/html;charset=utf-8,foobar", + onOpen: function (tab) { + let worker = tab.attach({ + contentScript: 'self.postMessage(document.location.href); ', + onMessage: function (msg) { + assert.equal(msg, "about:blank", + "Worker document url is about:blank on open"); + worker.destroy(); + close(window).then(done); + } + }); + } + }); + + }); +} + +exports.testAttachOnMultipleDocuments = function (assert, done) { + // Example of attach that process multiple tab documents + openBrowserWindow(function(window, browser) { + let firstLocation = "data:text/html;charset=utf-8,foobar"; + let secondLocation = "data:text/html;charset=utf-8,bar"; + let thirdLocation = "data:text/html;charset=utf-8,fox"; + let onReadyCount = 0; + let worker1 = null; + let worker2 = null; + let detachEventCount = 0; + + tabs.open({ + url: firstLocation, + onReady: function (tab) { + onReadyCount++; + if (onReadyCount == 1) { + worker1 = tab.attach({ + contentScript: 'self.on("message", ' + + ' function () self.postMessage(document.location.href)' + + ');', + onMessage: function (msg) { + assert.equal(msg, firstLocation, + "Worker url is equal to the 1st document"); + tab.url = secondLocation; + }, + onDetach: function () { + detachEventCount++; + assert.pass("Got worker1 detach event"); + assert.throws(function () { + worker1.postMessage("ex-1"); + }, + /Couldn't find the worker/, + "postMessage throw because worker1 is destroyed"); + checkEnd(); + } + }); + worker1.postMessage("new-doc-1"); + } + else if (onReadyCount == 2) { + + worker2 = tab.attach({ + contentScript: 'self.on("message", ' + + ' function () self.postMessage(document.location.href)' + + ');', + onMessage: function (msg) { + assert.equal(msg, secondLocation, + "Worker url is equal to the 2nd document"); + tab.url = thirdLocation; + }, + onDetach: function () { + detachEventCount++; + assert.pass("Got worker2 detach event"); + assert.throws(function () { + worker2.postMessage("ex-2"); + }, + /Couldn't find the worker/, + "postMessage throw because worker2 is destroyed"); + checkEnd(); + } + }); + worker2.postMessage("new-doc-2"); + } + else if (onReadyCount == 3) { + tab.close(); + } + } + }); + + function checkEnd() { + if (detachEventCount != 2) + return; + + assert.pass("Got all detach events"); + + close(window).then(done); + } + + }); +} + + +exports.testAttachWrappers = function (assert, done) { + // Check that content script has access to wrapped values by default + openBrowserWindow(function(window, browser) { + let document = "data:text/html;charset=utf-8,"; + let count = 0; + + tabs.open({ + url: document, + onReady: function (tab) { + let worker = tab.attach({ + contentScript: 'try {' + + ' self.postMessage(!("globalJSVar" in window));' + + ' self.postMessage(typeof window.globalJSVar == "undefined");' + + '} catch(e) {' + + ' self.postMessage(e.message);' + + '}', + onMessage: function (msg) { + assert.equal(msg, true, "Worker has wrapped objects ("+count+")"); + if (count++ == 1) + close(window).then(done); + } + }); + } + }); + + }); +} + +/* +// We do not offer unwrapped access to DOM since bug 601295 landed +// See 660780 to track progress of unwrap feature +exports.testAttachUnwrapped = function (assert, done) { + // Check that content script has access to unwrapped values through unsafeWindow + openBrowserWindow(function(window, browser) { + let document = "data:text/html;charset=utf-8,"; + let count = 0; + + tabs.open({ + url: document, + onReady: function (tab) { + let worker = tab.attach({ + contentScript: 'try {' + + ' self.postMessage(unsafeWindow.globalJSVar);' + + '} catch(e) {' + + ' self.postMessage(e.message);' + + '}', + onMessage: function (msg) { + assert.equal(msg, true, "Worker has access to javascript content globals ("+count+")"); + close(window).then(done); + } + }); + } + }); + + }); +} +*/ + +exports['test window focus changes active tab'] = function(assert, done) { + let url1 = "data:text/html;charset=utf-8," + encodeURIComponent("test window focus changes active tab

Window #1"); + + let win1 = openBrowserWindow(function() { + assert.pass("window 1 is open"); + + let win2 = openBrowserWindow(function() { + assert.pass("window 2 is open"); + + focus(win2).then(function() { + tabs.on("activate", function onActivate(tab) { + tabs.removeListener("activate", onActivate); + assert.pass("activate was called on windows focus change."); + assert.equal(tab.url, url1, 'the activated tab url is correct'); + + close(win2).then(function() { + assert.pass('window 2 was closed'); + return close(win1); + }).then(done); + }); + + win1.focus(); + }); + }, "data:text/html;charset=utf-8,test window focus changes active tab

Window #2"); + }, url1); +}; + +exports['test ready event on new window tab'] = function(assert, done) { + let uri = encodeURI("data:text/html;charset=utf-8,Waiting for ready event!"); + + require("sdk/tabs").on("ready", function onReady(tab) { + if (tab.url === uri) { + require("sdk/tabs").removeListener("ready", onReady); + assert.pass("ready event was emitted"); + close(window).then(done); + } + }); + + let window = openBrowserWindow(function(){}, uri); +}; + +exports['test unique tab ids'] = function(assert, done) { + var windows = require('sdk/windows').browserWindows; + var { all, defer } = require('sdk/core/promise'); + + function openWindow() { + // console.log('in openWindow'); + let deferred = defer(); + let win = windows.open({ + url: "data:text/html;charset=utf-8,foo", + }); + + win.on('open', function(window) { + assert.ok(window.tabs.length); + assert.ok(window.tabs.activeTab); + assert.ok(window.tabs.activeTab.id); + deferred.resolve({ + id: window.tabs.activeTab.id, + win: win + }); + }); + + return deferred.promise; + } + + var one = openWindow(), two = openWindow(); + all([one, two]).then(function(results) { + assert.notEqual(results[0].id, results[1].id, "tab Ids should not be equal."); + results[0].win.close(); + results[1].win.close(); + done(); + }); +} + +// related to Bug 671305 +exports.testOnLoadEventWithDOM = function(assert, done) { + let count = 0; + let title = 'testOnLoadEventWithDOM'; + + // open a about: url + tabs.open({ + url: 'data:text/html;charset=utf-8,' + title + '', + inBackground: true, + onLoad: function(tab) { + assert.equal(tab.title, title, 'tab passed in as arg, load called'); + + if (++count > 1) { + assert.pass('onLoad event called on reload'); + tab.close(done); + } + else { + assert.pass('first onLoad event occured'); + tab.reload(); + } + } + }); +}; + +// related to Bug 671305 +exports.testOnLoadEventWithImage = function(assert, done) { + let count = 0; + + tabs.open({ + url: base64png, + inBackground: true, + onLoad: function(tab) { + if (++count > 1) { + assert.pass('onLoad event called on reload with image'); + tab.close(done); + } + else { + assert.pass('first onLoad event occured'); + tab.reload(); + } + } + }); +}; + +exports.testFaviconGetterDeprecation = function (assert, done) { + setPref(DEPRECATE_PREF, true); + const { LoaderWithHookedConsole } = require("sdk/test/loader"); + let { loader, messages } = LoaderWithHookedConsole(module); + let tabs = loader.require('sdk/tabs'); + + tabs.open({ + url: 'data:text/html;charset=utf-8,', + onOpen: function (tab) { + let favicon = tab.favicon; + assert.ok(messages.length === 1, 'only one error is dispatched'); + assert.ok(messages[0].type, 'error', 'the console message is an error'); + + let msg = messages[0].msg; + assert.ok(msg.indexOf('tab.favicon is deprecated') !== -1, + 'message contains the given message'); + tab.close(done); + loader.unload(); + } + }); +} + +/******************* helpers *********************/ + +// Helper for getting the active window +this.__defineGetter__("activeWindow", function activeWindow() { + return Cc["@mozilla.org/appshell/window-mediator;1"]. + getService(Ci.nsIWindowMediator). + getMostRecentWindow("navigator:browser"); +}); + +// Utility function to open a new browser window. +function openBrowserWindow(callback, url) { + let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"]. + getService(Ci.nsIWindowWatcher); + let urlString = Cc["@mozilla.org/supports-string;1"]. + createInstance(Ci.nsISupportsString); + urlString.data = url; + let window = ww.openWindow(null, "chrome://browser/content/browser.xul", + "_blank", "chrome,all,dialog=no", urlString); + + if (callback) { + window.addEventListener("load", function onLoad(event) { + if (event.target && event.target.defaultView == window) { + window.removeEventListener("load", onLoad, true); + let browsers = window.document.getElementsByTagName("tabbrowser"); + try { + timer.setTimeout(function () { + callback(window, browsers[0]); + }, 10); + } + catch (e) { + console.exception(e); + } + } + }, true); + } + + return window; +} + +require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-addon-installer.js b/addon-sdk-1.16/test/test-addon-installer.js new file mode 100644 index 00000000..3857f2cd --- /dev/null +++ b/addon-sdk-1.16/test/test-addon-installer.js @@ -0,0 +1,179 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const { Cc, Ci, Cu } = require("chrome"); +const AddonInstaller = require("sdk/addon/installer"); +const { on, off } = require("sdk/system/events"); +const { setTimeout } = require("sdk/timers"); +const tmp = require("sdk/test/tmp-file"); +const system = require("sdk/system"); +const fixtures = require("./fixtures"); + +const testFolderURL = module.uri.split('test-addon-installer.js')[0]; +const ADDON_URL = testFolderURL + "fixtures/addon-install-unit-test@mozilla.com.xpi"; +const ADDON_PATH = tmp.createFromURL(ADDON_URL); + +exports["test Install"] = function (assert, done) { + + // Save all events distpatched by bootstrap.js of the installed addon + let events = []; + function eventsObserver({ data }) { + events.push(data); + } + on("addon-install-unit-test", eventsObserver); + + // Install the test addon + AddonInstaller.install(ADDON_PATH).then( + function onInstalled(id) { + assert.equal(id, "addon-install-unit-test@mozilla.com", "`id` is valid"); + + // Now uninstall it + AddonInstaller.uninstall(id).then(function () { + // Ensure that bootstrap.js methods of the addon have been called + // successfully and in the right order + let expectedEvents = ["install", "startup", "shutdown", "uninstall"]; + assert.equal(JSON.stringify(events), + JSON.stringify(expectedEvents), + "addon's bootstrap.js functions have been called"); + + off("addon-install-unit-test", eventsObserver); + done(); + }); + }, + function onFailure(code) { + assert.fail("Install failed: "+code); + off("addon-install-unit-test", eventsObserver); + done(); + } + ); +}; + +exports["test Failing Install With Invalid Path"] = function (assert, done) { + AddonInstaller.install("invalid-path").then( + function onInstalled(id) { + assert.fail("Unexpected success"); + done(); + }, + function onFailure(code) { + assert.equal(code, AddonInstaller.ERROR_FILE_ACCESS, + "Got expected error code"); + done(); + } + ); +}; + +exports["test Failing Install With Invalid File"] = function (assert, done) { + let directory = system.pathFor("ProfD"); + AddonInstaller.install(directory).then( + function onInstalled(id) { + assert.fail("Unexpected success"); + done(); + }, + function onFailure(code) { + assert.equal(code, AddonInstaller.ERROR_CORRUPT_FILE, + "Got expected error code"); + done(); + } + ); +} + +exports["test Update"] = function (assert, done) { + // Save all events distpatched by bootstrap.js of the installed addon + let events = []; + let iteration = 1; + let eventsObserver = ({data}) => events.push(data); + on("addon-install-unit-test", eventsObserver); + + function onInstalled(id) { + let prefix = "[" + iteration + "] "; + assert.equal(id, "addon-install-unit-test@mozilla.com", + prefix + "`id` is valid"); + + // On 2nd and 3rd iteration, we receive uninstall events from the last + // previously installed addon + let expectedEvents = + iteration == 1 + ? ["install", "startup"] + : ["shutdown", "uninstall", "install", "startup"]; + assert.equal(JSON.stringify(events), + JSON.stringify(expectedEvents), + prefix + "addon's bootstrap.js functions have been called"); + + if (iteration++ < 3) { + next(); + } + else { + events = []; + AddonInstaller.uninstall(id).then(function() { + let expectedEvents = ["shutdown", "uninstall"]; + assert.equal(JSON.stringify(events), + JSON.stringify(expectedEvents), + prefix + "addon's bootstrap.js functions have been called"); + + off("addon-install-unit-test", eventsObserver); + done(); + }); + } + } + function onFailure(code) { + assert.fail("Install failed: "+code); + off("addon-install-unit-test", eventsObserver); + done(); + } + + function next() { + events = []; + AddonInstaller.install(ADDON_PATH).then(onInstalled, onFailure); + } + + next(); +}; + +exports['test Uninstall failure'] = function (assert, done) { + AddonInstaller.uninstall('invalid-addon-path').then( + () => assert.fail('Addon uninstall should not resolve successfully'), + () => assert.pass('Addon correctly rejected invalid uninstall') + ).then(done, assert.fail); +}; + +exports['test Addon Disable and Enable'] = function (assert, done) { + let ensureActive = (addonId) => AddonInstaller.isActive(addonId).then(state => { + assert.equal(state, true, 'Addon should be enabled by default'); + return addonId; + }); + let ensureInactive = (addonId) => AddonInstaller.isActive(addonId).then(state => { + assert.equal(state, false, 'Addon should be disabled after disabling'); + return addonId; + }); + + AddonInstaller.install(ADDON_PATH) + .then(ensureActive) + .then(AddonInstaller.enable) // should do nothing, yet not fail + .then(ensureActive) + .then(AddonInstaller.disable) + .then(ensureInactive) + .then(AddonInstaller.disable) // should do nothing, yet not fail + .then(ensureInactive) + .then(AddonInstaller.enable) + .then(ensureActive) + .then(AddonInstaller.uninstall) + .then(done, assert.fail); +}; + +exports['test Disable failure'] = function (assert, done) { + AddonInstaller.disable('not-an-id').then( + () => assert.fail('Addon disable should not resolve successfully'), + () => assert.pass('Addon correctly rejected invalid disable') + ).then(done, assert.fail); +}; + +exports['test Enable failure'] = function (assert, done) { + AddonInstaller.enable('not-an-id').then( + () => assert.fail('Addon enable should not resolve successfully'), + () => assert.pass('Addon correctly rejected invalid enable') + ).then(done, assert.fail); +}; + +require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-addon-window.js b/addon-sdk-1.16/test/test-addon-window.js new file mode 100644 index 00000000..eda1ae7d --- /dev/null +++ b/addon-sdk-1.16/test/test-addon-window.js @@ -0,0 +1,22 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +'use strict'; + +let { Loader } = require('sdk/test/loader'); + +exports.testReady = function(assert, done) { + let loader = Loader(module); + let { ready, window } = loader.require('sdk/addon/window'); + let windowIsReady = false; + + ready.then(function() { + assert.equal(windowIsReady, false, 'ready promise was resolved only once'); + windowIsReady = true; + + loader.unload(); + done(); + }).then(null, assert.fail); +} + +require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-api-utils.js b/addon-sdk-1.16/test/test-api-utils.js new file mode 100644 index 00000000..a1239f60 --- /dev/null +++ b/addon-sdk-1.16/test/test-api-utils.js @@ -0,0 +1,316 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const apiUtils = require("sdk/deprecated/api-utils"); + +exports.testValidateOptionsEmpty = function (assert) { + let val = apiUtils.validateOptions(null, {}); + + assert.deepEqual(val, {}); + + val = apiUtils.validateOptions(null, { foo: {} }); + assert.deepEqual(val, {}); + + val = apiUtils.validateOptions({}, {}); + assert.deepEqual(val, {}); + + val = apiUtils.validateOptions({}, { foo: {} }); + assert.deepEqual(val, {}); +}; + +exports.testValidateOptionsNonempty = function (assert) { + let val = apiUtils.validateOptions({ foo: 123 }, {}); + assert.deepEqual(val, {}); + + val = apiUtils.validateOptions({ foo: 123, bar: 456 }, + { foo: {}, bar: {}, baz: {} }); + + assert.deepEqual(val, { foo: 123, bar: 456 }); +}; + +exports.testValidateOptionsMap = function (assert) { + let val = apiUtils.validateOptions({ foo: 3, bar: 2 }, { + foo: { map: function (v) v * v }, + bar: { map: function (v) undefined } + }); + assert.deepEqual(val, { foo: 9, bar: undefined }); +}; + +exports.testValidateOptionsMapException = function (assert) { + let val = apiUtils.validateOptions({ foo: 3 }, { + foo: { map: function () { throw new Error(); }} + }); + assert.deepEqual(val, { foo: 3 }); +}; + +exports.testValidateOptionsOk = function (assert) { + let val = apiUtils.validateOptions({ foo: 3, bar: 2, baz: 1 }, { + foo: { ok: function (v) v }, + bar: { ok: function (v) v } + }); + assert.deepEqual(val, { foo: 3, bar: 2 }); + + assert.throws( + function () apiUtils.validateOptions({ foo: 2, bar: 2 }, { + bar: { ok: function (v) v > 2 } + }), + /^The option "bar" is invalid/, + "ok should raise exception on invalid option" + ); + + assert.throws( + function () apiUtils.validateOptions(null, { foo: { ok: function (v) v }}), + /^The option "foo" is invalid/, + "ok should raise exception on invalid option" + ); +}; + +exports.testValidateOptionsIs = function (assert) { + let opts = { + array: [], + boolean: true, + func: function () {}, + nul: null, + number: 1337, + object: {}, + string: "foo", + undef1: undefined + }; + let requirements = { + array: { is: ["array"] }, + boolean: { is: ["boolean"] }, + func: { is: ["function"] }, + nul: { is: ["null"] }, + number: { is: ["number"] }, + object: { is: ["object"] }, + string: { is: ["string"] }, + undef1: { is: ["undefined"] }, + undef2: { is: ["undefined"] } + }; + let val = apiUtils.validateOptions(opts, requirements); + assert.deepEqual(val, opts); + + assert.throws( + function () apiUtils.validateOptions(null, { + foo: { is: ["object", "number"] } + }), + /^The option "foo" must be one of the following types: object, number/, + "Invalid type should raise exception" + ); +}; + +exports.testValidateOptionsIsWithExportedValue = function (assert) { + let { string, number, boolean, object } = apiUtils; + + let opts = { + boolean: true, + number: 1337, + object: {}, + string: "foo" + }; + let requirements = { + string: { is: string }, + number: { is: number }, + boolean: { is: boolean }, + object: { is: object } + }; + let val = apiUtils.validateOptions(opts, requirements); + assert.deepEqual(val, opts); + + // Test the types are optional by default + val = apiUtils.validateOptions({foo: 'bar'}, requirements); + assert.deepEqual(val, {}); +}; + +exports.testValidateOptionsIsWithEither = function (assert) { + let { string, number, boolean, either } = apiUtils; + let text = { is: either(string, number) }; + + let requirements = { + text: text, + boolOrText: { is: either(text, boolean) } + }; + + let val = apiUtils.validateOptions({text: 12}, requirements); + assert.deepEqual(val, {text: 12}); + + val = apiUtils.validateOptions({text: "12"}, requirements); + assert.deepEqual(val, {text: "12"}); + + val = apiUtils.validateOptions({boolOrText: true}, requirements); + assert.deepEqual(val, {boolOrText: true}); + + val = apiUtils.validateOptions({boolOrText: "true"}, requirements); + assert.deepEqual(val, {boolOrText: "true"}); + + val = apiUtils.validateOptions({boolOrText: 1}, requirements); + assert.deepEqual(val, {boolOrText: 1}); + + assert.throws( + () => apiUtils.validateOptions({text: true}, requirements), + /^The option "text" must be one of the following types/, + "Invalid type should raise exception" + ); + + assert.throws( + () => apiUtils.validateOptions({boolOrText: []}, requirements), + /^The option "boolOrText" must be one of the following types/, + "Invalid type should raise exception" + ); +}; + +exports.testValidateOptionsWithRequiredAndOptional = function (assert) { + let { string, number, required, optional } = apiUtils; + + let opts = { + number: 1337, + string: "foo" + }; + + let requirements = { + string: required(string), + number: number + }; + + let val = apiUtils.validateOptions(opts, requirements); + assert.deepEqual(val, opts); + + val = apiUtils.validateOptions({string: "foo"}, requirements); + assert.deepEqual(val, {string: "foo"}); + + assert.throws( + () => apiUtils.validateOptions({number: 10}, requirements), + /^The option "string" must be one of the following types/, + "Invalid type should raise exception" + ); + + // Makes string optional + requirements.string = optional(requirements.string); + + val = apiUtils.validateOptions({number: 10}, requirements), + assert.deepEqual(val, {number: 10}); + +}; + + + +exports.testValidateOptionsWithExportedValue = function (assert) { + let { string, number, boolean, object } = apiUtils; + + let opts = { + boolean: true, + number: 1337, + object: {}, + string: "foo" + }; + let requirements = { + string: string, + number: number, + boolean: boolean, + object: object + }; + let val = apiUtils.validateOptions(opts, requirements); + assert.deepEqual(val, opts); + + // Test the types are optional by default + val = apiUtils.validateOptions({foo: 'bar'}, requirements); + assert.deepEqual(val, {}); +}; + + +exports.testValidateOptionsMapIsOk = function (assert) { + let [map, is, ok] = [false, false, false]; + let val = apiUtils.validateOptions({ foo: 1337 }, { + foo: { + map: function (v) v.toString(), + is: ["string"], + ok: function (v) v.length > 0 + } + }); + assert.deepEqual(val, { foo: "1337" }); + + let requirements = { + foo: { + is: ["object"], + ok: function () assert.fail("is should have caused us to throw by now") + } + }; + assert.throws( + function () apiUtils.validateOptions(null, requirements), + /^The option "foo" must be one of the following types: object/, + "is should be used before ok is called" + ); +}; + +exports.testValidateOptionsErrorMsg = function (assert) { + assert.throws( + function () apiUtils.validateOptions(null, { + foo: { ok: function (v) v, msg: "foo!" } + }), + /^foo!/, + "ok should raise exception with customized message" + ); +}; + +exports.testValidateMapWithMissingKey = function (assert) { + let val = apiUtils.validateOptions({ }, { + foo: { + map: function (v) v || "bar" + } + }); + assert.deepEqual(val, { foo: "bar" }); + + val = apiUtils.validateOptions({ }, { + foo: { + map: function (v) { throw "bar" } + } + }); + assert.deepEqual(val, { }); +}; + +exports.testValidateMapWithMissingKeyAndThrown = function (assert) { + let val = apiUtils.validateOptions({}, { + bar: { + map: function(v) { throw "bar" } + }, + baz: { + map: function(v) "foo" + } + }); + assert.deepEqual(val, { baz: "foo" }); +}; + +exports.testAddIterator = function testAddIterator (assert) { + let obj = {}; + let keys = ["foo", "bar", "baz"]; + let vals = [1, 2, 3]; + let keysVals = [["foo", 1], ["bar", 2], ["baz", 3]]; + apiUtils.addIterator( + obj, + function keysValsGen() { + for each (let keyVal in keysVals) + yield keyVal; + } + ); + + let keysItr = []; + for (let key in obj) + keysItr.push(key); + + assert.equal(keysItr.length, keys.length, + "the keys iterator returns the correct number of items"); + for (let i = 0; i < keys.length; i++) + assert.equal(keysItr[i], keys[i], "the key is correct"); + + let valsItr = []; + for each (let val in obj) + valsItr.push(val); + assert.equal(valsItr.length, vals.length, + "the vals iterator returns the correct number of items"); + for (let i = 0; i < vals.length; i++) + assert.equal(valsItr[i], vals[i], "the val is correct"); + +}; + +require('test').run(exports); diff --git a/addon-sdk-1.16/test/test-array.js b/addon-sdk-1.16/test/test-array.js new file mode 100644 index 00000000..8a9cb440 --- /dev/null +++ b/addon-sdk-1.16/test/test-array.js @@ -0,0 +1,103 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +'use strict' + +const array = require('sdk/util/array'); + +exports.testHas = function(assert) { + var testAry = [1, 2, 3]; + assert.equal(array.has([1, 2, 3], 1), true); + assert.equal(testAry.length, 3); + assert.equal(testAry[0], 1); + assert.equal(testAry[1], 2); + assert.equal(testAry[2], 3); + assert.equal(array.has(testAry, 2), true); + assert.equal(array.has(testAry, 3), true); + assert.equal(array.has(testAry, 4), false); + assert.equal(array.has(testAry, '1'), false); +}; +exports.testHasAny = function(assert) { + var testAry = [1, 2, 3]; + assert.equal(array.hasAny([1, 2, 3], [1]), true); + assert.equal(array.hasAny([1, 2, 3], [1, 5]), true); + assert.equal(array.hasAny([1, 2, 3], [5, 1]), true); + assert.equal(array.hasAny([1, 2, 3], [5, 2]), true); + assert.equal(array.hasAny([1, 2, 3], [5, 3]), true); + assert.equal(array.hasAny([1, 2, 3], [5, 4]), false); + assert.equal(testAry.length, 3); + assert.equal(testAry[0], 1); + assert.equal(testAry[1], 2); + assert.equal(testAry[2], 3); + assert.equal(array.hasAny(testAry, [2]), true); + assert.equal(array.hasAny(testAry, [3]), true); + assert.equal(array.hasAny(testAry, [4]), false); + assert.equal(array.hasAny(testAry), false); + assert.equal(array.hasAny(testAry, '1'), false); +}; + +exports.testAdd = function(assert) { + var testAry = [1]; + assert.equal(array.add(testAry, 1), false); + assert.equal(testAry.length, 1); + assert.equal(testAry[0], 1); + assert.equal(array.add(testAry, 2), true); + assert.equal(testAry.length, 2); + assert.equal(testAry[0], 1); + assert.equal(testAry[1], 2); +}; + +exports.testRemove = function(assert) { + var testAry = [1, 2]; + assert.equal(array.remove(testAry, 3), false); + assert.equal(testAry.length, 2); + assert.equal(testAry[0], 1); + assert.equal(testAry[1], 2); + assert.equal(array.remove(testAry, 2), true); + assert.equal(testAry.length, 1); + assert.equal(testAry[0], 1); +}; + +exports.testFlatten = function(assert) { + assert.equal(array.flatten([1, 2, 3]).length, 3); + assert.equal(array.flatten([1, [2, 3]]).length, 3); + assert.equal(array.flatten([1, [2, [3]]]).length, 3); + assert.equal(array.flatten([[1], [[2, [3]]]]).length, 3); +}; + +exports.testUnique = function(assert) { + var Class = function () {}; + var A = {}; + var B = new Class(); + var C = [ 1, 2, 3 ]; + var D = {}; + var E = new Class(); + + assert.deepEqual(array.unique([1,2,3,1,2]), [1,2,3]); + assert.deepEqual(array.unique([1,1,1,4,9,5,5]), [1,4,9,5]); + assert.deepEqual(array.unique([A, A, A, B, B, D]), [A,B,D]); + assert.deepEqual(array.unique([A, D, A, E, E, D, A, A, C]), [A, D, E, C]) +}; + +exports.testUnion = function(assert) { + var Class = function () {}; + var A = {}; + var B = new Class(); + var C = [ 1, 2, 3 ]; + var D = {}; + var E = new Class(); + + assert.deepEqual(array.union([1, 2, 3],[7, 1, 2]), [1, 2, 3, 7]); + assert.deepEqual(array.union([1, 1, 1, 4, 9, 5, 5], [10, 1, 5]), [1, 4, 9, 5, 10]); + assert.deepEqual(array.union([A, B], [A, D]), [A, B, D]); + assert.deepEqual(array.union([A, D], [A, E], [E, D, A], [A, C]), [A, D, E, C]); +}; + +exports.testFind = function(assert) { + let isOdd = (x) => x % 2; + assert.equal(array.find([2, 4, 5, 7, 8, 9], isOdd), 5); + assert.equal(array.find([2, 4, 6, 8], isOdd), undefined); + assert.equal(array.find([2, 4, 6, 8], isOdd, null), null); +}; + +require('test').run(exports); diff --git a/addon-sdk-1.16/test/test-base64.js b/addon-sdk-1.16/test/test-base64.js new file mode 100644 index 00000000..6ca75002 --- /dev/null +++ b/addon-sdk-1.16/test/test-base64.js @@ -0,0 +1,75 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const base64 = require("sdk/base64"); + +const text = "Awesome!"; +const b64text = "QXdlc29tZSE="; + +const utf8text = "✓ à la mode"; +const b64utf8text = "4pyTIMOgIGxhIG1vZGU="; + +exports["test base64.encode"] = function (assert) { + assert.equal(base64.encode(text), b64text, "encode correctly") +} + +exports["test base64.decode"] = function (assert) { + assert.equal(base64.decode(b64text), text, "decode correctly") +} + +exports["test base64.encode Unicode"] = function (assert) { + + assert.equal(base64.encode(utf8text, "utf-8"), b64utf8text, + "encode correctly Unicode strings.") +} + +exports["test base64.decode Unicode"] = function (assert) { + + assert.equal(base64.decode(b64utf8text, "utf-8"), utf8text, + "decode correctly Unicode strings.") +} + +exports["test base64.encode with wrong charset"] = function (assert) { + + assert.throws(function() { + base64.encode(utf8text, "utf-16"); + }, "The charset argument can be only 'utf-8'"); + + assert.throws(function() { + base64.encode(utf8text, ""); + }, "The charset argument can be only 'utf-8'"); + + assert.throws(function() { + base64.encode(utf8text, 8); + }, "The charset argument can be only 'utf-8'"); + +} + +exports["test base64.decode with wrong charset"] = function (assert) { + + assert.throws(function() { + base64.decode(utf8text, "utf-16"); + }, "The charset argument can be only 'utf-8'"); + + assert.throws(function() { + base64.decode(utf8text, ""); + }, "The charset argument can be only 'utf-8'"); + + assert.throws(function() { + base64.decode(utf8text, 8); + }, "The charset argument can be only 'utf-8'"); + +} + +exports["test encode/decode Unicode without utf-8 as charset"] = function (assert) { + + assert.notEqual(base64.decode(base64.encode(utf8text)), utf8text, + "Unicode strings needs 'utf-8' charset" + ); + +} + +require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-browser-events.js b/addon-sdk-1.16/test/test-browser-events.js new file mode 100644 index 00000000..b4d8a1b0 --- /dev/null +++ b/addon-sdk-1.16/test/test-browser-events.js @@ -0,0 +1,101 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.metadata = { + engines: { + "Firefox": "*" + } +}; + +const { Loader } = require("sdk/test/loader"); +const { open, getMostRecentBrowserWindow, getOuterId } = require("sdk/window/utils"); +const { setTimeout } = require("sdk/timers"); + +exports["test browser events"] = function(assert, done) { + let loader = Loader(module); + let { events } = loader.require("sdk/browser/events"); + let { on, off } = loader.require("sdk/event/core"); + let actual = []; + + on(events, "data", function handler(e) { + actual.push(e); + if (e.type === "load") window.close(); + if (e.type === "close") { + // Unload the module so that all listeners set by observer are removed. + + let [ ready, load, close ] = actual; + + assert.equal(ready.type, "DOMContentLoaded"); + assert.equal(ready.target, window, "window ready"); + + assert.equal(load.type, "load"); + assert.equal(load.target, window, "window load"); + + assert.equal(close.type, "close"); + assert.equal(close.target, window, "window load"); + + // Note: If window is closed right after this GC won't have time + // to claim loader and there for this listener, there for it's safer + // to remove listener. + off(events, "data", handler); + loader.unload(); + done(); + } + }); + + // Open window and close it to trigger observers. + let window = open(); +}; + +exports["test browser events ignore other wins"] = function(assert, done) { + let loader = Loader(module); + let { events: windowEvents } = loader.require("sdk/window/events"); + let { events: browserEvents } = loader.require("sdk/browser/events"); + let { on, off } = loader.require("sdk/event/core"); + let actualBrowser = []; + let actualWindow = []; + + function browserEventHandler(e) actualBrowser.push(e) + on(browserEvents, "data", browserEventHandler); + on(windowEvents, "data", function handler(e) { + actualWindow.push(e); + // Delay close so that if "load" is also emitted on `browserEvents` + // `browserEventHandler` will be invoked. + if (e.type === "load") setTimeout(window.close); + if (e.type === "close") { + assert.deepEqual(actualBrowser, [], "browser events were not triggered"); + let [ open, ready, load, close ] = actualWindow; + + assert.equal(open.type, "open"); + assert.equal(open.target, window, "window is open"); + + + + assert.equal(ready.type, "DOMContentLoaded"); + assert.equal(ready.target, window, "window ready"); + + assert.equal(load.type, "load"); + assert.equal(load.target, window, "window load"); + + assert.equal(close.type, "close"); + assert.equal(close.target, window, "window load"); + + + // Note: If window is closed right after this GC won't have time + // to claim loader and there for this listener, there for it's safer + // to remove listener. + off(windowEvents, "data", handler); + off(browserEvents, "data", browserEventHandler); + loader.unload(); + done(); + } + }); + + // Open window and close it to trigger observers. + let window = open("data:text/html,not a browser"); +}; + +require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-buffer.js b/addon-sdk-1.16/test/test-buffer.js new file mode 100644 index 00000000..1e3379e0 --- /dev/null +++ b/addon-sdk-1.16/test/test-buffer.js @@ -0,0 +1,566 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +/* + * Many of these tests taken from Joyent's Node + * https://github.com/joyent/node/blob/master/test/simple/test-buffer.js + */ + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +const { Buffer, TextEncoder, TextDecoder } = require('sdk/io/buffer'); +const { safeMerge } = require('sdk/util/object'); + +const ENCODINGS = ['utf-8', 'utf-16le', 'utf-16be']; + +exports.testBufferMain = function (assert) { + let b = Buffer('abcdef'); + + // try to create 0-length buffers + new Buffer(''); + new Buffer(0); + // test encodings supported by node; + // this is different than what node supports, details + // in buffer.js + ENCODINGS.forEach(enc => { + new Buffer('', enc); + assert.pass('Creating a buffer with ' + enc + ' does not throw'); + }); + + ENCODINGS.forEach(function(encoding) { + // Does not work with utf8 + if (encoding === 'utf-8') return; + var b = new Buffer(10); + b.write('あいうえお', encoding); + assert.equal(b.toString(encoding), 'あいうえお', + 'encode and decodes buffer with ' + encoding); + }); + + // invalid encoding for Buffer.toString + assert.throws(() => { + b.toString('invalid'); + }, TypeError, 'invalid encoding for Buffer.toString'); + + // try to toString() a 0-length slice of a buffer, both within and without the + // valid buffer range + assert.equal(new Buffer('abc').toString('utf8', 0, 0), '', + 'toString 0-length buffer, valid range'); + assert.equal(new Buffer('abc').toString('utf8', -100, -100), '', + 'toString 0-length buffer, invalid range'); + assert.equal(new Buffer('abc').toString('utf8', 100, 100), '', + 'toString 0-length buffer, invalid range'); + + // try toString() with a object as a encoding + assert.equal(new Buffer('abc').toString({toString: function() { + return 'utf8'; + }}), 'abc', 'toString with object as an encoding'); + + // test for buffer overrun + var buf = new Buffer([0, 0, 0, 0, 0]); // length: 5 + var sub = buf.slice(0, 4); // length: 4 + var written = sub.write('12345', 'utf8'); + assert.equal(written, 4, 'correct bytes written in slice'); + assert.equal(buf[4], 0, 'correct origin buffer value'); + + // Check for fractional length args, junk length args, etc. + // https://github.com/joyent/node/issues/1758 + Buffer(3.3).toString(); // throws bad argument error in commit 43cb4ec + assert.equal(Buffer(-1).length, 0); + assert.equal(Buffer(NaN).length, 0); + assert.equal(Buffer(3.3).length, 3); + assert.equal(Buffer({length: 3.3}).length, 3); + assert.equal(Buffer({length: 'BAM'}).length, 0); + + // Make sure that strings are not coerced to numbers. + assert.equal(Buffer('99').length, 2); + assert.equal(Buffer('13.37').length, 5); +}; + +exports.testIsEncoding = function (assert) { + ENCODINGS.map(encoding => { + assert.ok(Buffer.isEncoding(encoding), + 'Buffer.isEncoding ' + encoding + ' truthy'); + }); + ['not-encoding', undefined, null, 100, {}].map(encoding => { + assert.ok(!Buffer.isEncoding(encoding), + 'Buffer.isEncoding ' + encoding + ' falsy'); + }); +}; + +exports.testBufferCopy = function (assert) { + // counter to ensure unique value is always copied + var cntr = 0; + var b = Buffer(1024); // safe constructor + + assert.strictEqual(1024, b.length); + b[0] = -1; + assert.strictEqual(b[0], 255); + + var shimArray = []; + for (var i = 0; i < 1024; i++) { + b[i] = i % 256; + shimArray[i] = i % 256; + } + + compareBuffers(assert, b, shimArray, 'array notation'); + + var c = new Buffer(512); + assert.strictEqual(512, c.length); + // copy 512 bytes, from 0 to 512. + b.fill(++cntr); + c.fill(++cntr); + var copied = b.copy(c, 0, 0, 512); + assert.strictEqual(512, copied, + 'copied ' + copied + ' bytes from b into c'); + + compareBuffers(assert, b, c, 'copied to other buffer'); + + // copy c into b, without specifying sourceEnd + b.fill(++cntr); + c.fill(++cntr); + var copied = c.copy(b, 0, 0); + assert.strictEqual(c.length, copied, + 'copied ' + copied + ' bytes from c into b w/o sourceEnd'); + compareBuffers(assert, b, c, + 'copied to other buffer without specifying sourceEnd'); + + // copy c into b, without specifying sourceStart + b.fill(++cntr); + c.fill(++cntr); + var copied = c.copy(b, 0); + assert.strictEqual(c.length, copied, + 'copied ' + copied + ' bytes from c into b w/o sourceStart'); + compareBuffers(assert, b, c, + 'copied to other buffer without specifying sourceStart'); + + // copy longer buffer b to shorter c without targetStart + b.fill(++cntr); + c.fill(++cntr); + + var copied = b.copy(c); + assert.strictEqual(c.length, copied, + 'copied ' + copied + ' bytes from b into c w/o targetStart'); + compareBuffers(assert, b, c, + 'copy long buffer to shorter buffer without targetStart'); + + // copy starting near end of b to c + b.fill(++cntr); + c.fill(++cntr); + var copied = b.copy(c, 0, b.length - Math.floor(c.length / 2)); + assert.strictEqual(Math.floor(c.length / 2), copied, + 'copied ' + copied + ' bytes from end of b into beg. of c'); + + let successStatus = true; + for (var i = 0; i < Math.floor(c.length / 2); i++) { + if (b[b.length - Math.floor(c.length / 2) + i] !== c[i]) + successStatus = false; + } + + for (var i = Math.floor(c.length /2) + 1; i < c.length; i++) { + if (c[c.length-1] !== c[i]) + successStatus = false; + } + assert.ok(successStatus, + 'Copied bytes from end of large buffer into beginning of small buffer'); + + // try to copy 513 bytes, and check we don't overrun c + b.fill(++cntr); + c.fill(++cntr); + var copied = b.copy(c, 0, 0, 513); + assert.strictEqual(c.length, copied, + 'copied ' + copied + ' bytes from b trying to overrun c'); + compareBuffers(assert, b, c, + 'copying to buffer that would overflow'); + + // copy 768 bytes from b into b + b.fill(++cntr); + b.fill(++cntr, 256); + var copied = b.copy(b, 0, 256, 1024); + assert.strictEqual(768, copied, + 'copied ' + copied + ' bytes from b into b'); + + compareBuffers(assert, b, shimArray.map(()=>cntr), + 'copy partial buffer to itself'); + + // copy string longer than buffer length (failure will segfault) + var bb = new Buffer(10); + bb.fill('hello crazy world'); + + // copy throws at negative sourceStart + assert.throws(function() { + Buffer(5).copy(Buffer(5), 0, -1); + }, RangeError, 'buffer copy throws at negative sourceStart'); + + // check sourceEnd resets to targetEnd if former is greater than the latter + b.fill(++cntr); + c.fill(++cntr); + var copied = b.copy(c, 0, 0, 1025); + assert.strictEqual(copied, c.length, + 'copied ' + copied + ' bytes from b into c'); + compareBuffers(assert, b, c, 'copying should reset sourceEnd if targetEnd if sourceEnd > targetEnd'); + + // throw with negative sourceEnd + assert.throws(function() { + b.copy(c, 0, 0, -1); + }, RangeError, 'buffer copy throws at negative sourceEnd'); + + // when sourceStart is greater than sourceEnd, zero copied + assert.equal(b.copy(c, 0, 100, 10), 0); + + // when targetStart > targetLength, zero copied + assert.equal(b.copy(c, 512, 0, 10), 0); + + // try to copy 0 bytes worth of data into an empty buffer + b.copy(new Buffer(0), 0, 0, 0); + + // try to copy 0 bytes past the end of the target buffer + b.copy(new Buffer(0), 1, 1, 1); + b.copy(new Buffer(1), 1, 1, 1); + + // try to copy 0 bytes from past the end of the source buffer + b.copy(new Buffer(1), 0, 2048, 2048); +}; + +exports.testBufferWrite = function (assert) { + let b = Buffer(1024); + b.fill(0); + + assert.throws(() => { + b.write('test string', 0, 5, 'invalid'); + }, TypeError, 'invalid encoding with buffer write throws'); + // try to write a 0-length string beyond the end of b + assert.throws(function() { + b.write('', 2048); + }, RangeError, 'writing a 0-length string beyond buffer throws'); + // throw when writing to negative offset + assert.throws(function() { + b.write('a', -1); + }, RangeError, 'writing negative offset on buffer throws'); + + // throw when writing past bounds from the pool + assert.throws(function() { + b.write('a', 2048); + }, RangeError, 'writing past buffer bounds from pool throws'); + + // testing for smart defaults and ability to pass string values as offset + + // previous write API was the following: + // write(string, encoding, offset, length) + // this is planned on being removed in node v0.13, + // we will not support it + var writeTest = new Buffer('abcdes'); + writeTest.write('n', 'utf8'); +// writeTest.write('o', 'utf8', '1'); + writeTest.write('d', '2', 'utf8'); + writeTest.write('e', 3, 'utf8'); +// writeTest.write('j', 'utf8', 4); + assert.equal(writeTest.toString(), 'nbdees', + 'buffer write API alternative syntax works'); +}; + +exports.testBufferWriteEncoding = function (assert) { + + // Node #1210 Test UTF-8 string includes null character + var buf = new Buffer('\0'); + assert.equal(buf.length, 1); + buf = new Buffer('\0\0'); + assert.equal(buf.length, 2); + + buf = new Buffer(2); + var written = buf.write(''); // 0byte + assert.equal(written, 0); + written = buf.write('\0'); // 1byte (v8 adds null terminator) + assert.equal(written, 1); + written = buf.write('a\0'); // 1byte * 2 + assert.equal(written, 2); + // TODO, these tests write 0, possibly due to character encoding +/* + written = buf.write('あ'); // 3bytes + assert.equal(written, 0); + written = buf.write('\0あ'); // 1byte + 3bytes + assert.equal(written, 1); +*/ + written = buf.write('\0\0あ'); // 1byte * 2 + 3bytes + buf = new Buffer(10); + written = buf.write('あいう'); // 3bytes * 3 (v8 adds null terminator) + assert.equal(written, 9); + written = buf.write('あいう\0'); // 3bytes * 3 + 1byte + assert.equal(written, 10); +}; + +exports.testBufferWriteWithMaxLength = function (assert) { + // Node #243 Test write() with maxLength + var buf = new Buffer(4); + buf.fill(0xFF); + var written = buf.write('abcd', 1, 2, 'utf8'); + assert.equal(written, 2); + assert.equal(buf[0], 0xFF); + assert.equal(buf[1], 0x61); + assert.equal(buf[2], 0x62); + assert.equal(buf[3], 0xFF); + + buf.fill(0xFF); + written = buf.write('abcd', 1, 4); + assert.equal(written, 3); + assert.equal(buf[0], 0xFF); + assert.equal(buf[1], 0x61); + assert.equal(buf[2], 0x62); + assert.equal(buf[3], 0x63); + + buf.fill(0xFF); + // Ignore legacy API + /* + written = buf.write('abcd', 'utf8', 1, 2); // legacy style + console.log(buf); + assert.equal(written, 2); + assert.equal(buf[0], 0xFF); + assert.equal(buf[1], 0x61); + assert.equal(buf[2], 0x62); + assert.equal(buf[3], 0xFF); + */ +}; + +exports.testBufferSlice = function (assert) { + var asciiString = 'hello world'; + var offset = 100; + var b = Buffer(1024); + b.fill(0); + + for (var i = 0; i < asciiString.length; i++) { + b[i] = asciiString.charCodeAt(i); + } + var asciiSlice = b.toString('utf8', 0, asciiString.length); + assert.equal(asciiString, asciiSlice); + + var written = b.write(asciiString, offset, 'utf8'); + assert.equal(asciiString.length, written); + asciiSlice = b.toString('utf8', offset, offset + asciiString.length); + assert.equal(asciiString, asciiSlice); + + var sliceA = b.slice(offset, offset + asciiString.length); + var sliceB = b.slice(offset, offset + asciiString.length); + compareBuffers(assert, sliceA, sliceB, + 'slicing is idempotent'); + + let sliceTest = true; + for (var j = 0; j < 100; j++) { + var slice = b.slice(100, 150); + if (50 !== slice.length) + sliceTest = false; + for (var i = 0; i < 50; i++) { + if (b[100 + i] !== slice[i]) + sliceTest = false; + } + } + assert.ok(sliceTest, 'massive slice runs do not affect buffer'); + + // Single argument slice + let testBuf = new Buffer('abcde'); + assert.equal('bcde', testBuf.slice(1).toString(), 'single argument slice'); + + // slice(0,0).length === 0 + assert.equal(0, Buffer('hello').slice(0, 0).length, 'slice(0,0) === 0'); + + var buf = new Buffer('0123456789'); + assert.equal(buf.slice(-10, 10), '0123456789', 'buffer slice range correct'); + assert.equal(buf.slice(-20, 10), '0123456789', 'buffer slice range correct'); + assert.equal(buf.slice(-20, -10), '', 'buffer slice range correct'); + assert.equal(buf.slice(0, -1), '012345678', 'buffer slice range correct'); + assert.equal(buf.slice(2, -2), '234567', 'buffer slice range correct'); + assert.equal(buf.slice(0, 65536), '0123456789', 'buffer slice range correct'); + assert.equal(buf.slice(65536, 0), '', 'buffer slice range correct'); + + let sliceTest = true; + for (var i = 0, s = buf.toString(); i < buf.length; ++i) { + if (buf.slice(-i) != s.slice(-i)) sliceTest = false; + if (buf.slice(0, -i) != s.slice(0, -i)) sliceTest = false; + } + assert.ok(sliceTest, 'buffer.slice should be consistent'); + + // Make sure modifying a sliced buffer, affects original and vice versa + b.fill(0); + let sliced = b.slice(0, 10); + let babyslice = sliced.slice(0, 5); + + for (let i = 0; i < sliced.length; i++) + sliced[i] = 'jetpack'.charAt(i); + + compareBuffers(assert, b, sliced, + 'modifying sliced buffer affects original'); + + compareBuffers(assert, b, babyslice, + 'modifying sliced buffer affects child-sliced buffer'); + + for (let i = 0; i < sliced.length; i++) + b[i] = 'odinmonkey'.charAt(i); + + compareBuffers(assert, b, sliced, + 'modifying original buffer affects sliced'); + + compareBuffers(assert, b, babyslice, + 'modifying original buffer affects grandchild sliced buffer'); +}; + +exports.testSlicingParents = function (assert) { + let root = Buffer(5); + let child = root.slice(0, 4); + let grandchild = child.slice(0, 3); + + assert.equal(root.parent, undefined, 'a new buffer should not have a parent'); + + // make sure a zero length slice doesn't set the .parent attribute + assert.equal(root.slice(0,0).parent, undefined, + '0-length slice should not have a parent'); + + assert.equal(child.parent, root, + 'a valid slice\'s parent should be the original buffer (child)'); + + assert.equal(grandchild.parent, root, + 'a valid slice\'s parent should be the original buffer (grandchild)'); +}; + +exports.testIsBuffer = function (assert) { + let buffer = new Buffer('content', 'utf8'); + assert.ok(Buffer.isBuffer(buffer), 'isBuffer truthy on buffers'); + assert.ok(!Buffer.isBuffer({}), 'isBuffer falsy on objects'); + assert.ok(!Buffer.isBuffer(new Uint8Array()), + 'isBuffer falsy on Uint8Array'); + assert.ok(Buffer.isBuffer(buffer.slice(0)), 'Buffer#slice should be a new buffer'); +}; + +exports.testBufferConcat = function (assert) { + let zero = []; + let one = [ new Buffer('asdf') ]; + let long = []; + for (let i = 0; i < 10; i++) long.push(new Buffer('asdf')); + + let flatZero = Buffer.concat(zero); + let flatOne = Buffer.concat(one); + let flatLong = Buffer.concat(long); + let flatLongLen = Buffer.concat(long, 40); + + assert.equal(flatZero.length, 0); + assert.equal(flatOne.toString(), 'asdf'); + assert.equal(flatOne, one[0]); + assert.ok(flatLong.toString(), (new Array(10+1).join('asdf'))); + assert.equal(flatLongLen.toString(), (new Array(10+1).join('asdf'))); +}; + +exports.testBufferByteLength = function (assert) { + let str = '\u00bd + \u00bc = \u00be'; + assert.equal(Buffer.byteLength(str), 12, + 'correct byteLength of string'); + + assert.equal(14, Buffer.byteLength('Il était tué')); + assert.equal(14, Buffer.byteLength('Il était tué', 'utf8')); + // We do not currently support these encodings + /* + ['ucs2', 'ucs-2', 'utf16le', 'utf-16le'].forEach(function(encoding) { + assert.equal(24, Buffer.byteLength('Il était tué', encoding)); + }); + assert.equal(12, Buffer.byteLength('Il était tué', 'ascii')); + assert.equal(12, Buffer.byteLength('Il était tué', 'binary')); + */ +}; + +exports.testTextEncoderDecoder = function (assert) { + assert.ok(TextEncoder, 'TextEncoder exists'); + assert.ok(TextDecoder, 'TextDecoder exists'); +}; + +exports.testOverflowedBuffers = function (assert) { + assert.throws(function() { + new Buffer(0xFFFFFFFF); + }, RangeError, 'correctly throws buffer overflow'); + + assert.throws(function() { + new Buffer(0xFFFFFFFFF); + }, RangeError, 'correctly throws buffer overflow'); + + assert.throws(function() { + var buf = new Buffer(8); + buf.readFloatLE(0xffffffff); + }, RangeError, 'correctly throws buffer overflow with readFloatLE'); + + assert.throws(function() { + var buf = new Buffer(8); + buf.writeFloatLE(0.0, 0xffffffff); + }, RangeError, 'correctly throws buffer overflow with writeFloatLE'); + + //ensure negative values can't get past offset + assert.throws(function() { + var buf = new Buffer(8); + buf.readFloatLE(-1); + }, RangeError, 'correctly throws with readFloatLE negative values'); + + assert.throws(function() { + var buf = new Buffer(8); + buf.writeFloatLE(0.0, -1); + }, RangeError, 'correctly throws with writeFloatLE with negative values'); + + assert.throws(function() { + var buf = new Buffer(8); + buf.readFloatLE(-1); + }, RangeError, 'correctly throws with readFloatLE with negative values'); +}; + +exports.testReadWriteDataTypeErrors = function (assert) { + var buf = new Buffer(0); + assert.throws(function() { buf.readUInt8(0); }, RangeError, + 'readUInt8(0) throws'); + assert.throws(function() { buf.readInt8(0); }, RangeError, + 'readInt8(0) throws'); + + [16, 32].forEach(function(bits) { + var buf = new Buffer(bits / 8 - 1); + assert.throws(function() { buf['readUInt' + bits + 'BE'](0); }, + RangeError, + 'readUInt' + bits + 'BE'); + + assert.throws(function() { buf['readUInt' + bits + 'LE'](0); }, + RangeError, + 'readUInt' + bits + 'LE'); + + assert.throws(function() { buf['readInt' + bits + 'BE'](0); }, + RangeError, + 'readInt' + bits + 'BE()'); + + assert.throws(function() { buf['readInt' + bits + 'LE'](0); }, + RangeError, + 'readInt' + bits + 'LE()'); + }); +}; + +safeMerge(exports, require('./buffers/test-write-types')); +safeMerge(exports, require('./buffers/test-read-types')); + +function compareBuffers (assert, buf1, buf2, message) { + let status = true; + for (let i = 0; i < Math.min(buf1.length, buf2.length); i++) { + if (buf1[i] !== buf2[i]) + status = false; + } + assert.ok(status, 'buffer successfully copied: ' + message); +} +require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-byte-streams.js b/addon-sdk-1.16/test/test-byte-streams.js new file mode 100644 index 00000000..24302dc6 --- /dev/null +++ b/addon-sdk-1.16/test/test-byte-streams.js @@ -0,0 +1,169 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const byteStreams = require("sdk/io/byte-streams"); +const file = require("sdk/io/file"); +const { pathFor } = require("sdk/system"); +const { Loader } = require("sdk/test/loader"); + +const STREAM_CLOSED_ERROR = new RegExp("The stream is closed and cannot be used."); + +// This should match the constant of the same name in byte-streams.js. +const BUFFER_BYTE_LEN = 0x8000; + +exports.testWriteRead = function (assert) { + let fname = dataFileFilename(); + + // Write a small string less than the stream's buffer size... + let str = "exports.testWriteRead data!"; + let stream = open(assert, fname, true); + assert.ok(!stream.closed, "stream.closed after open should be false"); + stream.write(str); + stream.close(); + assert.ok(stream.closed, "Stream should be closed after stream.close"); + assert.throws(function () stream.write("This shouldn't be written!"), + STREAM_CLOSED_ERROR, + "stream.write after close should raise error"); + + // ... and read it. + stream = open(assert, fname); + assert.equal(stream.read(), str, + "stream.read should return string written"); + assert.equal(stream.read(), "", + "stream.read at EOS should return empty string"); + stream.close(); + assert.ok(stream.closed, "Stream should be closed after stream.close"); + assert.throws(function () stream.read(), + STREAM_CLOSED_ERROR, + "stream.read after close should raise error"); + + file.remove(fname); +}; + +// Write a big string many times the size of the stream's buffer and read it. +exports.testWriteReadBig = function (assert) { + let str = ""; + let bufLen = BUFFER_BYTE_LEN; + let fileSize = bufLen * 10; + for (let i = 0; i < fileSize; i++) + str += i % 10; + let fname = dataFileFilename(); + let stream = open(assert, fname, true); + stream.write(str); + stream.close(); + stream = open(assert, fname); + assert.equal(stream.read(), str, + "stream.read should return string written"); + stream.close(); + file.remove(fname); +}; + +// The same, but write and read in chunks. +exports.testWriteReadChunks = function (assert) { + let str = ""; + let bufLen = BUFFER_BYTE_LEN; + let fileSize = bufLen * 10; + for (let i = 0; i < fileSize; i++) + str += i % 10; + let fname = dataFileFilename(); + let stream = open(assert, fname, true); + let i = 0; + while (i < str.length) { + // Use a chunk length that spans buffers. + let chunk = str.substr(i, bufLen + 1); + stream.write(chunk); + i += bufLen + 1; + } + stream.close(); + stream = open(assert, fname); + let readStr = ""; + bufLen = BUFFER_BYTE_LEN; + let readLen = bufLen + 1; + do { + var frag = stream.read(readLen); + readStr += frag; + } while (frag); + stream.close(); + assert.equal(readStr, str, + "stream.write and read in chunks should work as expected"); + file.remove(fname); +}; + +exports.testReadLengths = function (assert) { + let fname = dataFileFilename(); + let str = "exports.testReadLengths data!"; + let stream = open(assert, fname, true); + stream.write(str); + stream.close(); + + stream = open(assert, fname); + assert.equal(stream.read(str.length * 1000), str, + "stream.read with big byte length should return string " + + "written"); + stream.close(); + + stream = open(assert, fname); + assert.equal(stream.read(0), "", + "string.read with zero byte length should return empty " + + "string"); + stream.close(); + + stream = open(assert, fname); + assert.equal(stream.read(-1), "", + "string.read with negative byte length should return " + + "empty string"); + stream.close(); + + file.remove(fname); +}; + +exports.testTruncate = function (assert) { + let fname = dataFileFilename(); + let str = "exports.testReadLengths data!"; + let stream = open(assert, fname, true); + stream.write(str); + stream.close(); + + stream = open(assert, fname); + assert.equal(stream.read(), str, + "stream.read should return string written"); + stream.close(); + + stream = open(assert, fname, true); + stream.close(); + + stream = open(assert, fname); + assert.equal(stream.read(), "", + "stream.read after truncate should be empty"); + stream.close(); + + file.remove(fname); +}; + +exports.testUnload = function (assert) { + let loader = Loader(module); + let file = loader.require("sdk/io/file"); + + let filename = dataFileFilename("temp-b"); + let stream = file.open(filename, "wb"); + + loader.unload(); + assert.ok(stream.closed, "Stream should be closed after module unload"); +}; + +// Returns the name of a file that should be used to test writing and reading. +function dataFileFilename() { + return file.join(pathFor("ProfD"), "test-byte-streams-data"); +} + +// Opens and returns the given file and ensures it's of the correct class. +function open(assert, filename, forWriting) { + let stream = file.open(filename, forWriting ? "wb" : "b"); + let klass = forWriting ? "ByteWriter" : "ByteReader"; + assert.ok(stream instanceof byteStreams[klass], + "Opened stream should be a " + klass); + return stream; +} + +require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-chrome.js b/addon-sdk-1.16/test/test-chrome.js new file mode 100644 index 00000000..7d0addae --- /dev/null +++ b/addon-sdk-1.16/test/test-chrome.js @@ -0,0 +1,84 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +'use strict'; + +let chrome = require('chrome'); + +const FIXTURES_URL = module.uri.substr(0, module.uri.lastIndexOf('/') + 1) + + 'fixtures/chrome-worker/' + +exports['test addEventListener'] = function(assert, done) { + let uri = FIXTURES_URL + 'addEventListener.js'; + + let worker = new chrome.ChromeWorker(uri); + worker.addEventListener('message', function(event) { + assert.equal(event.data, 'Hello', 'message received'); + worker.terminate(); + done(); + }); +}; + +exports['test onmessage'] = function(assert, done) { + let uri = FIXTURES_URL + 'onmessage.js'; + + let worker = new chrome.ChromeWorker(uri); + worker.onmessage = function(event) { + assert.equal(event.data, 'ok', 'message received'); + worker.terminate(); + done(); + }; + worker.postMessage('ok'); +}; + +exports['test setTimeout'] = function(assert, done) { + let uri = FIXTURES_URL + 'setTimeout.js'; + + let worker = new chrome.ChromeWorker(uri); + worker.onmessage = function(event) { + assert.equal(event.data, 'ok', 'setTimeout fired'); + worker.terminate(); + done(); + }; +}; + +exports['test jsctypes'] = function(assert, done) { + let uri = FIXTURES_URL + 'jsctypes.js'; + + let worker = new chrome.ChromeWorker(uri); + worker.onmessage = function(event) { + assert.equal(event.data, 'function', 'ctypes.open is a function'); + worker.terminate(); + done(); + }; +}; + +exports['test XMLHttpRequest'] = function(assert, done) { + let uri = FIXTURES_URL + 'xhr.js'; + + let worker = new chrome.ChromeWorker(uri); + worker.onmessage = function(event) { + assert.equal(event.data, 'ok', 'XMLHttpRequest works'); + worker.terminate(); + done(); + }; +}; + +exports['test onerror'] = function(assert, done) { + let uri = FIXTURES_URL + 'onerror.js'; + + let worker = new chrome.ChromeWorker(uri); + worker.onerror = function(event) { + assert.equal(event.filename, uri, 'event reports the correct uri'); + assert.equal(event.lineno, 6, 'event reports the correct line number'); + assert.equal(event.target, worker, 'event reports the correct worker'); + assert.ok(event.message.match(/ok/), + 'event contains the exception message'); + // Call preventDefault in order to avoid being displayed in JS console. + event.preventDefault(); + worker.terminate(); + done(); + }; +}; + +require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-clipboard.js b/addon-sdk-1.16/test/test-clipboard.js new file mode 100644 index 00000000..52ef9a5e --- /dev/null +++ b/addon-sdk-1.16/test/test-clipboard.js @@ -0,0 +1,222 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +require("sdk/clipboard"); + +const { Cc, Ci } = require("chrome"); + +const imageTools = Cc["@mozilla.org/image/tools;1"]. + getService(Ci.imgITools); + +const io = Cc["@mozilla.org/network/io-service;1"]. + getService(Ci.nsIIOService); + +const base64png = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYA" + + "AABzenr0AAAASUlEQVRYhe3O0QkAIAwD0eyqe3Q993AQ3cBSUKpygfsNTy" + + "N5ugbQpK0BAADgP0BRDWXWlwEAAAAAgPsA3rzDaAAAAHgPcGrpgAnzQ2FG" + + "bWRR9AAAAABJRU5ErkJggg%3D%3D"; + +const base64jpeg = "data:image/jpeg;base64,%2F9j%2F4AAQSkZJRgABAQAAAQABAAD%2F" + + "2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCg" + + "sOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD%2F2wBDAQMDAwQDBAgEBAgQCw" + + "kLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ" + + "EBAQEBAQEBD%2FwAARCAAgACADAREAAhEBAxEB%2F8QAHwAAAQUBAQEBAQ" + + "EAAAAAAAAAAAECAwQFBgcICQoL%2F8QAtRAAAgEDAwIEAwUFBAQAAAF9AQ" + + "IDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRol" + + "JicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eX" + + "qDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJ" + + "ytLT1NXW19jZ2uHi4%2BTl5ufo6erx8vP09fb3%2BPn6%2F8QAHwEAAwEB" + + "AQEBAQEBAQAAAAAAAAECAwQFBgcICQoL%2F8QAtREAAgECBAQDBAcFBAQA" + + "AQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNO" + + "El8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0" + + "dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6ws" + + "PExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3%2BPn6%2F9oADAMB" + + "AAIRAxEAPwD5Kr8kP9CwoA5f4m%2F8iRqX%2FbH%2FANHJXr5F%2FwAjCn" + + "8%2F%2FSWfnnir%2FwAkji%2F%2B4f8A6dgeD1%2BiH8bn1BX5If6FmFqW" + + "pXtveyQwzbUXGBtB7D2r9l4U4UyjMsoo4rFUeacua75pLaUktFJLZH5NxN" + + "xNmmX5pVw2Gq8sI8tlyxe8U3q03uzD8S3dxqOi3NneSeZDJs3LgDOHBHI5" + + "6gV%2BkcG%2BH%2FDmJzuhSq4e8XzfbqfyS%2FvH5rx1xTm2MyDEUa1W8X" + + "yXXLFbTi%2BkThv7B0r%2FAJ9f%2FH2%2Fxr90%2FwCIVcI%2F9An%2FAJ" + + "Uq%2FwDyZ%2FO%2F16v%2FADfgv8j0r%2FhZvgj%2FAKDf%2FktN%2FwDE" + + "V%2Fnr%2FYWYf8%2B%2Fxj%2Fmf3R%2FxFXhH%2FoL%2FwDKdX%2F5Azrv" + + "xLouo3D3lne%2BZDJja3luM4GDwRnqDX9LeH%2FBud4nhzD1aVC8Xz%2Fa" + + "h%2Fz8l%2FePx%2FinjrIMZm1WtRxF4vls%2BSa2jFdYlDUdRsp7OSKKbc" + + "7YwNpHce1fqfCvCub5bm9HFYqjywjzXfNF7xklopN7s%2BC4l4lyvMMrq4" + + "fD1bzfLZcsltJPqktkYlfsZ%2BUnBV%2FnufVnXaD%2FAMgqD%2FgX%2Fo" + + "Rr%2BxvCr%2FkkcJ%2F3E%2F8ATsz5%2FHfx5fL8kX6%2FQjkCgD%2F%2F" + + "2Q%3D%3D"; + +const canvasHTML = "data:text/html," + encodeURIComponent( + "\ + \ + \ + \ + " +); + +function comparePixelImages(imageA, imageB, callback) { + let tabs = require("sdk/tabs"); + + tabs.open({ + url: canvasHTML, + + onReady: function onReady(tab) { + let worker = tab.attach({ + contentScript: "new " + function() { + let canvas = document.querySelector("canvas"); + let context = canvas.getContext("2d"); + + self.port.on("draw-image", function(imageURI) { + let img = new Image(); + + img.onload = function() { + context.drawImage(this, 0, 0); + + let pixels = Array.join(context.getImageData(0, 0, 32, 32).data); + self.port.emit("image-pixels", pixels); + } + + img.src = imageURI; + }); + } + }); + + let compared = ""; + + worker.port.on("image-pixels", function (pixels) { + if (!compared) { + compared = pixels; + this.emit("draw-image", imageB); + } else { + callback(compared === pixels); + tab.close() + } + }); + + worker.port.emit("draw-image", imageA); + } + }); +} + + +// Test the typical use case, setting & getting with no flavors specified +exports["test With No Flavor"] = function(assert) { + var contents = "hello there"; + var flavor = "text"; + var fullFlavor = "text/unicode"; + var clip = require("sdk/clipboard"); + + // Confirm we set the clipboard + assert.ok(clip.set(contents)); + + // Confirm flavor is set + assert.equal(clip.currentFlavors[0], flavor); + + // Confirm we set the clipboard + assert.equal(clip.get(), contents); + + // Confirm we can get the clipboard using the flavor + assert.equal(clip.get(flavor), contents); + + // Confirm we can still get the clipboard using the full flavor + assert.equal(clip.get(fullFlavor), contents); +}; + +// Test the slightly less common case where we specify the flavor +exports["test With Flavor"] = function(assert) { + var contents = "hello there"; + var contentsText = "hello there"; + var flavor = "html"; + var fullFlavor = "text/html"; + var unicodeFlavor = "text"; + var unicodeFullFlavor = "text/unicode"; + var clip = require("sdk/clipboard"); + + assert.ok(clip.set(contents, flavor)); + + assert.equal(clip.currentFlavors[0], unicodeFlavor); + assert.equal(clip.currentFlavors[1], flavor); + assert.equal(clip.get(), contentsText); + assert.equal(clip.get(flavor), contents); + assert.equal(clip.get(fullFlavor), contents); + assert.equal(clip.get(unicodeFlavor), contentsText); + assert.equal(clip.get(unicodeFullFlavor), contentsText); +}; + +// Test that the typical case still works when we specify the flavor to set +exports["test With Redundant Flavor"] = function(assert) { + var contents = "hello there"; + var flavor = "text"; + var fullFlavor = "text/unicode"; + var clip = require("sdk/clipboard"); + + assert.ok(clip.set(contents, flavor)); + assert.equal(clip.currentFlavors[0], flavor); + assert.equal(clip.get(), contents); + assert.equal(clip.get(flavor), contents); + assert.equal(clip.get(fullFlavor), contents); +}; + +exports["test Not In Flavor"] = function(assert) { + var contents = "hello there"; + var flavor = "html"; + var clip = require("sdk/clipboard"); + + assert.ok(clip.set(contents)); + // If there's nothing on the clipboard with this flavor, should return null + assert.equal(clip.get(flavor), null); +}; + +exports["test Set Image"] = function(assert) { + var clip = require("sdk/clipboard"); + var flavor = "image"; + var fullFlavor = "image/png"; + + assert.ok(clip.set(base64png, flavor), "clipboard set"); + assert.equal(clip.currentFlavors[0], flavor, "flavor is set"); +}; + +exports["test Get Image"] = function(assert, done) { + var clip = require("sdk/clipboard"); + + clip.set(base64png, "image"); + + var contents = clip.get(); + + comparePixelImages(base64png, contents, function (areEquals) { + assert.ok(areEquals, + "Image gets from clipboard equals to image sets to the clipboard"); + + done(); + }); +} + +exports["test Set Image Type Not Supported"] = function(assert) { + var clip = require("sdk/clipboard"); + var flavor = "image"; + + assert.throws(function () { + clip.set(base64jpeg, flavor); + }, "Invalid flavor for image/jpeg"); + +}; + +// Notice that `imageTools.decodeImageData`, used by `clipboard.set` method for +// images, write directly to the javascript console the error in case the image +// is corrupt, even if the error is catched. +// +// See: http://mxr.mozilla.org/mozilla-central/source/image/src/Decoder.cpp#136 +exports["test Set Image Type Wrong Data"] = function(assert) { + var clip = require("sdk/clipboard"); + var flavor = "image"; + + var wrongPNG = "data:image/png" + base64jpeg.substr(15); + + assert.throws(function () { + clip.set(wrongPNG, flavor); + }, "Unable to decode data given in a valid image."); +}; + +require("test").run(exports) diff --git a/addon-sdk-1.16/test/test-collection.js b/addon-sdk-1.16/test/test-collection.js new file mode 100644 index 00000000..d723c14c --- /dev/null +++ b/addon-sdk-1.16/test/test-collection.js @@ -0,0 +1,128 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const collection = require("sdk/util/collection"); + +exports.testAddRemove = function (assert) { + let coll = new collection.Collection(); + compare(assert, coll, []); + addRemove(assert, coll, [], false); +}; + +exports.testAddRemoveBackingArray = function (assert) { + let items = ["foo"]; + let coll = new collection.Collection(items); + compare(assert, coll, items); + addRemove(assert, coll, items, true); + + items = ["foo", "bar"]; + coll = new collection.Collection(items); + compare(assert, coll, items); + addRemove(assert, coll, items, true); +}; + +exports.testProperty = function (assert) { + let obj = makeObjWithCollProp(); + compare(assert, obj.coll, []); + addRemove(assert, obj.coll, [], false); + + // Test single-value set. + let items = ["foo"]; + obj.coll = items[0]; + compare(assert, obj.coll, items); + addRemove(assert, obj.coll, items, false); + + // Test array set. + items = ["foo", "bar"]; + obj.coll = items; + compare(assert, obj.coll, items); + addRemove(assert, obj.coll, items, false); +}; + +exports.testPropertyBackingArray = function (assert) { + let items = ["foo"]; + let obj = makeObjWithCollProp(items); + compare(assert, obj.coll, items); + addRemove(assert, obj.coll, items, true); + + items = ["foo", "bar"]; + obj = makeObjWithCollProp(items); + compare(assert, obj.coll, items); + addRemove(assert, obj.coll, items, true); +}; + +// Adds some values to coll and then removes them. initialItems is an array +// containing coll's initial items. isBacking is true if initialItems is coll's +// backing array; the point is that updates to coll should affect initialItems +// if that's the case. +function addRemove(assert, coll, initialItems, isBacking) { + let items = isBacking ? initialItems : initialItems.slice(0); + let numInitialItems = items.length; + + // Test add(val). + let numInsertions = 5; + for (let i = 0; i < numInsertions; i++) { + compare(assert, coll, items); + coll.add(i); + if (!isBacking) + items.push(i); + } + compare(assert, coll, items); + + // Add the items we just added to make sure duplicates aren't added. + for (let i = 0; i < numInsertions; i++) + coll.add(i); + compare(assert, coll, items); + + // Test remove(val). Do a kind of shuffled remove. Remove item 1, then + // item 0, 3, 2, 5, 4, ... + for (let i = 0; i < numInsertions; i++) { + let val = i % 2 ? i - 1 : + i === numInsertions - 1 ? i : i + 1; + coll.remove(val); + if (!isBacking) + items.splice(items.indexOf(val), 1); + compare(assert, coll, items); + } + assert.equal(coll.length, numInitialItems, + "All inserted items should be removed"); + + // Remove the items we just removed. coll should be unchanged. + for (let i = 0; i < numInsertions; i++) + coll.remove(i); + compare(assert, coll, items); + + // Test add and remove([val1, val2]). + let newItems = [0, 1]; + coll.add(newItems); + compare(assert, coll, isBacking ? items : items.concat(newItems)); + coll.remove(newItems); + compare(assert, coll, items); + assert.equal(coll.length, numInitialItems, + "All inserted items should be removed"); +} + +// Asserts that the items in coll are the items of array. +function compare(assert, coll, array) { + assert.equal(coll.length, array.length, + "Collection length should be correct"); + let numItems = 0; + for (let item in coll) { + assert.equal(item, array[numItems], "Items should be equal"); + numItems++; + } + assert.equal(numItems, array.length, + "Number of items in iteration should be correct"); +} + +// Returns a new object with a collection property named "coll". backingArray, +// if defined, will create the collection with that backing array. +function makeObjWithCollProp(backingArray) { + let obj = {}; + collection.addCollectionProperty(obj, "coll", backingArray); + return obj; +} + +require("sdk/test").run(exports); diff --git a/addon-sdk-1.16/test/test-commonjs-test-adapter.js b/addon-sdk-1.16/test/test-commonjs-test-adapter.js new file mode 100644 index 00000000..936bea91 --- /dev/null +++ b/addon-sdk-1.16/test/test-commonjs-test-adapter.js @@ -0,0 +1,11 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +exports["test custom `Assert`'s"] = require("./commonjs-test-adapter/asserts"); + +// Disabling this check since it is not yet supported by jetpack. +// if (module == require.main) + require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-content-events.js b/addon-sdk-1.16/test/test-content-events.js new file mode 100644 index 00000000..dd0bbf91 --- /dev/null +++ b/addon-sdk-1.16/test/test-content-events.js @@ -0,0 +1,135 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const { Loader } = require("sdk/test/loader"); +const { getMostRecentBrowserWindow, getInnerId } = require("sdk/window/utils"); +const { openTab, closeTab, getBrowserForTab } = require("sdk/tabs/utils"); +const { defer } = require("sdk/core/promise"); +const { curry, identity, partial } = require("sdk/lang/functional"); + +let when = curry(function(options, tab) { + let type = options.type || options; + let capture = options.capture || false; + let target = getBrowserForTab(tab); + let { promise, resolve } = defer(); + + target.addEventListener(type, function handler(event) { + if (!event.target.defaultView.frameElement) { + target.removeEventListener(type, handler, capture); + resolve(tab); + } + }, capture); + + return promise; +}); + +let use = function(value) function() value; + + +let open = curry(function(url, window) openTab(window, url)); +let close = function(tab) { + let promise = when("pagehide", tab); + closeTab(tab); + return promise; +} + +exports["test multiple tabs"] = function(assert, done) { + let loader = Loader(module); + let { events } = loader.require("sdk/content/events"); + let { on, off } = loader.require("sdk/event/core"); + let actual = []; + + on(events, "data", handler); + function handler ({type, target, timeStamp}) { + eventFilter(type, target, () => { + actual.push(type + " -> " + target.URL) + }); + } + + let window = getMostRecentBrowserWindow(); + let firstTab = open("data:text/html,first-tab", window); + + when("pageshow", firstTab). + then(close). + then(use(window)). + then(open("data:text/html,second-tab")). + then(when("pageshow")). + then(close). + then(function() { + assert.deepEqual(actual, [ + "document-element-inserted -> data:text/html,first-tab", + "DOMContentLoaded -> data:text/html,first-tab", + "load -> data:text/html,first-tab", + "pageshow -> data:text/html,first-tab", + "document-element-inserted -> data:text/html,second-tab", + "DOMContentLoaded -> data:text/html,second-tab", + "load -> data:text/html,second-tab", + "pageshow -> data:text/html,second-tab" + ], "all events dispatche as expeced") + }, function(reason) { + assert.fail(Error(reason)); + }).then(function() { + loader.unload(); + off(events, "data", handler); + done(); + }); +}; + +exports["test nested frames"] = function(assert, done) { + let loader = Loader(module); + let { events } = loader.require("sdk/content/events"); + let { on, off } = loader.require("sdk/event/core"); + let actual = []; + on(events, "data", handler); + function handler ({type, target, timeStamp}) { + eventFilter(type, target, () => { + actual.push(type + " -> " + target.URL) + }); + } + + let window = getMostRecentBrowserWindow(); + let uri = encodeURI("data:text/html, +

+ +

+ + A targetted link. + +

+ +

+ +

+ +

+ + A link with no ID and an anchor, used by PredicateContext tests. + +

+ +

+ +

+ +

+ +

+ +

+ +

+ +

+

This content is editable.

+

+ + diff --git a/addon-sdk-1.16/test/test-context-menu.js b/addon-sdk-1.16/test/test-context-menu.js new file mode 100644 index 00000000..f2b1c3c0 --- /dev/null +++ b/addon-sdk-1.16/test/test-context-menu.js @@ -0,0 +1,4045 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + 'use strict'; + +let { Cc, Ci } = require("chrome"); + +require("sdk/context-menu"); + +const { Loader } = require('sdk/test/loader'); +const timer = require("sdk/timers"); +const { merge } = require("sdk/util/object"); + +// These should match the same constants in the module. +const ITEM_CLASS = "addon-context-menu-item"; +const SEPARATOR_CLASS = "addon-context-menu-separator"; +const OVERFLOW_THRESH_DEFAULT = 10; +const OVERFLOW_THRESH_PREF = + "extensions.addon-sdk.context-menu.overflowThreshold"; +const OVERFLOW_MENU_CLASS = "addon-content-menu-overflow-menu"; +const OVERFLOW_POPUP_CLASS = "addon-content-menu-overflow-popup"; + +const TEST_DOC_URL = module.uri.replace(/\.js$/, ".html"); +const data = require("./fixtures"); + +// Tests that when present the separator is placed before the separator from +// the old context-menu module +exports.testSeparatorPosition = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + // Create the old separator + let oldSeparator = test.contextMenuPopup.ownerDocument.createElement("menuseparator"); + oldSeparator.id = "jetpack-context-menu-separator"; + test.contextMenuPopup.appendChild(oldSeparator); + + // Create an item. + let item = new loader.cm.Item({ label: "item" }); + + test.showMenu(null, function (popup) { + assert.equal(test.contextMenuSeparator.nextSibling.nextSibling, oldSeparator, + "New separator should appear before the old one"); + test.contextMenuPopup.removeChild(oldSeparator); + test.done(); + }); +}; + +// Destroying items that were previously created should cause them to be absent +// from the menu. +exports.testConstructDestroy = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + // Create an item. + let item = new loader.cm.Item({ label: "item" }); + assert.equal(item.parentMenu, loader.cm.contentContextMenu, + "item's parent menu should be correct"); + + test.showMenu(null, function (popup) { + + // It should be present when the menu is shown. + test.checkMenu([item], [], []); + popup.hidePopup(); + + // Destroy the item. Multiple destroys should be harmless. + item.destroy(); + item.destroy(); + test.showMenu(null, function (popup) { + + // It should be removed from the menu. + test.checkMenu([item], [], [item]); + test.done(); + }); + }); +}; + + +// Destroying an item twice should not cause an error. +exports.testDestroyTwice = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let item = new loader.cm.Item({ label: "item" }); + item.destroy(); + item.destroy(); + + test.pass("Destroying an item twice should not cause an error."); + test.done(); +}; + + +// CSS selector contexts should cause their items to be present in the menu +// when the menu is invoked on nodes that match the selectors. +exports.testSelectorContextMatch = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let item = new loader.cm.Item({ + label: "item", + data: "item", + context: loader.cm.SelectorContext("img") + }); + + test.withTestDoc(function (window, doc) { + test.showMenu(doc.getElementById("image"), function (popup) { + test.checkMenu([item], [], []); + test.done(); + }); + }); +}; + + +// CSS selector contexts should cause their items to be present in the menu +// when the menu is invoked on nodes that have ancestors that match the +// selectors. +exports.testSelectorAncestorContextMatch = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let item = new loader.cm.Item({ + label: "item", + data: "item", + context: loader.cm.SelectorContext("a[href]") + }); + + test.withTestDoc(function (window, doc) { + test.showMenu(doc.getElementById("span-link"), function (popup) { + test.checkMenu([item], [], []); + test.done(); + }); + }); +}; + + +// CSS selector contexts should cause their items to be absent from the menu +// when the menu is not invoked on nodes that match or have ancestors that +// match the selectors. +exports.testSelectorContextNoMatch = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let item = new loader.cm.Item({ + label: "item", + data: "item", + context: loader.cm.SelectorContext("img") + }); + + test.showMenu(null, function (popup) { + test.checkMenu([item], [item], []); + test.done(); + }); +}; + + +// Page contexts should cause their items to be present in the menu when the +// menu is not invoked on an active element. +exports.testPageContextMatch = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let items = [ + new loader.cm.Item({ + label: "item 0" + }), + new loader.cm.Item({ + label: "item 1", + context: undefined + }), + new loader.cm.Item({ + label: "item 2", + context: loader.cm.PageContext() + }), + new loader.cm.Item({ + label: "item 3", + context: [loader.cm.PageContext()] + }) + ]; + + test.showMenu(null, function (popup) { + test.checkMenu(items, [], []); + test.done(); + }); +}; + + +// Page contexts should cause their items to be absent from the menu when the +// menu is invoked on an active element. +exports.testPageContextNoMatch = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let items = [ + new loader.cm.Item({ + label: "item 0" + }), + new loader.cm.Item({ + label: "item 1", + context: undefined + }), + new loader.cm.Item({ + label: "item 2", + context: loader.cm.PageContext() + }), + new loader.cm.Item({ + label: "item 3", + context: [loader.cm.PageContext()] + }) + ]; + + test.withTestDoc(function (window, doc) { + test.showMenu(doc.getElementById("image"), function (popup) { + test.checkMenu(items, items, []); + test.done(); + }); + }); +}; + + +// Selection contexts should cause items to appear when a selection exists. +exports.testSelectionContextMatch = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let item = loader.cm.Item({ + label: "item", + context: loader.cm.SelectionContext() + }); + + test.withTestDoc(function (window, doc) { + window.getSelection().selectAllChildren(doc.body); + test.showMenu(null, function (popup) { + test.checkMenu([item], [], []); + test.done(); + }); + }); +}; + + +// Selection contexts should cause items to appear when a selection exists in +// a text field. +exports.testSelectionContextMatchInTextField = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let item = loader.cm.Item({ + label: "item", + context: loader.cm.SelectionContext() + }); + + test.withTestDoc(function (window, doc) { + let textfield = doc.getElementById("textfield"); + textfield.setSelectionRange(0, textfield.value.length); + test.showMenu(textfield, function (popup) { + test.checkMenu([item], [], []); + test.done(); + }); + }); +}; + + +// Selection contexts should not cause items to appear when a selection does +// not exist in a text field. +exports.testSelectionContextNoMatchInTextField = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let item = loader.cm.Item({ + label: "item", + context: loader.cm.SelectionContext() + }); + + test.withTestDoc(function (window, doc) { + let textfield = doc.getElementById("textfield"); + textfield.setSelectionRange(0, 0); + test.showMenu(textfield, function (popup) { + test.checkMenu([item], [item], []); + test.done(); + }); + }); +}; + + +// Selection contexts should not cause items to appear when a selection does +// not exist. +exports.testSelectionContextNoMatch = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let item = loader.cm.Item({ + label: "item", + context: loader.cm.SelectionContext() + }); + + test.showMenu(null, function (popup) { + test.checkMenu([item], [item], []); + test.done(); + }); +}; + + +// Selection contexts should cause items to appear when a selection exists even +// for newly opened pages +exports.testSelectionContextInNewTab = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let item = loader.cm.Item({ + label: "item", + context: loader.cm.SelectionContext() + }); + + test.withTestDoc(function (window, doc) { + let link = doc.getElementById("targetlink"); + link.click(); + + test.delayedEventListener(this.tabBrowser, "load", function () { + let browser = test.tabBrowser.selectedBrowser; + let window = browser.contentWindow; + let doc = browser.contentDocument; + window.getSelection().selectAllChildren(doc.body); + + test.showMenu(null, function (popup) { + test.checkMenu([item], [], []); + popup.hidePopup(); + + test.tabBrowser.removeTab(test.tabBrowser.selectedTab); + test.tabBrowser.selectedTab = test.tab; + + test.showMenu(null, function (popup) { + test.checkMenu([item], [item], []); + test.done(); + }); + }); + }, true); + }); +}; + + +// Selection contexts should work when right clicking a form button +exports.testSelectionContextButtonMatch = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let item = loader.cm.Item({ + label: "item", + context: loader.cm.SelectionContext() + }); + + test.withTestDoc(function (window, doc) { + window.getSelection().selectAllChildren(doc.body); + let button = doc.getElementById("button"); + test.showMenu(button, function (popup) { + test.checkMenu([item], [], []); + test.done(); + }); + }); +}; + + +//Selection contexts should work when right clicking a form button +exports.testSelectionContextButtonNoMatch = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let item = loader.cm.Item({ + label: "item", + context: loader.cm.SelectionContext() + }); + + test.withTestDoc(function (window, doc) { + let button = doc.getElementById("button"); + test.showMenu(button, function (popup) { + test.checkMenu([item], [item], []); + test.done(); + }); + }); +}; + + +// URL contexts should cause items to appear on pages that match. +exports.testURLContextMatch = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let items = [ + loader.cm.Item({ + label: "item 0", + context: loader.cm.URLContext(TEST_DOC_URL) + }), + loader.cm.Item({ + label: "item 1", + context: loader.cm.URLContext([TEST_DOC_URL, "*.bogus.com"]) + }), + loader.cm.Item({ + label: "item 2", + context: loader.cm.URLContext([new RegExp(".*\\.html")]) + }) + ]; + + test.withTestDoc(function (window, doc) { + test.showMenu(null, function (popup) { + test.checkMenu(items, [], []); + test.done(); + }); + }); +}; + + +// URL contexts should not cause items to appear on pages that do not match. +exports.testURLContextNoMatch = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let items = [ + loader.cm.Item({ + label: "item 0", + context: loader.cm.URLContext("*.bogus.com") + }), + loader.cm.Item({ + label: "item 1", + context: loader.cm.URLContext(["*.bogus.com", "*.gnarly.com"]) + }), + loader.cm.Item({ + label: "item 2", + context: loader.cm.URLContext([new RegExp(".*\\.js")]) + }) + ]; + + test.withTestDoc(function (window, doc) { + test.showMenu(null, function (popup) { + test.checkMenu(items, items, []); + test.done(); + }); + }); +}; + + +// Removing a non-matching URL context after its item is created and the page is +// loaded should cause the item's content script to be evaluated when the +// context menu is next opened. +exports.testURLContextRemove = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let shouldBeEvaled = false; + let context = loader.cm.URLContext("*.bogus.com"); + let item = loader.cm.Item({ + label: "item", + context: context, + contentScript: 'self.postMessage("ok"); self.on("context", function () true);', + onMessage: function (msg) { + assert.ok(shouldBeEvaled, + "content script should be evaluated when expected"); + assert.equal(msg, "ok", "Should have received the right message"); + shouldBeEvaled = false; + } + }); + + test.withTestDoc(function (window, doc) { + test.showMenu(null, function (popup) { + test.checkMenu([item], [item], []); + + item.context.remove(context); + + shouldBeEvaled = true; + + test.hideMenu(function () { + test.showMenu(null, function (popup) { + test.checkMenu([item], [], []); + + assert.ok(!shouldBeEvaled, + "content script should have been evaluated"); + + test.hideMenu(function () { + // Shouldn't get evaluated again + test.showMenu(null, function (popup) { + test.checkMenu([item], [], []); + test.done(); + }); + }); + }); + }); + }); + }); +}; + +// Loading a new page in the same tab should correctly start a new worker for +// any content scripts +exports.testPageReload = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let item = loader.cm.Item({ + label: "Item", + contentScript: "var doc = document; self.on('context', function(node) doc.body.getAttribute('showItem') == 'true');" + }); + + test.withTestDoc(function (window, doc) { + // Set a flag on the document that the item uses + doc.body.setAttribute("showItem", "true"); + + test.showMenu(null, function (popup) { + // With the attribute true the item should be visible in the menu + test.checkMenu([item], [], []); + test.hideMenu(function() { + let browser = this.tabBrowser.getBrowserForTab(this.tab) + test.delayedEventListener(browser, "load", function() { + test.delayedEventListener(browser, "load", function() { + window = browser.contentWindow; + doc = window.document; + + // Set a flag on the document that the item uses + doc.body.setAttribute("showItem", "false"); + + test.showMenu(null, function (popup) { + // In the new document with the attribute false the item should be + // hidden, but if the contentScript hasn't been reloaded it will + // still see the old value + test.checkMenu([item], [item], []); + + test.done(); + }); + }, true); + browser.loadURI(TEST_DOC_URL, null, null); + }, true); + // Required to make sure we load a new page in history rather than + // just reloading the current page which would unload it + browser.loadURI("about:blank", null, null); + }); + }); + }); +}; + +// Closing a page after it's been used with a worker should cause the worker +// to be destroyed +/*exports.testWorkerDestroy = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let loadExpected = false; + + let item = loader.cm.Item({ + label: "item", + contentScript: 'self.postMessage("loaded"); self.on("detach", function () { console.log("saw detach"); self.postMessage("detach") });', + onMessage: function (msg) { + console.log("Saw " + msg) + switch (msg) { + case "loaded": + assert.ok(loadExpected, "Should have seen the load event at the right time"); + loadExpected = false; + break; + case "detach": + test.done(); + break; + } + } + }); + + test.withTestDoc(function (window, doc) { + loadExpected = true; + test.showMenu(null, function (popup) { + assert.ok(!loadExpected, "Should have seen a message"); + + test.checkMenu([item], [], []); + + test.closeTab(); + }); + }); +};*/ + + +// Content contexts that return true should cause their items to be present +// in the menu. +exports.testContentContextMatch = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let item = new loader.cm.Item({ + label: "item", + contentScript: 'self.on("context", function () true);' + }); + + test.showMenu(null, function (popup) { + test.checkMenu([item], [], []); + test.done(); + }); +}; + + +// Content contexts that return false should cause their items to be absent +// from the menu. +exports.testContentContextNoMatch = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let item = new loader.cm.Item({ + label: "item", + contentScript: 'self.on("context", function () false);' + }); + + test.showMenu(null, function (popup) { + test.checkMenu([item], [item], []); + test.done(); + }); +}; + + +// Content contexts that return undefined should cause their items to be absent +// from the menu. +exports.testContentContextUndefined = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let item = new loader.cm.Item({ + label: "item", + contentScript: 'self.on("context", function () {});' + }); + + test.showMenu(null, function (popup) { + test.checkMenu([item], [item], []); + test.done(); + }); +}; + + +// Content contexts that return an empty string should cause their items to be +// absent from the menu and shouldn't wipe the label +exports.testContentContextEmptyString = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let item = new loader.cm.Item({ + label: "item", + contentScript: 'self.on("context", function () "");' + }); + + test.showMenu(null, function (popup) { + test.checkMenu([item], [item], []); + assert.equal(item.label, "item", "Label should still be correct"); + test.done(); + }); +}; + + +// If any content contexts returns true then their items should be present in +// the menu. +exports.testMultipleContentContextMatch1 = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let item = new loader.cm.Item({ + label: "item", + contentScript: 'self.on("context", function () true); ' + + 'self.on("context", function () false);', + onMessage: function() { + test.fail("Should not have called the second context listener"); + } + }); + + test.showMenu(null, function (popup) { + test.checkMenu([item], [], []); + test.done(); + }); +}; + + +// If any content contexts returns true then their items should be present in +// the menu. +exports.testMultipleContentContextMatch2 = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let item = new loader.cm.Item({ + label: "item", + contentScript: 'self.on("context", function () false); ' + + 'self.on("context", function () true);' + }); + + test.showMenu(null, function (popup) { + test.checkMenu([item], [], []); + test.done(); + }); +}; + + +// If any content contexts returns a string then their items should be present +// in the menu. +exports.testMultipleContentContextString1 = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let item = new loader.cm.Item({ + label: "item", + contentScript: 'self.on("context", function () "new label"); ' + + 'self.on("context", function () false);' + }); + + test.showMenu(null, function (popup) { + test.checkMenu([item], [], []); + assert.equal(item.label, "new label", "Label should have changed"); + test.done(); + }); +}; + + +// If any content contexts returns a string then their items should be present +// in the menu. +exports.testMultipleContentContextString2 = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let item = new loader.cm.Item({ + label: "item", + contentScript: 'self.on("context", function () false); ' + + 'self.on("context", function () "new label");' + }); + + test.showMenu(null, function (popup) { + test.checkMenu([item], [], []); + assert.equal(item.label, "new label", "Label should have changed"); + test.done(); + }); +}; + + +// If many content contexts returns a string then the first should take effect +exports.testMultipleContentContextString3 = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let item = new loader.cm.Item({ + label: "item", + contentScript: 'self.on("context", function () "new label 1"); ' + + 'self.on("context", function () "new label 2");' + }); + + test.showMenu(null, function (popup) { + test.checkMenu([item], [], []); + assert.equal(item.label, "new label 1", "Label should have changed"); + test.done(); + }); +}; + + +// Content contexts that return true should cause their items to be present +// in the menu when context clicking an active element. +exports.testContentContextMatchActiveElement = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let items = [ + new loader.cm.Item({ + label: "item 1", + contentScript: 'self.on("context", function () true);' + }), + new loader.cm.Item({ + label: "item 2", + context: undefined, + contentScript: 'self.on("context", function () true);' + }), + // These items will always be hidden by the declarative usage of PageContext + new loader.cm.Item({ + label: "item 3", + context: loader.cm.PageContext(), + contentScript: 'self.on("context", function () true);' + }), + new loader.cm.Item({ + label: "item 4", + context: [loader.cm.PageContext()], + contentScript: 'self.on("context", function () true);' + }) + ]; + + test.withTestDoc(function (window, doc) { + test.showMenu(doc.getElementById("image"), function (popup) { + test.checkMenu(items, [items[2], items[3]], []); + test.done(); + }); + }); +}; + + +// Content contexts that return false should cause their items to be absent +// from the menu when context clicking an active element. +exports.testContentContextNoMatchActiveElement = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let items = [ + new loader.cm.Item({ + label: "item 1", + contentScript: 'self.on("context", function () false);' + }), + new loader.cm.Item({ + label: "item 2", + context: undefined, + contentScript: 'self.on("context", function () false);' + }), + // These items will always be hidden by the declarative usage of PageContext + new loader.cm.Item({ + label: "item 3", + context: loader.cm.PageContext(), + contentScript: 'self.on("context", function () false);' + }), + new loader.cm.Item({ + label: "item 4", + context: [loader.cm.PageContext()], + contentScript: 'self.on("context", function () false);' + }) + ]; + + test.withTestDoc(function (window, doc) { + test.showMenu(doc.getElementById("image"), function (popup) { + test.checkMenu(items, items, []); + test.done(); + }); + }); +}; + + +// Content contexts that return undefined should cause their items to be absent +// from the menu when context clicking an active element. +exports.testContentContextNoMatchActiveElement = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let items = [ + new loader.cm.Item({ + label: "item 1", + contentScript: 'self.on("context", function () {});' + }), + new loader.cm.Item({ + label: "item 2", + context: undefined, + contentScript: 'self.on("context", function () {});' + }), + // These items will always be hidden by the declarative usage of PageContext + new loader.cm.Item({ + label: "item 3", + context: loader.cm.PageContext(), + contentScript: 'self.on("context", function () {});' + }), + new loader.cm.Item({ + label: "item 4", + context: [loader.cm.PageContext()], + contentScript: 'self.on("context", function () {});' + }) + ]; + + test.withTestDoc(function (window, doc) { + test.showMenu(doc.getElementById("image"), function (popup) { + test.checkMenu(items, items, []); + test.done(); + }); + }); +}; + + +// Content contexts that return a string should cause their items to be present +// in the menu and the items' labels to be updated. +exports.testContentContextMatchString = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let item = new loader.cm.Item({ + label: "first label", + contentScript: 'self.on("context", function () "second label");' + }); + + test.showMenu(null, function (popup) { + test.checkMenu([item], [], []); + assert.equal(item.label, "second label", + "item's label should be updated"); + test.done(); + }); +}; + + +// Ensure that contentScriptFile is working correctly +exports.testContentScriptFile = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + // Reject remote files + assert.throws(function() { + new loader.cm.Item({ + label: "item", + contentScriptFile: "http://mozilla.com/context-menu.js" + }); + }, + new RegExp("The 'contentScriptFile' option must be a local file URL " + + "or an array of local file URLs."), + "Item throws when contentScriptFile is a remote URL"); + + // But accept files from data folder + let item = new loader.cm.Item({ + label: "item", + contentScriptFile: data.url("test-context-menu.js") + }); + + test.showMenu(null, function (popup) { + test.checkMenu([item], [], []); + test.done(); + }); +}; + + +// The args passed to context listeners should be correct. +exports.testContentContextArgs = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + let callbacks = 0; + + let item = new loader.cm.Item({ + label: "item", + contentScript: 'self.on("context", function (node) {' + + ' self.postMessage(node.tagName);' + + ' return false;' + + '});', + onMessage: function (tagName) { + assert.equal(tagName, "HTML", "node should be an HTML element"); + if (++callbacks == 2) test.done(); + } + }); + + test.showMenu(null, function () { + if (++callbacks == 2) test.done(); + }); +}; + +// Multiple contexts imply intersection, not union, and content context +// listeners should not be called if all declarative contexts are not current. +exports.testMultipleContexts = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let item = new loader.cm.Item({ + label: "item", + context: [loader.cm.SelectorContext("a[href]"), loader.cm.PageContext()], + contentScript: 'self.on("context", function () self.postMessage());', + onMessage: function () { + test.fail("Context listener should not be called"); + } + }); + + test.withTestDoc(function (window, doc) { + test.showMenu(doc.getElementById("span-link"), function (popup) { + test.checkMenu([item], [item], []); + test.done(); + }); + }); +}; + +// Once a context is removed, it should no longer cause its item to appear. +exports.testRemoveContext = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let ctxt = loader.cm.SelectorContext("img"); + let item = new loader.cm.Item({ + label: "item", + context: ctxt + }); + + test.withTestDoc(function (window, doc) { + test.showMenu(doc.getElementById("image"), function (popup) { + + // The item should be present at first. + test.checkMenu([item], [], []); + popup.hidePopup(); + + // Remove the img context and check again. + item.context.remove(ctxt); + test.showMenu(doc.getElementById("image"), function (popup) { + test.checkMenu([item], [item], []); + test.done(); + }); + }); + }); +}; + + +// Lots of items should overflow into the overflow submenu. +exports.testOverflow = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let items = []; + for (let i = 0; i < OVERFLOW_THRESH_DEFAULT + 1; i++) { + let item = new loader.cm.Item({ label: "item " + i }); + items.push(item); + } + + test.showMenu(null, function (popup) { + test.checkMenu(items, [], []); + test.done(); + }); +}; + + +// Module unload should cause all items to be removed. +exports.testUnload = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let item = new loader.cm.Item({ label: "item" }); + + test.showMenu(null, function (popup) { + + // The menu should contain the item. + test.checkMenu([item], [], []); + popup.hidePopup(); + + // Unload the module. + loader.unload(); + test.showMenu(null, function (popup) { + + // The item should be removed from the menu. + test.checkMenu([item], [], [item]); + test.done(); + }); + }); +}; + + +// Using multiple module instances to add items without causing overflow should +// work OK. Assumes OVERFLOW_THRESH_DEFAULT >= 2. +exports.testMultipleModulesAdd = function (assert, done) { + let test = new TestHelper(assert, done); + let loader0 = test.newLoader(); + let loader1 = test.newLoader(); + + // Use each module to add an item, then unload each module in turn. + let item0 = new loader0.cm.Item({ label: "item 0" }); + let item1 = new loader1.cm.Item({ label: "item 1" }); + + test.showMenu(null, function (popup) { + + // The menu should contain both items. + test.checkMenu([item0, item1], [], []); + popup.hidePopup(); + + // Unload the first module. + loader0.unload(); + test.showMenu(null, function (popup) { + + // The first item should be removed from the menu. + test.checkMenu([item0, item1], [], [item0]); + popup.hidePopup(); + + // Unload the second module. + loader1.unload(); + test.showMenu(null, function (popup) { + + // Both items should be removed from the menu. + test.checkMenu([item0, item1], [], [item0, item1]); + test.done(); + }); + }); + }); +}; + + +// Using multiple module instances to add items causing overflow should work OK. +exports.testMultipleModulesAddOverflow = function (assert, done) { + let test = new TestHelper(assert, done); + let loader0 = test.newLoader(); + let loader1 = test.newLoader(); + + // Use module 0 to add OVERFLOW_THRESH_DEFAULT items. + let items0 = []; + for (let i = 0; i < OVERFLOW_THRESH_DEFAULT; i++) { + let item = new loader0.cm.Item({ label: "item 0 " + i }); + items0.push(item); + } + + // Use module 1 to add one item. + let item1 = new loader1.cm.Item({ label: "item 1" }); + + let allItems = items0.concat(item1); + + test.showMenu(null, function (popup) { + + // The menu should contain all items in overflow. + test.checkMenu(allItems, [], []); + popup.hidePopup(); + + // Unload the first module. + loader0.unload(); + test.showMenu(null, function (popup) { + + // The first items should be removed from the menu, which should not + // overflow. + test.checkMenu(allItems, [], items0); + popup.hidePopup(); + + // Unload the second module. + loader1.unload(); + test.showMenu(null, function (popup) { + + // All items should be removed from the menu. + test.checkMenu(allItems, [], allItems); + test.done(); + }); + }); + }); +}; + + +// Using multiple module instances to modify the menu without causing overflow +// should work OK. This test creates two loaders and: +// loader0 create item -> loader1 create item -> loader0.unload -> +// loader1.unload +exports.testMultipleModulesDiffContexts1 = function (assert, done) { + let test = new TestHelper(assert, done); + let loader0 = test.newLoader(); + let loader1 = test.newLoader(); + + let item0 = new loader0.cm.Item({ + label: "item 0", + context: loader0.cm.SelectorContext("img") + }); + + let item1 = new loader1.cm.Item({ label: "item 1" }); + + test.showMenu(null, function (popup) { + + // The menu should contain item1. + test.checkMenu([item0, item1], [item0], []); + popup.hidePopup(); + + // Unload module 0. + loader0.unload(); + test.showMenu(null, function (popup) { + + // item0 should be removed from the menu. + test.checkMenu([item0, item1], [], [item0]); + popup.hidePopup(); + + // Unload module 1. + loader1.unload(); + test.showMenu(null, function (popup) { + + // Both items should be removed from the menu. + test.checkMenu([item0, item1], [], [item0, item1]); + test.done(); + }); + }); + }); +}; + + +// Using multiple module instances to modify the menu without causing overflow +// should work OK. This test creates two loaders and: +// loader1 create item -> loader0 create item -> loader0.unload -> +// loader1.unload +exports.testMultipleModulesDiffContexts2 = function (assert, done) { + let test = new TestHelper(assert, done); + let loader0 = test.newLoader(); + let loader1 = test.newLoader(); + + let item1 = new loader1.cm.Item({ label: "item 1" }); + + let item0 = new loader0.cm.Item({ + label: "item 0", + context: loader0.cm.SelectorContext("img") + }); + + test.showMenu(null, function (popup) { + + // The menu should contain item1. + test.checkMenu([item0, item1], [item0], []); + popup.hidePopup(); + + // Unload module 0. + loader0.unload(); + test.showMenu(null, function (popup) { + + // item0 should be removed from the menu. + test.checkMenu([item0, item1], [], [item0]); + popup.hidePopup(); + + // Unload module 1. + loader1.unload(); + test.showMenu(null, function (popup) { + + // Both items should be removed from the menu. + test.checkMenu([item0, item1], [], [item0, item1]); + test.done(); + }); + }); + }); +}; + + +// Using multiple module instances to modify the menu without causing overflow +// should work OK. This test creates two loaders and: +// loader0 create item -> loader1 create item -> loader1.unload -> +// loader0.unload +exports.testMultipleModulesDiffContexts3 = function (assert, done) { + let test = new TestHelper(assert, done); + let loader0 = test.newLoader(); + let loader1 = test.newLoader(); + + let item0 = new loader0.cm.Item({ + label: "item 0", + context: loader0.cm.SelectorContext("img") + }); + + let item1 = new loader1.cm.Item({ label: "item 1" }); + + test.showMenu(null, function (popup) { + + // The menu should contain item1. + test.checkMenu([item0, item1], [item0], []); + popup.hidePopup(); + + // Unload module 1. + loader1.unload(); + test.showMenu(null, function (popup) { + + // item1 should be removed from the menu. + test.checkMenu([item0, item1], [item0], [item1]); + popup.hidePopup(); + + // Unload module 0. + loader0.unload(); + test.showMenu(null, function (popup) { + + // Both items should be removed from the menu. + test.checkMenu([item0, item1], [], [item0, item1]); + test.done(); + }); + }); + }); +}; + + +// Using multiple module instances to modify the menu without causing overflow +// should work OK. This test creates two loaders and: +// loader1 create item -> loader0 create item -> loader1.unload -> +// loader0.unload +exports.testMultipleModulesDiffContexts4 = function (assert, done) { + let test = new TestHelper(assert, done); + let loader0 = test.newLoader(); + let loader1 = test.newLoader(); + + let item1 = new loader1.cm.Item({ label: "item 1" }); + + let item0 = new loader0.cm.Item({ + label: "item 0", + context: loader0.cm.SelectorContext("img") + }); + + test.showMenu(null, function (popup) { + + // The menu should contain item1. + test.checkMenu([item0, item1], [item0], []); + popup.hidePopup(); + + // Unload module 1. + loader1.unload(); + test.showMenu(null, function (popup) { + + // item1 should be removed from the menu. + test.checkMenu([item0, item1], [item0], [item1]); + popup.hidePopup(); + + // Unload module 0. + loader0.unload(); + test.showMenu(null, function (popup) { + + // Both items should be removed from the menu. + test.checkMenu([item0, item1], [], [item0, item1]); + test.done(); + }); + }); + }); +}; + + +// Test interactions between a loaded module, unloading another module, and the +// menu separator and overflow submenu. +exports.testMultipleModulesAddRemove = function (assert, done) { + let test = new TestHelper(assert, done); + let loader0 = test.newLoader(); + let loader1 = test.newLoader(); + + let item = new loader0.cm.Item({ label: "item" }); + + test.showMenu(null, function (popup) { + + // The menu should contain the item. + test.checkMenu([item], [], []); + popup.hidePopup(); + + // Remove the item. + item.destroy(); + test.showMenu(null, function (popup) { + + // The item should be removed from the menu. + test.checkMenu([item], [], [item]); + popup.hidePopup(); + + // Unload module 1. + loader1.unload(); + test.showMenu(null, function (popup) { + + // There shouldn't be any errors involving the menu separator or + // overflow submenu. + test.checkMenu([item], [], [item]); + test.done(); + }); + }); + }); +}; + + +// Checks that the order of menu items is correct when adding/removing across +// multiple modules. All items from a single module should remain in a group +exports.testMultipleModulesOrder = function (assert, done) { + let test = new TestHelper(assert, done); + let loader0 = test.newLoader(); + let loader1 = test.newLoader(); + + // Use each module to add an item, then unload each module in turn. + let item0 = new loader0.cm.Item({ label: "item 0" }); + let item1 = new loader1.cm.Item({ label: "item 1" }); + + test.showMenu(null, function (popup) { + + // The menu should contain both items. + test.checkMenu([item0, item1], [], []); + popup.hidePopup(); + + let item2 = new loader0.cm.Item({ label: "item 2" }); + + test.showMenu(null, function (popup) { + + // The new item should be grouped with the same items from loader0. + test.checkMenu([item0, item2, item1], [], []); + popup.hidePopup(); + + let item3 = new loader1.cm.Item({ label: "item 3" }); + + test.showMenu(null, function (popup) { + + // Same again + test.checkMenu([item0, item2, item1, item3], [], []); + test.done(); + }); + }); + }); +}; + + +// Checks that the order of menu items is correct when adding/removing across +// multiple modules when overflowing. All items from a single module should +// remain in a group +exports.testMultipleModulesOrderOverflow = function (assert, done) { + let test = new TestHelper(assert, done); + let loader0 = test.newLoader(); + let loader1 = test.newLoader(); + + let prefs = loader0.loader.require("sdk/preferences/service"); + prefs.set(OVERFLOW_THRESH_PREF, 0); + + // Use each module to add an item, then unload each module in turn. + let item0 = new loader0.cm.Item({ label: "item 0" }); + let item1 = new loader1.cm.Item({ label: "item 1" }); + + test.showMenu(null, function (popup) { + + // The menu should contain both items. + test.checkMenu([item0, item1], [], []); + popup.hidePopup(); + + let item2 = new loader0.cm.Item({ label: "item 2" }); + + test.showMenu(null, function (popup) { + + // The new item should be grouped with the same items from loader0. + test.checkMenu([item0, item2, item1], [], []); + popup.hidePopup(); + + let item3 = new loader1.cm.Item({ label: "item 3" }); + + test.showMenu(null, function (popup) { + + // Same again + test.checkMenu([item0, item2, item1, item3], [], []); + test.done(); + }); + }); + }); +}; + + +// Checks that if a module's items are all hidden then the overflow menu doesn't +// get hidden +exports.testMultipleModulesOverflowHidden = function (assert, done) { + let test = new TestHelper(assert, done); + let loader0 = test.newLoader(); + let loader1 = test.newLoader(); + + let prefs = loader0.loader.require("sdk/preferences/service"); + prefs.set(OVERFLOW_THRESH_PREF, 0); + + // Use each module to add an item, then unload each module in turn. + let item0 = new loader0.cm.Item({ label: "item 0" }); + let item1 = new loader1.cm.Item({ + label: "item 1", + context: loader1.cm.SelectorContext("a") + }); + + test.showMenu(null, function (popup) { + // One should be hidden + test.checkMenu([item0, item1], [item1], []); + test.done(); + }); +}; + + +// Checks that if a module's items are all hidden then the overflow menu doesn't +// get hidden (reverse order to above) +exports.testMultipleModulesOverflowHidden2 = function (assert, done) { + let test = new TestHelper(assert, done); + let loader0 = test.newLoader(); + let loader1 = test.newLoader(); + + let prefs = loader0.loader.require("sdk/preferences/service"); + prefs.set(OVERFLOW_THRESH_PREF, 0); + + // Use each module to add an item, then unload each module in turn. + let item0 = new loader0.cm.Item({ + label: "item 0", + context: loader0.cm.SelectorContext("a") + }); + let item1 = new loader1.cm.Item({ label: "item 1" }); + + test.showMenu(null, function (popup) { + // One should be hidden + test.checkMenu([item0, item1], [item0], []); + test.done(); + }); +}; + + +// Checks that we don't overflow if there are more items than the overflow +// threshold but not all of them are visible +exports.testOverflowIgnoresHidden = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let prefs = loader.loader.require("sdk/preferences/service"); + prefs.set(OVERFLOW_THRESH_PREF, 2); + + let allItems = [ + new loader.cm.Item({ + label: "item 0" + }), + new loader.cm.Item({ + label: "item 1" + }), + new loader.cm.Item({ + label: "item 2", + context: loader.cm.SelectorContext("a") + }) + ]; + + test.showMenu(null, function (popup) { + // One should be hidden + test.checkMenu(allItems, [allItems[2]], []); + test.done(); + }); +}; + + +// Checks that we don't overflow if there are more items than the overflow +// threshold but not all of them are visible +exports.testOverflowIgnoresHiddenMultipleModules1 = function (assert, done) { + let test = new TestHelper(assert, done); + let loader0 = test.newLoader(); + let loader1 = test.newLoader(); + + let prefs = loader0.loader.require("sdk/preferences/service"); + prefs.set(OVERFLOW_THRESH_PREF, 2); + + let allItems = [ + new loader0.cm.Item({ + label: "item 0" + }), + new loader0.cm.Item({ + label: "item 1" + }), + new loader1.cm.Item({ + label: "item 2", + context: loader1.cm.SelectorContext("a") + }), + new loader1.cm.Item({ + label: "item 3", + context: loader1.cm.SelectorContext("a") + }) + ]; + + test.showMenu(null, function (popup) { + // One should be hidden + test.checkMenu(allItems, [allItems[2], allItems[3]], []); + test.done(); + }); +}; + + +// Checks that we don't overflow if there are more items than the overflow +// threshold but not all of them are visible +exports.testOverflowIgnoresHiddenMultipleModules2 = function (assert, done) { + let test = new TestHelper(assert, done); + let loader0 = test.newLoader(); + let loader1 = test.newLoader(); + + let prefs = loader0.loader.require("sdk/preferences/service"); + prefs.set(OVERFLOW_THRESH_PREF, 2); + + let allItems = [ + new loader0.cm.Item({ + label: "item 0" + }), + new loader0.cm.Item({ + label: "item 1", + context: loader0.cm.SelectorContext("a") + }), + new loader1.cm.Item({ + label: "item 2" + }), + new loader1.cm.Item({ + label: "item 3", + context: loader1.cm.SelectorContext("a") + }) + ]; + + test.showMenu(null, function (popup) { + // One should be hidden + test.checkMenu(allItems, [allItems[1], allItems[3]], []); + test.done(); + }); +}; + + +// Checks that we don't overflow if there are more items than the overflow +// threshold but not all of them are visible +exports.testOverflowIgnoresHiddenMultipleModules3 = function (assert, done) { + let test = new TestHelper(assert, done); + let loader0 = test.newLoader(); + let loader1 = test.newLoader(); + + let prefs = loader0.loader.require("sdk/preferences/service"); + prefs.set(OVERFLOW_THRESH_PREF, 2); + + let allItems = [ + new loader0.cm.Item({ + label: "item 0", + context: loader0.cm.SelectorContext("a") + }), + new loader0.cm.Item({ + label: "item 1", + context: loader0.cm.SelectorContext("a") + }), + new loader1.cm.Item({ + label: "item 2" + }), + new loader1.cm.Item({ + label: "item 3" + }) + ]; + + test.showMenu(null, function (popup) { + // One should be hidden + test.checkMenu(allItems, [allItems[0], allItems[1]], []); + test.done(); + }); +}; + + +// Tests that we transition between overflowing to non-overflowing to no items +// and back again +exports.testOverflowTransition = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let prefs = loader.loader.require("sdk/preferences/service"); + prefs.set(OVERFLOW_THRESH_PREF, 2); + + let pItems = [ + new loader.cm.Item({ + label: "item 0", + context: loader.cm.SelectorContext("p") + }), + new loader.cm.Item({ + label: "item 1", + context: loader.cm.SelectorContext("p") + }) + ]; + + let aItems = [ + new loader.cm.Item({ + label: "item 2", + context: loader.cm.SelectorContext("a") + }), + new loader.cm.Item({ + label: "item 3", + context: loader.cm.SelectorContext("a") + }) + ]; + + let allItems = pItems.concat(aItems); + + test.withTestDoc(function (window, doc) { + test.showMenu(doc.getElementById("link"), function (popup) { + // The menu should contain all items and will overflow + test.checkMenu(allItems, [], []); + popup.hidePopup(); + + test.showMenu(doc.getElementById("text"), function (popup) { + // Only contains hald the items and will not overflow + test.checkMenu(allItems, aItems, []); + popup.hidePopup(); + + test.showMenu(null, function (popup) { + // None of the items will be visible + test.checkMenu(allItems, allItems, []); + popup.hidePopup(); + + test.showMenu(doc.getElementById("text"), function (popup) { + // Only contains hald the items and will not overflow + test.checkMenu(allItems, aItems, []); + popup.hidePopup(); + + test.showMenu(doc.getElementById("link"), function (popup) { + // The menu should contain all items and will overflow + test.checkMenu(allItems, [], []); + popup.hidePopup(); + + test.showMenu(null, function (popup) { + // None of the items will be visible + test.checkMenu(allItems, allItems, []); + popup.hidePopup(); + + test.showMenu(doc.getElementById("link"), function (popup) { + // The menu should contain all items and will overflow + test.checkMenu(allItems, [], []); + test.done(); + }); + }); + }); + }); + }); + }); + }); + }); +}; + + +// An item's command listener should work. +exports.testItemCommand = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let item = new loader.cm.Item({ + label: "item", + data: "item data", + contentScript: 'self.on("click", function (node, data) {' + + ' self.postMessage({' + + ' tagName: node.tagName,' + + ' data: data' + + ' });' + + '});', + onMessage: function (data) { + assert.equal(this, item, "`this` inside onMessage should be item"); + assert.equal(data.tagName, "HTML", "node should be an HTML element"); + assert.equal(data.data, item.data, "data should be item data"); + test.done(); + } + }); + + test.showMenu(null, function (popup) { + test.checkMenu([item], [], []); + let elt = test.getItemElt(popup, item); + + // create a command event + let evt = elt.ownerDocument.createEvent('Event'); + evt.initEvent('command', true, true); + elt.dispatchEvent(evt); + }); +}; + + +// A menu's click listener should work and receive bubbling 'command' events from +// sub-items appropriately. This also tests menus and ensures that when a CSS +// selector context matches the clicked node's ancestor, the matching ancestor +// is passed to listeners as the clicked node. +exports.testMenuCommand = function (assert, done) { + // Create a top-level menu, submenu, and item, like this: + // topMenu -> submenu -> item + // Click the item and make sure the click bubbles. + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let item = new loader.cm.Item({ + label: "submenu item", + data: "submenu item data", + context: loader.cm.SelectorContext("a"), + }); + + let submenu = new loader.cm.Menu({ + label: "submenu", + context: loader.cm.SelectorContext("a"), + items: [item] + }); + + let topMenu = new loader.cm.Menu({ + label: "top menu", + contentScript: 'self.on("click", function (node, data) {' + + ' self.postMessage({' + + ' tagName: node.tagName,' + + ' data: data' + + ' });' + + '});', + onMessage: function (data) { + assert.equal(this, topMenu, "`this` inside top menu should be menu"); + assert.equal(data.tagName, "A", "Clicked node should be anchor"); + assert.equal(data.data, item.data, + "Clicked item data should be correct"); + test.done(); + }, + items: [submenu], + context: loader.cm.SelectorContext("a") + }); + + test.withTestDoc(function (window, doc) { + test.showMenu(doc.getElementById("span-link"), function (popup) { + test.checkMenu([topMenu], [], []); + let topMenuElt = test.getItemElt(popup, topMenu); + let topMenuPopup = topMenuElt.firstChild; + let submenuElt = test.getItemElt(topMenuPopup, submenu); + let submenuPopup = submenuElt.firstChild; + let itemElt = test.getItemElt(submenuPopup, item); + + // create a command event + let evt = itemElt.ownerDocument.createEvent('Event'); + evt.initEvent('command', true, true); + itemElt.dispatchEvent(evt); + }); + }); +}; + + +// Click listeners should work when multiple modules are loaded. +exports.testItemCommandMultipleModules = function (assert, done) { + let test = new TestHelper(assert, done); + let loader0 = test.newLoader(); + let loader1 = test.newLoader(); + + let item0 = loader0.cm.Item({ + label: "loader 0 item", + contentScript: 'self.on("click", self.postMessage);', + onMessage: function () { + test.fail("loader 0 item should not emit click event"); + } + }); + let item1 = loader1.cm.Item({ + label: "loader 1 item", + contentScript: 'self.on("click", self.postMessage);', + onMessage: function () { + test.pass("loader 1 item clicked as expected"); + test.done(); + } + }); + + test.showMenu(null, function (popup) { + test.checkMenu([item0, item1], [], []); + let item1Elt = test.getItemElt(popup, item1); + + // create a command event + let evt = item1Elt.ownerDocument.createEvent('Event'); + evt.initEvent('command', true, true); + item1Elt.dispatchEvent(evt); + }); +}; + + + + +// An item's click listener should work. +exports.testItemClick = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let item = new loader.cm.Item({ + label: "item", + data: "item data", + contentScript: 'self.on("click", function (node, data) {' + + ' self.postMessage({' + + ' tagName: node.tagName,' + + ' data: data' + + ' });' + + '});', + onMessage: function (data) { + assert.equal(this, item, "`this` inside onMessage should be item"); + assert.equal(data.tagName, "HTML", "node should be an HTML element"); + assert.equal(data.data, item.data, "data should be item data"); + test.done(); + } + }); + + test.showMenu(null, function (popup) { + test.checkMenu([item], [], []); + let elt = test.getItemElt(popup, item); + elt.click(); + }); +}; + + +// A menu's click listener should work and receive bubbling clicks from +// sub-items appropriately. This also tests menus and ensures that when a CSS +// selector context matches the clicked node's ancestor, the matching ancestor +// is passed to listeners as the clicked node. +exports.testMenuClick = function (assert, done) { + // Create a top-level menu, submenu, and item, like this: + // topMenu -> submenu -> item + // Click the item and make sure the click bubbles. + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let item = new loader.cm.Item({ + label: "submenu item", + data: "submenu item data", + context: loader.cm.SelectorContext("a"), + }); + + let submenu = new loader.cm.Menu({ + label: "submenu", + context: loader.cm.SelectorContext("a"), + items: [item] + }); + + let topMenu = new loader.cm.Menu({ + label: "top menu", + contentScript: 'self.on("click", function (node, data) {' + + ' self.postMessage({' + + ' tagName: node.tagName,' + + ' data: data' + + ' });' + + '});', + onMessage: function (data) { + assert.equal(this, topMenu, "`this` inside top menu should be menu"); + assert.equal(data.tagName, "A", "Clicked node should be anchor"); + assert.equal(data.data, item.data, + "Clicked item data should be correct"); + test.done(); + }, + items: [submenu], + context: loader.cm.SelectorContext("a") + }); + + test.withTestDoc(function (window, doc) { + test.showMenu(doc.getElementById("span-link"), function (popup) { + test.checkMenu([topMenu], [], []); + let topMenuElt = test.getItemElt(popup, topMenu); + let topMenuPopup = topMenuElt.firstChild; + let submenuElt = test.getItemElt(topMenuPopup, submenu); + let submenuPopup = submenuElt.firstChild; + let itemElt = test.getItemElt(submenuPopup, item); + itemElt.click(); + }); + }); +}; + +// Click listeners should work when multiple modules are loaded. +exports.testItemClickMultipleModules = function (assert, done) { + let test = new TestHelper(assert, done); + let loader0 = test.newLoader(); + let loader1 = test.newLoader(); + + let item0 = loader0.cm.Item({ + label: "loader 0 item", + contentScript: 'self.on("click", self.postMessage);', + onMessage: function () { + test.fail("loader 0 item should not emit click event"); + } + }); + let item1 = loader1.cm.Item({ + label: "loader 1 item", + contentScript: 'self.on("click", self.postMessage);', + onMessage: function () { + test.pass("loader 1 item clicked as expected"); + test.done(); + } + }); + + test.showMenu(null, function (popup) { + test.checkMenu([item0, item1], [], []); + let item1Elt = test.getItemElt(popup, item1); + item1Elt.click(); + }); +}; + + +// Adding a separator to a submenu should work OK. +exports.testSeparator = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let menu = new loader.cm.Menu({ + label: "submenu", + items: [new loader.cm.Separator()] + }); + + test.showMenu(null, function (popup) { + test.checkMenu([menu], [], []); + test.done(); + }); +}; + + +// The parentMenu option should work +exports.testParentMenu = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let menu = new loader.cm.Menu({ + label: "submenu", + items: [loader.cm.Item({ label: "item 1" })], + parentMenu: loader.cm.contentContextMenu + }); + + let item = loader.cm.Item({ + label: "item 2", + parentMenu: menu, + }); + + assert.equal(menu.items[1], item, "Item should be in the sub menu"); + + test.showMenu(null, function (popup) { + test.checkMenu([menu], [], []); + test.done(); + }); +}; + + +// Existing context menu modifications should apply to new windows. +exports.testNewWindow = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let item = new loader.cm.Item({ label: "item" }); + + test.withNewWindow(function () { + test.showMenu(null, function (popup) { + test.checkMenu([item], [], []); + test.done(); + }); + }); +}; + + +// When a new window is opened, items added by an unloaded module should not +// be present in the menu. +exports.testNewWindowMultipleModules = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + let item = new loader.cm.Item({ label: "item" }); + + test.showMenu(null, function (popup) { + test.checkMenu([item], [], []); + popup.hidePopup(); + loader.unload(); + test.withNewWindow(function () { + test.showMenu(null, function (popup) { + test.checkMenu([item], [], [item]); + test.done(); + }); + }); + }); +}; + + +// Existing context menu modifications should not apply to new private windows. +exports.testNewPrivateWindow = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let item = new loader.cm.Item({ label: "item" }); + + test.showMenu(null, function (popup) { + test.checkMenu([item], [], []); + popup.hidePopup(); + + test.withNewPrivateWindow(function () { + test.showMenu(null, function (popup) { + test.checkMenu([], [], []); + test.done(); + }); + }); + }); +}; + + +// Existing context menu modifications should apply to new private windows when +// private browsing support is enabled. +exports.testNewPrivateEnabledWindow = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newPrivateLoader(); + + let item = new loader.cm.Item({ label: "item" }); + + test.showMenu(null, function (popup) { + test.checkMenu([item], [], []); + popup.hidePopup(); + + test.withNewPrivateWindow(function () { + test.showMenu(null, function (popup) { + test.checkMenu([item], [], []); + test.done(); + }); + }); + }); +}; + + +// Existing context menu modifications should apply to new private windows when +// private browsing support is enabled unless unloaded. +exports.testNewPrivateEnabledWindowUnloaded = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newPrivateLoader(); + + let item = new loader.cm.Item({ label: "item" }); + + test.showMenu(null, function (popup) { + test.checkMenu([item], [], []); + popup.hidePopup(); + + loader.unload(); + + test.withNewPrivateWindow(function () { + test.showMenu(null, function (popup) { + test.checkMenu([], [], []); + test.done(); + }); + }); + }); +}; + + +// Items in the context menu should be sorted according to locale. +exports.testSorting = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + // Make an unsorted items list. It'll look like this: + // item 1, item 0, item 3, item 2, item 5, item 4, ... + let items = []; + for (let i = 0; i < OVERFLOW_THRESH_DEFAULT; i += 2) { + items.push(new loader.cm.Item({ label: "item " + (i + 1) })); + items.push(new loader.cm.Item({ label: "item " + i })); + } + + test.showMenu(null, function (popup) { + test.checkMenu(items, [], []); + test.done(); + }); +}; + + +// Items in the overflow menu should be sorted according to locale. +exports.testSortingOverflow = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + // Make an unsorted items list. It'll look like this: + // item 1, item 0, item 3, item 2, item 5, item 4, ... + let items = []; + for (let i = 0; i < OVERFLOW_THRESH_DEFAULT * 2; i += 2) { + items.push(new loader.cm.Item({ label: "item " + (i + 1) })); + items.push(new loader.cm.Item({ label: "item " + i })); + } + + test.showMenu(null, function (popup) { + test.checkMenu(items, [], []); + test.done(); + }); +}; + + +// Multiple modules shouldn't interfere with sorting. +exports.testSortingMultipleModules = function (assert, done) { + let test = new TestHelper(assert, done); + let loader0 = test.newLoader(); + let loader1 = test.newLoader(); + + let items0 = []; + let items1 = []; + for (let i = 0; i < OVERFLOW_THRESH_DEFAULT; i++) { + if (i % 2) { + let item = new loader0.cm.Item({ label: "item " + i }); + items0.push(item); + } + else { + let item = new loader1.cm.Item({ label: "item " + i }); + items1.push(item); + } + } + let allItems = items0.concat(items1); + + test.showMenu(null, function (popup) { + + // All items should be present and sorted. + test.checkMenu(allItems, [], []); + popup.hidePopup(); + loader0.unload(); + loader1.unload(); + test.showMenu(null, function (popup) { + + // All items should be removed. + test.checkMenu(allItems, [], allItems); + test.done(); + }); + }); +}; + + +// Content click handlers and context handlers should be able to communicate, +// i.e., they're eval'ed in the same worker and sandbox. +exports.testContentCommunication = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let item = new loader.cm.Item({ + label: "item", + contentScript: 'var potato;' + + 'self.on("context", function () {' + + ' potato = "potato";' + + ' return true;' + + '});' + + 'self.on("click", function () {' + + ' self.postMessage(potato);' + + '});', + }); + + item.on("message", function (data) { + assert.equal(data, "potato", "That's a lot of potatoes!"); + test.done(); + }); + + test.showMenu(null, function (popup) { + test.checkMenu([item], [], []); + let elt = test.getItemElt(popup, item); + elt.click(); + }); +}; + + +// When the context menu is invoked on a tab that was already open when the +// module was loaded, it should contain the expected items and content workers +// should function as expected. +exports.testLoadWithOpenTab = function (assert, done) { + let test = new TestHelper(assert, done); + test.withTestDoc(function (window, doc) { + let loader = test.newLoader(); + let item = new loader.cm.Item({ + label: "item", + contentScript: + 'self.on("click", function () self.postMessage("click"));', + onMessage: function (msg) { + if (msg === "click") + test.done(); + } + }); + test.showMenu(null, function (popup) { + test.checkMenu([item], [], []); + test.getItemElt(popup, item).click(); + }); + }); +}; + +// Bug 732716: Ensure that the node given in `click` event works fine +// (i.e. is correctly wrapped) +exports.testDrawImageOnClickNode = function (assert, done) { + let test = new TestHelper(assert, done); + test.withTestDoc(function (window, doc) { + let loader = test.newLoader(); + let item = new loader.cm.Item({ + label: "item", + context: loader.cm.SelectorContext("img"), + contentScript: "new " + function() { + self.on("click", function (img, data) { + let ctx = document.createElement("canvas").getContext("2d"); + ctx.drawImage(img, 1, 1, 1, 1); + self.postMessage("done"); + }); + }, + onMessage: function (msg) { + if (msg === "done") + test.done(); + } + }); + test.showMenu(doc.getElementById("image"), function (popup) { + test.checkMenu([item], [], []); + test.getItemElt(popup, item).click(); + }); + }); +}; + + +// Setting an item's label before the menu is ever shown should correctly change +// its label. +exports.testSetLabelBeforeShow = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let items = [ + new loader.cm.Item({ label: "a" }), + new loader.cm.Item({ label: "b" }) + ] + items[0].label = "z"; + assert.equal(items[0].label, "z"); + + test.showMenu(null, function (popup) { + test.checkMenu(items, [], []); + test.done(); + }); +}; + + +// Setting an item's label after the menu is shown should correctly change its +// label. +exports.testSetLabelAfterShow = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let items = [ + new loader.cm.Item({ label: "a" }), + new loader.cm.Item({ label: "b" }) + ]; + + test.showMenu(null, function (popup) { + test.checkMenu(items, [], []); + popup.hidePopup(); + + items[0].label = "z"; + assert.equal(items[0].label, "z"); + test.showMenu(null, function (popup) { + test.checkMenu(items, [], []); + test.done(); + }); + }); +}; + + +// Setting an item's label before the menu is ever shown should correctly change +// its label. +exports.testSetLabelBeforeShowOverflow = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let prefs = loader.loader.require("sdk/preferences/service"); + prefs.set(OVERFLOW_THRESH_PREF, 0); + + let items = [ + new loader.cm.Item({ label: "a" }), + new loader.cm.Item({ label: "b" }) + ] + items[0].label = "z"; + assert.equal(items[0].label, "z"); + + test.showMenu(null, function (popup) { + test.checkMenu(items, [], []); + test.done(); + }); +}; + + +// Setting an item's label after the menu is shown should correctly change its +// label. +exports.testSetLabelAfterShowOverflow = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let prefs = loader.loader.require("sdk/preferences/service"); + prefs.set(OVERFLOW_THRESH_PREF, 0); + + let items = [ + new loader.cm.Item({ label: "a" }), + new loader.cm.Item({ label: "b" }) + ]; + + test.showMenu(null, function (popup) { + test.checkMenu(items, [], []); + popup.hidePopup(); + + items[0].label = "z"; + assert.equal(items[0].label, "z"); + test.showMenu(null, function (popup) { + test.checkMenu(items, [], []); + test.done(); + }); + }); +}; + + +// Setting the label of an item in a Menu should work. +exports.testSetLabelMenuItem = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let menu = loader.cm.Menu({ + label: "menu", + items: [loader.cm.Item({ label: "a" })] + }); + menu.items[0].label = "z"; + + assert.equal(menu.items[0].label, "z"); + + test.showMenu(null, function (popup) { + test.checkMenu([menu], [], []); + test.done(); + }); +}; + + +// Menu.addItem() should work. +exports.testMenuAddItem = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let menu = loader.cm.Menu({ + label: "menu", + items: [ + loader.cm.Item({ label: "item 0" }) + ] + }); + menu.addItem(loader.cm.Item({ label: "item 1" })); + menu.addItem(loader.cm.Item({ label: "item 2" })); + + assert.equal(menu.items.length, 3, + "menu should have correct number of items"); + for (let i = 0; i < 3; i++) { + assert.equal(menu.items[i].label, "item " + i, + "item label should be correct"); + assert.equal(menu.items[i].parentMenu, menu, + "item's parent menu should be correct"); + } + + test.showMenu(null, function (popup) { + test.checkMenu([menu], [], []); + test.done(); + }); +}; + + +// Adding the same item twice to a menu should work as expected. +exports.testMenuAddItemTwice = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let menu = loader.cm.Menu({ + label: "menu", + items: [] + }); + let subitem = loader.cm.Item({ label: "item 1" }) + menu.addItem(subitem); + menu.addItem(loader.cm.Item({ label: "item 0" })); + menu.addItem(subitem); + + assert.equal(menu.items.length, 2, + "menu should have correct number of items"); + for (let i = 0; i < 2; i++) { + assert.equal(menu.items[i].label, "item " + i, + "item label should be correct"); + } + + test.showMenu(null, function (popup) { + test.checkMenu([menu], [], []); + test.done(); + }); +}; + + +// Menu.removeItem() should work. +exports.testMenuRemoveItem = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let subitem = loader.cm.Item({ label: "item 1" }); + let menu = loader.cm.Menu({ + label: "menu", + items: [ + loader.cm.Item({ label: "item 0" }), + subitem, + loader.cm.Item({ label: "item 2" }) + ] + }); + + // Removing twice should be harmless. + menu.removeItem(subitem); + menu.removeItem(subitem); + + assert.equal(subitem.parentMenu, null, + "item's parent menu should be correct"); + + assert.equal(menu.items.length, 2, + "menu should have correct number of items"); + assert.equal(menu.items[0].label, "item 0", + "item label should be correct"); + assert.equal(menu.items[1].label, "item 2", + "item label should be correct"); + + test.showMenu(null, function (popup) { + test.checkMenu([menu], [], []); + test.done(); + }); +}; + + +// Adding an item currently contained in one menu to another menu should work. +exports.testMenuItemSwap = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let subitem = loader.cm.Item({ label: "item" }); + let menu0 = loader.cm.Menu({ + label: "menu 0", + items: [subitem] + }); + let menu1 = loader.cm.Menu({ + label: "menu 1", + items: [] + }); + menu1.addItem(subitem); + + assert.equal(menu0.items.length, 0, + "menu should have correct number of items"); + + assert.equal(menu1.items.length, 1, + "menu should have correct number of items"); + assert.equal(menu1.items[0].label, "item", + "item label should be correct"); + + assert.equal(subitem.parentMenu, menu1, + "item's parent menu should be correct"); + + test.showMenu(null, function (popup) { + test.checkMenu([menu0, menu1], [menu0], []); + test.done(); + }); +}; + + +// Destroying an item should remove it from its parent menu. +exports.testMenuItemDestroy = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let subitem = loader.cm.Item({ label: "item" }); + let menu = loader.cm.Menu({ + label: "menu", + items: [subitem] + }); + subitem.destroy(); + + assert.equal(menu.items.length, 0, + "menu should have correct number of items"); + assert.equal(subitem.parentMenu, null, + "item's parent menu should be correct"); + + test.showMenu(null, function (popup) { + test.checkMenu([menu], [menu], []); + test.done(); + }); +}; + + +// Setting Menu.items should work. +exports.testMenuItemsSetter = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let menu = loader.cm.Menu({ + label: "menu", + items: [ + loader.cm.Item({ label: "old item 0" }), + loader.cm.Item({ label: "old item 1" }) + ] + }); + menu.items = [ + loader.cm.Item({ label: "new item 0" }), + loader.cm.Item({ label: "new item 1" }), + loader.cm.Item({ label: "new item 2" }) + ]; + + assert.equal(menu.items.length, 3, + "menu should have correct number of items"); + for (let i = 0; i < 3; i++) { + assert.equal(menu.items[i].label, "new item " + i, + "item label should be correct"); + assert.equal(menu.items[i].parentMenu, menu, + "item's parent menu should be correct"); + } + + test.showMenu(null, function (popup) { + test.checkMenu([menu], [], []); + test.done(); + }); +}; + + +// Setting Item.data should work. +exports.testItemDataSetter = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let item = loader.cm.Item({ label: "old item 0", data: "old" }); + item.data = "new"; + + assert.equal(item.data, "new", "item should have correct data"); + + test.showMenu(null, function (popup) { + test.checkMenu([item], [], []); + test.done(); + }); +}; + + +// Open the test doc, load the module, make sure items appear when context- +// clicking the iframe. +exports.testAlreadyOpenIframe = function (assert, done) { + let test = new TestHelper(assert, done); + test.withTestDoc(function (window, doc) { + let loader = test.newLoader(); + let item = new loader.cm.Item({ + label: "item" + }); + test.showMenu(doc.getElementById("iframe"), function (popup) { + test.checkMenu([item], [], []); + test.done(); + }); + }); +}; + + +// Tests that a missing label throws an exception +exports.testItemNoLabel = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + try { + new loader.cm.Item({}); + assert.ok(false, "Should have seen exception"); + } + catch (e) { + assert.ok(true, "Should have seen exception"); + } + + try { + new loader.cm.Item({ label: null }); + assert.ok(false, "Should have seen exception"); + } + catch (e) { + assert.ok(true, "Should have seen exception"); + } + + try { + new loader.cm.Item({ label: undefined }); + assert.ok(false, "Should have seen exception"); + } + catch (e) { + assert.ok(true, "Should have seen exception"); + } + + try { + new loader.cm.Item({ label: "" }); + assert.ok(false, "Should have seen exception"); + } + catch (e) { + assert.ok(true, "Should have seen exception"); + } + + test.done(); +} + + +// Tests that items can have an empty data property +exports.testItemNoData = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + function checkData(data) { + assert.equal(data, undefined, "Data should be undefined"); + } + + let item1 = new loader.cm.Item({ + label: "item 1", + contentScript: 'self.on("click", function(node, data) self.postMessage(data))', + onMessage: checkData + }); + let item2 = new loader.cm.Item({ + label: "item 2", + data: null, + contentScript: 'self.on("click", function(node, data) self.postMessage(data))', + onMessage: checkData + }); + let item3 = new loader.cm.Item({ + label: "item 3", + data: undefined, + contentScript: 'self.on("click", function(node, data) self.postMessage(data))', + onMessage: checkData + }); + + assert.equal(item1.data, undefined, "Should be no defined data"); + assert.equal(item2.data, null, "Should be no defined data"); + assert.equal(item3.data, undefined, "Should be no defined data"); + + test.showMenu(null, function (popup) { + test.checkMenu([item1, item2, item3], [], []); + + let itemElt = test.getItemElt(popup, item1); + itemElt.click(); + + test.hideMenu(function() { + test.showMenu(null, function (popup) { + let itemElt = test.getItemElt(popup, item2); + itemElt.click(); + + test.hideMenu(function() { + test.showMenu(null, function (popup) { + let itemElt = test.getItemElt(popup, item3); + itemElt.click(); + + test.done(); + }); + }); + }); + }); + }); +} + + +// Tests that items without an image don't attempt to show one +exports.testItemNoImage = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let item1 = new loader.cm.Item({ label: "item 1" }); + let item2 = new loader.cm.Item({ label: "item 2", image: null }); + let item3 = new loader.cm.Item({ label: "item 3", image: undefined }); + + assert.equal(item1.image, undefined, "Should be no defined image"); + assert.equal(item2.image, null, "Should be no defined image"); + assert.equal(item3.image, undefined, "Should be no defined image"); + + test.showMenu(null, function (popup) { + test.checkMenu([item1, item2, item3], [], []); + + test.done(); + }); +} + + +// Test image support. +exports.testItemImage = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let imageURL = data.url("moz_favicon.ico"); + let item = new loader.cm.Item({ label: "item", image: imageURL }); + let menu = new loader.cm.Menu({ label: "menu", image: imageURL, items: [ + loader.cm.Item({ label: "subitem" }) + ]}); + assert.equal(item.image, imageURL, "Should have set the image correctly"); + assert.equal(menu.image, imageURL, "Should have set the image correctly"); + + test.showMenu(null, function (popup) { + test.checkMenu([item, menu], [], []); + + let imageURL2 = data.url("dummy.ico"); + item.image = imageURL2; + menu.image = imageURL2; + assert.equal(item.image, imageURL2, "Should have set the image correctly"); + assert.equal(menu.image, imageURL2, "Should have set the image correctly"); + test.checkMenu([item, menu], [], []); + + item.image = null; + menu.image = null; + assert.equal(item.image, null, "Should have set the image correctly"); + assert.equal(menu.image, null, "Should have set the image correctly"); + test.checkMenu([item, menu], [], []); + + test.done(); + }); +}; + +// Test image URL validation. +exports.testItemImageValidURL = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + assert.throws(function(){ + new loader.cm.Item({ + label: "item 1", + image: "foo" + }) + }, /Image URL validation failed/ + ); + + assert.throws(function(){ + new loader.cm.Item({ + label: "item 2", + image: false + }) + }, /Image URL validation failed/ + ); + + assert.throws(function(){ + new loader.cm.Item({ + label: "item 3", + image: 0 + }) + }, /Image URL validation failed/ + ); + + let imageURL = data.url("moz_favicon.ico"); + let item4 = new loader.cm.Item({ label: "item 4", image: imageURL }); + let item5 = new loader.cm.Item({ label: "item 5", image: null }); + let item6 = new loader.cm.Item({ label: "item 6", image: undefined }); + + assert.equal(item4.image, imageURL, "Should be proper image URL"); + assert.equal(item5.image, null, "Should be null image"); + assert.equal(item6.image, undefined, "Should be undefined image"); + + test.done(); +}; + + +// Menu.destroy should destroy the item tree rooted at that menu. +exports.testMenuDestroy = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let menu = loader.cm.Menu({ + label: "menu", + items: [ + loader.cm.Item({ label: "item 0" }), + loader.cm.Menu({ + label: "item 1", + items: [ + loader.cm.Item({ label: "subitem 0" }), + loader.cm.Item({ label: "subitem 1" }), + loader.cm.Item({ label: "subitem 2" }) + ] + }), + loader.cm.Item({ label: "item 2" }) + ] + }); + menu.destroy(); + + /*let numRegistryEntries = 0; + loader.globalScope.browserManager.browserWins.forEach(function (bwin) { + for (let itemID in bwin.items) + numRegistryEntries++; + }); + assert.equal(numRegistryEntries, 0, "All items should be unregistered.");*/ + + test.showMenu(null, function (popup) { + test.checkMenu([menu], [], [menu]); + test.done(); + }); +}; + +// Checks that if a menu contains sub items that are hidden then the menu is +// hidden too. Also checks that content scripts and contexts work for sub items. +exports.testSubItemContextNoMatchHideMenu = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let items = [ + loader.cm.Menu({ + label: "menu 1", + items: [ + loader.cm.Item({ + label: "subitem 1", + context: loader.cm.SelectorContext(".foo") + }) + ] + }), + loader.cm.Menu({ + label: "menu 2", + items: [ + loader.cm.Item({ + label: "subitem 2", + contentScript: 'self.on("context", function () false);' + }) + ] + }), + loader.cm.Menu({ + label: "menu 3", + items: [ + loader.cm.Item({ + label: "subitem 3", + context: loader.cm.SelectorContext(".foo") + }), + loader.cm.Item({ + label: "subitem 4", + contentScript: 'self.on("context", function () false);' + }) + ] + }) + ]; + + test.showMenu(null, function (popup) { + test.checkMenu(items, items, []); + test.done(); + }); +}; + + +// Checks that if a menu contains a combination of hidden and visible sub items +// then the menu is still visible too. +exports.testSubItemContextMatch = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let hiddenItems = [ + loader.cm.Item({ + label: "subitem 3", + context: loader.cm.SelectorContext(".foo") + }), + loader.cm.Item({ + label: "subitem 6", + contentScript: 'self.on("context", function () false);' + }) + ]; + + let items = [ + loader.cm.Menu({ + label: "menu 1", + items: [ + loader.cm.Item({ + label: "subitem 1", + context: loader.cm.URLContext(TEST_DOC_URL) + }) + ] + }), + loader.cm.Menu({ + label: "menu 2", + items: [ + loader.cm.Item({ + label: "subitem 2", + contentScript: 'self.on("context", function () true);' + }) + ] + }), + loader.cm.Menu({ + label: "menu 3", + items: [ + hiddenItems[0], + loader.cm.Item({ + label: "subitem 4", + contentScript: 'self.on("context", function () true);' + }) + ] + }), + loader.cm.Menu({ + label: "menu 4", + items: [ + loader.cm.Item({ + label: "subitem 5", + context: loader.cm.URLContext(TEST_DOC_URL) + }), + hiddenItems[1] + ] + }), + loader.cm.Menu({ + label: "menu 5", + items: [ + loader.cm.Item({ + label: "subitem 7", + context: loader.cm.URLContext(TEST_DOC_URL) + }), + loader.cm.Item({ + label: "subitem 8", + contentScript: 'self.on("context", function () true);' + }) + ] + }) + ]; + + test.withTestDoc(function (window, doc) { + test.showMenu(null, function (popup) { + test.checkMenu(items, hiddenItems, []); + test.done(); + }); + }); +}; + + +// Child items should default to visible, not to PageContext +exports.testSubItemDefaultVisible = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let items = [ + loader.cm.Menu({ + label: "menu 1", + context: loader.cm.SelectorContext("img"), + items: [ + loader.cm.Item({ + label: "subitem 1" + }), + loader.cm.Item({ + label: "subitem 2", + context: loader.cm.SelectorContext("img") + }), + loader.cm.Item({ + label: "subitem 3", + context: loader.cm.SelectorContext("a") + }) + ] + }) + ]; + + // subitem 3 will be hidden + let hiddenItems = [items[0].items[2]]; + + test.withTestDoc(function (window, doc) { + test.showMenu(doc.getElementById("image"), function (popup) { + test.checkMenu(items, hiddenItems, []); + test.done(); + }); + }); +}; + +// Tests that the click event on sub menuitem +// tiggers the click event for the sub menuitem and the parent menu +exports.testSubItemClick = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let state = 0; + + let items = [ + loader.cm.Menu({ + label: "menu 1", + items: [ + loader.cm.Item({ + label: "subitem 1", + data: "foobar", + contentScript: 'self.on("click", function (node, data) {' + + ' self.postMessage({' + + ' tagName: node.tagName,' + + ' data: data' + + ' });' + + '});', + onMessage: function(msg) { + assert.equal(msg.tagName, "HTML", "should have seen the right node"); + assert.equal(msg.data, "foobar", "should have seen the right data"); + assert.equal(state, 0, "should have seen the event at the right time"); + state++; + } + }) + ], + contentScript: 'self.on("click", function (node, data) {' + + ' self.postMessage({' + + ' tagName: node.tagName,' + + ' data: data' + + ' });' + + '});', + onMessage: function(msg) { + assert.equal(msg.tagName, "HTML", "should have seen the right node"); + assert.equal(msg.data, "foobar", "should have seen the right data"); + assert.equal(state, 1, "should have seen the event at the right time"); + + test.done(); + } + }) + ]; + + test.withTestDoc(function (window, doc) { + test.showMenu(null, function (popup) { + test.checkMenu(items, [], []); + + let topMenuElt = test.getItemElt(popup, items[0]); + let topMenuPopup = topMenuElt.firstChild; + let itemElt = test.getItemElt(topMenuPopup, items[0].items[0]); + itemElt.click(); + }); + }); +}; + +// Tests that the command event on sub menuitem +// tiggers the click event for the sub menuitem and the parent menu +exports.testSubItemCommand = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let state = 0; + + let items = [ + loader.cm.Menu({ + label: "menu 1", + items: [ + loader.cm.Item({ + label: "subitem 1", + data: "foobar", + contentScript: 'self.on("click", function (node, data) {' + + ' self.postMessage({' + + ' tagName: node.tagName,' + + ' data: data' + + ' });' + + '});', + onMessage: function(msg) { + assert.equal(msg.tagName, "HTML", "should have seen the right node"); + assert.equal(msg.data, "foobar", "should have seen the right data"); + assert.equal(state, 0, "should have seen the event at the right time"); + state++; + } + }) + ], + contentScript: 'self.on("click", function (node, data) {' + + ' self.postMessage({' + + ' tagName: node.tagName,' + + ' data: data' + + ' });' + + '});', + onMessage: function(msg) { + assert.equal(msg.tagName, "HTML", "should have seen the right node"); + assert.equal(msg.data, "foobar", "should have seen the right data"); + assert.equal(state, 1, "should have seen the event at the right time"); + state++ + + test.done(); + } + }) + ]; + + test.withTestDoc(function (window, doc) { + test.showMenu(null, function (popup) { + test.checkMenu(items, [], []); + + let topMenuElt = test.getItemElt(popup, items[0]); + let topMenuPopup = topMenuElt.firstChild; + let itemElt = test.getItemElt(topMenuPopup, items[0].items[0]); + + // create a command event + let evt = itemElt.ownerDocument.createEvent('Event'); + evt.initEvent('command', true, true); + itemElt.dispatchEvent(evt); + }); + }); +}; + +// Tests that opening a context menu for an outer frame when an inner frame +// has a selection doesn't activate the SelectionContext +exports.testSelectionInInnerFrameNoMatch = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let state = 0; + + let items = [ + loader.cm.Item({ + label: "test item", + context: loader.cm.SelectionContext() + }) + ]; + + test.withTestDoc(function (window, doc) { + let frame = doc.getElementById("iframe"); + frame.contentWindow.getSelection().selectAllChildren(frame.contentDocument.body); + + test.showMenu(null, function (popup) { + test.checkMenu(items, items, []); + test.done(); + }); + }); +}; + +// Tests that opening a context menu for an inner frame when the inner frame +// has a selection does activate the SelectionContext +exports.testSelectionInInnerFrameMatch = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let state = 0; + + let items = [ + loader.cm.Item({ + label: "test item", + context: loader.cm.SelectionContext() + }) + ]; + + test.withTestDoc(function (window, doc) { + let frame = doc.getElementById("iframe"); + frame.contentWindow.getSelection().selectAllChildren(frame.contentDocument.body); + + test.showMenu(frame.contentDocument.getElementById("text"), function (popup) { + test.checkMenu(items, [], []); + test.done(); + }); + }); +}; + +// Tests that opening a context menu for an inner frame when the outer frame +// has a selection doesn't activate the SelectionContext +exports.testSelectionInOuterFrameNoMatch = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let state = 0; + + let items = [ + loader.cm.Item({ + label: "test item", + context: loader.cm.SelectionContext() + }) + ]; + + test.withTestDoc(function (window, doc) { + let frame = doc.getElementById("iframe"); + window.getSelection().selectAllChildren(doc.body); + + test.showMenu(frame.contentDocument.getElementById("text"), function (popup) { + test.checkMenu(items, items, []); + test.done(); + }); + }); +}; + + +// Test that the return value of the predicate function determines if +// item is shown +exports.testPredicateContextControl = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let itemTrue = loader.cm.Item({ + label: "visible", + context: loader.cm.PredicateContext(function () { return true; }) + }); + + let itemFalse = loader.cm.Item({ + label: "hidden", + context: loader.cm.PredicateContext(function () { return false; }) + }); + + test.showMenu(null, function (popup) { + test.checkMenu([itemTrue, itemFalse], [itemFalse], []); + test.done(); + }); +}; + +// Test that the data object has the correct document type +exports.testPredicateContextDocumentType = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let items = [loader.cm.Item({ + label: "item", + context: loader.cm.PredicateContext(function (data) { + assert.equal(data.documentType, 'text/html'); + return true; + }) + })]; + + test.withTestDoc(function (window, doc) { + test.showMenu(null, function (popup) { + test.checkMenu(items, [], []); + test.done(); + }); + }); +}; + +// Test that the data object has the correct document URL +exports.testPredicateContextDocumentURL = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let items = [loader.cm.Item({ + label: "item", + context: loader.cm.PredicateContext(function (data) { + assert.equal(data.documentURL, TEST_DOC_URL); + return true; + }) + })]; + + test.withTestDoc(function (window, doc) { + test.showMenu(null, function (popup) { + test.checkMenu(items, [], []); + test.done(); + }); + }); +}; + + +// Test that the data object has the correct element name +exports.testPredicateContextTargetName = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let items = [loader.cm.Item({ + label: "item", + context: loader.cm.PredicateContext(function (data) { + assert.strictEqual(data.targetName, "input"); + return true; + }) + })]; + + test.withTestDoc(function (window, doc) { + test.showMenu(doc.getElementById("button"), function (popup) { + test.checkMenu(items, [], []); + test.done(); + }); + }); +}; + + +// Test that the data object has the correct ID +exports.testPredicateContextTargetIDSet = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let items = [loader.cm.Item({ + label: "item", + context: loader.cm.PredicateContext(function (data) { + assert.strictEqual(data.targetID, "button"); + return true; + }) + })]; + + test.withTestDoc(function (window, doc) { + test.showMenu(doc.getElementById("button"), function (popup) { + test.checkMenu(items, [], []); + test.done(); + }); + }); +}; + +// Test that the data object has the correct ID +exports.testPredicateContextTargetIDNotSet = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let items = [loader.cm.Item({ + label: "item", + context: loader.cm.PredicateContext(function (data) { + assert.strictEqual(data.targetID, null); + return true; + }) + })]; + + test.withTestDoc(function (window, doc) { + test.showMenu(doc.getElementsByClassName("predicate-test-a")[0], function (popup) { + test.checkMenu(items, [], []); + test.done(); + }); + }); +}; + +// Test that the data object is showing editable correctly for regular text inputs +exports.testPredicateContextTextBoxIsEditable = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let items = [loader.cm.Item({ + label: "item", + context: loader.cm.PredicateContext(function (data) { + assert.strictEqual(data.isEditable, true); + return true; + }) + })]; + + test.withTestDoc(function (window, doc) { + test.showMenu(doc.getElementById("textbox"), function (popup) { + test.checkMenu(items, [], []); + test.done(); + }); + }); +}; + +// Test that the data object is showing editable correctly for readonly text inputs +exports.testPredicateContextReadonlyTextBoxIsNotEditable = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let items = [loader.cm.Item({ + label: "item", + context: loader.cm.PredicateContext(function (data) { + assert.strictEqual(data.isEditable, false); + return true; + }) + })]; + + test.withTestDoc(function (window, doc) { + test.showMenu(doc.getElementById("readonly-textbox"), function (popup) { + test.checkMenu(items, [], []); + test.done(); + }); + }); +}; + +// Test that the data object is showing editable correctly for disabled text inputs +exports.testPredicateContextDisabledTextBoxIsNotEditable = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let items = [loader.cm.Item({ + label: "item", + context: loader.cm.PredicateContext(function (data) { + assert.strictEqual(data.isEditable, false); + return true; + }) + })]; + + test.withTestDoc(function (window, doc) { + test.showMenu(doc.getElementById("disabled-textbox"), function (popup) { + test.checkMenu(items, [], []); + test.done(); + }); + }); +}; + +// Test that the data object is showing editable correctly for text areas +exports.testPredicateContextTextAreaIsEditable = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let items = [loader.cm.Item({ + label: "item", + context: loader.cm.PredicateContext(function (data) { + assert.strictEqual(data.isEditable, true); + return true; + }) + })]; + + test.withTestDoc(function (window, doc) { + test.showMenu(doc.getElementById("textfield"), function (popup) { + test.checkMenu(items, [], []); + test.done(); + }); + }); +}; + +// Test that non-text inputs are not considered editable +exports.testPredicateContextButtonIsNotEditable = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let items = [loader.cm.Item({ + label: "item", + context: loader.cm.PredicateContext(function (data) { + assert.strictEqual(data.isEditable, false); + return true; + }) + })]; + + test.withTestDoc(function (window, doc) { + test.showMenu(doc.getElementById("button"), function (popup) { + test.checkMenu(items, [], []); + test.done(); + }); + }); +}; + + +// Test that the data object is showing editable correctly +exports.testPredicateContextNonInputIsNotEditable = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let items = [loader.cm.Item({ + label: "item", + context: loader.cm.PredicateContext(function (data) { + assert.strictEqual(data.isEditable, false); + return true; + }) + })]; + + test.withTestDoc(function (window, doc) { + test.showMenu(doc.getElementById("image"), function (popup) { + test.checkMenu(items, [], []); + test.done(); + }); + }); +}; + + +// Test that the data object is showing editable correctly for HTML contenteditable elements +exports.testPredicateContextEditableElement = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let items = [loader.cm.Item({ + label: "item", + context: loader.cm.PredicateContext(function (data) { + assert.strictEqual(data.isEditable, true); + return true; + }) + })]; + + test.withTestDoc(function (window, doc) { + test.showMenu(doc.getElementById("editable"), function (popup) { + test.checkMenu(items, [], []); + test.done(); + }); + }); +}; + + +// Test that the data object does not have a selection when there is none +exports.testPredicateContextNoSelectionInPage = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let items = [loader.cm.Item({ + label: "item", + context: loader.cm.PredicateContext(function (data) { + assert.strictEqual(data.selectionText, null); + return true; + }) + })]; + + test.withTestDoc(function (window, doc) { + test.showMenu(null, function (popup) { + test.checkMenu(items, [], []); + test.done(); + }); + }); +}; + +// Test that the data object includes the selected page text +exports.testPredicateContextSelectionInPage = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let items = [loader.cm.Item({ + label: "item", + context: loader.cm.PredicateContext(function (data) { + // since we might get whitespace + assert.ok(data.selectionText && data.selectionText.search(/^\s*Some text.\s*$/) != -1, + 'Expected "Some text.", got "' + data.selectionText + '"'); + return true; + }) + })]; + + test.withTestDoc(function (window, doc) { + window.getSelection().selectAllChildren(doc.getElementById("text")); + test.showMenu(null, function (popup) { + test.checkMenu(items, [], []); + test.done(); + }); + }); +}; + +// Test that the data object includes the selected input text +exports.testPredicateContextSelectionInTextBox = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let items = [loader.cm.Item({ + label: "item", + context: loader.cm.PredicateContext(function (data) { + // since we might get whitespace + assert.strictEqual(data.selectionText, "t v"); + return true; + }) + })]; + + test.withTestDoc(function (window, doc) { + let textbox = doc.getElementById("textbox"); + textbox.focus(); + textbox.setSelectionRange(3, 6); + test.showMenu(textbox, function (popup) { + test.checkMenu(items, [], []); + test.done(); + }); + }); +}; + +// Test that the data object has the correct src for an image +exports.testPredicateContextTargetSrcSet = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + let image; + + let items = [loader.cm.Item({ + label: "item", + context: loader.cm.PredicateContext(function (data) { + assert.strictEqual(data.srcURL, image.src); + return true; + }) + })]; + + test.withTestDoc(function (window, doc) { + image = doc.getElementById("image"); + test.showMenu(image, function (popup) { + test.checkMenu(items, [], []); + test.done(); + }); + }); +}; + +// Test that the data object has no src for a link +exports.testPredicateContextTargetSrcNotSet = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let items = [loader.cm.Item({ + label: "item", + context: loader.cm.PredicateContext(function (data) { + assert.strictEqual(data.srcURL, null); + return true; + }) + })]; + + test.withTestDoc(function (window, doc) { + test.showMenu(doc.getElementById("link"), function (popup) { + test.checkMenu(items, [], []); + test.done(); + }); + }); +}; + + +// Test that the data object has the correct link set +exports.testPredicateContextTargetLinkSet = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + let image; + + let items = [loader.cm.Item({ + label: "item", + context: loader.cm.PredicateContext(function (data) { + assert.strictEqual(data.linkURL, TEST_DOC_URL + "#test"); + return true; + }) + })]; + + test.withTestDoc(function (window, doc) { + test.showMenu(doc.getElementsByClassName("predicate-test-a")[0], function (popup) { + test.checkMenu(items, [], []); + test.done(); + }); + }); +}; + +// Test that the data object has no link for an image +exports.testPredicateContextTargetLinkNotSet = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let items = [loader.cm.Item({ + label: "item", + context: loader.cm.PredicateContext(function (data) { + assert.strictEqual(data.linkURL, null); + return true; + }) + })]; + + test.withTestDoc(function (window, doc) { + test.showMenu(doc.getElementById("image"), function (popup) { + test.checkMenu(items, [], []); + test.done(); + }); + }); +}; + +// Test that the data object has the value for an input textbox +exports.testPredicateContextTargetValueSet = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + let image; + + let items = [loader.cm.Item({ + label: "item", + context: loader.cm.PredicateContext(function (data) { + assert.strictEqual(data.value, "test value"); + return true; + }) + })]; + + test.withTestDoc(function (window, doc) { + test.showMenu(doc.getElementById("textbox"), function (popup) { + test.checkMenu(items, [], []); + test.done(); + }); + }); +}; + +// Test that the data object has no value for an image +exports.testPredicateContextTargetValueNotSet = function (assert, done) { + let test = new TestHelper(assert, done); + let loader = test.newLoader(); + + let items = [loader.cm.Item({ + label: "item", + context: loader.cm.PredicateContext(function (data) { + assert.strictEqual(data.value, null); + return true; + }) + })]; + + test.withTestDoc(function (window, doc) { + test.showMenu(doc.getElementById("image"), function (popup) { + test.checkMenu(items, [], []); + test.done(); + }); + }); +}; + + +// NO TESTS BELOW THIS LINE! /////////////////////////////////////////////////// + +// This makes it easier to run tests by handling things like opening the menu, +// opening new windows, making assertions, etc. Methods on |test| can be called +// on instances of this class. Don't forget to call done() to end the test! +// WARNING: This looks up items in popups by comparing labels, so don't give two +// items the same label. +function TestHelper(assert, done) { + this.assert = assert; + this.end = done; + this.loaders = []; + this.browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"]. + getService(Ci.nsIWindowMediator). + getMostRecentWindow("navigator:browser"); + this.overflowThreshValue = require("sdk/preferences/service"). + get(OVERFLOW_THRESH_PREF, OVERFLOW_THRESH_DEFAULT); +} + +TestHelper.prototype = { + get contextMenuPopup() { + return this.browserWindow.document.getElementById("contentAreaContextMenu"); + }, + + get contextMenuSeparator() { + return this.browserWindow.document.querySelector("." + SEPARATOR_CLASS); + }, + + get overflowPopup() { + return this.browserWindow.document.querySelector("." + OVERFLOW_POPUP_CLASS); + }, + + get overflowSubmenu() { + return this.browserWindow.document.querySelector("." + OVERFLOW_MENU_CLASS); + }, + + get tabBrowser() { + return this.browserWindow.gBrowser; + }, + + // Methods on the wrapped test can be called on this object. + __noSuchMethod__: function (methodName, args) { + this.assert[methodName].apply(this.assert, args); + }, + + // Asserts that elt, a DOM element representing item, looks OK. + checkItemElt: function (elt, item) { + let itemType = this.getItemType(item); + + switch (itemType) { + case "Item": + this.assert.equal(elt.localName, "menuitem", + "Item DOM element should be a xul:menuitem"); + if (typeof(item.data) === "string") { + this.assert.equal(elt.getAttribute("value"), item.data, + "Item should have correct data"); + } + break + case "Menu": + this.assert.equal(elt.localName, "menu", + "Menu DOM element should be a xul:menu"); + let subPopup = elt.firstChild; + this.assert.ok(subPopup, "xul:menu should have a child"); + this.assert.equal(subPopup.localName, "menupopup", + "xul:menu's first child should be a menupopup"); + break; + case "Separator": + this.assert.equal(elt.localName, "menuseparator", + "Separator DOM element should be a xul:menuseparator"); + break; + } + + if (itemType === "Item" || itemType === "Menu") { + this.assert.equal(elt.getAttribute("label"), item.label, + "Item should have correct title"); + if (typeof(item.image) === "string") { + this.assert.equal(elt.getAttribute("image"), item.image, + "Item should have correct image"); + if (itemType === "Menu") + this.assert.ok(elt.classList.contains("menu-iconic"), + "Menus with images should have the correct class") + else + this.assert.ok(elt.classList.contains("menuitem-iconic"), + "Items with images should have the correct class") + } + else { + this.assert.ok(!elt.getAttribute("image"), + "Item should not have image"); + this.assert.ok(!elt.classList.contains("menu-iconic") && !elt.classList.contains("menuitem-iconic"), + "The iconic classes should not be present") + } + } + }, + + // Asserts that the context menu looks OK given the arguments. presentItems + // are items that have been added to the menu. absentItems are items that + // shouldn't match the current context. removedItems are items that have been + // removed from the menu. + checkMenu: function (presentItems, absentItems, removedItems) { + // Count up how many top-level items there are + let total = 0; + for (let item of presentItems) { + if (absentItems.indexOf(item) < 0 && removedItems.indexOf(item) < 0) + total++; + } + + let separator = this.contextMenuSeparator; + if (total == 0) { + this.assert.ok(!separator || separator.hidden, + "separator should not be present"); + } + else { + this.assert.ok(separator && !separator.hidden, + "separator should be present"); + } + + let mainNodes = this.browserWindow.document.querySelectorAll("#contentAreaContextMenu > ." + ITEM_CLASS); + let overflowNodes = this.browserWindow.document.querySelectorAll("." + OVERFLOW_POPUP_CLASS + " > ." + ITEM_CLASS); + + this.assert.ok(mainNodes.length == 0 || overflowNodes.length == 0, + "Should only see nodes at the top level or in overflow"); + + let overflow = this.overflowSubmenu; + if (this.shouldOverflow(total)) { + this.assert.ok(overflow && !overflow.hidden, + "overflow menu should be present"); + this.assert.equal(mainNodes.length, 0, + "should be no items in the main context menu"); + } + else { + this.assert.ok(!overflow || overflow.hidden, + "overflow menu should not be present"); + // When visible nodes == 0 they could be in overflow or top level + if (total > 0) { + this.assert.equal(overflowNodes.length, 0, + "should be no items in the overflow context menu"); + } + } + + // Iterate over wherever the nodes have ended up + let nodes = mainNodes.length ? mainNodes : overflowNodes; + this.checkNodes(nodes, presentItems, absentItems, removedItems) + let pos = 0; + }, + + // Recurses through the item hierarchy of presentItems comparing it to the + // node hierarchy of nodes. Any items in removedItems will be skipped (so + // should not exist in the XUL), any items in absentItems must exist and be + // hidden + checkNodes: function (nodes, presentItems, absentItems, removedItems) { + let pos = 0; + for (let item of presentItems) { + // Removed items shouldn't be in the list + if (removedItems.indexOf(item) >= 0) + continue; + + if (nodes.length <= pos) { + this.assert.ok(false, "Not enough nodes"); + return; + } + + let hidden = absentItems.indexOf(item) >= 0; + + this.checkItemElt(nodes[pos], item); + this.assert.equal(nodes[pos].hidden, hidden, + "hidden should be set correctly"); + + // The contents of hidden menus doesn't matter so much + if (!hidden && this.getItemType(item) == "Menu") { + this.assert.equal(nodes[pos].firstChild.localName, "menupopup", + "menu XUL should contain a menupopup"); + this.checkNodes(nodes[pos].firstChild.childNodes, item.items, absentItems, removedItems); + } + + if (pos > 0) + this.assert.equal(nodes[pos].previousSibling, nodes[pos - 1], + "nodes should all be in the same group"); + pos++; + } + + this.assert.equal(nodes.length, pos, + "should have checked all the XUL nodes"); + }, + + // Attaches an event listener to node. The listener is automatically removed + // when it's fired (so it's assumed it will fire), and callback is called + // after a short delay. Since the module we're testing relies on the same + // event listeners to do its work, this is to give them a little breathing + // room before callback runs. Inside callback |this| is this object. + // Optionally you can pass a function to test if the event is the event you + // want. + delayedEventListener: function (node, event, callback, useCapture, isValid) { + const self = this; + node.addEventListener(event, function handler(evt) { + if (isValid && !isValid(evt)) + return; + node.removeEventListener(event, handler, useCapture); + timer.setTimeout(function () { + try { + callback.call(self, evt); + } + catch (err) { + self.assert.fail(err); + self.end(); + } + }, 20); + }, useCapture); + }, + + // Call to finish the test. + done: function () { + const self = this; + function commonDone() { + this.closeTab(); + + while (this.loaders.length) { + this.loaders[0].unload(); + } + + require("sdk/preferences/service").set(OVERFLOW_THRESH_PREF, self.overflowThreshValue); + + this.end(); + } + + function closeBrowserWindow() { + if (this.oldBrowserWindow) { + this.delayedEventListener(this.browserWindow, "unload", commonDone, + false); + this.browserWindow.close(); + this.browserWindow = this.oldBrowserWindow; + delete this.oldBrowserWindow; + } + else { + commonDone.call(this); + } + }; + + if (this.contextMenuPopup.state == "closed") { + closeBrowserWindow.call(this); + } + else { + this.delayedEventListener(this.contextMenuPopup, "popuphidden", + function () closeBrowserWindow.call(this), + false); + this.contextMenuPopup.hidePopup(); + } + }, + + closeTab: function() { + if (this.tab) { + this.tabBrowser.removeTab(this.tab); + this.tabBrowser.selectedTab = this.oldSelectedTab; + this.tab = null; + } + }, + + // Returns the DOM element in popup corresponding to item. + // WARNING: The element is found by comparing labels, so don't give two items + // the same label. + getItemElt: function (popup, item) { + let nodes = popup.childNodes; + for (let i = nodes.length - 1; i >= 0; i--) { + if (this.getItemType(item) === "Separator") { + if (nodes[i].localName === "menuseparator") + return nodes[i]; + } + else if (nodes[i].getAttribute("label") === item.label) + return nodes[i]; + } + return null; + }, + + // Returns "Item", "Menu", or "Separator". + getItemType: function (item) { + // Could use instanceof here, but that would require accessing the loader + // that created the item, and I don't want to A) somehow search through the + // this.loaders list to find it, and B) assume there are any live loaders at + // all. + return /^\[object (Item|Menu|Separator)/.exec(item.toString())[1]; + }, + + // Returns a wrapper around a new loader: { loader, cm, unload, globalScope }. + // loader is a Cuddlefish sandboxed loader, cm is the context menu module, + // globalScope is the context menu module's global scope, and unload is a + // function that unloads the loader and associated resources. + newLoader: function () { + const self = this; + let loader = Loader(module); + let wrapper = { + loader: loader, + cm: loader.require("sdk/context-menu"), + globalScope: loader.sandbox("sdk/context-menu"), + unload: function () { + loader.unload(); + let idx = self.loaders.indexOf(wrapper); + if (idx < 0) + throw new Error("Test error: tried to unload nonexistent loader"); + self.loaders.splice(idx, 1); + } + }; + this.loaders.push(wrapper); + return wrapper; + }, + + // As above but the loader has private-browsing support enabled. + newPrivateLoader: function() { + let base = require("@loader/options"); + + // Clone current loader's options adding the private-browsing permission + let options = merge({}, base, { + metadata: merge({}, base.metadata || {}, { + permissions: merge({}, base.metadata.permissions || {}, { + 'private-browsing': true + }) + }) + }); + + const self = this; + let loader = Loader(module, null, options); + let wrapper = { + loader: loader, + cm: loader.require("sdk/context-menu"), + globalScope: loader.sandbox("sdk/context-menu"), + unload: function () { + loader.unload(); + let idx = self.loaders.indexOf(wrapper); + if (idx < 0) + throw new Error("Test error: tried to unload nonexistent loader"); + self.loaders.splice(idx, 1); + } + }; + this.loaders.push(wrapper); + return wrapper; + }, + + // Returns true if the count crosses the overflow threshold. + shouldOverflow: function (count) { + return count > + (this.loaders.length ? + this.loaders[0].loader.require("sdk/preferences/service"). + get(OVERFLOW_THRESH_PREF, OVERFLOW_THRESH_DEFAULT) : + OVERFLOW_THRESH_DEFAULT); + }, + + // Opens the context menu on the current page. If targetNode is null, the + // menu is opened in the top-left corner. onShowncallback is passed the + // popup. + showMenu: function(targetNode, onshownCallback) { + function sendEvent() { + this.delayedEventListener(this.browserWindow, "popupshowing", + function (e) { + let popup = e.target; + onshownCallback.call(this, popup); + }, false); + + let rect = targetNode ? + targetNode.getBoundingClientRect() : + { left: 0, top: 0, width: 0, height: 0 }; + let contentWin = targetNode ? targetNode.ownerDocument.defaultView + : this.browserWindow.content; + contentWin. + QueryInterface(Ci.nsIInterfaceRequestor). + getInterface(Ci.nsIDOMWindowUtils). + sendMouseEvent("contextmenu", + rect.left + (rect.width / 2), + rect.top + (rect.height / 2), + 2, 1, 0); + } + + // If a new tab or window has not yet been opened, open a new tab now. For + // some reason using the tab already opened when the test starts causes + // leaks. See bug 566351 for details. + if (!targetNode && !this.oldSelectedTab && !this.oldBrowserWindow) { + this.oldSelectedTab = this.tabBrowser.selectedTab; + this.tab = this.tabBrowser.addTab("about:blank"); + let browser = this.tabBrowser.getBrowserForTab(this.tab); + + this.delayedEventListener(browser, "load", function () { + this.tabBrowser.selectedTab = this.tab; + sendEvent.call(this); + }, true); + } + else + sendEvent.call(this); + }, + + hideMenu: function(onhiddenCallback) { + this.delayedEventListener(this.browserWindow, "popuphidden", onhiddenCallback); + + this.contextMenuPopup.hidePopup(); + }, + + // Opens a new browser window. The window will be closed automatically when + // done() is called. + withNewWindow: function (onloadCallback) { + let win = this.browserWindow.OpenBrowserWindow(); + this.delayedEventListener(win, "load", onloadCallback, true); + this.oldBrowserWindow = this.browserWindow; + this.browserWindow = win; + }, + + // Opens a new private browser window. The window will be closed + // automatically when done() is called. + withNewPrivateWindow: function (onloadCallback) { + let win = this.browserWindow.OpenBrowserWindow({private: true}); + this.delayedEventListener(win, "load", onloadCallback, true); + this.oldBrowserWindow = this.browserWindow; + this.browserWindow = win; + }, + + // Opens a new tab with our test page in the current window. The tab will + // be closed automatically when done() is called. + withTestDoc: function (onloadCallback) { + this.oldSelectedTab = this.tabBrowser.selectedTab; + this.tab = this.tabBrowser.addTab(TEST_DOC_URL); + let browser = this.tabBrowser.getBrowserForTab(this.tab); + + this.delayedEventListener(browser, "load", function () { + this.tabBrowser.selectedTab = this.tab; + onloadCallback.call(this, browser.contentWindow, browser.contentDocument); + }, true, function(evt) { + return evt.target.location == TEST_DOC_URL; + }); + } +}; + +require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-cortex.js b/addon-sdk-1.16/test/test-cortex.js new file mode 100644 index 00000000..6040ab41 --- /dev/null +++ b/addon-sdk-1.16/test/test-cortex.js @@ -0,0 +1,120 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +var Cortex = require("sdk/deprecated/cortex").Cortex; + +exports["test property changes propagate"] = function (assert) { + var source = { + _foo: "secret", + get foo() { + return this._foo; + }, + set foo(value) { + this._foo = value; + }, + get getOnly() { + return this._foo; + }, + set setOnly(value) { + this._setOnly = value; + }, + bar: "public", + method: function method(a, b) { + return this._foo + a + b + } + }; + var fixture = Cortex(source); + + assert.ok(!('_foo' in fixture), + "properties that start with `_` are omitted"); + assert.equal(fixture.foo, "secret", "get accessor alias works"); + fixture.foo = "new secret"; + assert.equal(fixture.foo, "new secret", "set accessor alias works"); + assert.equal(source.foo, "new secret", "accessor delegates to the source"); + assert.equal(fixture.bar, "public", "data property alias works"); + fixture.bar = "bar"; + assert.equal(source.bar, "bar", "data property change propagates"); + source.bar = "foo" + assert.equal(fixture.bar, "foo", "data property change probagets back"); + assert.equal(fixture.method("a", "b"), "new secretab", + "public methods are callable"); + assert.equal(fixture.method.call({ _foo: "test" }, " a,", "b"), + "new secret a,b", + "`this` pseudo-variable can not be passed through call."); + assert.equal(fixture.method.apply({ _foo: "test" }, [" a,", "b"]), + "new secret a,b", + "`this` pseudo-variable can not be passed through apply."); + assert.equal(fixture.getOnly, source._foo, + "getter returned property of wrapped object"); + fixture.setOnly = 'bar' + assert.equal(source._setOnly, 'bar', "setter modified wrapped object") +}; + + +exports["test immunity of inheritance"] = function(assert) { + function Type() {} + Type.prototype = { + constructor: Type, + _bar: 2, + bar: 3, + get_Foo: function getFoo() { + return this._foo; + } + } + var source = Object.create(Type.prototype, { + _foo: { value: 'secret' }, + getBar: { value: function get_Bar() { + return this.bar + }}, + get_Bar: { value: function getBar() { + return this._bar + }} + }); + + var fixture = Cortex(source); + + assert.ok(Cortex({}, null, Type.prototype) instanceof Type, + "if custom prototype is providede cortex will inherit from it"); + assert.ok(fixture instanceof Type, + "if no prototype is given cortex inherits from object's prototype"); + + source.bar += 1; + assert.notEqual(fixture.bar, source.bar, + "chages of properties don't propagate to non-aliases"); + assert.equal(fixture.getBar(), source.bar, + "methods accessing public properties are bound to the source"); + + fixture._bar += 1; + assert.notEqual(fixture._bar, source._bar, + "changes of non aliased properties don't propagate"); + assert.equal(fixture.get_Bar(), source._bar, + "methods accessing privates are bound to the source"); + assert.notEqual(fixture.get_Foo(), source._foo, + "prototoype methods are not bound to the source"); +} + +exports["test customized public properties"] = function(assert) { + var source = { + _a: 'a', + b: 'b', + get: function get(name) { + return this[name]; + } + }; + + var fixture = Cortex(source, ['_a', 'get']); + fixture._a += "#change"; + + + assert.ok(!("b" in fixture), "non-public own property is not defined"); + assert.equal(fixture.get("b"), source.b, + "public methods preserve access to the private properties"); + assert.equal(fixture._a, source._a, + "custom public property changes propagate"); +} + +//if (require.main == module) + require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-cuddlefish.js b/addon-sdk-1.16/test/test-cuddlefish.js new file mode 100644 index 00000000..2d8bb610 --- /dev/null +++ b/addon-sdk-1.16/test/test-cuddlefish.js @@ -0,0 +1,47 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +'use strict'; + +const { Loader, Require, unload, override } = require('sdk/loader/cuddlefish'); +const packaging = require('@loader/options'); + +exports['test loader'] = function(assert) { + var prints = []; + function print(message) { + prints.push(message); + } + + let loader = Loader(override(packaging, { + globals: { + print: print, + foo: 1 + } + })); + let require = Require(loader, module); + + var fixture = require('./loader/fixture'); + + assert.equal(fixture.foo, 1, 'custom globals must work.'); + assert.equal(fixture.bar, 2, 'exports are set'); + + assert.equal(prints[0], 'testing', 'global print must be injected.'); + + var unloadsCalled = ''; + + require("sdk/system/unload").when(function(reason) { + assert.equal(reason, 'test', 'unload reason is passed'); + unloadsCalled += 'a'; + }); + require('sdk/system/unload.js').when(function() { + unloadsCalled += 'b'; + }); + + unload(loader, 'test'); + + assert.equal(unloadsCalled, 'ba', + 'loader.unload() must call listeners in LIFO order.'); +}; + +require('test').run(exports); diff --git a/addon-sdk-1.16/test/test-deprecate.js b/addon-sdk-1.16/test/test-deprecate.js new file mode 100644 index 00000000..ce306b15 --- /dev/null +++ b/addon-sdk-1.16/test/test-deprecate.js @@ -0,0 +1,160 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +'use strict'; + +const deprecate = require("sdk/util/deprecate"); +const { LoaderWithHookedConsole } = require("sdk/test/loader"); +const { get, set } = require("sdk/preferences/service"); +const PREFERENCE = "devtools.errorconsole.deprecation_warnings"; + +exports["test Deprecate Usage"] = function testDeprecateUsage(assert) { + set(PREFERENCE, true); + let { loader, messages } = LoaderWithHookedConsole(module); + let deprecate = loader.require("sdk/util/deprecate"); + + function functionIsDeprecated() { + deprecate.deprecateUsage("foo"); + } + + functionIsDeprecated(); + + assert.equal(messages.length, 1, "only one error is dispatched"); + assert.equal(messages[0].type, "error", "the console message is an error"); + + let msg = messages[0].msg; + + assert.ok(msg.indexOf("foo") !== -1, + "message contains the given message"); + assert.ok(msg.indexOf("functionIsDeprecated") !== -1, + "message contains name of the caller function"); + assert.ok(msg.indexOf(module.uri) !== -1, + "message contains URI of the caller module"); + + loader.unload(); +} + +exports["test Deprecate Function"] = function testDeprecateFunction(assert) { + set(PREFERENCE, true); + let { loader, messages } = LoaderWithHookedConsole(module); + let deprecate = loader.require("sdk/util/deprecate"); + + let self = {}; + let arg1 = "foo"; + let arg2 = {}; + + function originalFunction(a1, a2) { + assert.equal(this, self); + assert.equal(a1, arg1); + assert.equal(a2, arg2); + }; + + let deprecateFunction = deprecate.deprecateFunction(originalFunction, + "bar"); + + deprecateFunction.call(self, arg1, arg2); + + assert.equal(messages.length, 1, "only one error is dispatched"); + assert.equal(messages[0].type, "error", "the console message is an error"); + + let msg = messages[0].msg; + assert.ok(msg.indexOf("bar") !== -1, "message contains the given message"); + assert.ok(msg.indexOf("testDeprecateFunction") !== -1, + "message contains name of the caller function"); + assert.ok(msg.indexOf(module.uri) !== -1, + "message contains URI of the caller module"); + + loader.unload(); +} + +exports.testDeprecateEvent = function(assert, done) { + set(PREFERENCE, true); + let { loader, messages } = LoaderWithHookedConsole(module); + let deprecate = loader.require("sdk/util/deprecate"); + + let { on, emit } = loader.require('sdk/event/core'); + let testObj = {}; + testObj.on = deprecate.deprecateEvent(on.bind(null, testObj), 'BAD', ['fire']); + + testObj.on('fire', function() { + testObj.on('water', function() { + assert.equal(messages.length, 1, "only one error is dispatched"); + loader.unload(); + done(); + }) + assert.equal(messages.length, 1, "only one error is dispatched"); + emit(testObj, 'water'); + }); + assert.equal(messages.length, 1, "only one error is dispatched"); + assert.equal(messages[0].type, "error", "the console message is an error"); + let msg = messages[0].msg; + assert.ok(msg.indexOf("BAD") !== -1, "message contains the given message"); + assert.ok(msg.indexOf("deprecateEvent") !== -1, + "message contains name of the caller function"); + assert.ok(msg.indexOf(module.uri) !== -1, + "message contains URI of the caller module"); + + emit(testObj, 'fire'); +} + +exports.testDeprecateSettingToggle = function (assert, done) { + let { loader, messages } = LoaderWithHookedConsole(module); + let deprecate = loader.require("sdk/util/deprecate"); + + function fn () { deprecate.deprecateUsage("foo"); } + + set(PREFERENCE, false); + fn(); + assert.equal(messages.length, 0, 'no deprecation warnings'); + + set(PREFERENCE, true); + fn(); + assert.equal(messages.length, 1, 'deprecation warnings when toggled'); + + set(PREFERENCE, false); + fn(); + assert.equal(messages.length, 1, 'no new deprecation warnings'); + done(); +}; + +exports.testDeprecateSetting = function (assert, done) { + // Set devtools.errorconsole.deprecation_warnings to false + set(PREFERENCE, false); + + let { loader, messages } = LoaderWithHookedConsole(module); + let deprecate = loader.require("sdk/util/deprecate"); + + // deprecateUsage test + function functionIsDeprecated() { + deprecate.deprecateUsage("foo"); + } + functionIsDeprecated(); + + assert.equal(messages.length, 0, + "no errors dispatched on deprecateUsage"); + + // deprecateFunction test + function originalFunction() {}; + + let deprecateFunction = deprecate.deprecateFunction(originalFunction, + "bar"); + deprecateFunction(); + + assert.equal(messages.length, 0, + "no errors dispatched on deprecateFunction"); + + // deprecateEvent + let { on, emit } = loader.require('sdk/event/core'); + let testObj = {}; + testObj.on = deprecate.deprecateEvent(on.bind(null, testObj), 'BAD', ['fire']); + + testObj.on('fire', () => { + assert.equal(messages.length, 0, + "no errors dispatched on deprecateEvent"); + done(); + }); + + emit(testObj, 'fire'); +} +require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-deprecated-list.js b/addon-sdk-1.16/test/test-deprecated-list.js new file mode 100644 index 00000000..beae09f6 --- /dev/null +++ b/addon-sdk-1.16/test/test-deprecated-list.js @@ -0,0 +1,200 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +'use strict'; + +const { List } = require('sdk/deprecated/list'); + +function assertList(assert, array, list) { + for (let i = 0, l = array.length; i < l; i++) { + assert.equal( + array.length, + list.length, + 'list must contain same amount of elements as array' + ); + assert.equal( + 'List(' + array + ')', + list + '', + 'toString must output array like result' + ); + assert.ok(i in list, 'must contain element with index: ' + i); + assert.equal( + array[i], + list[i], + 'element with index: ' + i + ' should match' + ); + } +} + +exports['test:test for'] = function(assert) { + let fixture = List(3, 2, 1); + + assert.equal(3, fixture.length, 'length is 3'); + let i = 0; + for (let key in fixture) { + assert.equal(i++, key, 'key should match'); + } +}; + +exports['test:test for each'] = function(assert) { + let fixture = new List(3, 2, 1); + + assert.equal(3, fixture.length, 'length is 3'); + let i = 3; + for (let value of fixture) { + assert.equal(i--, value, 'value should match'); + } +}; + +exports['test:test for of'] = function(assert) { + let fixture = new List(3, 2, 1); + + assert.equal(3, fixture.length, 'length is 3'); + let i = 3; + for (let value of fixture) { + assert.equal(i--, value, 'value should match'); + } +}; + +exports['test:test toString'] = function(assert) { + let fixture = List(3, 2, 1); + + assert.equal( + 'List(3,2,1)', + fixture + '', + 'toString must output array like result' + ) +}; + +exports['test:test constructor with apply'] = function(assert) { + let array = ['a', 'b', 'c']; + let fixture = List.apply(null, array); + + assert.equal( + 3, + fixture.length, + 'should have applied arguments' + ); +}; + +exports['test:direct element access'] = function(assert) { + let array = [1, 'foo', 2, 'bar', {}, 'bar', function a() {}, assert, 1]; + let fixture = List.apply(null, array); + array.splice(5, 1); + array.splice(7, 1); + + assert.equal( + array.length, + fixture.length, + 'list should omit duplicate elements' + ); + + assert.equal( + 'List(' + array + ')', + fixture.toString(), + 'elements should not be rearranged' + ); + + for (let key in array) { + assert.ok(key in fixture,'should contain key for index:' + key); + assert.equal(array[key], fixture[key], 'values should match for: ' + key); + } +}; + +exports['test:removing adding elements'] = function(assert) { + let array = [1, 'foo', 2, 'bar', {}, 'bar', function a() {}, assert, 1]; + let fixture = List.compose({ + add: function() this._add.apply(this, arguments), + remove: function() this._remove.apply(this, arguments), + clear: function() this._clear() + }).apply(null, array); + array.splice(5, 1); + array.splice(7, 1); + + assertList(assert, array, fixture); + + array.splice(array.indexOf(2), 1); + fixture.remove(2); + assertList(assert, array, fixture); + + array.splice(array.indexOf('foo'), 1); + fixture.remove('foo'); + array.splice(array.indexOf(1), 1); + fixture.remove(1); + array.push('foo'); + fixture.add('foo'); + assertList(assert, array, fixture); + + array.splice(0); + fixture.clear(0); + assertList(assert, array, fixture); + + array.push(1, 'foo', 2, 'bar', 3); + fixture.add(1); + fixture.add('foo'); + fixture.add(2); + fixture.add('bar'); + fixture.add(3); + + assertList(assert, array, fixture); +}; + +exports['test: remove does not leave invalid numerical properties'] = function(assert) { + let fixture = List.compose({ + remove: function() this._remove.apply(this, arguments), + }).apply(null, [1, 2, 3]); + + fixture.remove(1); + assert.equal(fixture[fixture.length], undefined); +} + +exports['test:add list item from Iterator'] = function(assert) { + let array = [1, 2, 3, 4], sum = 0, added = false; + + let fixture = List.compose({ + add: function() this._add.apply(this, arguments), + }).apply(null, array); + + for (let item of fixture) { + sum += item; + + if (!added) { + fixture.add(5); + added = true; + } + } + + assert.equal(sum, 1 + 2 + 3 + 4, 'sum = 1 + 2 + 3 + 4'); +}; + +exports['test:remove list item from Iterator'] = function(assert) { + let array = [1, 2, 3, 4], sum = 0; + + let fixture = List.compose({ + remove: function() this._remove.apply(this, arguments), + }).apply(null, array); + + for (let item of fixture) { + sum += item; + fixture.remove(item); + } + + assert.equal(sum, 1 + 2 + 3 + 4, 'sum = 1 + 2 + 3 + 4'); +}; + +exports['test:clear list from Iterator'] = function(assert) { + let array = [1, 2, 3, 4], sum = 0; + + let fixture = List.compose({ + clear: function() this._clear() + }).apply(null, array); + + for (let item of fixture) { + sum += item; + fixture.clear(); + } + + assert.equal(sum, 1 + 2 + 3 + 4, 'sum = 1 + 2 + 3 + 4'); +}; + +require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-diffpatcher.js b/addon-sdk-1.16/test/test-diffpatcher.js new file mode 100644 index 00000000..47126c93 --- /dev/null +++ b/addon-sdk-1.16/test/test-diffpatcher.js @@ -0,0 +1,8 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +exports["test diffpatcher"] = require("diffpatcher/test/index"); +require("sdk/test").run(exports); diff --git a/addon-sdk-1.16/test/test-disposable.js b/addon-sdk-1.16/test/test-disposable.js new file mode 100644 index 00000000..95b438d4 --- /dev/null +++ b/addon-sdk-1.16/test/test-disposable.js @@ -0,0 +1,222 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const { Loader } = require("sdk/test/loader"); +const { Class } = require("sdk/core/heritage"); +const { Cc, Ci, Cu } = require("chrome"); +const { setTimeout } = require("sdk/timers"); + +exports["test disposables are desposed on unload"] = function(assert) { + let loader = Loader(module); + let { Disposable } = loader.require("sdk/core/disposable"); + + let arg1 = {} + let arg2 = 2 + let disposals = 0 + + let Foo = Class({ + extends: Disposable, + setup: function setup(a, b) { + assert.equal(a, arg1, + "arguments passed to constructur is passed to setup"); + assert.equal(b, arg2, + "second argument is also passed to a setup"); + assert.ok(this instanceof Foo, + "this is an instance in the scope of the setup method"); + + this.fooed = true + }, + dispose: function dispose() { + assert.equal(this.fooed, true, "attribute was set") + this.fooed = false + disposals = disposals + 1 + } + }) + + let foo1 = Foo(arg1, arg2) + let foo2 = Foo(arg1, arg2) + + loader.unload(); + + assert.equal(disposals, 2, "both instances were disposed") +} + +exports["test destroyed windows dispose before unload"] = function(assert) { + let loader = Loader(module); + let { Disposable } = loader.require("sdk/core/disposable"); + + let arg1 = {} + let arg2 = 2 + let disposals = 0 + + let Foo = Class({ + extends: Disposable, + setup: function setup(a, b) { + assert.equal(a, arg1, + "arguments passed to constructur is passed to setup"); + assert.equal(b, arg2, + "second argument is also passed to a setup"); + assert.ok(this instanceof Foo, + "this is an instance in the scope of the setup method"); + + this.fooed = true + }, + dispose: function dispose() { + assert.equal(this.fooed, true, "attribute was set") + this.fooed = false + disposals = disposals + 1 + } + }) + + let foo1 = Foo(arg1, arg2) + let foo2 = Foo(arg1, arg2) + + foo1.destroy(); + assert.equal(disposals, 1, "destroy disposes instance") + + loader.unload(); + + assert.equal(disposals, 2, "unload disposes alive instances") +} + +exports["test disposables are GC-able"] = function(assert, done) { + let loader = Loader(module); + let { Disposable } = loader.require("sdk/core/disposable"); + + let arg1 = {} + let arg2 = 2 + let disposals = 0 + + let Foo = Class({ + extends: Disposable, + setup: function setup(a, b) { + assert.equal(a, arg1, + "arguments passed to constructur is passed to setup"); + assert.equal(b, arg2, + "second argument is also passed to a setup"); + assert.ok(this instanceof Foo, + "this is an instance in the scope of the setup method"); + + this.fooed = true + }, + dispose: function dispose() { + assert.equal(this.fooed, true, "attribute was set") + this.fooed = false + disposals = disposals + 1 + } + }) + + let foo1 = Foo(arg1, arg2) + let foo2 = Foo(arg1, arg2) + + let foo1 = null + let foo2 = null + + Cu.schedulePreciseGC(function() { + loader.unload(); + assert.equal(disposals, 0, "GC removed dispose listeners"); + done(); + }); +} + + +exports["test loader unloads do not affect other loaders"] = function(assert) { + let loader1 = Loader(module); + let loader2 = Loader(module); + let { Disposable: Disposable1 } = loader1.require("sdk/core/disposable"); + let { Disposable: Disposable2 } = loader2.require("sdk/core/disposable"); + + let arg1 = {} + let arg2 = 2 + let disposals = 0 + + let Foo1 = Class({ + extends: Disposable1, + dispose: function dispose() { + disposals = disposals + 1; + } + }); + + let Foo2 = Class({ + extends: Disposable2, + dispose: function dispose() { + disposals = disposals + 1; + } + }); + + let foo1 = Foo1(arg1, arg2); + let foo2 = Foo2(arg1, arg2); + + assert.equal(disposals, 0, "no destroy calls"); + + loader1.unload(); + + assert.equal(disposals, 1, "1 destroy calls"); + + loader2.unload(); + + assert.equal(disposals, 2, "2 destroy calls"); +} + +exports["test disposables that throw"] = function(assert) { + let loader = Loader(module); + let { Disposable } = loader.require("sdk/core/disposable"); + + let disposals = 0 + + let Foo = Class({ + extends: Disposable, + setup: function setup(a, b) { + throw Error("Boom!") + }, + dispose: function dispose() { + disposals = disposals + 1 + } + }) + + assert.throws(function() { + let foo1 = Foo() + }, /Boom/, "disposable constructors may throw"); + + loader.unload(); + + assert.equal(disposals, 0, "no disposal if constructor threw"); +} + +exports["test multiple destroy"] = function(assert) { + let loader = Loader(module); + let { Disposable } = loader.require("sdk/core/disposable"); + + let disposals = 0 + + let Foo = Class({ + extends: Disposable, + dispose: function dispose() { + disposals = disposals + 1 + } + }) + + let foo1 = Foo(); + let foo2 = Foo(); + let foo3 = Foo(); + + assert.equal(disposals, 0, "no disposals yet"); + + foo1.destroy(); + assert.equal(disposals, 1, "disposed properly"); + foo1.destroy(); + assert.equal(disposals, 1, "didn't attempt to dispose twice"); + + foo2.destroy(); + assert.equal(disposals, 2, "other instances still dispose fine"); + foo2.destroy(); + assert.equal(disposals, 2, "but not twice"); + + loader.unload(); + + assert.equal(disposals, 3, "unload only disposed the remaining instance"); +} + +require('test').run(exports); diff --git a/addon-sdk-1.16/test/test-dom.js b/addon-sdk-1.16/test/test-dom.js new file mode 100644 index 00000000..0b50a763 --- /dev/null +++ b/addon-sdk-1.16/test/test-dom.js @@ -0,0 +1,88 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const events = require("sdk/dom/events"); +const { activeBrowserWindow: { document } } = require("sdk/deprecated/window-utils"); +const window = document.window; +/* +exports["test on / emit"] = function (assert, done) { + let element = document.createElement("div"); + events.on(element, "click", function listener(event) { + assert.equal(event.target, element, "event has correct target"); + events.removeListener(element, "click", listener); + done(); + }); + + events.emit(element, "click", { + category: "MouseEvents", + settings: [ + true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null + ] + }); +}; + +exports["test remove"] = function (assert, done) { + let element = document.createElement("span"); + let l1 = 0; + let l2 = 0; + let options = { + category: "MouseEvents", + settings: [ + true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null + ] + }; + + events.on(element, "click", function listener1(event) { + l1 ++; + assert.equal(event.target, element, "event has correct target"); + events.removeListener(element, "click", listener1); + }); + + events.on(element, "click", function listener2(event) { + l2 ++; + if (l1 < l2) { + assert.equal(l1, 1, "firs listener was called and then romeved"); + events.removeListener(element, "click", listener2); + done(); + } + events.emit(element, "click", options); + }); + + events.emit(element, "click", options); +}; + +exports["test once"] = function (assert, done) { + let element = document.createElement("h1"); + let l1 = 0; + let l2 = 0; + let options = { + category: "MouseEvents", + settings: [ + true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null + ] + }; + + + events.once(element, "click", function listener(event) { + assert.equal(event.target, element, "event target is a correct element"); + l1 ++; + }); + + events.on(element, "click", function listener(event) { + l2 ++; + if (l2 > 3) { + events.removeListener(element, "click", listener); + assert.equal(event.target, element, "event has correct target"); + assert.equal(l1, 1, "once was called only once"); + done(); + } + events.emit(element, "click", options); + }); + + events.emit(element, "click", options); +} +*/ +require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-environment.js b/addon-sdk-1.16/test/test-environment.js new file mode 100644 index 00000000..8475d244 --- /dev/null +++ b/addon-sdk-1.16/test/test-environment.js @@ -0,0 +1,50 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +'use strict'; + +const { env } = require('sdk/system/environment'); +const { Cc, Ci } = require('chrome'); +const { get, set, exists } = Cc['@mozilla.org/process/environment;1']. + getService(Ci.nsIEnvironment); + +exports['test exists'] = function(assert) { + assert.equal('PATH' in env, exists('PATH'), + 'PATH environment variable is defined'); + assert.equal('FOO1' in env, exists('FOO1'), + 'FOO1 environment variable is not defined'); + set('FOO1', 'foo'); + assert.equal('FOO1' in env, true, + 'FOO1 environment variable was set'); + set('FOO1', null); + assert.equal('FOO1' in env, false, + 'FOO1 environment variable was unset'); +}; + +exports['test get'] = function(assert) { + assert.equal(env.PATH, get('PATH'), 'PATH env variable matches'); + assert.equal(env.BAR2, undefined, 'BAR2 env variable is not defined'); + set('BAR2', 'bar'); + assert.equal(env.BAR2, 'bar', 'BAR2 env variable was set'); + set('BAR2', null); + assert.equal(env.BAR2, undefined, 'BAR2 env variable was unset'); +}; + +exports['test set'] = function(assert) { + assert.equal(get('BAZ3'), '', 'BAZ3 env variable is not set'); + assert.equal(env.BAZ3, undefined, 'BAZ3 is not set'); + env.BAZ3 = 'baz'; + assert.equal(env.BAZ3, get('BAZ3'), 'BAZ3 env variable is set'); + assert.equal(get('BAZ3'), 'baz', 'BAZ3 env variable was set to "baz"'); +}; + +exports['test unset'] = function(assert) { + env.BLA4 = 'bla'; + assert.equal(env.BLA4, 'bla', 'BLA4 env variable is set'); + assert.equal(delete env.BLA4, true, 'BLA4 env variable is removed'); + assert.equal(env.BLA4, undefined, 'BLA4 env variable is unset'); + assert.equal('BLA4' in env, false, 'BLA4 env variable no longer exists' ); +}; + +require('test').run(exports); diff --git a/addon-sdk-1.16/test/test-errors.js b/addon-sdk-1.16/test/test-errors.js new file mode 100644 index 00000000..7ebc888c --- /dev/null +++ b/addon-sdk-1.16/test/test-errors.js @@ -0,0 +1,72 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const errors = require("sdk/deprecated/errors"); + +exports.testCatchAndLog = function(assert) { + var caught = []; + function dummyLog(e) { caught.push(e); } + + var wrapped = errors.catchAndLog(function(x) { + throw Error("blah" + x + this); + }, "boop", dummyLog); + + assert.equal(wrapped.call("hi", 1), "boop", + "exceptions should be trapped, def. resp. returned"); + assert.equal(caught.length, 1, + "logging function should be called"); + assert.equal(caught[0].message, "blah1hi", + "args and this should be passed to wrapped func"); +}; + +exports.testCatchAndLogProps = function(assert) { + var caught = []; + function dummyLog(e) { caught.push(e); } + + var thing = { + foo: function(x) { throw Error("nowai" + x); }, + bar: function() { throw Error("blah"); }, + baz: function() { throw Error("fnarg"); } + }; + + errors.catchAndLogProps(thing, "foo", "ugh", dummyLog); + + assert.equal(thing.foo(1), "ugh", + "props should be wrapped"); + assert.equal(caught.length, 1, + "logging function should be called"); + assert.equal(caught[0].message, "nowai1", + "args should be passed to wrapped func"); + assert.throws(function() { thing.bar(); }, + /blah/, + "non-wrapped props should be wrapped"); + + errors.catchAndLogProps(thing, ["bar", "baz"], "err", dummyLog); + assert.ok((thing.bar() == thing.baz()) && + (thing.bar() == "err"), + "multiple props should be wrapped if array passed in"); +}; + +exports.testCatchAndReturn = function(assert) { + var wrapped = errors.catchAndReturn(function(x) { + if (x == 1) + return "one"; + if (x == 2) + throw new Error("two"); + return this + x; + }); + + assert.equal(wrapped(1).returnValue, "one", + "arg should be passed; return value should be returned"); + assert.ok(wrapped(2).exception, "exception should be returned"); + assert.equal(wrapped(2).exception.message, "two", "message is correct"); + assert.ok(wrapped(2).exception.fileName.indexOf("test-errors.js") != -1, + "filename is present"); + assert.ok(wrapped(2).exception.stack, "stack is available"); + assert.equal(wrapped.call("hi", 3).returnValue, "hi3", + "`this` should be set correctly"); +}; + +require("sdk/test").run(exports); diff --git a/addon-sdk-1.16/test/test-event-core.js b/addon-sdk-1.16/test/test-event-core.js new file mode 100644 index 00000000..ecee12bd --- /dev/null +++ b/addon-sdk-1.16/test/test-event-core.js @@ -0,0 +1,245 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +'use strict'; + +const { on, once, off, emit, count } = require('sdk/event/core'); +const { LoaderWithHookedConsole } = require("sdk/test/loader"); + +exports['test add a listener'] = function(assert) { + let events = [ { name: 'event#1' }, 'event#2' ]; + let target = { name: 'target' }; + + on(target, 'message', function(message) { + assert.equal(this, target, 'this is a target object'); + assert.equal(message, events.shift(), 'message is emitted event'); + }); + emit(target, 'message', events[0]); + emit(target, 'message', events[0]); +}; + +exports['test that listener is unique per type'] = function(assert) { + let actual = [] + let target = {} + function listener() { actual.push(1) } + on(target, 'message', listener); + on(target, 'message', listener); + on(target, 'message', listener); + on(target, 'foo', listener); + on(target, 'foo', listener); + + emit(target, 'message'); + assert.deepEqual([ 1 ], actual, 'only one message listener added'); + emit(target, 'foo'); + assert.deepEqual([ 1, 1 ], actual, 'same listener added for other event'); +}; + +exports['test event type matters'] = function(assert) { + let target = { name: 'target' } + on(target, 'message', function() { + assert.fail('no event is expected'); + }); + on(target, 'done', function() { + assert.pass('event is emitted'); + }); + emit(target, 'foo') + emit(target, 'done'); +}; + +exports['test all arguments are pasesd'] = function(assert) { + let foo = { name: 'foo' }, bar = 'bar'; + let target = { name: 'target' }; + on(target, 'message', function(a, b) { + assert.equal(a, foo, 'first argument passed'); + assert.equal(b, bar, 'second argument passed'); + }); + emit(target, 'message', foo, bar); +}; + +exports['test no side-effects in emit'] = function(assert) { + let target = { name: 'target' }; + on(target, 'message', function() { + assert.pass('first listener is called'); + on(target, 'message', function() { + assert.fail('second listener is called'); + }); + }); + emit(target, 'message'); +}; + +exports['test can remove next listener'] = function(assert) { + let target = { name: 'target' }; + function fail() assert.fail('Listener should be removed'); + + on(target, 'data', function() { + assert.pass('first litener called'); + off(target, 'data', fail); + }); + on(target, 'data', fail); + + emit(target, 'data', 'hello'); +}; + +exports['test order of propagation'] = function(assert) { + let actual = []; + let target = { name: 'target' }; + on(target, 'message', function() { actual.push(1); }); + on(target, 'message', function() { actual.push(2); }); + on(target, 'message', function() { actual.push(3); }); + emit(target, 'message'); + assert.deepEqual([ 1, 2, 3 ], actual, 'called in order they were added'); +}; + +exports['test remove a listener'] = function(assert) { + let target = { name: 'target' }; + let actual = []; + on(target, 'message', function listener() { + actual.push(1); + on(target, 'message', function() { + off(target, 'message', listener); + actual.push(2); + }) + }); + + emit(target, 'message'); + assert.deepEqual([ 1 ], actual, 'first listener called'); + emit(target, 'message'); + assert.deepEqual([ 1, 1, 2 ], actual, 'second listener called'); + + emit(target, 'message'); + assert.deepEqual([ 1, 1, 2, 2, 2 ], actual, 'first listener removed'); +}; + +exports['test remove all listeners for type'] = function(assert) { + let actual = []; + let target = { name: 'target' } + on(target, 'message', function() { actual.push(1); }); + on(target, 'message', function() { actual.push(2); }); + on(target, 'message', function() { actual.push(3); }); + on(target, 'bar', function() { actual.push('b') }); + off(target, 'message'); + + emit(target, 'message'); + emit(target, 'bar'); + + assert.deepEqual([ 'b' ], actual, 'all message listeners were removed'); +}; + +exports['test remove all listeners'] = function(assert) { + let actual = []; + let target = { name: 'target' } + on(target, 'message', function() { actual.push(1); }); + on(target, 'message', function() { actual.push(2); }); + on(target, 'message', function() { actual.push(3); }); + on(target, 'bar', function() { actual.push('b') }); + off(target); + + emit(target, 'message'); + emit(target, 'bar'); + + assert.deepEqual([], actual, 'all listeners events were removed'); +}; + +exports['test falsy arguments are fine'] = function(assert) { + let type, listener, actual = []; + let target = { name: 'target' } + on(target, 'bar', function() { actual.push(0) }); + + off(target, 'bar', listener); + emit(target, 'bar'); + assert.deepEqual([ 0 ], actual, '3rd bad ard will keep listeners'); + + off(target, type); + emit(target, 'bar'); + assert.deepEqual([ 0, 0 ], actual, '2nd bad arg will keep listener'); + + off(target, type, listener); + emit(target, 'bar'); + assert.deepEqual([ 0, 0, 0 ], actual, '2nd&3rd bad args will keep listener'); +}; + +exports['test error handling'] = function(assert) { + let target = Object.create(null); + let error = Error('boom!'); + + on(target, 'message', function() { throw error; }) + on(target, 'error', function(boom) { + assert.equal(boom, error, 'thrown exception causes error event'); + }); + emit(target, 'message'); +}; + +exports['test unhandled exceptions'] = function(assert) { + let exceptions = []; + let { loader, messages } = LoaderWithHookedConsole(module); + + let { emit, on } = loader.require('sdk/event/core'); + let target = {}; + let boom = Error('Boom!'); + let drax = Error('Draax!!'); + + on(target, 'message', function() { throw boom; }); + + emit(target, 'message'); + assert.equal(messages.length, 1, 'Got the first exception'); + assert.equal(messages[0].type, 'exception', 'The console message is exception'); + assert.ok(~String(messages[0].msg).indexOf('Boom!'), + 'unhandled exception is logged'); + + on(target, 'error', function() { throw drax; }); + emit(target, 'message'); + assert.equal(messages.length, 2, 'Got the second exception'); + assert.equal(messages[1].type, 'exception', 'The console message is exception'); + assert.ok(~String(messages[1].msg).indexOf('Draax!'), + 'error in error handler is logged'); +}; + +exports['test unhandled errors'] = function(assert) { + let exceptions = []; + let { loader, messages } = LoaderWithHookedConsole(module); + + let { emit, on } = loader.require('sdk/event/core'); + let target = {}; + let boom = Error('Boom!'); + + emit(target, 'error', boom); + assert.equal(messages.length, 1, 'Error was logged'); + assert.equal(messages[0].type, 'exception', 'The console message is exception'); + assert.ok(~String(messages[0].msg).indexOf('Boom!'), + 'unhandled exception is logged'); +}; + + +exports['test count'] = function(assert) { + let target = {}; + + assert.equal(count(target, 'foo'), 0, 'no listeners for "foo" events'); + on(target, 'foo', function() {}); + assert.equal(count(target, 'foo'), 1, 'listener registered'); + on(target, 'foo', function() {}, 2, 'another listener registered'); + off(target) + assert.equal(count(target, 'foo'), 0, 'listeners unregistered'); +}; + +exports['test listen to all events'] = function(assert) { + let actual = []; + let target = {}; + + on(target, 'foo', message => actual.push(message)); + on(target, '*', (type, ...message) => { + actual.push([type].concat(message)); + }); + + emit(target, 'foo', 'hello'); + assert.equal(actual[0], 'hello', + 'non-wildcard listeners still work'); + assert.deepEqual(actual[1], ['foo', 'hello'], + 'wildcard listener called'); + + emit(target, 'bar', 'goodbye'); + assert.deepEqual(actual[2], ['bar', 'goodbye'], + 'wildcard listener called for unbound event name'); +}; + +require('test').run(exports); diff --git a/addon-sdk-1.16/test/test-event-target.js b/addon-sdk-1.16/test/test-event-target.js new file mode 100644 index 00000000..972906ea --- /dev/null +++ b/addon-sdk-1.16/test/test-event-target.js @@ -0,0 +1,205 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +'use strict'; + +const { emit } = require('sdk/event/core'); +const { EventTarget } = require('sdk/event/target'); +const { Loader } = require('sdk/test/loader'); + +exports['test add a listener'] = function(assert) { + let events = [ { name: 'event#1' }, 'event#2' ]; + let target = EventTarget(); + + target.on('message', function(message) { + assert.equal(this, target, 'this is a target object'); + assert.equal(message, events.shift(), 'message is emitted event'); + }); + + emit(target, 'message', events[0]); + emit(target, 'message', events[0]); +}; + +exports['test pass in listeners'] = function(assert) { + let actual = [ ]; + let target = EventTarget({ + onMessage: function onMessage(message) { + assert.equal(this, target, 'this is an event target'); + actual.push(1); + }, + onFoo: null, + onbla: function() { + assert.fail('`onbla` is not supposed to be called'); + } + }); + target.on('message', function(message) { + assert.equal(this, target, 'this is an event target'); + actual.push(2); + }); + + emit(target, 'message'); + emit(target, 'missing'); + + assert.deepEqual([ 1, 2 ], actual, 'all listeners trigerred in right order'); +}; + +exports['test that listener is unique per type'] = function(assert) { + let actual = [] + let target = EventTarget(); + function listener() { actual.push(1) } + target.on('message', listener); + target.on('message', listener); + target.on('message', listener); + target.on('foo', listener); + target.on('foo', listener); + + emit(target, 'message'); + assert.deepEqual([ 1 ], actual, 'only one message listener added'); + emit(target, 'foo'); + assert.deepEqual([ 1, 1 ], actual, 'same listener added for other event'); +}; + +exports['test event type matters'] = function(assert) { + let target = EventTarget(); + target.on('message', function() { + assert.fail('no event is expected'); + }); + target.on('done', function() { + assert.pass('event is emitted'); + }); + + emit(target, 'foo'); + emit(target, 'done'); +}; + +exports['test all arguments are pasesd'] = function(assert) { + let foo = { name: 'foo' }, bar = 'bar'; + let target = EventTarget(); + target.on('message', function(a, b) { + assert.equal(a, foo, 'first argument passed'); + assert.equal(b, bar, 'second argument passed'); + }); + emit(target, 'message', foo, bar); +}; + +exports['test no side-effects in emit'] = function(assert) { + let target = EventTarget(); + target.on('message', function() { + assert.pass('first listener is called'); + target.on('message', function() { + assert.fail('second listener is called'); + }); + }); + emit(target, 'message'); +}; + +exports['test order of propagation'] = function(assert) { + let actual = []; + let target = EventTarget(); + target.on('message', function() { actual.push(1); }); + target.on('message', function() { actual.push(2); }); + target.on('message', function() { actual.push(3); }); + emit(target, 'message'); + assert.deepEqual([ 1, 2, 3 ], actual, 'called in order they were added'); +}; + +exports['test remove a listener'] = function(assert) { + let target = EventTarget(); + let actual = []; + target.on('message', function listener() { + actual.push(1); + target.on('message', function() { + target.removeListener('message', listener); + actual.push(2); + }) + }); + + target.off('message'); // must do nothing. + emit(target, 'message'); + assert.deepEqual([ 1 ], actual, 'first listener called'); + emit(target, 'message'); + assert.deepEqual([ 1, 1, 2 ], actual, 'second listener called'); + emit(target, 'message'); + assert.deepEqual([ 1, 1, 2, 2, 2 ], actual, 'first listener removed'); +}; + +exports['test error handling'] = function(assert) { + let target = EventTarget(); + let error = Error('boom!'); + + target.on('message', function() { throw error; }) + target.on('error', function(boom) { + assert.equal(boom, error, 'thrown exception causes error event'); + }); + emit(target, 'message'); +}; + +exports['test unhandled errors'] = function(assert) { + let exceptions = []; + let loader = Loader(module); + let { emit } = loader.require('sdk/event/core'); + let { EventTarget } = loader.require('sdk/event/target'); + Object.defineProperties(loader.sandbox('sdk/event/core'), { + console: { value: { + exception: function(e) { + exceptions.push(e); + } + }} + }); + let target = EventTarget(); + let boom = Error('Boom!'); + let drax = Error('Draax!!'); + + target.on('message', function() { throw boom; }); + + emit(target, 'message'); + assert.ok(~String(exceptions[0]).indexOf('Boom!'), + 'unhandled exception is logged'); + + target.on('error', function() { throw drax; }); + emit(target, 'message'); + assert.ok(~String(exceptions[1]).indexOf('Draax!'), + 'error in error handler is logged'); +}; + +exports['test target is chainable'] = function (assert, done) { + let loader = Loader(module); + let exceptions = []; + let { EventTarget } = loader.require('sdk/event/target'); + let { emit } = loader.require('sdk/event/core'); + Object.defineProperties(loader.sandbox('sdk/event/core'), { + console: { value: { + exception: function(e) { + exceptions.push(e); + } + }} + }); + + let emitter = EventTarget(); + let boom = Error('Boom'); + let onceCalled = 0; + + emitter.once('oneTime', function () { + assert.equal(++onceCalled, 1, 'once event called only once'); + }).on('data', function (message) { + assert.equal(message, 'message', 'handles event'); + emit(emitter, 'oneTime'); + emit(emitter, 'data2', 'message2'); + }).on('phony', function () { + assert.fail('removeListener does not remove chained event'); + }).removeListener('phony') + .on('data2', function (message) { + assert.equal(message, 'message2', 'handle chained event'); + emit(emitter, 'oneTime'); + throw boom; + }).on('error', function (error) { + assert.equal(error, boom, 'error handled in chained event'); + done(); + }); + + emit(emitter, 'data', 'message'); +}; + +require('test').run(exports); + diff --git a/addon-sdk-1.16/test/test-event-utils.js b/addon-sdk-1.16/test/test-event-utils.js new file mode 100644 index 00000000..649c3a3b --- /dev/null +++ b/addon-sdk-1.16/test/test-event-utils.js @@ -0,0 +1,282 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +'use strict'; + +const { on, emit } = require("sdk/event/core"); +const { filter, map, merge, expand, pipe, stripListeners } = require("sdk/event/utils"); +const $ = require("./event/helpers"); + +function isEven(x) !(x % 2) +function inc(x) x + 1 + +exports["test filter events"] = function(assert) { + let input = {}; + let evens = filter(input, isEven); + let actual = []; + on(evens, "data", function(e) actual.push(e)); + + [1, 2, 3, 4, 5, 6, 7].forEach(function(x) emit(input, "data", x)); + + assert.deepEqual(actual, [2, 4, 6], "only even numbers passed through"); +}; + +exports["test filter emits"] = $.emits(function(input, assert) { + let output = filter(input, isEven); + assert(output, [1, 2, 3, 4, 5], [2, 4], "this is `output` & evens passed"); +});; + +exports["test filter reg once"] = $.registerOnce(function(input, assert) { + assert(filter(input, isEven), [1, 2, 3, 4, 5, 6], [2, 4, 6], + "listener can be registered only once"); +}); + +exports["test filter ignores new"] = $.ignoreNew(function(input, assert) { + assert(filter(input, isEven), [1, 2, 3], [2], + "new listener is ignored") +}); + +exports["test filter is FIFO"] = $.FIFO(function(input, assert) { + assert(filter(input, isEven), [1, 2, 3, 4], [2, 4], + "listeners are invoked in fifo order") +}); + +exports["test map events"] = function(assert) { + let input = {}; + let incs = map(input, inc); + let actual = []; + on(incs, "data", function(e) actual.push(e)); + + [1, 2, 3, 4].forEach(function(x) emit(input, "data", x)); + + assert.deepEqual(actual, [2, 3, 4, 5], "all numbers were incremented"); +}; + +exports["test map emits"] = $.emits(function(input, assert) { + let output = map(input, inc); + assert(output, [1, 2, 3], [2, 3, 4], "this is `output` & evens passed"); +});; + +exports["test map reg once"] = $.registerOnce(function(input, assert) { + assert(map(input, inc), [1, 2, 3], [2, 3, 4], + "listener can be registered only once"); +}); + +exports["test map ignores new"] = $.ignoreNew(function(input, assert) { + assert(map(input, inc), [1], [2], + "new listener is ignored") +}); + +exports["test map is FIFO"] = $.FIFO(function(input, assert) { + assert(map(input, inc), [1, 2, 3, 4], [2, 3, 4, 5], + "listeners are invoked in fifo order") +}); + +exports["test merge stream[stream]"] = function(assert) { + let a = {}, b = {}, c = {}; + let inputs = {}; + let actual = []; + + on(merge(inputs), "data", function($) actual.push($)) + + emit(inputs, "data", a); + emit(a, "data", "a1"); + emit(inputs, "data", b); + emit(b, "data", "b1"); + emit(a, "data", "a2"); + emit(inputs, "data", c); + emit(c, "data", "c1"); + emit(c, "data", "c2"); + emit(b, "data", "b2"); + emit(a, "data", "a3"); + + assert.deepEqual(actual, ["a1", "b1", "a2", "c1", "c2", "b2", "a3"], + "all inputs data merged into one"); +}; + +exports["test merge array[stream]"] = function(assert) { + let a = {}, b = {}, c = {}; + let inputs = {}; + let actual = []; + + on(merge([a, b, c]), "data", function($) actual.push($)) + + emit(a, "data", "a1"); + emit(b, "data", "b1"); + emit(a, "data", "a2"); + emit(c, "data", "c1"); + emit(c, "data", "c2"); + emit(b, "data", "b2"); + emit(a, "data", "a3"); + + assert.deepEqual(actual, ["a1", "b1", "a2", "c1", "c2", "b2", "a3"], + "all inputs data merged into one"); +}; + +exports["test merge emits"] = $.emits(function(input, assert) { + let evens = filter(input, isEven) + let output = merge([evens, input]); + assert(output, [1, 2, 3], [1, 2, 2, 3], "this is `output` & evens passed"); +}); + + +exports["test merge reg once"] = $.registerOnce(function(input, assert) { + let evens = filter(input, isEven) + let output = merge([input, evens]); + assert(output, [1, 2, 3, 4], [1, 2, 2, 3, 4, 4], + "listener can be registered only once"); +}); + +exports["test merge ignores new"] = $.ignoreNew(function(input, assert) { + let evens = filter(input, isEven) + let output = merge([input, evens]) + assert(output, [1], [1], + "new listener is ignored") +}); + +exports["test marge is FIFO"] = $.FIFO(function(input, assert) { + let evens = filter(input, isEven) + let output = merge([input, evens]) + + assert(output, [1, 2, 3, 4], [1, 2, 2, 3, 4, 4], + "listeners are invoked in fifo order") +}); + +exports["test expand"] = function(assert) { + let a = {}, b = {}, c = {}; + let inputs = {}; + let actual = []; + + on(expand(inputs, function($) $()), "data", function($) actual.push($)) + + emit(inputs, "data", function() a); + emit(a, "data", "a1"); + emit(inputs, "data", function() b); + emit(b, "data", "b1"); + emit(a, "data", "a2"); + emit(inputs, "data", function() c); + emit(c, "data", "c1"); + emit(c, "data", "c2"); + emit(b, "data", "b2"); + emit(a, "data", "a3"); + + assert.deepEqual(actual, ["a1", "b1", "a2", "c1", "c2", "b2", "a3"], + "all inputs data merged into one"); +}; + +exports["test pipe"] = function (assert, done) { + let src = {}; + let dest = {}; + + let aneventCount = 0, multiargsCount = 0; + let wildcardCount = {}; + + on(dest, 'an-event', arg => { + assert.equal(arg, 'my-arg', 'piped argument to event'); + ++aneventCount; + check(); + }); + on(dest, 'multiargs', (...data) => { + assert.equal(data[0], 'a', 'multiple arguments passed via pipe'); + assert.equal(data[1], 'b', 'multiple arguments passed via pipe'); + assert.equal(data[2], 'c', 'multiple arguments passed via pipe'); + ++multiargsCount; + check(); + }); + + on(dest, '*', (name, ...data) => { + wildcardCount[name] = (wildcardCount[name] || 0) + 1; + if (name === 'multiargs') { + assert.equal(data[0], 'a', 'multiple arguments passed via pipe, wildcard'); + assert.equal(data[1], 'b', 'multiple arguments passed via pipe, wildcard'); + assert.equal(data[2], 'c', 'multiple arguments passed via pipe, wildcard'); + } + if (name === 'an-event') + assert.equal(data[0], 'my-arg', 'argument passed via pipe, wildcard'); + check(); + }); + + pipe(src, dest); + + for (let i = 0; i < 3; i++) + emit(src, 'an-event', 'my-arg'); + + emit(src, 'multiargs', 'a', 'b', 'c'); + + function check () { + if (aneventCount === 3 && multiargsCount === 1 && + wildcardCount['an-event'] === 3 && + wildcardCount['multiargs'] === 1) + done(); + } +}; + +exports["test pipe multiple targets"] = function (assert) { + let src1 = {}; + let src2 = {}; + let middle = {}; + let dest = {}; + + pipe(src1, middle); + pipe(src2, middle); + pipe(middle, dest); + + let middleFired = 0; + let destFired = 0; + let src1Fired = 0; + let src2Fired = 0; + + on(src1, '*', () => src1Fired++); + on(src2, '*', () => src2Fired++); + on(middle, '*', () => middleFired++); + on(dest, '*', () => destFired++); + + emit(src1, 'ev'); + assert.equal(src1Fired, 1, 'event triggers in source in pipe chain'); + assert.equal(middleFired, 1, 'event passes through the middle of pipe chain'); + assert.equal(destFired, 1, 'event propagates to end of pipe chain'); + assert.equal(src2Fired, 0, 'event does not fire on alternative chain routes'); + + emit(src2, 'ev'); + assert.equal(src2Fired, 1, 'event triggers in source in pipe chain'); + assert.equal(middleFired, 2, + 'event passes through the middle of pipe chain from different src'); + assert.equal(destFired, 2, + 'event propagates to end of pipe chain from different src'); + assert.equal(src1Fired, 1, 'event does not fire on alternative chain routes'); + + emit(middle, 'ev'); + assert.equal(middleFired, 3, + 'event triggers in source of pipe chain'); + assert.equal(destFired, 3, + 'event propagates to end of pipe chain from middle src'); + assert.equal(src1Fired, 1, 'event does not fire on alternative chain routes'); + assert.equal(src2Fired, 1, 'event does not fire on alternative chain routes'); +}; + +exports['test stripListeners'] = function (assert) { + var options = { + onAnEvent: noop1, + onMessage: noop2, + verb: noop1, + value: 100 + }; + + var stripped = stripListeners(options); + assert.ok(stripped !== options, 'stripListeners should return a new object'); + assert.equal(options.onAnEvent, noop1, 'stripListeners does not affect original'); + assert.equal(options.onMessage, noop2, 'stripListeners does not affect original'); + assert.equal(options.verb, noop1, 'stripListeners does not affect original'); + assert.equal(options.value, 100, 'stripListeners does not affect original'); + + assert.ok(!stripped.onAnEvent, 'stripListeners removes `on*` values'); + assert.ok(!stripped.onMessage, 'stripListeners removes `on*` values'); + assert.equal(stripped.verb, noop1, 'stripListeners leaves not `on*` values'); + assert.equal(stripped.value, 100, 'stripListeners leaves not `on*` values'); + + function noop1 () {} + function noop2 () {} +}; + +require('test').run(exports); diff --git a/addon-sdk-1.16/test/test-events.js b/addon-sdk-1.16/test/test-events.js new file mode 100644 index 00000000..99786adf --- /dev/null +++ b/addon-sdk-1.16/test/test-events.js @@ -0,0 +1,281 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +'use strict'; + +const { LoaderWithHookedConsole } = require("sdk/test/loader"); + +// Exposing private methods as public in order to test +const EventEmitter = require('sdk/deprecated/events').EventEmitter.compose({ + listeners: function(type) this._listeners(type), + emit: function() this._emit.apply(this, arguments), + emitOnObject: function() this._emitOnObject.apply(this, arguments), + removeAllListeners: function(type) this._removeAllListeners(type) +}); + +exports['test:add listeners'] = function(assert) { + let e = new EventEmitter(); + + let events_new_listener_emited = []; + let times_hello_emited = 0; + + e.on("newListener", function (event, listener) { + events_new_listener_emited.push(event) + }) + + e.on("hello", function (a, b) { + times_hello_emited += 1 + assert.equal("a", a) + assert.equal("b", b) + assert.equal(this, e, '`this` pseudo-variable is bound to instance'); + }) + + e.emit("hello", "a", "b") +}; + +exports['test:removeListener'] = function(assert) { + let count = 0; + + function listener1 () { + count++; + } + function listener2 () { + count++; + } + + // test adding and removing listener + let e1 = new EventEmitter(); + assert.equal(0, e1.listeners('hello').length); + e1.on("hello", listener1); + assert.equal(1, e1.listeners('hello').length); + assert.equal(listener1, e1.listeners('hello')[0]); + e1.removeListener("hello", listener1); + assert.equal(0, e1.listeners('hello').length); + e1.emit("hello", ""); + assert.equal(0, count); + + // test adding one listener and removing another which was not added + let e2 = new EventEmitter(); + assert.equal(0, e2.listeners('hello').length); + e2.on("hello", listener1); + assert.equal(1, e2.listeners('hello').length); + e2.removeListener("hello", listener2); + assert.equal(1, e2.listeners('hello').length); + assert.equal(listener1, e2.listeners('hello')[0]); + e2.emit("hello", ""); + assert.equal(1, count); + + // test adding 2 listeners, and removing one + let e3 = new EventEmitter(); + assert.equal(0, e3.listeners('hello').length); + e3.on("hello", listener1); + assert.equal(1, e3.listeners('hello').length); + e3.on("hello", listener2); + assert.equal(2, e3.listeners('hello').length); + e3.removeListener("hello", listener1); + assert.equal(1, e3.listeners('hello').length); + assert.equal(listener2, e3.listeners('hello')[0]); + e3.emit("hello", ""); + assert.equal(2, count); +}; + +exports['test:removeAllListeners'] = function(assert) { + let count = 0; + + function listener1 () { + count++; + } + function listener2 () { + count++; + } + + // test adding a listener and removing all of that type + let e1 = new EventEmitter(); + e1.on("hello", listener1); + assert.equal(1, e1.listeners('hello').length); + e1.removeAllListeners("hello"); + assert.equal(0, e1.listeners('hello').length); + e1.emit("hello", ""); + assert.equal(0, count); + + // test adding a listener and removing all of another type + let e2 = new EventEmitter(); + e2.on("hello", listener1); + assert.equal(1, e2.listeners('hello').length); + e2.removeAllListeners('goodbye'); + assert.equal(1, e2.listeners('hello').length); + e2.emit("hello", ""); + assert.equal(1, count); + + // test adding 1+ listeners and removing all of that type + let e3 = new EventEmitter(); + e3.on("hello", listener1); + assert.equal(1, e3.listeners('hello').length); + e3.on("hello", listener2); + assert.equal(2, e3.listeners('hello').length); + e3.removeAllListeners("hello"); + assert.equal(0, e3.listeners('hello').length); + e3.emit("hello", ""); + assert.equal(1, count); + + // test adding 2 listeners for 2 types and removing all listeners + let e4 = new EventEmitter(); + e4.on("hello", listener1); + assert.equal(1, e4.listeners('hello').length); + e4.on('goodbye', listener2); + assert.equal(1, e4.listeners('goodbye').length); + e4.emit("goodbye", ""); + e4.removeAllListeners(); + assert.equal(0, e4.listeners('hello').length); + assert.equal(0, e4.listeners('goodbye').length); + e4.emit("hello", ""); + e4.emit("goodbye", ""); + assert.equal(2, count); +}; + +exports['test: modify in emit'] = function(assert) { + let callbacks_called = [ ]; + let e = new EventEmitter(); + + function callback1() { + callbacks_called.push("callback1"); + e.on("foo", callback2); + e.on("foo", callback3); + e.removeListener("foo", callback1); + } + function callback2() { + callbacks_called.push("callback2"); + e.removeListener("foo", callback2); + } + function callback3() { + callbacks_called.push("callback3"); + e.removeListener("foo", callback3); + } + + e.on("foo", callback1); + assert.equal(1, e.listeners("foo").length); + + e.emit("foo"); + assert.equal(2, e.listeners("foo").length); + assert.equal(1, callbacks_called.length); + assert.equal('callback1', callbacks_called[0]); + + e.emit("foo"); + assert.equal(0, e.listeners("foo").length); + assert.equal(3, callbacks_called.length); + assert.equal('callback1', callbacks_called[0]); + assert.equal('callback2', callbacks_called[1]); + assert.equal('callback3', callbacks_called[2]); + + e.emit("foo"); + assert.equal(0, e.listeners("foo").length); + assert.equal(3, callbacks_called.length); + assert.equal('callback1', callbacks_called[0]); + assert.equal('callback2', callbacks_called[1]); + assert.equal('callback3', callbacks_called[2]); + + e.on("foo", callback1); + e.on("foo", callback2); + assert.equal(2, e.listeners("foo").length); + e.removeAllListeners("foo"); + assert.equal(0, e.listeners("foo").length); + + // Verify that removing callbacks while in emit allows emits to propagate to + // all listeners + callbacks_called = [ ]; + + e.on("foo", callback2); + e.on("foo", callback3); + assert.equal(2, e.listeners("foo").length); + e.emit("foo"); + assert.equal(2, callbacks_called.length); + assert.equal('callback2', callbacks_called[0]); + assert.equal('callback3', callbacks_called[1]); + assert.equal(0, e.listeners("foo").length); +}; + +exports['test:adding same listener'] = function(assert) { + function foo() {} + let e = new EventEmitter(); + e.on("foo", foo); + e.on("foo", foo); + assert.equal( + 1, + e.listeners("foo").length, + "listener reregistration is ignored" + ); +} + +exports['test:errors are reported if listener throws'] = function(assert) { + let e = new EventEmitter(), + reported = false; + e.on('error', function(e) reported = true) + e.on('boom', function() { throw new Error('Boom!') }); + e.emit('boom', 3); + assert.ok(reported, 'error should be reported through event'); +}; + +exports['test:emitOnObject'] = function(assert) { + let e = new EventEmitter(); + + e.on("foo", function() { + assert.equal(this, e, "`this` should be emitter"); + }); + e.emitOnObject(e, "foo"); + + e.on("bar", function() { + assert.equal(this, obj, "`this` should be other object"); + }); + let obj = {}; + e.emitOnObject(obj, "bar"); +}; + +exports['test:once'] = function(assert) { + let e = new EventEmitter(); + let called = false; + + e.once('foo', function(value) { + assert.ok(!called, "listener called only once"); + assert.equal(value, "bar", "correct argument was passed"); + }); + + e.emit('foo', 'bar'); + e.emit('foo', 'baz'); +}; + +exports["test:removing once"] = function(assert) { + let e = require("sdk/deprecated/events").EventEmitterTrait.create(); + e.once("foo", function() { assert.pass("listener was called"); }); + e.once("error", function() { assert.fail("error event was emitted"); }); + e._emit("foo", "bug-656684"); +}; + +// Bug 726967: Ensure that `emit` doesn't do an infinite loop when `error` +// listener throws an exception +exports['test:emitLoop'] = function(assert) { + // Override the console for this test so it doesn't log the exception to the + // test output + let { loader } = LoaderWithHookedConsole(module); + + let EventEmitter = loader.require('sdk/deprecated/events').EventEmitter.compose({ + listeners: function(type) this._listeners(type), + emit: function() this._emit.apply(this, arguments), + emitOnObject: function() this._emitOnObject.apply(this, arguments), + removeAllListeners: function(type) this._removeAllListeners(type) + }); + + let e = new EventEmitter(); + + e.on("foo", function() { + throw new Error("foo"); + }); + + e.on("error", function() { + throw new Error("error"); + }); + e.emit("foo"); + + assert.pass("emit didn't looped"); +}; + +require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-file.js b/addon-sdk-1.16/test/test-file.js new file mode 100644 index 00000000..9dc6c3b4 --- /dev/null +++ b/addon-sdk-1.16/test/test-file.js @@ -0,0 +1,275 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const { pathFor } = require('sdk/system'); +const file = require("sdk/io/file"); +const url = require("sdk/url"); + +const byteStreams = require("sdk/io/byte-streams"); +const textStreams = require("sdk/io/text-streams"); + +const ERRORS = { + FILE_NOT_FOUND: /^path does not exist: .+$/, + NOT_A_DIRECTORY: /^path is not a directory: .+$/, + NOT_A_FILE: /^path is not a file: .+$/, +}; + +// Use profile directory to list / read / write files. +const profilePath = pathFor('ProfD'); +const fileNameInProfile = 'compatibility.ini'; +const dirNameInProfile = 'extensions'; +const filePathInProfile = file.join(profilePath, fileNameInProfile); +const dirPathInProfile = file.join(profilePath, dirNameInProfile); + +exports.testDirName = function(assert) { + assert.equal(file.dirname(dirPathInProfile), profilePath, + "file.dirname() of dir should return parent dir"); + + assert.equal(file.dirname(filePathInProfile), profilePath, + "file.dirname() of file should return its dir"); + + let dir = profilePath; + while (dir) + dir = file.dirname(dir); + + assert.equal(dir, "", + "dirname should return empty string when dir has no parent"); +}; + +exports.testBasename = function(assert) { + // Get the top-most path -- the path with no basename. E.g., on Unix-like + // systems this will be /. We'll use it below to build up some test paths. + // We have to go to this trouble because file.join() needs a legal path as a + // base case; join("foo", "bar") doesn't work unfortunately. + let topPath = profilePath; + let parentPath = file.dirname(topPath); + while (parentPath) { + topPath = parentPath; + parentPath = file.dirname(topPath); + } + + let path = topPath; + assert.equal(file.basename(path), "", + "basename should work on paths with no components"); + + path = file.join(path, "foo"); + assert.equal(file.basename(path), "foo", + "basename should work on paths with a single component"); + + path = file.join(path, "bar"); + assert.equal(file.basename(path), "bar", + "basename should work on paths with multiple components"); +}; + +exports.testList = function(assert) { + let list = file.list(profilePath); + let found = [ true for each (name in list) + if (name === fileNameInProfile) ]; + + if (found.length > 1) + assert.fail("a dir can't contain two files of the same name!"); + assert.equal(found[0], true, "file.list() should work"); + + assert.throws(function() { + file.list(filePathInProfile); + }, ERRORS.NOT_A_DIRECTORY, "file.list() on non-dir should raise error"); + + assert.throws(function() { + file.list(file.join(dirPathInProfile, "does-not-exist")); + }, ERRORS.FILE_NOT_FOUND, "file.list() on nonexistent should raise error"); +}; + +exports.testRead = function(assert) { + let contents = file.read(filePathInProfile); + assert.ok(/Compatibility/.test(contents), + "file.read() should work"); + + assert.throws(function() { + file.read(file.join(dirPathInProfile, "does-not-exists")); + }, ERRORS.FILE_NOT_FOUND, "file.read() on nonexistent file should throw"); + + assert.throws(function() { + file.read(dirPathInProfile); + }, ERRORS.NOT_A_FILE, "file.read() on dir should throw"); +}; + +exports.testJoin = function(assert) { + let baseDir = file.dirname(filePathInProfile); + + assert.equal(file.join(baseDir, fileNameInProfile), + filePathInProfile, "file.join() should work"); +}; + +exports.testOpenNonexistentForRead = function (assert) { + var filename = file.join(profilePath, 'does-not-exists'); + assert.throws(function() { + file.open(filename); + }, ERRORS.FILE_NOT_FOUND, "file.open() on nonexistent file should throw"); + + assert.throws(function() { + file.open(filename, "r"); + }, ERRORS.FILE_NOT_FOUND, "file.open('r') on nonexistent file should throw"); + + assert.throws(function() { + file.open(filename, "zz"); + }, ERRORS.FILE_NOT_FOUND, "file.open('zz') on nonexistent file should throw"); +}; + +exports.testOpenNonexistentForWrite = function (assert) { + let filename = file.join(profilePath, 'open.txt'); + + let stream = file.open(filename, "w"); + stream.close(); + + assert.ok(file.exists(filename), + "file.exists() should return true after file.open('w')"); + file.remove(filename); + assert.ok(!file.exists(filename), + "file.exists() should return false after file.remove()"); + + stream = file.open(filename, "rw"); + stream.close(); + + assert.ok(file.exists(filename), + "file.exists() should return true after file.open('rw')"); + file.remove(filename); + assert.ok(!file.exists(filename), + "file.exists() should return false after file.remove()"); +}; + +exports.testOpenDirectory = function (assert) { + let dir = dirPathInProfile; + assert.throws(function() { + file.open(dir); + }, ERRORS.NOT_A_FILE, "file.open() on directory should throw"); + + assert.throws(function() { + file.open(dir, "w"); + }, ERRORS.NOT_A_FILE, "file.open('w') on directory should throw"); +}; + +exports.testOpenTypes = function (assert) { + let filename = file.join(profilePath, 'open-types.txt'); + + + // Do the opens first to create the data file. + var stream = file.open(filename, "w"); + assert.ok(stream instanceof textStreams.TextWriter, + "open(w) should return a TextWriter"); + stream.close(); + + stream = file.open(filename, "wb"); + assert.ok(stream instanceof byteStreams.ByteWriter, + "open(wb) should return a ByteWriter"); + stream.close(); + + stream = file.open(filename); + assert.ok(stream instanceof textStreams.TextReader, + "open() should return a TextReader"); + stream.close(); + + stream = file.open(filename, "r"); + assert.ok(stream instanceof textStreams.TextReader, + "open(r) should return a TextReader"); + stream.close(); + + stream = file.open(filename, "b"); + assert.ok(stream instanceof byteStreams.ByteReader, + "open(b) should return a ByteReader"); + stream.close(); + + stream = file.open(filename, "rb"); + assert.ok(stream instanceof byteStreams.ByteReader, + "open(rb) should return a ByteReader"); + stream.close(); + + file.remove(filename); +}; + +exports.testMkpathRmdir = function (assert) { + let basePath = profilePath; + let dirs = []; + for (let i = 0; i < 3; i++) + dirs.push("test-file-dir"); + + let paths = []; + for (let i = 0; i < dirs.length; i++) { + let args = [basePath].concat(dirs.slice(0, i + 1)); + paths.unshift(file.join.apply(null, args)); + } + + for (let i = 0; i < paths.length; i++) { + assert.ok(!file.exists(paths[i]), + "Sanity check: path should not exist: " + paths[i]); + } + + file.mkpath(paths[0]); + assert.ok(file.exists(paths[0]), "mkpath should create path: " + paths[0]); + + for (let i = 0; i < paths.length; i++) { + file.rmdir(paths[i]); + assert.ok(!file.exists(paths[i]), + "rmdir should remove path: " + paths[i]); + } +}; + +exports.testMkpathTwice = function (assert) { + let dir = profilePath; + let path = file.join(dir, "test-file-dir"); + assert.ok(!file.exists(path), + "Sanity check: path should not exist: " + path); + file.mkpath(path); + assert.ok(file.exists(path), "mkpath should create path: " + path); + file.mkpath(path); + assert.ok(file.exists(path), + "After second mkpath, path should still exist: " + path); + file.rmdir(path); + assert.ok(!file.exists(path), "rmdir should remove path: " + path); +}; + +exports.testMkpathExistingNondirectory = function (assert) { + var fname = file.join(profilePath, 'conflict.txt'); + file.open(fname, "w").close(); + assert.ok(file.exists(fname), "File should exist"); + assert.throws(function() file.mkpath(fname), + /^The path already exists and is not a directory: .+$/, + "mkpath on file should raise error"); + file.remove(fname); +}; + +exports.testRmdirNondirectory = function (assert) { + var fname = file.join(profilePath, 'not-a-dir') + file.open(fname, "w").close(); + assert.ok(file.exists(fname), "File should exist"); + assert.throws(function() { + file.rmdir(fname); + }, ERRORS.NOT_A_DIRECTORY, "rmdir on file should raise error"); + file.remove(fname); + assert.ok(!file.exists(fname), "File should not exist"); + assert.throws(function () file.rmdir(fname), + ERRORS.FILE_NOT_FOUND, + "rmdir on non-existing file should raise error"); +}; + +exports.testRmdirNonempty = function (assert) { + let dir = profilePath; + let path = file.join(dir, "test-file-dir"); + assert.ok(!file.exists(path), + "Sanity check: path should not exist: " + path); + file.mkpath(path); + let filePath = file.join(path, "file"); + file.open(filePath, "w").close(); + assert.ok(file.exists(filePath), + "Sanity check: path should exist: " + filePath); + assert.throws(function () file.rmdir(path), + /^The directory is not empty: .+$/, + "rmdir on non-empty directory should raise error"); + file.remove(filePath); + file.rmdir(path); + assert.ok(!file.exists(path), "Path should not exist"); +}; + +require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-frame-utils.js b/addon-sdk-1.16/test/test-frame-utils.js new file mode 100644 index 00000000..402016d1 --- /dev/null +++ b/addon-sdk-1.16/test/test-frame-utils.js @@ -0,0 +1,59 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +'use strict'; + +const { create } = require('sdk/frame/utils'); +const { open, close } = require('sdk/window/helpers'); + +exports['test frame creation'] = function(assert, done) { + open('data:text/html;charset=utf-8,Window').then(function (window) { + let frame = create(window.document); + + assert.equal(frame.getAttribute('type'), 'content', + 'frame type is content'); + assert.ok(frame.contentWindow, 'frame has contentWindow'); + assert.equal(frame.contentWindow.location.href, 'about:blank', + 'by default "about:blank" is loaded'); + assert.equal(frame.docShell.allowAuth, false, 'auth disabled by default'); + assert.equal(frame.docShell.allowJavascript, false, 'js disabled by default'); + assert.equal(frame.docShell.allowPlugins, false, + 'plugins disabled by default'); + close(window).then(done); + }); +}; + +exports['test fram has js disabled by default'] = function(assert, done) { + open('data:text/html;charset=utf-8,window').then(function (window) { + let frame = create(window.document, { + uri: 'data:text/html;charset=utf-8,', + }); + frame.contentWindow.addEventListener('DOMContentLoaded', function ready() { + frame.contentWindow.removeEventListener('DOMContentLoaded', ready, false); + assert.ok(!~frame.contentDocument.documentElement.innerHTML.indexOf('JS'), + 'JS was executed'); + + close(window).then(done); + }, false); + }); +}; + +exports['test frame with js enabled'] = function(assert, done) { + open('data:text/html;charset=utf-8,window').then(function (window) { + let frame = create(window.document, { + uri: 'data:text/html;charset=utf-8,', + allowJavascript: true + }); + frame.contentWindow.addEventListener('DOMContentLoaded', function ready() { + frame.contentWindow.removeEventListener('DOMContentLoaded', ready, false); + assert.ok(~frame.contentDocument.documentElement.innerHTML.indexOf('JS'), + 'JS was executed'); + + close(window).then(done); + }, false); + }); +}; + +require('test').run(exports); diff --git a/addon-sdk-1.16/test/test-fs.js b/addon-sdk-1.16/test/test-fs.js new file mode 100644 index 00000000..efb6df70 --- /dev/null +++ b/addon-sdk-1.16/test/test-fs.js @@ -0,0 +1,621 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const { pathFor, platform } = require("sdk/system"); +const fs = require("sdk/io/fs"); +const url = require("sdk/url"); +const path = require("sdk/fs/path"); +const { defer } = require("sdk/core/promise"); +const { Buffer } = require("sdk/io/buffer"); +const { is } = require("sdk/system/xul-app"); + +// Use profile directory to list / read / write files. +const profilePath = pathFor("ProfD"); +const fileNameInProfile = "compatibility.ini"; +const dirNameInProfile = "extensions"; +const filePathInProfile = path.join(profilePath, fileNameInProfile); +const dirPathInProfile = path.join(profilePath, dirNameInProfile); +const mkdirPath = path.join(profilePath, "sdk-fixture-mkdir"); +const writePath = path.join(profilePath, "sdk-fixture-writeFile"); +const unlinkPath = path.join(profilePath, "sdk-fixture-unlink"); +const truncatePath = path.join(profilePath, "sdk-fixture-truncate"); +const renameFromPath = path.join(profilePath, "sdk-fixture-rename-from"); +const renameToPath = path.join(profilePath, "sdk-fixture-rename-to"); +const chmodPath = path.join(profilePath, "sdk-fixture-chmod"); + +const profileEntries = [ + "compatibility.ini", + "extensions", + "prefs.js" + // There are likely to be a lot more files but we can"t really + // on consistent list so we limit to this. +]; + +const isWindows = platform.indexOf('win') === 0; + +exports["test readdir"] = function(assert, end) { + var async = false; + fs.readdir(profilePath, function(error, entries) { + assert.ok(async, "readdir is async"); + assert.ok(!error, "there is no error when reading directory"); + assert.ok(profileEntries.length <= entries.length, + "got at least number of entries we expect"); + assert.ok(profileEntries.every(function(entry) { + return entries.indexOf(entry) >= 0; + }), "all profiles are present"); + end(); + }); + + async = true; +}; + +exports["test readdir error"] = function(assert, end) { + var async = false; + var path = profilePath + "-does-not-exists"; + fs.readdir(path, function(error, entries) { + assert.ok(async, "readdir is async"); + assert.equal(error.message, "ENOENT, readdir " + path); + assert.equal(error.code, "ENOENT", "error has a code"); + assert.equal(error.path, path, "error has a path"); + assert.equal(error.errno, 34, "error has a errno"); + end(); + }); + + async = true; +}; + +exports["test readdirSync"] = function(assert) { + var async = false; + var entries = fs.readdirSync(profilePath); + assert.ok(profileEntries.length <= entries.length, + "got at least number of entries we expect"); + assert.ok(profileEntries.every(function(entry) { + return entries.indexOf(entry) >= 0; + }), "all profiles are present"); +}; + +exports["test readdirSync error"] = function(assert) { + var async = false; + var path = profilePath + "-does-not-exists"; + try { + fs.readdirSync(path); + assert.fail(Error("No error was thrown")); + } catch (error) { + assert.equal(error.message, "ENOENT, readdir " + path); + assert.equal(error.code, "ENOENT", "error has a code"); + assert.equal(error.path, path, "error has a path"); + assert.equal(error.errno, 34, "error has a errno"); + } +}; + +exports["test readFile"] = function(assert, end) { + let async = false; + fs.readFile(filePathInProfile, function(error, content) { + assert.ok(async, "readFile is async"); + assert.ok(!error, "error is falsy"); + + assert.ok(Buffer.isBuffer(content), "readFile returns buffer"); + assert.ok(typeof(content.length) === "number", "buffer has length"); + assert.ok(content.toString().indexOf("[Compatibility]") >= 0, + "content contains expected data"); + end(); + }); + async = true; +}; + +exports["test readFile error"] = function(assert, end) { + let async = false; + let path = filePathInProfile + "-does-not-exists"; + fs.readFile(path, function(error, content) { + assert.ok(async, "readFile is async"); + assert.equal(error.message, "ENOENT, open " + path); + assert.equal(error.code, "ENOENT", "error has a code"); + assert.equal(error.path, path, "error has a path"); + assert.equal(error.errno, 34, "error has a errno"); + + end(); + }); + async = true; +}; + +exports["test readFileSync not implemented"] = function(assert) { + let buffer = fs.readFileSync(filePathInProfile); + assert.ok(buffer.toString().indexOf("[Compatibility]") >= 0, + "read content"); +}; + +exports["test fs.stat file"] = function(assert, end) { + let async = false; + let path = filePathInProfile; + fs.stat(path, function(error, stat) { + assert.ok(async, "fs.stat is async"); + assert.ok(!error, "error is falsy"); + assert.ok(!stat.isDirectory(), "not a dir"); + assert.ok(stat.isFile(), "is a file"); + assert.ok(!stat.isSymbolicLink(), "isn't a symlink"); + assert.ok(typeof(stat.size) === "number", "size is a number"); + assert.ok(stat.exists === true, "file exists"); + assert.ok(typeof(stat.isBlockDevice()) === "boolean"); + assert.ok(typeof(stat.isCharacterDevice()) === "boolean"); + assert.ok(typeof(stat.isFIFO()) === "boolean"); + assert.ok(typeof(stat.isSocket()) === "boolean"); + assert.ok(typeof(stat.hidden) === "boolean"); + assert.ok(typeof(stat.writable) === "boolean") + assert.ok(stat.readable === true); + + end(); + }); + async = true; +}; + +exports["test fs.stat dir"] = function(assert, end) { + let async = false; + let path = dirPathInProfile; + fs.stat(path, function(error, stat) { + assert.ok(async, "fs.stat is async"); + assert.ok(!error, "error is falsy"); + assert.ok(stat.isDirectory(), "is a dir"); + assert.ok(!stat.isFile(), "not a file"); + assert.ok(!stat.isSymbolicLink(), "isn't a symlink"); + assert.ok(typeof(stat.size) === "number", "size is a number"); + assert.ok(stat.exists === true, "file exists"); + assert.ok(typeof(stat.isBlockDevice()) === "boolean"); + assert.ok(typeof(stat.isCharacterDevice()) === "boolean"); + assert.ok(typeof(stat.isFIFO()) === "boolean"); + assert.ok(typeof(stat.isSocket()) === "boolean"); + assert.ok(typeof(stat.hidden) === "boolean"); + assert.ok(typeof(stat.writable) === "boolean") + assert.ok(typeof(stat.readable) === "boolean"); + + end(); + }); + async = true; +}; + +exports["test fs.stat error"] = function(assert, end) { + let async = false; + let path = filePathInProfile + "-does-not-exists"; + fs.stat(path, function(error, stat) { + assert.ok(async, "fs.stat is async"); + assert.equal(error.message, "ENOENT, stat " + path); + assert.equal(error.code, "ENOENT", "error has a code"); + assert.equal(error.path, path, "error has a path"); + assert.equal(error.errno, 34, "error has a errno"); + + end(); + }); + async = true; +}; + +exports["test fs.exists NO"] = function(assert, end) { + let async = false + let path = filePathInProfile + "-does-not-exists"; + fs.exists(path, function(error, value) { + assert.ok(async, "fs.exists is async"); + assert.ok(!error, "error is falsy"); + assert.ok(!value, "file does not exists"); + end(); + }); + async = true; +}; + +exports["test fs.exists YES"] = function(assert, end) { + let async = false + let path = filePathInProfile + fs.exists(path, function(error, value) { + assert.ok(async, "fs.exists is async"); + assert.ok(!error, "error is falsy"); + assert.ok(value, "file exists"); + end(); + }); + async = true; +}; + +exports["test fs.exists NO"] = function(assert, end) { + let async = false + let path = filePathInProfile + "-does-not-exists"; + fs.exists(path, function(error, value) { + assert.ok(async, "fs.exists is async"); + assert.ok(!error, "error is falsy"); + assert.ok(!value, "file does not exists"); + end(); + }); + async = true; +}; + +exports["test fs.existsSync"] = function(assert) { + let path = filePathInProfile + assert.equal(fs.existsSync(path), true, "exists"); + assert.equal(fs.existsSync(path + "-does-not-exists"), false, "exists"); +}; + +exports["test fs.mkdirSync fs.rmdirSync"] = function(assert) { + let path = mkdirPath; + + assert.equal(fs.existsSync(path), false, "does not exists"); + fs.mkdirSync(path); + assert.equal(fs.existsSync(path), true, "dir was created"); + try { + fs.mkdirSync(path); + assert.fail(Error("mkdir on existing should throw")); + } catch (error) { + assert.equal(error.message, "EEXIST, mkdir " + path); + assert.equal(error.code, "EEXIST", "error has a code"); + assert.equal(error.path, path, "error has a path"); + assert.equal(error.errno, 47, "error has a errno"); + } + fs.rmdirSync(path); + assert.equal(fs.existsSync(path), false, "dir was removed"); +}; + +exports["test fs.mkdir"] = function(assert, end) { + let path = mkdirPath; + + if (!fs.existsSync(path)) { + let async = false; + fs.mkdir(path, function(error) { + assert.ok(async, "mkdir is async"); + assert.ok(!error, "no error"); + assert.equal(fs.existsSync(path), true, "dir was created"); + fs.rmdirSync(path); + assert.equal(fs.existsSync(path), false, "dir was removed"); + end(); + }); + async = true; + } +}; + +exports["test fs.mkdir error"] = function(assert, end) { + let path = mkdirPath; + + if (!fs.existsSync(path)) { + fs.mkdirSync(path); + let async = false; + fs.mkdir(path, function(error) { + assert.ok(async, "mkdir is async"); + assert.equal(error.message, "EEXIST, mkdir " + path); + assert.equal(error.code, "EEXIST", "error has a code"); + assert.equal(error.path, path, "error has a path"); + assert.equal(error.errno, 47, "error has a errno"); + fs.rmdirSync(path); + assert.equal(fs.existsSync(path), false, "dir was removed"); + end(); + }); + async = true; + } +}; + +exports["test fs.rmdir"] = function(assert, end) { + let path = mkdirPath; + + if (!fs.existsSync(path)) { + fs.mkdirSync(path); + assert.equal(fs.existsSync(path), true, "dir exists"); + let async = false; + fs.rmdir(path, function(error) { + assert.ok(async, "mkdir is async"); + assert.ok(!error, "no error"); + assert.equal(fs.existsSync(path), false, "dir was removed"); + end(); + }); + async = true; + } +}; + + +exports["test fs.rmdir error"] = function(assert, end) { + let path = mkdirPath; + + if (!fs.existsSync(path)) { + assert.equal(fs.existsSync(path), false, "dir doesn't exists"); + let async = false; + fs.rmdir(path, function(error) { + assert.ok(async, "mkdir is async"); + assert.equal(error.message, "ENOENT, remove " + path); + assert.equal(error.code, "ENOENT", "error has a code"); + assert.equal(error.path, path, "error has a path"); + assert.equal(error.errno, 34, "error has a errno"); + assert.equal(fs.existsSync(path), false, "dir is removed"); + end(); + }); + async = true; + } +}; + +exports["test fs.truncateSync fs.unlinkSync"] = function(assert) { + let path = truncatePath; + + assert.equal(fs.existsSync(path), false, "does not exists"); + fs.truncateSync(path); + assert.equal(fs.existsSync(path), true, "file was created"); + fs.truncateSync(path); + fs.unlinkSync(path); + assert.equal(fs.existsSync(path), false, "file was removed"); +}; + + +exports["test fs.truncate"] = function(assert, end) { + let path = truncatePath; + if (!fs.existsSync(path)) { + let async = false; + fs.truncate(path, 0, function(error) { + assert.ok(async, "truncate is async"); + assert.ok(!error, "no error"); + assert.equal(fs.existsSync(path), true, "file was created"); + fs.unlinkSync(path); + assert.equal(fs.existsSync(path), false, "file was removed"); + end(); + }) + async = true; + } +}; + +exports["test fs.unlink"] = function(assert, end) { + let path = unlinkPath; + let async = false; + assert.ok(!fs.existsSync(path), "file doesn't exists yet"); + fs.truncateSync(path, 0); + assert.ok(fs.existsSync(path), "file was created"); + fs.unlink(path, function(error) { + assert.ok(async, "fs.unlink is async"); + assert.ok(!error, "error is falsy"); + assert.ok(!fs.existsSync(path), "file was removed"); + end(); + }); + async = true; +}; + +exports["test fs.unlink error"] = function(assert, end) { + let path = unlinkPath; + let async = false; + assert.ok(!fs.existsSync(path), "file doesn't exists yet"); + fs.unlink(path, function(error) { + assert.ok(async, "fs.unlink is async"); + assert.equal(error.message, "ENOENT, remove " + path); + assert.equal(error.code, "ENOENT", "error has a code"); + assert.equal(error.path, path, "error has a path"); + assert.equal(error.errno, 34, "error has a errno"); + end(); + }); + async = true; +}; + +exports["test fs.rename"] = function(assert, end) { + let fromPath = renameFromPath; + let toPath = renameToPath; + + fs.truncateSync(fromPath); + assert.ok(fs.existsSync(fromPath), "source file exists"); + assert.ok(!fs.existsSync(toPath), "destination doesn't exists"); + var async = false; + fs.rename(fromPath, toPath, function(error) { + assert.ok(async, "fs.rename is async"); + assert.ok(!error, "error is falsy"); + assert.ok(!fs.existsSync(fromPath), "source path no longer exists"); + assert.ok(fs.existsSync(toPath), "destination file exists"); + fs.unlinkSync(toPath); + assert.ok(!fs.existsSync(toPath), "cleaned up properly"); + end(); + }); + async = true; +}; + +exports["test fs.rename (missing source file)"] = function(assert, end) { + let fromPath = renameFromPath; + let toPath = renameToPath; + + assert.ok(!fs.existsSync(fromPath), "source file doesn't exists"); + assert.ok(!fs.existsSync(toPath), "destination doesn't exists"); + var async = false; + fs.rename(fromPath, toPath, function(error) { + assert.ok(async, "fs.rename is async"); + assert.equal(error.message, "ENOENT, rename " + fromPath); + assert.equal(error.code, "ENOENT", "error has a code"); + assert.equal(error.path, fromPath, "error has a path"); + assert.equal(error.errno, 34, "error has a errno"); + end(); + }); + async = true; +}; + +exports["test fs.rename (existing target file)"] = function(assert, end) { + let fromPath = renameFromPath; + let toPath = renameToPath; + + fs.truncateSync(fromPath); + fs.truncateSync(toPath); + assert.ok(fs.existsSync(fromPath), "source file exists"); + assert.ok(fs.existsSync(toPath), "destination file exists"); + var async = false; + fs.rename(fromPath, toPath, function(error) { + assert.ok(async, "fs.rename is async"); + assert.ok(!error, "error is falsy"); + assert.ok(!fs.existsSync(fromPath), "source path no longer exists"); + assert.ok(fs.existsSync(toPath), "destination file exists"); + fs.unlinkSync(toPath); + assert.ok(!fs.existsSync(toPath), "cleaned up properly"); + end(); + }); + async = true; +}; + +exports["test fs.writeFile"] = function(assert, end) { + let path = writePath; + let content = ["hello world", + "this is some text"].join("\n"); + + var async = false; + fs.writeFile(path, content, function(error) { + assert.ok(async, "fs write is async"); + assert.ok(!error, "error is falsy"); + assert.ok(fs.existsSync(path), "file was created"); + assert.equal(fs.readFileSync(path).toString(), + content, + "contet was written"); + fs.unlinkSync(path); + assert.ok(!fs.exists(path), "file was removed"); + + end(); + }); + async = true; +}; + +exports["test fs.writeFile (with large files)"] = function(assert, end) { + let path = writePath; + let content = ""; + + for (var i = 0; i < 100000; i++) { + content += "buffer\n"; + } + + var async = false; + fs.writeFile(path, content, function(error) { + assert.ok(async, "fs write is async"); + assert.ok(!error, "error is falsy"); + assert.ok(fs.existsSync(path), "file was created"); + assert.equal(fs.readFileSync(path).toString(), + content, + "contet was written"); + fs.unlinkSync(path); + assert.ok(!fs.exists(path), "file was removed"); + + end(); + }); + async = true; +}; + +exports["test fs.writeFile error"] = function (assert, done) { + try { + fs.writeFile({}, 'content', function (err) { + assert.fail('Error thrown from TypeError should not be caught'); + }); + } catch (e) { + assert.ok(e, + 'writeFile with a non-string error should not be caught'); + assert.equal(e.name, 'TypeError', 'error should be TypeError'); + } + fs.writeFile('not/a/valid/path', 'content', function (err) { + assert.ok(err, 'error caught and handled in callback'); + done(); + }); +}; + +exports["test fs.chmod"] = function (assert, done) { + let content = ["hej från sverige"]; + + fs.writeFile(chmodPath, content, function (err) { + testPerm("0755")() + .then(testPerm("0777")) + .then(testPerm("0666")) + .then(testPerm(parseInt("0511", 8))) + .then(testPerm(parseInt("0200", 8))) + .then(testPerm("0040")) + .then(testPerm("0000")) + .then(testPermSync(parseInt("0777", 8))) + .then(testPermSync(parseInt("0666", 8))) + .then(testPermSync("0511")) + .then(testPermSync("0200")) + .then(testPermSync("0040")) + .then(testPermSync("0000")) + .then(() => { + assert.pass("Successful chmod passes"); + }, assert.fail) + // Test invalid paths + .then(() => chmod("not-a-valid-file", parseInt("0755", 8))) + .then(assert.fail, (err) => { + checkPermError(err, "not-a-valid-file"); + }) + .then(() => chmod("not-a-valid-file", parseInt("0755", 8), "sync")) + .then(assert.fail, (err) => { + checkPermError(err, "not-a-valid-file"); + }) + // Test invalid files + .then(() => chmod("resource://not-a-real-file", parseInt("0755", 8))) + .then(assert.fail, (err) => { + checkPermError(err, "resource://not-a-real-file"); + }) + .then(() => chmod("resource://not-a-real-file", parseInt("0755", 8), 'sync')) + .then(assert.fail, (err) => { + checkPermError(err, "resource://not-a-real-file"); + }) + .then(done, assert.fail); + }); + + function checkPermError (err, path) { + assert.equal(err.message, "ENOENT, chmod " + path); + assert.equal(err.code, "ENOENT", "error has a code"); + assert.equal(err.path, path, "error has a path"); + assert.equal(err.errno, 34, "error has a errno"); + } + + function chmod (path, mode, sync) { + let { promise, resolve, reject } = defer(); + if (!sync) { + fs.chmod(path, mode, (err) => { + if (err) reject(err); + else resolve(); + }); + } else { + fs.chmodSync(path, mode); + resolve(); + } + return promise; + } + + function testPerm (mode, sync) { + return function () { + return chmod(chmodPath, mode, sync) + .then(() => getPerm(chmodPath)) + .then(perm => { + let nMode = normalizeMode(mode); + if (isWindows) + assert.equal(perm, nMode, + "mode correctly set to " + mode + " (" + nMode + " on windows)"); + else + assert.equal(perm, nMode, "mode correctly set to " + nMode); + }); + }; + } + + function testPermSync (mode) { + return testPerm(mode, true); + } + + function getPerm (path) { + let { promise, resolve, reject } = defer(); + fs.stat(path, function (err, stat) { + if (err) reject(err); + else resolve(stat.mode); + }); + return promise; + } + + /* + * Converts a unix mode `0755` into a Windows version of unix permissions + */ + function normalizeMode (mode) { + if (typeof mode === "string") + mode = parseInt(mode, 8); + + if (!isWindows) + return mode; + + var ANY_READ = parseInt("0444", 8); + var ANY_WRITE = parseInt("0222", 8); + var winMode = 0; + + // On Windows, if WRITE is true, then READ is also true + if (mode & ANY_WRITE) + winMode |= ANY_WRITE | ANY_READ; + // Minimum permissions are READ for Windows + else + winMode |= ANY_READ; + + return winMode; + } +}; + +require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-functional.js b/addon-sdk-1.16/test/test-functional.js new file mode 100644 index 00000000..84c587fc --- /dev/null +++ b/addon-sdk-1.16/test/test-functional.js @@ -0,0 +1,422 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const { setTimeout } = require('sdk/timers'); +const utils = require('sdk/lang/functional'); +const { invoke, defer, partial, compose, memoize, once, is, isnt, + delay, wrap, curry, chainable, field, query, isInstance } = utils; +const { LoaderWithHookedConsole } = require('sdk/test/loader'); + +exports['test forwardApply'] = function(assert) { + function sum(b, c) { return this.a + b + c; } + assert.equal(invoke(sum, [2, 3], { a: 1 }), 6, + 'passed arguments and pseoude-variable are used'); + + assert.equal(invoke(sum.bind({ a: 2 }), [2, 3], { a: 1 }), 7, + 'bounded `this` pseoudo variable is used'); +}; + +exports['test deferred function'] = function(assert, done) { + let nextTurn = false; + function sum(b, c) { + assert.ok(nextTurn, 'enqueued is called in next turn of event loop'); + assert.equal(this.a + b + c, 6, + 'passed arguments an pseoude-variable are used'); + done(); + } + + let fixture = { a: 1, method: defer(sum) }; + fixture.method(2, 3); + nextTurn = true; +}; + +exports['test partial function'] = function(assert) { + function sum(b, c) { return this.a + b + c; } + + let foo = { a : 5 }; + + foo.sum7 = partial(sum, 7); + foo.sum8and4 = partial(sum, 8, 4); + + assert.equal(foo.sum7(2), 14, 'partial one arguments works'); + + assert.equal(foo.sum8and4(), 17, 'partial both arguments works'); +}; + +exports["test curry defined numeber of arguments"] = function(assert) { + var sum = curry(function(a, b, c) { + return a + b + c; + }); + + assert.equal(sum(2, 2, 1), 5, "sum(2, 2, 1) => 5"); + assert.equal(sum(2, 4)(1), 7, "sum(2, 4)(1) => 7"); + assert.equal(sum(2)(4, 2), 8, "sum(2)(4, 2) => 8"); + assert.equal(sum(2)(4)(3), 9, "sum(2)(4)(3) => 9"); +}; + +exports['test compose'] = function(assert) { + let greet = function(name) { return 'hi: ' + name; }; + let exclaim = function(sentence) { return sentence + '!'; }; + + assert.equal(compose(exclaim, greet)('moe'), 'hi: moe!', + 'can compose a function that takes another'); + + assert.equal(compose(greet, exclaim)('moe'), 'hi: moe!', + 'in this case, the functions are also commutative'); + + let target = { + name: 'Joe', + greet: compose(function exclaim(sentence) { + return sentence + '!'; + }, function(title) { + return 'hi : ' + title + ' ' + this.name; + }) + }; + + assert.equal(target.greet('Mr'), 'hi : Mr Joe!', + 'this can be passed in'); + assert.equal(target.greet.call({ name: 'Alex' }, 'Dr'), 'hi : Dr Alex!', + 'this can be applied'); + + let single = compose(function(value) { + return value + ':suffix'; + }); + + assert.equal(single('text'), 'text:suffix', 'works with single function'); + + let identity = compose(); + assert.equal(identity('bla'), 'bla', 'works with zero functions'); +}; + +exports['test wrap'] = function(assert) { + let greet = function(name) { return 'hi: ' + name; }; + let backwards = wrap(greet, function(f, name) { + return f(name) + ' ' + name.split('').reverse().join(''); + }); + + assert.equal(backwards('moe'), 'hi: moe eom', + 'wrapped the saluation function'); + + let inner = function () { return 'Hello '; }; + let target = { + name: 'Matteo', + hi: wrap(inner, function(f) { return f() + this.name; }) + }; + + assert.equal(target.hi(), 'Hello Matteo', 'works with this'); + + function noop() { } + let wrapped = wrap(noop, function(f) { + return Array.slice(arguments); + }); + + let actual = wrapped([ 'whats', 'your' ], 'vector', 'victor'); + assert.deepEqual(actual, [ noop, ['whats', 'your'], 'vector', 'victor' ], + 'works with fancy stuff'); +}; + +exports['test memoize'] = function(assert) { + const fib = n => n < 2 ? n : fib(n - 1) + fib(n - 2); + let fibnitro = memoize(fib); + + assert.equal(fib(10), 55, + 'a memoized version of fibonacci produces identical results'); + assert.equal(fibnitro(10), 55, + 'a memoized version of fibonacci produces identical results'); + + function o(key, value) { return value; } + let oo = memoize(o), v1 = {}, v2 = {}; + + + assert.equal(oo(1, v1), v1, 'returns value back'); + assert.equal(oo(1, v2), v1, 'memoized by a first argument'); + assert.equal(oo(2, v2), v2, 'returns back value if not memoized'); + assert.equal(oo(2), v2, 'memoized new value'); + assert.notEqual(oo(1), oo(2), 'values do not override'); + assert.equal(o(3, v2), oo(2, 3), 'returns same value as un-memoized'); + + let get = memoize(function(attribute) { return this[attribute]; }); + let target = { name: 'Bob', get: get }; + + assert.equal(target.get('name'), 'Bob', 'has correct `this`'); + assert.equal(target.get.call({ name: 'Jack' }, 'name'), 'Bob', + 'name is memoized'); + assert.equal(get('name'), 'Bob', 'once memoized can be called without this'); +}; + +exports['test delay'] = function(assert, done) { + let delayed = false; + delay(function() { + assert.ok(delayed, 'delayed the function'); + done(); + }, 1); + delayed = true; +}; + +exports['test delay with this'] = function(assert, done) { + let context = {}; + delay.call(context, function(name) { + assert.equal(this, context, 'this was passed in'); + assert.equal(name, 'Tom', 'argument was passed in'); + done(); + }, 10, 'Tom'); +}; + +exports['test once'] = function(assert) { + let n = 0; + let increment = once(function() { n ++; }); + + increment(); + increment(); + + assert.equal(n, 1, 'only incremented once'); + + let target = { + state: 0, + update: once(function() { + return this.state ++; + }) + }; + + target.update(); + target.update(); + + assert.equal(target.state, 1, 'this was passed in and called only once'); +}; + +exports['test once with argument'] = function(assert) { + let n = 0; + let increment = once(a => n++); + + increment(); + increment('foo'); + + assert.equal(n, 1, 'only incremented once'); + + increment(); + increment('foo'); + + assert.equal(n, 1, 'only incremented once'); +}; + +exports['test complement'] = assert => { + let { complement } = require("sdk/lang/functional"); + + let isOdd = x => Boolean(x % 2); + + assert.equal(isOdd(1), true); + assert.equal(isOdd(2), false); + + let isEven = complement(isOdd); + + assert.equal(isEven(1), false); + assert.equal(isEven(2), true); + + let foo = {}; + let isFoo = function() { return this === foo; }; + let insntFoo = complement(isFoo); + + assert.equal(insntFoo.call(foo), false); + assert.equal(insntFoo.call({}), true); +}; + +exports['test constant'] = assert => { + let { constant } = require("sdk/lang/functional"); + + let one = constant(1); + + assert.equal(one(1), 1); + assert.equal(one(2), 1); +}; + +exports['test apply'] = assert => { + let { apply } = require("sdk/lang/functional"); + + let dashify = (...args) => args.join("-"); + + assert.equal(apply(dashify, 1, [2, 3]), "1-2-3"); + assert.equal(apply(dashify, "a"), "a"); + assert.equal(apply(dashify, ["a", "b"]), "a-b"); + assert.equal(apply(dashify, ["a", "b"], "c"), "a,b-c"); + assert.equal(apply(dashify, [1, 2], [3, 4]), "1,2-3-4"); +}; + +exports['test flip'] = assert => { + let { flip } = require("sdk/lang/functional"); + + let append = (left, right) => left + " " + right; + let prepend = flip(append); + + assert.equal(append("hello", "world"), "hello world"); + assert.equal(prepend("hello", "world"), "world hello"); + + let wrap = function(left, right) { + return left + " " + this + " " + right; + }; + let invertWrap = flip(wrap); + + assert.equal(wrap.call("@", "hello", "world"), "hello @ world"); + assert.equal(invertWrap.call("@", "hello", "world"), "world @ hello"); + + let reverse = flip((...args) => args); + + assert.deepEqual(reverse(1, 2, 3, 4), [4, 3, 2, 1]); + assert.deepEqual(reverse(1), [1]); + assert.deepEqual(reverse(), []); + + // currying still works + let prependr = curry(prepend); + + assert.equal(prependr("hello", "world"), "world hello"); + assert.equal(prependr("hello")("world"), "world hello"); +}; + +exports["test when"] = assert => { + let { when } = require("sdk/lang/functional"); + + let areNums = (...xs) => xs.every(x => typeof(x) === "number"); + + let sum = when(areNums, (...xs) => xs.reduce((y, x) => x + y, 0)); + + assert.equal(sum(1, 2, 3), 6); + assert.equal(sum(1, 2, "3"), undefined); + + let multiply = when(areNums, + (...xs) => xs.reduce((y, x) => x * y, 1), + (...xs) => xs); + + assert.equal(multiply(2), 2); + assert.equal(multiply(2, 3), 6); + assert.deepEqual(multiply(2, "4"), [2, "4"]); + + function Point(x, y) { + this.x = x; + this.y = y; + } + + let isPoint = x => x instanceof Point; + + let inc = when(isPoint, ({x, y}) => new Point(x + 1, y + 1)); + + assert.equal(inc({}), undefined); + assert.deepEqual(inc(new Point(0, 0)), { x: 1, y: 1 }); + + let axis = when(isPoint, + ({ x, y }) => [x, y], + _ => [0, 0]); + + assert.deepEqual(axis(new Point(1, 4)), [1, 4]); + assert.deepEqual(axis({ foo: "bar" }), [0, 0]); +}; + +exports["test chainable"] = function(assert) { + let Player = function () { this.volume = 5; }; + Player.prototype = { + setBand: chainable(function (band) { return (this.band = band); }), + incVolume: chainable(function () { return this.volume++; }) + }; + let player = new Player(); + player + .setBand('Animals As Leaders') + .incVolume().incVolume().incVolume().incVolume().incVolume().incVolume(); + + assert.equal(player.band, 'Animals As Leaders', 'passes arguments into chained'); + assert.equal(player.volume, 11, 'accepts no arguments in chain'); +}; + +exports["test field"] = assert => { + let Num = field("constructor", 0); + assert.equal(Num.name, Number.name); + assert.ok(typeof(Num), "function"); + + let x = field("x"); + + [ + [field("foo", { foo: 1 }), 1], + [field("foo")({ foo: 1 }), 1], + [field("bar", {}), undefined], + [field("bar")({}), undefined], + [field("hey", undefined), undefined], + [field("hey")(undefined), undefined], + [field("how", null), null], + [field("how")(null), null], + [x(1), undefined], + [x(undefined), undefined], + [x(null), null], + [x({ x: 1 }), 1], + [x({ x: 2 }), 2], + ].forEach(([actual, expected]) => assert.equal(actual, expected)); +}; + +exports["test query"] = assert => { + let Num = query("constructor", 0); + assert.equal(Num.name, Number.name); + assert.ok(typeof(Num), "function"); + + let x = query("x"); + let xy = query("x.y"); + + [ + [query("foo", { foo: 1 }), 1], + [query("foo")({ foo: 1 }), 1], + [query("foo.bar", { foo: { bar: 2 } }), 2], + [query("foo.bar")({ foo: { bar: 2 } }), 2], + [query("foo.bar", { foo: 1 }), undefined], + [query("foo.bar")({ foo: 1 }), undefined], + [x(1), undefined], + [x(undefined), undefined], + [x(null), null], + [x({ x: 1 }), 1], + [x({ x: 2 }), 2], + [xy(1), undefined], + [xy(undefined), undefined], + [xy(null), null], + [xy({ x: 1 }), undefined], + [xy({ x: 2 }), undefined], + [xy({ x: { y: 1 } }), 1], + [xy({ x: { y: 2 } }), 2] + ].forEach(([actual, expected]) => assert.equal(actual, expected)); +}; + +exports["test isInstance"] = assert => { + function X() {} + function Y() {} + let isX = isInstance(X); + + [ + isInstance(X, new X()), + isInstance(X)(new X()), + !isInstance(X, new Y()), + !isInstance(X)(new Y()), + isX(new X()), + !isX(new Y()) + ].forEach(x => assert.ok(x)); +}; + +exports["test is"] = assert => { + + assert.deepEqual([ 1, 0, 1, 0, 1 ].map(is(1)), + [ true, false, true, false, true ], + "is can be partially applied"); + + assert.ok(is(1, 1)); + assert.ok(!is({}, {})); + assert.ok(is()(1)()(1), "is is curried"); + assert.ok(!is()(1)()(2)); +}; + +exports["test isnt"] = assert => { + + assert.deepEqual([ 1, 0, 1, 0, 1 ].map(isnt(0)), + [ true, false, true, false, true ], + "is can be partially applied"); + + assert.ok(!isnt(1, 1)); + assert.ok(isnt({}, {})); + assert.ok(!isnt()(1)()(1)); + assert.ok(isnt()(1)()(2)); +}; + +require('test').run(exports); diff --git a/addon-sdk-1.16/test/test-globals.js b/addon-sdk-1.16/test/test-globals.js new file mode 100644 index 00000000..ba38ed7f --- /dev/null +++ b/addon-sdk-1.16/test/test-globals.js @@ -0,0 +1,30 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +'use strict'; + +Object.defineProperty(this, 'global', { value: this }); + +exports.testGlobals = function(assert) { + // the only globals in module scope should be: + // module, exports, require, dump, console + assert.equal(typeof module, 'object', 'have "module", good'); + assert.equal(typeof exports, 'object', 'have "exports", good'); + assert.equal(typeof require, 'function', 'have "require", good'); + assert.equal(typeof dump, 'function', 'have "dump", good'); + assert.equal(typeof console, 'object', 'have "console", good'); + + // in particular, these old globals should no longer be present + assert.ok(!('packaging' in global), "no 'packaging', good"); + assert.ok(!('memory' in global), "no 'memory', good"); + assert.ok(/test-globals\.js$/.test(module.uri), + 'should contain filename'); +}; + +exports.testComponent = function (assert) { + assert.throws(() => { + Components; + }, /`Components` is not available/, 'using `Components` throws'); +}; + +require('test').run(exports); diff --git a/addon-sdk-1.16/test/test-heritage.js b/addon-sdk-1.16/test/test-heritage.js new file mode 100644 index 00000000..f94861d7 --- /dev/null +++ b/addon-sdk-1.16/test/test-heritage.js @@ -0,0 +1,302 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const { Class, extend, mix, obscure } = require('sdk/core/heritage'); + +exports['test extend'] = function(assert) { + let ancestor = { a: 1 }; + let descendant = extend(ancestor, { + b: 2, + get c() { return 3 }, + d: function() { return 4 } + }); + + assert.ok(ancestor.isPrototypeOf(descendant), + 'descendant inherits from ancestor'); + assert.ok(descendant.b, 2, 'proprety was implemented'); + assert.ok(descendant.c, 3, 'getter was implemented'); + assert.ok(descendant.d(), 4, 'method was implemented'); + + /* Will be fixed once Bug 674195 is shipped. + assert.ok(Object.isFrozen(descendant), + 'extend returns frozen objects'); + */ +}; + +exports['test mix'] = function(assert) { + let ancestor = { a: 1 } + let mixed = mix(extend(ancestor, { b: 1, c: 1 }), { c: 2 }, { d: 3 }); + + assert.deepEqual(JSON.parse(JSON.stringify(mixed)), { b: 1, c: 2, d: 3 }, + 'properties mixed as expected'); + assert.ok(ancestor.isPrototypeOf(mixed), + 'first arguments ancestor is ancestor of result'); +}; + +exports['test obscure'] = function(assert) { + let fixture = mix({ a: 1 }, obscure({ b: 2 })); + + assert.equal(fixture.a, 1, 'a property is included'); + assert.equal(fixture.b, 2, 'b proprety is included'); + assert.ok(!Object.getOwnPropertyDescriptor(fixture, 'b').enumerable, + 'obscured properties are non-enumerable'); +}; + +exports['test inheritance'] = function(assert) { + let Ancestor = Class({ + name: 'ancestor', + method: function () { + return 'hello ' + this.name; + } + }); + + assert.ok(Ancestor() instanceof Ancestor, + 'can be instantiated without new'); + assert.ok(new Ancestor() instanceof Ancestor, + 'can also be instantiated with new'); + assert.ok(Ancestor() instanceof Class, + 'if ancestor not specified than defaults to Class'); + assert.ok(Ancestor.prototype.extends, Class.prototype, + 'extends of prototype points to ancestors prototype'); + + + assert.equal(Ancestor().method(), 'hello ancestor', + 'instance inherits defined properties'); + + let Descendant = Class({ + extends: Ancestor, + name: 'descendant' + }); + + assert.ok(Descendant() instanceof Descendant, + 'instantiates correctly'); + assert.ok(Descendant() instanceof Ancestor, + 'Inherits for passed `extends`'); + assert.equal(Descendant().method(), 'hello descendant', + 'propreties inherited'); +}; + +exports['test immunity against __proto__'] = function(assert) { + let Foo = Class({ name: 'foo', hacked: false }); + + let Bar = Class({ extends: Foo, name: 'bar' }); + + assert.throws(function() { + Foo.prototype.__proto__ = { hacked: true }; + if (Foo() instanceof Base && !Foo().hacked) + throw Error('can not change prototype chain'); + }, 'prototype chain is immune to __proto__ hacks'); + + assert.throws(function() { + Foo.prototype.__proto__ = { hacked: true }; + if (Bar() instanceof Foo && !Bar().hacked) + throw Error('can not change prototype chain'); + }, 'prototype chain of decedants immune to __proto__ hacks'); +}; + +exports['test super'] = function(assert) { + var Foo = Class({ + initialize: function initialize(options) { + this.name = options.name; + } + }); + + var Bar = Class({ + extends: Foo, + initialize: function Bar(options) { + Foo.prototype.initialize.call(this, options); + this.type = 'bar'; + } + }); + + var bar = Bar({ name: 'test' }); + + assert.equal(bar.type, 'bar', 'bar initializer was called'); + assert.equal(bar.name, 'test', 'bar initializer called Foo initializer'); +}; + +exports['test initialize'] = function(assert) { + var Dog = Class({ + initialize: function initialize(name) { + this.name = name; + }, + type: 'dog', + bark: function bark() { + return 'Ruff! Ruff!' + } + }); + + var fluffy = Dog('Fluffy'); // instatiation + assert.ok(fluffy instanceof Dog, + 'instanceof works as expected'); + assert.ok(fluffy instanceof Class, + 'inherits form Class if not specified otherwise'); + assert.ok(fluffy.name, 'fluffy', + 'initialize unless specified otherwise'); +}; + +exports['test complements regular inheritace'] = function(assert) { + let Base = Class({ name: 'base' }); + + function Type() { + // ... + } + Type.prototype = Object.create(Base.prototype); + Type.prototype.run = function() { + // ... + }; + + let value = new Type(); + + assert.ok(value instanceof Type, 'creates instance of Type'); + assert.ok(value instanceof Base, 'inherits from Base'); + assert.equal(value.name, 'base', 'inherits properties from Base'); + + + let SubType = Class({ + extends: Type, + sub: 'type' + }); + + let fixture = SubType(); + + assert.ok(fixture instanceof Base, 'is instance of Base'); + assert.ok(fixture instanceof Type, 'is instance of Type'); + assert.ok(fixture instanceof SubType, 'is instance of SubType'); + + assert.equal(fixture.sub, 'type', 'proprety is defined'); + assert.equal(fixture.run, Type.prototype.run, 'proprety is inherited'); + assert.equal(fixture.name, 'base', 'inherits base properties'); +}; + +exports['test extends object'] = function(assert) { + let prototype = { constructor: function() { return this; }, name: 'me' }; + let Foo = Class({ + extends: prototype, + value: 2 + }); + let foo = new Foo(); + + assert.ok(foo instanceof Foo, 'instance of Foo'); + assert.ok(!(foo instanceof Class), 'is not instance of Class'); + assert.ok(prototype.isPrototypeOf(foo), 'inherits from given prototype'); + assert.equal(Object.getPrototypeOf(Foo.prototype), prototype, + 'contsructor prototype inherits from extends option'); + assert.equal(foo.value, 2, 'property is defined'); + assert.equal(foo.name, 'me', 'prototype proprety is inherited'); +}; + + +var HEX = Class({ + hex: function hex() { + return '#' + this.color; + } +}); + +var RGB = Class({ + red: function red() { + return parseInt(this.color.substr(0, 2), 16); + }, + green: function green() { + return parseInt(this.color.substr(2, 2), 16); + }, + blue: function blue() { + return parseInt(this.color.substr(4, 2), 16); + } +}); + +var CMYK = Class({ + black: function black() { + var color = Math.max(Math.max(this.red(), this.green()), this.blue()); + return (1 - color / 255).toFixed(4); + }, + magenta: function magenta() { + var K = this.black(); + return (((1 - this.green() / 255).toFixed(4) - K) / (1 - K)).toFixed(4); + }, + yellow: function yellow() { + var K = this.black(); + return (((1 - this.blue() / 255).toFixed(4) - K) / (1 - K)).toFixed(4); + }, + cyan: function cyan() { + var K = this.black(); + return (((1 - this.red() / 255).toFixed(4) - K) / (1 - K)).toFixed(4); + } +}); + +var Color = Class({ + implements: [ HEX, RGB, CMYK ], + initialize: function initialize(color) { + this.color = color; + } +}); + +exports['test composition'] = function(assert) { + var pink = Color('FFC0CB'); + + assert.equal(pink.red(), 255, 'red() works'); + assert.equal(pink.green(), 192, 'green() works'); + assert.equal(pink.blue(), 203, 'blue() works'); + + assert.equal(pink.magenta(), 0.2471, 'magenta() works'); + assert.equal(pink.yellow(), 0.2039, 'yellow() works'); + assert.equal(pink.cyan(), 0.0000, 'cyan() works'); + + assert.ok(pink instanceof Color, 'is instance of Color'); + assert.ok(pink instanceof Class, 'is instance of Class'); +}; + +var Point = Class({ + initialize: function initialize(x, y) { + this.x = x; + this.y = y; + }, + toString: function toString() { + return this.x + ':' + this.y; + } +}) + +var Pixel = Class({ + extends: Point, + implements: [ Color ], + initialize: function initialize(x, y, color) { + Color.prototype.initialize.call(this, color); + Point.prototype.initialize.call(this, x, y); + }, + toString: function toString() { + return this.hex() + '@' + Point.prototype.toString.call(this) + } +}); + +exports['test compostion with inheritance'] = function(assert) { + var pixel = Pixel(11, 23, 'CC3399'); + + assert.equal(pixel.toString(), '#CC3399@11:23', 'stringifies correctly'); + assert.ok(pixel instanceof Pixel, 'instance of Pixel'); + assert.ok(pixel instanceof Point, 'instance of Point'); +}; + +exports['test composition with objects'] = function(assert) { + var A = { a: 1, b: 1 }; + var B = Class({ b: 2, c: 2 }); + var C = { c: 3 }; + var D = { d: 4 }; + + var ABCD = Class({ + implements: [ A, B, C, D ], + e: 5 + }); + + var f = ABCD(); + + assert.equal(f.a, 1, 'inherits A.a'); + assert.equal(f.b, 2, 'inherits B.b overrides A.b'); + assert.equal(f.c, 3, 'inherits C.c overrides B.c'); + assert.equal(f.d, 4, 'inherits D.d'); + assert.equal(f.e, 5, 'implements e'); +}; + +require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-hidden-frame.js b/addon-sdk-1.16/test/test-hidden-frame.js new file mode 100644 index 00000000..3a375b9b --- /dev/null +++ b/addon-sdk-1.16/test/test-hidden-frame.js @@ -0,0 +1,71 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const { Loader } = require("sdk/test/loader"); +const hiddenFrames = require("sdk/frame/hidden-frame"); +const { HiddenFrame } = hiddenFrames; + +exports["test Frame"] = function(assert, done) { + let url = "data:text/html;charset=utf-8,"; + + let hiddenFrame = hiddenFrames.add(HiddenFrame({ + onReady: function () { + assert.equal(this.element.contentWindow.location, "about:blank", + "HiddenFrame loads about:blank by default."); + + function onDOMReady() { + hiddenFrame.element.removeEventListener("DOMContentLoaded", onDOMReady, + false); + assert.equal(hiddenFrame.element.contentWindow.location, url, + "HiddenFrame loads the specified content."); + done(); + } + + this.element.addEventListener("DOMContentLoaded", onDOMReady, false); + this.element.setAttribute("src", url); + } + })); +}; + +exports["test frame removed properly"] = function(assert, done) { + let url = "data:text/html;charset=utf-8,"; + + let hiddenFrame = hiddenFrames.add(HiddenFrame({ + onReady: function () { + let frame = this.element; + assert.ok(frame.parentNode, "frame has a parent node"); + hiddenFrames.remove(hiddenFrame); + assert.ok(!frame.parentNode, "frame no longer has a parent node"); + done(); + } + })); +}; + +exports["test unload detaches panels"] = function(assert, done) { + let loader = Loader(module); + let { add, remove, HiddenFrame } = loader.require("sdk/frame/hidden-frame"); + let frames = [] + + function ready() { + frames.push(this.element); + if (frames.length === 2) complete(); + } + + add(HiddenFrame({ onReady: ready })); + add(HiddenFrame({ onReady: ready })); + + function complete() { + frames.forEach(function(frame) { + assert.ok(frame.parentNode, "frame is in the document"); + }) + loader.unload(); + frames.forEach(function(frame) { + assert.ok(!frame.parentNode, "frame isn't in the document'"); + }); + done(); + } +}; + +require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-host-events.js b/addon-sdk-1.16/test/test-host-events.js new file mode 100644 index 00000000..72e03f39 --- /dev/null +++ b/addon-sdk-1.16/test/test-host-events.js @@ -0,0 +1,99 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +'use strict'; + +const { Cc, Ci } = require('chrome'); +const { defer, all } = require('sdk/core/promise'); +const { setTimeout } = require('sdk/timers'); +const { request, response } = require('sdk/addon/host'); +const { send } = require('sdk/addon/events'); +const { filter } = require('sdk/event/utils'); +const { on, emit, off } = require('sdk/event/core'); + +let stream = filter(request, (data) => /sdk-x-test/.test(data.event)); + +exports.testSend = function (assert, done) { + on(stream, 'data', handle); + send('sdk-x-test-simple', { title: 'my test data' }).then((data) => { + assert.equal(data.title, 'my response', 'response is handled'); + off(stream, 'data', handle); + done(); + }, (reason) => { + assert.fail('should not call reject'); + }); + function handle (e) { + assert.equal(e.event, 'sdk-x-test-simple', 'correct event name'); + assert.ok(e.id != null, 'message has an ID'); + assert.equal(e.data.title, 'my test data', 'serialized data passes'); + e.data.title = 'my response'; + emit(response, 'data', e); + } +}; + +exports.testSendError = function (assert, done) { + on(stream, 'data', handle); + send('sdk-x-test-error', { title: 'my test data' }).then((data) => { + assert.fail('should not call success'); + }, (reason) => { + assert.equal(reason, 'ErrorInfo', 'should reject with error/reason'); + off(stream, 'data', handle); + done(); + }); + function handle (e) { + e.error = 'ErrorInfo'; + emit(response, 'data', e); + } +}; + +exports.testMultipleSends = function (assert, done) { + let count = 0; + let ids = []; + on(stream, 'data', handle); + ['firefox', 'thunderbird', 'rust'].map(data => + send('sdk-x-test-multi', { data: data }).then(val => { + assert.ok(val === 'firefox' || val === 'rust', 'successful calls resolve correctly'); + if (++count === 3) { + off(stream, 'data', handle); + done(); + } + }, reason => { + assert.equal(reason.error, 'too blue', 'rejected calls are rejected'); + if (++count === 3) { + off(stream, 'data', handle); + done(); + } + })); + + function handle (e) { + if (e.data !== 'firefox' || e.data !== 'rust') + e.error = { data: e.data, error: 'too blue' }; + assert.ok(!~ids.indexOf(e.id), 'ID should be unique'); + assert.equal(e.event, 'sdk-x-test-multi', 'has event name'); + ids.push(e.id); + emit(response, 'data', e); + } +}; + +exports.testSerialization = function (assert, done) { + on(stream, 'data', handle); + let object = { title: 'my test data' }; + let resObject; + send('sdk-x-test-serialize', object).then(data => { + data.title = 'another title'; + assert.equal(object.title, 'my test data', 'original object not modified'); + assert.equal(resObject.title, 'new title', 'object passed by value from host'); + off(stream, 'data', handle); + done(); + }, (reason) => { + assert.fail('should not call reject'); + }); + function handle (e) { + e.data.title = 'new title'; + assert.equal(object.title, 'my test data', 'object passed by value to host'); + resObject = e.data; + emit(response, 'data', e); + } +}; + +require('test').run(exports); diff --git a/addon-sdk-1.16/test/test-hotkeys.js b/addon-sdk-1.16/test/test-hotkeys.js new file mode 100644 index 00000000..77fe9a45 --- /dev/null +++ b/addon-sdk-1.16/test/test-hotkeys.js @@ -0,0 +1,164 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const { Hotkey } = require("sdk/hotkeys"); +const { keyDown } = require("sdk/dom/events/keys"); +const { Loader } = require('sdk/test/loader'); +const timer = require("sdk/timers"); +const winUtils = require("sdk/deprecated/window-utils"); + +exports["test hotkey: function key"] = function(assert, done) { + var element = winUtils.activeBrowserWindow.document.documentElement; + var showHotKey = Hotkey({ + combo: "f1", + onPress: function() { + assert.pass("first callback is called"); + assert.equal(this, showHotKey, + 'Context `this` in `onPress` should be the hotkey object'); + keyDown(element, "f2"); + showHotKey.destroy(); + } + }); + + var hideHotKey = Hotkey({ + combo: "f2", + onPress: function() { + assert.pass("second callback is called"); + hideHotKey.destroy(); + done(); + } + }); + + keyDown(element, "f1"); +}; + +exports["test hotkey: accel alt shift"] = function(assert, done) { + var element = winUtils.activeBrowserWindow.document.documentElement; + var showHotKey = Hotkey({ + combo: "accel-shift-6", + onPress: function() { + assert.pass("first callback is called"); + keyDown(element, "accel-alt-shift-6"); + showHotKey.destroy(); + } + }); + + var hideHotKey = Hotkey({ + combo: "accel-alt-shift-6", + onPress: function() { + assert.pass("second callback is called"); + hideHotKey.destroy(); + done(); + } + }); + + keyDown(element, "accel-shift-6"); +}; + +exports["test hotkey meta & control"] = function(assert, done) { + var element = winUtils.activeBrowserWindow.document.documentElement; + var showHotKey = Hotkey({ + combo: "meta-3", + onPress: function() { + assert.pass("first callback is called"); + keyDown(element, "alt-control-shift-b"); + showHotKey.destroy(); + } + }); + + var hideHotKey = Hotkey({ + combo: "Ctrl-Alt-Shift-B", + onPress: function() { + assert.pass("second callback is called"); + hideHotKey.destroy(); + done(); + } + }); + + keyDown(element, "meta-3"); +}; + +exports["test hotkey: control-1 / meta--"] = function(assert, done) { + var element = winUtils.activeBrowserWindow.document.documentElement; + var showHotKey = Hotkey({ + combo: "control-1", + onPress: function() { + assert.pass("first callback is called"); + keyDown(element, "meta--"); + showHotKey.destroy(); + } + }); + + var hideHotKey = Hotkey({ + combo: "meta--", + onPress: function() { + assert.pass("second callback is called"); + hideHotKey.destroy(); + done(); + } + }); + + keyDown(element, "control-1"); +}; + +exports["test invalid combos"] = function(assert) { + assert.throws(function() { + Hotkey({ + combo: "d", + onPress: function() {} + }); + }, "throws if no modifier is present"); + assert.throws(function() { + Hotkey({ + combo: "alt", + onPress: function() {} + }); + }, "throws if no key is present"); + assert.throws(function() { + Hotkey({ + combo: "alt p b", + onPress: function() {} + }); + }, "throws if more then one key is present"); +}; + +exports["test no exception on unmodified keypress"] = function(assert) { + var element = winUtils.activeBrowserWindow.document.documentElement; + var someHotkey = Hotkey({ + combo: "control-alt-1", + onPress: function() { + } + }); + keyDown(element, "a"); + assert.pass("No exception throw, unmodified keypress passed"); +}; + +exports["test hotkey: automatic destroy"] = function(assert, done) { + // Hacky way to be able to create unloadable modules via makeSandboxedLoader. + let loader = Loader(module); + + var called = false; + var element = loader.require("sdk/deprecated/window-utils").activeBrowserWindow.document.documentElement; + var hotkey = loader.require("sdk/hotkeys").Hotkey({ + combo: "accel-shift-x", + onPress: function() { + called = true; + } + }); + + // Unload the module so that previous hotkey is automatically destroyed + loader.unload(); + + // Ensure that the hotkey is really destroyed + keyDown(element, "accel-shift-x"); + + timer.setTimeout(function () { + assert.ok(!called, "Hotkey is destroyed and not called."); + done(); + }, 0); +}; + +require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-httpd.js b/addon-sdk-1.16/test/test-httpd.js new file mode 100644 index 00000000..2426148e --- /dev/null +++ b/addon-sdk-1.16/test/test-httpd.js @@ -0,0 +1,73 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const port = 8099; +const file = require("sdk/io/file"); +const { pathFor } = require("sdk/system"); +const { Loader } = require("sdk/test/loader"); +const options = require("@test/options"); + +const loader = Loader(module); +const httpd = loader.require("sdk/test/httpd"); +if (options.parseable || options.verbose) + loader.sandbox("sdk/test/httpd").DEBUG = true; + +exports.testBasicHTTPServer = function(assert, done) { + // Use the profile directory for the temporary file as that will be deleted + // when tests are complete + let basePath = pathFor("ProfD"); + let filePath = file.join(basePath, 'test-httpd.txt'); + let content = "This is the HTTPD test file.\n"; + let fileStream = file.open(filePath, 'w'); + fileStream.write(content); + fileStream.close(); + + let srv = httpd.startServerAsync(port, basePath); + + // Request this very file. + let Request = require('sdk/request').Request; + Request({ + url: "http://localhost:" + port + "/test-httpd.txt", + onComplete: function (response) { + assert.equal(response.text, content); + srv.stop(done); + } + }).get(); +}; + +exports.testDynamicServer = function (assert, done) { + let content = "This is the HTTPD test file.\n"; + + let srv = httpd.startServerAsync(port); + + // See documentation here: + //http://doxygen.db48x.net/mozilla/html/interfacensIHttpServer.html#a81fc7e7e29d82aac5ce7d56d0bedfb3a + //http://doxygen.db48x.net/mozilla/html/interfacensIHttpRequestHandler.html + srv.registerPathHandler("/test-httpd.txt", function handle(request, response) { + // Add text content type, only to avoid error in `Request` API + response.setHeader("Content-Type", "text/plain", false); + response.write(content); + }); + + // Request this very file. + let Request = require('sdk/request').Request; + Request({ + url: "http://localhost:" + port + "/test-httpd.txt", + onComplete: function (response) { + assert.equal(response.text, content); + srv.stop(done); + } + }).get(); +}; + +exports.testAutomaticPortSelection = function (assert, done) { + const srv = httpd.startServerAsync(-1); + + const port = srv.identity.primaryPort; + assert.ok(0 <= port && port <= 65535); + + srv.stop(done); +}; + +require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-indexed-db.js b/addon-sdk-1.16/test/test-indexed-db.js new file mode 100644 index 00000000..4f66859e --- /dev/null +++ b/addon-sdk-1.16/test/test-indexed-db.js @@ -0,0 +1,128 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +let xulApp = require("sdk/system/xul-app"); +if (xulApp.versionInRange(xulApp.platformVersion, "16.0a1", "*")) { +new function tests() { + +const { indexedDB, IDBKeyRange, DOMException + } = require("sdk/indexed-db"); + +exports["test indexedDB is frozen"] = function(assert){ + let original = indexedDB.open; + let f = function(){}; + assert.throws(function(){indexedDB.open = f}); + assert.equal(indexedDB.open,original); + assert.notEqual(indexedDB.open,f); + +}; + +exports["test db variables"] = function(assert) { + [ indexedDB, IDBKeyRange, DOMException + ].forEach(function(value) { + assert.notEqual(typeof(value), "undefined", "variable is defined"); + }); +} + +exports["test open"] = function(assert, done) { + let request = indexedDB.open("MyTestDatabase"); + request.onerror = function(event) { + assert.fail("Failed to open indexedDB") + done(); + }; + request.onsuccess = function(event) { + assert.pass("IndexedDB was open"); + done(); + }; +}; + +exports["test dbname is unprefixed"] = function(assert, done) { + // verify fixes in https://bugzilla.mozilla.org/show_bug.cgi?id=786688 + let dbName = "dbname-unprefixed"; + let request = indexedDB.open(dbName); + request.onerror = function(event) { + assert.fail("Failed to open db"); + done(); + }; + request.onsuccess = function(event) { + assert.equal(request.result.name, dbName); + done(); + }; +}; + +exports["test structuring the database"] = function(assert, done) { + // This is what our customer data looks like. + let customerData = [ + { ssn: "444-44-4444", name: "Bill", age: 35, email: "bill@company.com" }, + { ssn: "555-55-5555", name: "Donna", age: 32, email: "donna@home.org" } + ]; + let dbName = "the_name"; + let request = indexedDB.open(dbName, 2); + request.onerror = function(event) { + assert.fail("Failed to open db"); + done(); + }; + request.onsuccess = function(event) { + assert.pass("transaction is complete"); + testRead(assert, done); + } + request.onupgradeneeded = function(event) { + assert.pass("data base upgrade") + + var db = event.target.result; + + // Create an objectStore to hold information about our customers. We"re + // going to use "ssn" as our key path because it"s guaranteed to be + // unique. + var objectStore = db.createObjectStore("customers", { keyPath: "ssn" }); + + // Create an index to search customers by name. We may have duplicates + // so we can"t use a unique index. + objectStore.createIndex("name", "name", { unique: false }); + + // Create an index to search customers by email. We want to ensure that + // no two customers have the same email, so use a unique index. + objectStore.createIndex("email", "email", { unique: true }); + + // Store values in the newly created objectStore. + customerData.forEach(function(data) { + objectStore.add(data); + }); + assert.pass("data added to object store"); + }; +}; + +function testRead(assert, done) { + let dbName = "the_name"; + let request = indexedDB.open(dbName, 2); + request.onsuccess = function(event) { + assert.pass("data opened") + var db = event.target.result; + let transaction = db.transaction(["customers"]); + var objectStore = transaction.objectStore("customers"); + var request = objectStore.get("444-44-4444"); + request.onerror = function(event) { + assert.fail("Failed to retrive data") + }; + request.onsuccess = function(event) { + // Do something with the request.result! + assert.equal(request.result.name, "Bill", "Name is correct"); + done(); + }; + }; + request.onerror = function() { + assert.fail("failed to open db"); + }; +}; + +} +} else { + exports.testDB = function(assert) { + assert.pass("IndexedDB is not implemented") + } +} + +require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-keyboard-observer.js b/addon-sdk-1.16/test/test-keyboard-observer.js new file mode 100644 index 00000000..091ec73c --- /dev/null +++ b/addon-sdk-1.16/test/test-keyboard-observer.js @@ -0,0 +1,37 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const { keyPress } = require("sdk/dom/events/keys"); +const { Loader } = require("sdk/test/loader"); +const timer = require("sdk/timers"); + +exports["test unload keyboard observer"] = function(assert, done) { + let loader = Loader(module); + let element = loader.require("sdk/deprecated/window-utils"). + activeBrowserWindow.document.documentElement; + let observer = loader.require("sdk/keyboard/observer"). + observer; + let called = 0; + + observer.on("keypress", function () { called++; }); + + // dispatching "keypress" event to trigger observer listeners. + keyPress(element, "accel-%"); + + // Unload the module. + loader.unload(); + + // dispatching "keypress" even once again. + keyPress(element, "accel-%"); + + // Enqueuing asserts to make sure that assertion is not performed early. + timer.setTimeout(function () { + assert.equal(called, 1, "observer was called before unload only."); + done(); + }, 0); +}; + +require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-keyboard-utils.js b/addon-sdk-1.16/test/test-keyboard-utils.js new file mode 100644 index 00000000..19981dec --- /dev/null +++ b/addon-sdk-1.16/test/test-keyboard-utils.js @@ -0,0 +1,62 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const utils = require("sdk/keyboard/utils"); +const runtime = require("sdk/system/runtime"); + +const isMac = runtime.OS === "Darwin"; + +exports["test toString"] = function(assert) { + assert.equal(utils.toString({ + key: "B", + modifiers: [ "Shift", "Ctrl" ] + }), "Shift-Ctrl-B", "toString does not normalizes JSON"); + + assert.equal(utils.toString({ + key: "C", + modifiers: [], + }), "C", "Works with objects with empty array of modifiers"); + + assert.equal(utils.toString(Object.create((function Type() {}).prototype, { + key: { value: "d" }, + modifiers: { value: [ "alt" ] }, + method: { value: function() {} } + })), "alt-d", "Works with non-json objects"); + + assert.equal(utils.toString({ + modifiers: [ "shift", "alt" ] + }), "shift-alt-", "works with only modifiers"); +}; + +exports["test toJSON"] = function(assert) { + assert.deepEqual(utils.toJSON("Shift-Ctrl-B"), { + key: "b", + modifiers: [ "control", "shift" ] + }, "toJSON normalizes input"); + + assert.deepEqual(utils.toJSON("Meta-Alt-option-C"), { + key: "c", + modifiers: [ "alt", "meta" ] + }, "removes dublicates"); + + assert.deepEqual(utils.toJSON("AccEl+sHiFt+Z", "+"), { + key: "z", + modifiers: isMac ? [ "meta", "shift" ] : [ "control", "shift" ] + }, "normalizes OS specific keys and adjustes seperator"); +}; + +exports["test normalize"] = function assert(assert) { + assert.equal(utils.normalize("Shift Ctrl A control ctrl", " "), + "control shift a", "removes reapeted modifiers"); + assert.equal(utils.normalize("shift-ctrl-left"), "control-shift-left", + "normilizes non printed characters"); + + assert.throws(function() { + utils.normalize("shift-alt-b-z"); + }, "throws if contains more then on non-modifier key"); +}; + +require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-l10n-locale.js b/addon-sdk-1.16/test/test-l10n-locale.js new file mode 100644 index 00000000..87be1ca4 --- /dev/null +++ b/addon-sdk-1.16/test/test-l10n-locale.js @@ -0,0 +1,143 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const { getPreferedLocales, findClosestLocale } = require("sdk/l10n/locale"); +const prefs = require("sdk/preferences/service"); +const { Cc, Ci, Cu } = require("chrome"); +const { Services } = Cu.import("resource://gre/modules/Services.jsm"); +const BundleService = Cc["@mozilla.org/intl/stringbundle;1"].getService(Ci.nsIStringBundleService); + +const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS"; +const PREF_SELECTED_LOCALE = "general.useragent.locale"; +const PREF_ACCEPT_LANGUAGES = "intl.accept_languages"; + +function assertPrefered(assert, expected, msg) { + assert.equal(JSON.stringify(getPreferedLocales()), JSON.stringify(expected), + msg); +} + +exports.testGetPreferedLocales = function(assert) { + prefs.set(PREF_MATCH_OS_LOCALE, false); + prefs.set(PREF_SELECTED_LOCALE, ""); + prefs.set(PREF_ACCEPT_LANGUAGES, ""); + assertPrefered(assert, ["en-us"], + "When all preferences are empty, we only have en-us"); + + prefs.set(PREF_SELECTED_LOCALE, "fr"); + prefs.set(PREF_ACCEPT_LANGUAGES, "jp"); + assertPrefered(assert, ["fr", "jp", "en-us"], + "We first have useragent locale, then web one and finally en-US"); + + prefs.set(PREF_SELECTED_LOCALE, "en-US"); + prefs.set(PREF_ACCEPT_LANGUAGES, "en-US"); + assertPrefered(assert, ["en-us"], + "We do not have duplicates"); + + prefs.set(PREF_SELECTED_LOCALE, "en-US"); + prefs.set(PREF_ACCEPT_LANGUAGES, "fr"); + assertPrefered(assert, ["en-us", "fr"], + "en-US can be first if specified by higher priority preference"); + + // Reset what we changed + prefs.reset(PREF_MATCH_OS_LOCALE); + prefs.reset(PREF_SELECTED_LOCALE); + prefs.reset(PREF_ACCEPT_LANGUAGES); +} + +// In some cases, mainly on Fennec and on Linux version, +// `general.useragent.locale` is a special 'localized' value, like: +// "chrome://global/locale/intl.properties" +exports.testPreferedLocalizedLocale = function(assert) { + prefs.set(PREF_MATCH_OS_LOCALE, false); + let bundleURL = "chrome://global/locale/intl.properties"; + prefs.setLocalized(PREF_SELECTED_LOCALE, bundleURL); + let contentLocale = "ja"; + prefs.set(PREF_ACCEPT_LANGUAGES, contentLocale); + + // Read manually the expected locale value from the property file + let expectedLocale = BundleService.createBundle(bundleURL). + GetStringFromName(PREF_SELECTED_LOCALE). + toLowerCase(); + + // First add the useragent locale + let expectedLocaleList = [expectedLocale]; + + // Then the content locale + if (expectedLocaleList.indexOf(contentLocale) == -1) + expectedLocaleList.push(contentLocale); + + // Add default "en-us" fallback if the main language is not already en-us + if (expectedLocaleList.indexOf("en-us") == -1) + expectedLocaleList.push("en-us"); + + assertPrefered(assert, expectedLocaleList, "test localized pref value"); + + // Reset what we have changed + prefs.reset(PREF_MATCH_OS_LOCALE); + prefs.reset(PREF_SELECTED_LOCALE); + prefs.reset(PREF_ACCEPT_LANGUAGES); +} + +exports.testPreferedOsLocale = function(assert) { + prefs.set(PREF_MATCH_OS_LOCALE, true); + prefs.set(PREF_SELECTED_LOCALE, ""); + prefs.set(PREF_ACCEPT_LANGUAGES, ""); + + let expectedLocale = Services.locale.getLocaleComponentForUserAgent(). + toLowerCase(); + let expectedLocaleList = [expectedLocale]; + + // Add default "en-us" fallback if the main language is not already en-us + if (expectedLocale != "en-us") + expectedLocaleList.push("en-us"); + + assertPrefered(assert, expectedLocaleList, "Ensure that we select OS locale when related preference is set"); + + // Reset what we have changed + prefs.reset(PREF_MATCH_OS_LOCALE); + prefs.reset(PREF_SELECTED_LOCALE); + prefs.reset(PREF_ACCEPT_LANGUAGES); +} + +exports.testFindClosestLocale = function(assert) { + // Second param of findClosestLocale (aMatchLocales) have to be in lowercase + assert.equal(findClosestLocale([], []), null, + "When everything is empty we get null"); + + assert.equal(findClosestLocale(["en", "en-US"], ["en"]), + "en", "We always accept exact match first 1/5"); + assert.equal(findClosestLocale(["en-US", "en"], ["en"]), + "en", "We always accept exact match first 2/5"); + assert.equal(findClosestLocale(["en", "en-US"], ["en-us"]), + "en-US", "We always accept exact match first 3/5"); + assert.equal(findClosestLocale(["ja-JP-mac", "ja", "ja-JP"], ["ja-jp"]), + "ja-JP", "We always accept exact match first 4/5"); + assert.equal(findClosestLocale(["ja-JP-mac", "ja", "ja-JP"], ["ja-jp-mac"]), + "ja-JP-mac", "We always accept exact match first 5/5"); + + assert.equal(findClosestLocale(["en", "en-GB"], ["en-us"]), + "en", "We accept more generic locale, when there is no exact match 1/2"); + assert.equal(findClosestLocale(["en-ZA", "en"], ["en-gb"]), + "en", "We accept more generic locale, when there is no exact match 2/2"); + + assert.equal(findClosestLocale(["ja-JP"], ["ja"]), + "ja-JP", "We accept more specialized locale, when there is no exact match 1/2"); + // Better to select "ja" in this case but behave same as current AddonManager + assert.equal(findClosestLocale(["ja-JP-mac", "ja"], ["ja-jp"]), + "ja-JP-mac", "We accept more specialized locale, when there is no exact match 2/2"); + + assert.equal(findClosestLocale(["en-US"], ["en-us"]), + "en-US", "We keep the original one as result 1/2"); + assert.equal(findClosestLocale(["en-us"], ["en-us"]), + "en-us", "We keep the original one as result 2/2"); + + assert.equal(findClosestLocale(["ja-JP-mac"], ["ja-jp-mac"]), + "ja-JP-mac", "We accept locale with 3 parts"); + assert.equal(findClosestLocale(["ja-JP"], ["ja-jp-mac"]), + "ja-JP", "We accept locale with 2 parts from locale with 3 parts"); + assert.equal(findClosestLocale(["ja"], ["ja-jp-mac"]), + "ja", "We accept locale with 1 part from locale with 3 parts"); +}; + +require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-l10n-plural-rules.js b/addon-sdk-1.16/test/test-l10n-plural-rules.js new file mode 100644 index 00000000..6c56ec49 --- /dev/null +++ b/addon-sdk-1.16/test/test-l10n-plural-rules.js @@ -0,0 +1,84 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const { getRulesForLocale } = require("sdk/l10n/plural-rules"); + +// For more information, please visit unicode website: +// http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html + +function map(assert, f, n, form) { + assert.equal(f(n), form, n + " maps to '" + form + "'"); +} + +exports.testFrench = function(assert) { + let f = getRulesForLocale("fr"); + map(assert, f, -1, "other"); + map(assert, f, 0, "one"); + map(assert, f, 1, "one"); + map(assert, f, 1.5, "one"); + map(assert, f, 2, "other"); + map(assert, f, 100, "other"); +} + +exports.testEnglish = function(assert) { + let f = getRulesForLocale("en"); + map(assert, f, -1, "other"); + map(assert, f, 0, "other"); + map(assert, f, 1, "one"); + map(assert, f, 1.5, "other"); + map(assert, f, 2, "other"); + map(assert, f, 100, "other"); +} + +exports.testArabic = function(assert) { + let f = getRulesForLocale("ar"); + map(assert, f, -1, "other"); + map(assert, f, 0, "zero"); + map(assert, f, 0.5, "other"); + + map(assert, f, 1, "one"); + map(assert, f, 1.5, "other"); + + map(assert, f, 2, "two"); + map(assert, f, 2.5, "other"); + + map(assert, f, 3, "few"); + map(assert, f, 3.5, "few"); // I'd expect it to be 'other', but the unicode.org + // algorithm computes 'few'. + map(assert, f, 5, "few"); + map(assert, f, 10, "few"); + map(assert, f, 103, "few"); + map(assert, f, 105, "few"); + map(assert, f, 110, "few"); + map(assert, f, 203, "few"); + map(assert, f, 205, "few"); + map(assert, f, 210, "few"); + + map(assert, f, 11, "many"); + map(assert, f, 50, "many"); + map(assert, f, 99, "many"); + map(assert, f, 111, "many"); + map(assert, f, 150, "many"); + map(assert, f, 199, "many"); + + map(assert, f, 100, "other"); + map(assert, f, 101, "other"); + map(assert, f, 102, "other"); + map(assert, f, 200, "other"); + map(assert, f, 201, "other"); + map(assert, f, 202, "other"); +} + +exports.testJapanese = function(assert) { + // Japanese doesn't have plural forms. + let f = getRulesForLocale("ja"); + map(assert, f, -1, "other"); + map(assert, f, 0, "other"); + map(assert, f, 1, "other"); + map(assert, f, 1.5, "other"); + map(assert, f, 2, "other"); + map(assert, f, 100, "other"); +} + +require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-light-traits.js b/addon-sdk-1.16/test/test-light-traits.js new file mode 100644 index 00000000..7c5ca42d --- /dev/null +++ b/addon-sdk-1.16/test/test-light-traits.js @@ -0,0 +1,11 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +exports["test traits from objects"] = require("./traits/object-tests"); +exports["test traits from descriptors"] = require("./traits/descriptor-tests"); +exports["test inheritance"] = require("./traits/inheritance-tests"); + +require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-list.js b/addon-sdk-1.16/test/test-list.js new file mode 100644 index 00000000..10be4bee --- /dev/null +++ b/addon-sdk-1.16/test/test-list.js @@ -0,0 +1,58 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +'use strict'; + +const { List, addListItem, removeListItem } = require('sdk/util/list'); +const { Class } = require('sdk/core/heritage'); + +exports.testList = function(assert) { + let list = List(); + addListItem(list, 1); + + for (let key in list) { + assert.equal(key, 0, 'key is correct'); + assert.equal(list[key], 1, 'value is correct'); + } + + let count = 0; + for each (let ele in list) { + assert.equal(ele, 1, 'ele is correct'); + assert.equal(++count, 1, 'count is correct'); + } + + count = 0; + for (let ele of list) { + assert.equal(ele, 1, 'ele is correct'); + assert.equal(++count, 1, 'count is correct'); + } + + removeListItem(list, 1); + assert.equal(list.length, 0, 'remove worked'); +}; + +exports.testImplementsList = function(assert) { + let List2 = Class({ + implements: [List], + initialize: function() { + List.prototype.initialize.apply(this, [0, 1, 2]); + } + }); + let list2 = List2(); + let count = 0; + + for each (let ele in list2) { + assert.equal(ele, count++, 'ele is correct'); + } + + count = 0; + for (let ele of list2) { + assert.equal(ele, count++, 'ele is correct'); + } + + addListItem(list2, 3); + assert.equal(list2.length, 4, '3 was added'); + assert.equal(list2[list2.length-1], 3, '3 was added'); +} + +require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-loader.js b/addon-sdk-1.16/test/test-loader.js new file mode 100644 index 00000000..4ddcc008 --- /dev/null +++ b/addon-sdk-1.16/test/test-loader.js @@ -0,0 +1,346 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +'use strict'; + +let { + Loader, main, unload, parseStack, generateMap, resolve, join +} = require('toolkit/loader'); +let { readURI } = require('sdk/net/url'); + +let root = module.uri.substr(0, module.uri.lastIndexOf('/')) + + +// The following adds Debugger constructor to the global namespace. +const { Cu } = require('chrome'); +const { addDebuggerToGlobal } = Cu.import('resource://gre/modules/jsdebugger.jsm', {}); +addDebuggerToGlobal(this); + +exports['test resolve'] = function (assert) { + let cuddlefish_id = 'sdk/loader/cuddlefish'; + assert.equal(resolve('../index.js', './dir/c.js'), './index.js'); + assert.equal(resolve('./index.js', './dir/c.js'), './dir/index.js'); + assert.equal(resolve('./dir/c.js', './index.js'), './dir/c.js'); + assert.equal(resolve('../utils/file.js', './dir/b.js'), './utils/file.js'); + + assert.equal(resolve('../utils/./file.js', './dir/b.js'), './utils/file.js'); + assert.equal(resolve('../utils/file.js', './'), './../utils/file.js'); + assert.equal(resolve('./utils/file.js', './'), './utils/file.js'); + assert.equal(resolve('./utils/file.js', './index.js'), './utils/file.js'); + + assert.equal(resolve('../utils/./file.js', cuddlefish_id), 'sdk/utils/file.js'); + assert.equal(resolve('../utils/file.js', cuddlefish_id), 'sdk/utils/file.js'); + assert.equal(resolve('./utils/file.js', cuddlefish_id), 'sdk/loader/utils/file.js'); + + assert.equal(resolve('..//index.js', './dir/c.js'), './index.js'); + assert.equal(resolve('../../gre/modules/XPCOMUtils.jsm', 'resource://thing/utils/file.js'), 'resource://gre/modules/XPCOMUtils.jsm'); + assert.equal(resolve('../../gre/modules/XPCOMUtils.jsm', 'chrome://thing/utils/file.js'), 'chrome://gre/modules/XPCOMUtils.jsm'); + assert.equal(resolve('../../a/b/c.json', 'file:///thing/utils/file.js'), 'file:///a/b/c.json'); + + // Does not change absolute paths + assert.equal(resolve('resource://gre/modules/file.js', './dir/b.js'), + 'resource://gre/modules/file.js'); + assert.equal(resolve('file:///gre/modules/file.js', './dir/b.js'), + 'file:///gre/modules/file.js'); + assert.equal(resolve('/root.js', './dir/b.js'), + '/root.js'); +}; + +exports['test join'] = function (assert) { + assert.equal(join('a/path', '../../../module'), '../module'); + assert.equal(join('a/path/to', '../module'), 'a/path/module'); + assert.equal(join('a/path/to', './module'), 'a/path/to/module'); + assert.equal(join('a/path/to', '././../module'), 'a/path/module'); + assert.equal(join('resource://my/path/yeah/yuh', '../whoa'), + 'resource://my/path/yeah/whoa'); + assert.equal(join('resource://my/path/yeah/yuh', './whoa'), + 'resource://my/path/yeah/yuh/whoa'); + assert.equal(join('file:///my/path/yeah/yuh', '../whoa'), + 'file:///my/path/yeah/whoa'); + assert.equal(join('file:///my/path/yeah/yuh', './whoa'), + 'file:///my/path/yeah/yuh/whoa'); + assert.equal(join('a/path/to', '..//module'), 'a/path/module'); +}; + +exports['test dependency cycles'] = function(assert) { + let uri = root + '/fixtures/loader/cycles/'; + let loader = Loader({ paths: { '': uri } }); + + let program = main(loader, 'main'); + + assert.equal(program.a.b, program.b, 'module `a` gets correct `b`'); + assert.equal(program.b.a, program.a, 'module `b` gets correct `a`'); + assert.equal(program.c.main, program, 'module `c` gets correct `main`'); + + unload(loader); +} + +exports['test syntax errors'] = function(assert) { + let uri = root + '/fixtures/loader/syntax-error/'; + let loader = Loader({ paths: { '': uri } }); + + try { + let program = main(loader, 'main'); + } catch (error) { + assert.equal(error.name, "SyntaxError", "throws syntax error"); + assert.equal(error.fileName.split("/").pop(), "error.js", + "Error contains filename"); + assert.equal(error.lineNumber, 11, "error is on line 11"); + let stack = parseStack(error.stack); + + assert.equal(stack.pop().fileName, uri + "error.js", + "last frame file containing syntax error"); + assert.equal(stack.pop().fileName, uri + "main.js", + "previous frame is a requirer module"); + assert.equal(stack.pop().fileName, module.uri, + "previous to it is a test module"); + + } finally { + unload(loader); + } +} + +exports['test sandboxes are not added if error'] = function (assert) { + let uri = root + '/fixtures/loader/missing-twice/'; + let loader = Loader({ paths: { '': uri } }); + let program = main(loader, 'main'); + assert.ok(!(uri + 'not-found.js' in loader.sandboxes), 'not-found.js not in loader.sandboxes'); +} + +exports['test missing module'] = function(assert) { + let uri = root + '/fixtures/loader/missing/' + let loader = Loader({ paths: { '': uri } }); + + try { + let program = main(loader, 'main') + } catch (error) { + assert.equal(error.message, "Module `not-found` is not found at " + + uri + "not-found.js", "throws if error not found"); + + assert.equal(error.fileName.split("/").pop(), "main.js", + "Error fileName is requirer module"); + + assert.equal(error.lineNumber, 7, "error is on line 7"); + + let stack = parseStack(error.stack); + + assert.equal(stack.pop().fileName, uri + "main.js", + "loader stack is omitted"); + + assert.equal(stack.pop().fileName, module.uri, + "previous in the stack is test module"); + } finally { + unload(loader); + } +} + +exports["test invalid module not cached and throws everytime"] = function(assert) { + let uri = root + "/fixtures/loader/missing-twice/"; + let loader = Loader({ paths: { "": uri } }); + + let { firstError, secondError, invalidJSON1, invalidJSON2 } = main(loader, "main"); + assert.equal(firstError.message, "Module `not-found` is not found at " + + uri + "not-found.js", "throws on first invalid require"); + assert.equal(firstError.lineNumber, 8, "first error is on line 7"); + assert.equal(secondError.message, "Module `not-found` is not found at " + + uri + "not-found.js", "throws on second invalid require"); + assert.equal(secondError.lineNumber, 14, "second error is on line 14"); + + assert.equal(invalidJSON1.message, + "JSON.parse: unexpected character at line 1 column 1 of the JSON data", + "throws on invalid JSON"); + assert.equal(invalidJSON2.message, + "JSON.parse: unexpected character at line 1 column 1 of the JSON data", + "throws on invalid JSON second time"); +}; + +exports['test exceptions in modules'] = function(assert) { + let uri = root + '/fixtures/loader/exceptions/' + + let loader = Loader({ paths: { '': uri } }); + + try { + let program = main(loader, 'main') + } catch (error) { + assert.equal(error.message, "Boom!", "thrown errors propagate"); + + assert.equal(error.fileName.split("/").pop(), "boomer.js", + "Error comes from the module that threw it"); + + assert.equal(error.lineNumber, 8, "error is on line 8"); + + let stack = parseStack(error.stack); + + let frame = stack.pop() + assert.equal(frame.fileName, uri + "boomer.js", + "module that threw is first in the stack"); + assert.equal(frame.name, "exports.boom", + "name is in the stack"); + + frame = stack.pop() + assert.equal(frame.fileName, uri + "main.js", + "module that called it is next in the stack"); + assert.equal(frame.lineNumber, 9, "caller line is in the stack"); + + + assert.equal(stack.pop().fileName, module.uri, + "this test module is next in the stack"); + } finally { + unload(loader); + } +} + +exports['test early errors in module'] = function(assert) { + let uri = root + '/fixtures/loader/errors/'; + let loader = Loader({ paths: { '': uri } }); + + try { + let program = main(loader, 'main') + } catch (error) { + assert.equal(String(error), + "Error: opening input stream (invalid filename?)", + "thrown errors propagate"); + + assert.equal(error.fileName.split("/").pop(), "boomer.js", + "Error comes from the module that threw it"); + + assert.equal(error.lineNumber, 7, "error is on line 7"); + + let stack = parseStack(error.stack); + + let frame = stack.pop() + assert.equal(frame.fileName, uri + "boomer.js", + "module that threw is first in the stack"); + + frame = stack.pop() + assert.equal(frame.fileName, uri + "main.js", + "module that called it is next in the stack"); + assert.equal(frame.lineNumber, 7, "caller line is in the stack"); + + + assert.equal(stack.pop().fileName, module.uri, + "this test module is next in the stack"); + } finally { + unload(loader); + } +}; + +exports['test require json'] = function (assert) { + let data = require('./fixtures/loader/json/manifest.json'); + assert.equal(data.name, 'Jetpack Loader Test', 'loads json with strings'); + assert.equal(data.version, '1.0.1', 'loads json with strings'); + assert.equal(data.dependencies.async, '*', 'loads json with objects'); + assert.equal(data.dependencies.underscore, '*', 'loads json with objects'); + assert.equal(data.contributors.length, 4, 'loads json with arrays'); + assert.ok(Array.isArray(data.contributors), 'loads json with arrays'); + data.version = '2.0.0'; + let newdata = require('./fixtures/loader/json/manifest.json'); + assert.equal(newdata.version, '2.0.0', + 'JSON objects returned should be cached and the same instance'); + + try { + require('./fixtures/loader/json/invalid.json'); + assert.fail('Error not thrown when loading invalid json'); + } catch (err) { + assert.ok(err, 'error thrown when loading invalid json'); + assert.ok(/JSON\.parse/.test(err.message), + 'should thrown an error from JSON.parse, not attempt to load .json.js'); + } + + // Try again to ensure an empty module isn't loaded from cache + try { + require('./fixtures/loader/json/invalid.json'); + assert.fail('Error not thrown when loading invalid json a second time'); + } catch (err) { + assert.ok(err, + 'error thrown when loading invalid json a second time'); + assert.ok(/JSON\.parse/.test(err.message), + 'should thrown an error from JSON.parse a second time, not attempt to load .json.js'); + } +}; + +exports['test setting metadata for newly created sandboxes'] = function(assert) { + let addonID = 'random-addon-id'; + let uri = root + '/fixtures/loader/cycles/'; + let loader = Loader({ paths: { '': uri }, id: addonID }); + + let dbg = new Debugger(); + dbg.onNewGlobalObject = function(global) { + dbg.onNewGlobalObject = undefined; + + let metadata = Cu.getSandboxMetadata(global.unsafeDereference()); + assert.ok(metadata, 'this global has attached metadata'); + assert.equal(metadata.URI, uri + 'main.js', 'URI is set properly'); + assert.equal(metadata.addonID, addonID, 'addon ID is set'); + } + + let program = main(loader, 'main'); +}; + +exports['test require .json, .json.js'] = function (assert) { + let testjson = require('./fixtures/loader/json/test.json'); + assert.equal(testjson.filename, 'test.json', + 'require("./x.json") should load x.json, not x.json.js'); + + let nodotjson = require('./fixtures/loader/json/nodotjson.json'); + assert.equal(nodotjson.filename, 'nodotjson.json.js', + 'require("./x.json") should load x.json.js when x.json does not exist'); + nodotjson.data.prop = 'hydralisk'; + + // require('nodotjson.json') and require('nodotjson.json.js') + // should resolve to the same file + let nodotjsonjs = require('./fixtures/loader/json/nodotjson.json.js'); + assert.equal(nodotjsonjs.data.prop, 'hydralisk', + 'js modules are cached whether access via .json.js or .json'); +}; + +exports['test invisibleToDebugger: false'] = function (assert) { + let uri = root + '/fixtures/loader/cycles/'; + let loader = Loader({ paths: { '': uri } }); + main(loader, 'main'); + + let dbg = new Debugger(); + let sandbox = loader.sandboxes[uri + 'main.js']; + + try { + dbg.addDebuggee(sandbox); + assert.ok(true, 'debugger added visible value'); + } catch(e) { + assert.fail('debugger could not add visible value'); + } +}; + +exports['test invisibleToDebugger: true'] = function (assert) { + let uri = root + '/fixtures/loader/cycles/'; + let loader = Loader({ paths: { '': uri }, invisibleToDebugger: true }); + main(loader, 'main'); + + let dbg = new Debugger(); + let sandbox = loader.sandboxes[uri + 'main.js']; + + try { + dbg.addDebuggee(sandbox); + assert.fail('debugger added invisible value'); + } catch(e) { + assert.ok(true, 'debugger did not add invisible value'); + } +}; + +exports['test console global by default'] = function (assert) { + let uri = root + '/fixtures/loader/globals/'; + let loader = Loader({ paths: { '': uri }}); + let program = main(loader, 'main'); + + assert.ok(typeof program.console === 'object', 'global `console` exists'); + assert.ok(typeof program.console.log === 'function', 'global `console.log` exists'); + + let loader2 = Loader({ paths: { '': uri }, globals: { console: fakeConsole }}); + let program2 = main(loader2, 'main'); + + assert.equal(program2.console, fakeConsole, + 'global console can be overridden with Loader options'); + function fakeConsole () {}; +}; + +require('test').run(exports); diff --git a/addon-sdk-1.16/test/test-match-pattern.js b/addon-sdk-1.16/test/test-match-pattern.js new file mode 100644 index 00000000..7e970899 --- /dev/null +++ b/addon-sdk-1.16/test/test-match-pattern.js @@ -0,0 +1,141 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const { MatchPattern } = require("sdk/util/match-pattern"); + +exports.testMatchPatternTestTrue = function(assert) { + function ok(pattern, url) { + let mp = new MatchPattern(pattern); + assert.ok(mp.test(url), pattern + " should match " + url); + } + + ok("*", "http://example.com"); + ok("*", "https://example.com"); + ok("*", "ftp://example.com"); + + ok("*.example.com", "http://example.com"); + ok("*.example.com", "http://hamburger.example.com"); + ok("*.example.com", "http://hotdog.hamburger.example.com"); + + ok("http://example.com*", "http://example.com"); + ok("http://example.com*", "http://example.com/"); + ok("http://example.com/*", "http://example.com/"); + ok("http://example.com/*", "http://example.com/potato-salad"); + ok("http://example.com/pickles/*", "http://example.com/pickles/"); + ok("http://example.com/pickles/*", "http://example.com/pickles/lemonade"); + + ok("http://example.com", "http://example.com"); + ok("http://example.com/ice-cream", "http://example.com/ice-cream"); + + ok(/.*zilla.*/, "https://bugzilla.redhat.com/show_bug.cgi?id=569753"); + ok(/https:.*zilla.*/, "https://bugzilla.redhat.com/show_bug.cgi?id=569753"); + ok('*.sample.com', 'http://ex.sample.com/foo.html'); + ok('*.amp.le.com', 'http://ex.amp.le.com'); + + ok('data:*', 'data:text/html;charset=utf-8,'); +}; + +exports.testMatchPatternTestFalse = function(assert) { + function ok(pattern, url) { + let mp = new MatchPattern(pattern); + assert.ok(!mp.test(url), pattern + " should not match " + url); + } + + ok("*", null); + ok("*", ""); + ok("*", "bogus"); + ok("*", "chrome://browser/content/browser.xul"); + ok("*", "nttp://example.com"); + + ok("*.example.com", null); + ok("*.example.com", ""); + ok("*.example.com", "bogus"); + ok("*.example.com", "http://example.net"); + ok("*.example.com", "http://foo.com"); + ok("*.example.com", "http://example.com.foo"); + ok("*.example2.com", "http://example.com"); + + ok("http://example.com/*", null); + ok("http://example.com/*", ""); + ok("http://example.com/*", "bogus"); + ok("http://example.com/*", "http://example.com"); + ok("http://example.com/*", "http://foo.com/"); + + ok("http://example.com", null); + ok("http://example.com", ""); + ok("http://example.com", "bogus"); + ok("http://example.com", "http://example.com/"); + + ok(/zilla.*/, "https://bugzilla.redhat.com/show_bug.cgi?id=569753"); + ok(/.*zilla/, "https://bugzilla.redhat.com/show_bug.cgi?id=569753"); + ok(/.*Zilla.*/, "https://bugzilla.redhat.com/show_bug.cgi?id=655464"); // bug 655464 + ok(/https:.*zilla/, "https://bugzilla.redhat.com/show_bug.cgi?id=569753"); + + // bug 856913 + ok('*.ign.com', 'http://www.design.com'); + ok('*.ign.com', 'http://design.com'); + ok('*.zilla.com', 'http://bugzilla.mozilla.com'); + ok('*.zilla.com', 'http://mo-zilla.com'); + ok('*.amp.le.com', 'http://amp-le.com'); + ok('*.amp.le.com', 'http://examp.le.com'); +}; + +exports.testMatchPatternErrors = function(assert) { + assert.throws( + function() new MatchPattern("*.google.com/*"), + /There can be at most one/, + "MatchPattern throws when supplied multiple '*'" + ); + + assert.throws( + function() new MatchPattern("google.com"), + /expected to be either an exact URL/, + "MatchPattern throws when the wildcard doesn't use '*' and doesn't " + + "look like a URL" + ); + + assert.throws( + function() new MatchPattern("http://google*.com"), + /expected to be the first or the last/, + "MatchPattern throws when a '*' is in the middle of the wildcard" + ); + + assert.throws( + function() new MatchPattern(/ /g), + /^A RegExp match pattern cannot be set to `global` \(i\.e\. \/\/g\)\.$/, + "MatchPattern throws on a RegExp set to `global` (i.e. //g)." + ); + + assert.throws( + function() new MatchPattern(/ /i), + /^A RegExp match pattern cannot be set to `ignoreCase` \(i\.e\. \/\/i\)\.$/, + "MatchPattern throws on a RegExp set to `ignoreCase` (i.e. //i)." + ); + + assert.throws( + function() new MatchPattern( / /m ), + /^A RegExp match pattern cannot be set to `multiline` \(i\.e\. \/\/m\)\.$/, + "MatchPattern throws on a RegExp set to `multiline` (i.e. //m)." + ); +}; + +exports.testMatchPatternInternals = function(assert) { + assert.equal( + new MatchPattern("http://google.com/test").exactURL, + "http://google.com/test" + ); + + assert.equal( + new MatchPattern("http://google.com/test/*").urlPrefix, + "http://google.com/test/" + ); + + assert.equal( + new MatchPattern("*.example.com").domain, + "example.com" + ); +}; + +require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-memory.js b/addon-sdk-1.16/test/test-memory.js new file mode 100644 index 00000000..694172d0 --- /dev/null +++ b/addon-sdk-1.16/test/test-memory.js @@ -0,0 +1,22 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const memory = require("sdk/deprecated/memory"); +const { gc } = require("sdk/test/memory"); + +exports.testMemory = function(assert) { + var obj = {}; + memory.track(obj, "testMemory.testObj"); + + var objs = memory.getObjects("testMemory.testObj"); + assert.equal(objs[0].weakref.get(), obj); + obj = null; + + gc().then(function() { + assert.equal(objs[0].weakref.get(), null); + }); +}; + +require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-method.js b/addon-sdk-1.16/test/test-method.js new file mode 100644 index 00000000..0478593c --- /dev/null +++ b/addon-sdk-1.16/test/test-method.js @@ -0,0 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +module.exports = require("method/test/common"); diff --git a/addon-sdk-1.16/test/test-module.js b/addon-sdk-1.16/test/test-module.js new file mode 100644 index 00000000..6c9c6c2b --- /dev/null +++ b/addon-sdk-1.16/test/test-module.js @@ -0,0 +1,37 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +/** Disabled because of Bug 672199 +exports["test module exports are frozen"] = function(assert) { + assert.ok(Object.isFrozen(require("sdk/hotkeys")), + "module exports are frozen"); +}; + +exports["test redefine exported property"] = function(assert) { + let hotkeys = require("sdk/hotkeys"); + let { Hotkey } = hotkeys; + try { Object.defineProperty(hotkeys, 'Hotkey', { value: {} }); } catch(e) {} + assert.equal(hotkeys.Hotkey, Hotkey, "exports can't be redefined"); +}; +*/ + +exports["test can't delete exported property"] = function(assert) { + let hotkeys = require("sdk/hotkeys"); + let { Hotkey } = hotkeys; + + try { delete hotkeys.Hotkey; } catch(e) {} + assert.equal(hotkeys.Hotkey, Hotkey, "exports can't be deleted"); +}; + +exports["test can't override exported property"] = function(assert) { + let hotkeys = require("sdk/hotkeys"); + let { Hotkey } = hotkeys; + + try { hotkeys.Hotkey = Object } catch(e) {} + assert.equal(hotkeys.Hotkey, Hotkey, "exports can't be overriden"); +}; + +require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-modules.js b/addon-sdk-1.16/test/test-modules.js new file mode 100644 index 00000000..ee9d3d9b --- /dev/null +++ b/addon-sdk-1.16/test/test-modules.js @@ -0,0 +1,150 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +exports.testDefine = function(assert) { + let tiger = require('./modules/tiger'); + assert.equal(tiger.name, 'tiger', 'name proprety was exported properly'); + assert.equal(tiger.type, 'cat', 'property form other module exported'); +}; + +exports.testDefineInoresNonFactory = function(assert) { + let mod = require('./modules/async2'); + assert.equal(mod.name, 'async2', 'name proprety was exported properly'); + assert.ok(mod.traditional2Name !== 'traditional2', '1st is ignored'); +}; +/* Disable test that require AMD specific functionality: + +// define() that exports a function as the module value, +// specifying a module name. +exports.testDefExport = function(assert) { + var add = require('modules/add'); + assert.equal(add(1, 1), 2, 'Named define() exporting a function'); +}; + +// define() that exports function as a value, but is anonymous +exports.testAnonDefExport = function (assert) { + var subtract = require('modules/subtract'); + assert.equal(subtract(4, 2), 2, + 'Anonymous define() exporting a function'); +} + +// using require([], function () {}) to load modules. +exports.testSimpleRequire = function (assert) { + require(['modules/blue', 'modules/orange'], function (blue, orange) { + assert.equal(blue.name, 'blue', 'Simple require for blue'); + assert.equal(orange.name, 'orange', 'Simple require for orange'); + assert.equal(orange.parentType, 'color', + 'Simple require dependency check for orange'); + }); +} + +// using nested require([]) calls. +exports.testSimpleRequireNested = function (assert) { + require(['modules/blue', 'modules/orange', 'modules/green'], + function (blue, orange, green) { + + require(['modules/orange', 'modules/red'], function (orange, red) { + assert.equal(red.name, 'red', 'Simple require for red'); + assert.equal(red.parentType, 'color', + 'Simple require dependency check for red'); + assert.equal(blue.name, 'blue', 'Simple require for blue'); + assert.equal(orange.name, 'orange', 'Simple require for orange'); + assert.equal(orange.parentType, 'color', + 'Simple require dependency check for orange'); + assert.equal(green.name, 'green', 'Simple require for green'); + assert.equal(green.parentType, 'color', + 'Simple require dependency check for green'); + }); + + }); +} + +// requiring a traditional module, that uses async, that use traditional and +// async, with a circular reference +exports.testMixedCircular = function (assert) { + var t = require('modules/traditional1'); + assert.equal(t.name, 'traditional1', 'Testing name'); + assert.equal(t.traditional2Name, 'traditional2', + 'Testing dependent name'); + assert.equal(t.traditional1Name, 'traditional1', 'Testing circular name'); + assert.equal(t.async2Name, 'async2', 'Testing async2 name'); + assert.equal(t.async2Traditional2Name, 'traditional2', + 'Testing nested traditional2 name'); +} + +// Testing define()(function(require) {}) with some that use exports, +// some that use return. +exports.testAnonExportsReturn = function (assert) { + var lion = require('modules/lion'); + require(['modules/tiger', 'modules/cheetah'], function (tiger, cheetah) { + assert.equal('lion', lion, 'Check lion name'); + assert.equal('tiger', tiger.name, 'Check tiger name'); + assert.equal('cat', tiger.type, 'Check tiger type'); + assert.equal('cheetah', cheetah(), 'Check cheetah name'); + }); +} + +// circular dependency +exports.testCircular = function (assert) { + var pollux = require('modules/pollux'), + castor = require('modules/castor'); + + assert.equal(pollux.name, 'pollux', 'Pollux\'s name'); + assert.equal(pollux.getCastorName(), + 'castor', 'Castor\'s name from Pollux.'); + assert.equal(castor.name, 'castor', 'Castor\'s name'); + assert.equal(castor.getPolluxName(), 'pollux', + 'Pollux\'s name from Castor.'); +} + +// test a bad module that asks for exports but also does a define() return +exports.testBadExportAndReturn = function (assert) { + var passed = false; + try { + var bad = require('modules/badExportAndReturn'); + } catch(e) { + passed = /cannot use exports and also return/.test(e.toString()); + } + assert.equal(passed, true, 'Make sure exports and return fail'); +} + +// test a bad circular dependency, where an exported value is needed, but +// the return value happens too late, a module already asked for the exported +// value. +exports.testBadExportAndReturnCircular = function (assert) { + var passed = false; + try { + var bad = require('modules/badFirst'); + } catch(e) { + passed = /after another module has referenced its exported value/ + .test(e.toString()); + } + assert.equal(passed, true, 'Make sure return after an exported ' + + 'value is grabbed by another module fails.'); +} + +// only allow one define call per file. +exports.testOneDefine = function (assert) { + var passed = false; + try { + var dupe = require('modules/dupe'); + } catch(e) { + passed = /Only one call to define/.test(e.toString()); + } + assert.equal(passed, true, 'Only allow one define call per module'); +} + +// only allow one define call per file, testing a bad nested define call. +exports.testOneDefineNested = function (assert) { + var passed = false; + try { + var dupe = require('modules/dupeNested'); + } catch(e) { + passed = /Only one call to define/.test(e.toString()); + } + assert.equal(passed, true, 'Only allow one define call per module'); +} +*/ + +require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-namespace.js b/addon-sdk-1.16/test/test-namespace.js new file mode 100644 index 00000000..46f194a3 --- /dev/null +++ b/addon-sdk-1.16/test/test-namespace.js @@ -0,0 +1,121 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const { ns } = require("sdk/core/namespace"); +const { Cc, Ci, Cu } = require("chrome"); +const { setTimeout } = require("sdk/timers") + +exports["test post GC references"] = function (assert, done) { + var target = {}, local = ns() + local(target).there = true + + assert.equal(local(target).there, true, "namespaced preserved"); + + Cu.schedulePreciseGC(function() { + assert.equal(local(target).there, true, "namespace is preserved post GC"); + done(); + }); +}; + +exports["test namsepace basics"] = function(assert) { + var privates = ns(); + var object = { foo: function foo() { return "hello foo"; } }; + + assert.notEqual(privates(object), object, + "namespaced object is not the same"); + assert.ok(!('foo' in privates(object)), + "public properties are not in the namespace"); + + assert.equal(privates(object), privates(object), + "same namespaced object is returned on each call"); +}; + +exports["test namespace overlays"] = function(assert) { + var _ = ns(); + var object = { foo: 'foo' }; + + _(object).foo = 'bar'; + + assert.equal(_(object).foo, "bar", + "namespaced property `foo` changed value"); + + assert.equal(object.foo, "foo", + "public property `foo` has original value"); + + object.foo = "baz"; + assert.equal(_(object).foo, "bar", + "property changes do not affect namespaced properties"); + + object.bar = "foo"; + assert.ok(!("bar" in _(object)), + "new public properties are not reflected in namespace"); +}; + +exports["test shared namespaces"] = function(assert) { + var _ = ns(); + + var f1 = { hello: 1 }; + var f2 = { foo: 'foo', hello: 2 }; + _(f1).foo = _(f2).foo = 'bar'; + + assert.equal(_(f1).hello, _(f2).hello, "namespace can be shared"); + assert.notEqual(f1.hello, _(f1).hello, "shared namespace can overlay"); + assert.notEqual(f2.hello, _(f2).hello, "target is not affected"); + + _(f1).hello = 3; + + assert.notEqual(_(f1).hello, _(f2).hello, + "namespaced property can be overided"); + assert.equal(_(f2).hello, _({}).hello, "namespace does not change"); +}; + +exports["test multi namespace"] = function(assert) { + var n1 = ns(); + var n2 = ns(); + var object = { baz: 1 }; + n1(object).foo = 1; + n2(object).foo = 2; + n1(object).bar = n2(object).bar = 3; + + assert.notEqual(n1(object).foo, n2(object).foo, + "object can have multiple namespaces"); + assert.equal(n1(object).bar, n2(object).bar, + "object can have matching props in diff namespaces"); +}; + +exports["test ns alias"] = function(assert) { + assert.strictEqual(ns, require('sdk/core/namespace').Namespace, + "ns is an alias of Namespace"); +}; + +exports["test ns inheritance"] = function(assert) { + let _ = ns(); + + let prototype = { level: 1 }; + let object = Object.create(prototype); + let delegee = Object.create(object); + + _(prototype).foo = {}; + + assert.ok(!Object.prototype.hasOwnProperty.call(_(delegee), "foo"), + "namespaced property is not copied to descendants"); + assert.equal(_(delegee).foo, _(prototype).foo, + "namespaced properties are inherited by descendants"); + + _(object).foo = {}; + assert.notEqual(_(object).foo, _(prototype).foo, + "namespaced properties may be shadowed"); + assert.equal(_(object).foo, _(delegee).foo, + "shadwed properties are inherited by descendants"); + + _(object).bar = {}; + assert.ok(!("bar" in _(prototype)), + "descendants properties are not copied to ancestors"); + assert.ok(_(object).bar, _(delegee).bar, + "descendants properties are inherited"); +}; + +require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-native-loader.js b/addon-sdk-1.16/test/test-native-loader.js new file mode 100644 index 00000000..d787d407 --- /dev/null +++ b/addon-sdk-1.16/test/test-native-loader.js @@ -0,0 +1,234 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +'use strict'; + +let { + Loader, main, unload, parseStack, generateMap, resolve, nodeResolve +} = require('toolkit/loader'); +let { readURI } = require('sdk/net/url'); +let { all } = require('sdk/core/promise'); +let testOptions = require('@test/options'); + +let root = module.uri.substr(0, module.uri.lastIndexOf('/')) +// The following adds Debugger constructor to the global namespace. +const { Cu } = require('chrome'); +const { addDebuggerToGlobal } = Cu.import('resource://gre/modules/jsdebugger.jsm', {}); +addDebuggerToGlobal(this); + + +exports['test nodeResolve'] = function (assert) { + let rootURI = root + '/fixtures/native-addon-test/'; + let manifest = {}; + manifest.dependencies = {}; + + // Handles extensions + resolveTest('../package.json', './dir/c.js', './package.json'); + resolveTest('../dir/b.js', './dir/c.js', './dir/b.js'); + + resolveTest('./dir/b', './index.js', './dir/b.js'); + resolveTest('../index', './dir/b.js', './index.js'); + resolveTest('../', './dir/b.js', './index.js'); + resolveTest('./dir/a', './index.js', './dir/a.js', 'Precedence dir/a.js over dir/a/'); + resolveTest('../utils', './dir/a.js', './utils/index.js', 'Requiring a directory defaults to dir/index.js'); + resolveTest('../newmodule', './dir/c.js', './newmodule/lib/file.js', 'Uses package.json main in dir to load appropriate "main"'); + resolveTest('test-math', './utils/index.js', './node_modules/test-math/index.js', + 'Dependencies default to their index.js'); + resolveTest('test-custom-main', './utils/index.js', './node_modules/test-custom-main/lib/custom-entry.js', + 'Dependencies use "main" entry'); + resolveTest('test-math/lib/sqrt', './utils/index.js', './node_modules/test-math/lib/sqrt.js', + 'Dependencies\' files can be consumed via "/"'); + + resolveTest('sdk/tabs/utils', './index.js', undefined, + 'correctly ignores SDK references in paths'); + resolveTest('fs', './index.js', undefined, + 'correctly ignores built in node modules in paths'); + + resolveTest('test-add', './node_modules/test-math/index.js', + './node_modules/test-math/node_modules/test-add/index.js', + 'Dependencies\' dependencies can be found'); + + + function resolveTest (id, requirer, expected, msg) { + let result = nodeResolve(id, requirer, { manifest: manifest, rootURI: rootURI }); + assert.equal(result, expected, 'nodeResolve ' + id + ' from ' + requirer + ' ' +msg); + } +} + +/* +// TODO not working in current env +exports['test bundle'] = function (assert, done) { + loadAddon('/native-addons/native-addon-test/') +}; +*/ + +exports['test generateMap()'] = function (assert, done) { + getJSON('/fixtures/native-addon-test/expectedmap.json').then(expected => { + generateMap({ + rootURI: root + '/fixtures/native-addon-test/' + }, map => { + assert.deepEqual(map, expected, 'generateMap returns expected mappings'); + assert.equal(map['./index.js']['./dir/a'], './dir/a.js', + 'sanity check on correct mappings'); + done(); + }); + }).then(null, (reason) => console.error(reason)); +}; + +exports['test JSM loading'] = function (assert, done) { + getJSON('/fixtures/jsm-package/package.json').then(manifest => { + let rootURI = root + '/fixtures/jsm-package/'; + let loader = Loader({ + paths: makePaths(rootURI), + rootURI: rootURI, + manifest: manifest, + isNative: true + }); + + let program = main(loader); + assert.ok(program.localJSMCached, 'local relative JSMs are cached'); + assert.ok(program.isCachedJSAbsolute , 'absolute resource:// js are cached'); + assert.ok(program.isCachedPath, 'JSMs resolved in paths are cached'); + assert.ok(program.isCachedAbsolute, 'absolute resource:// JSMs are cached'); + + assert.ok(program.localJSM, 'able to load local relative JSMs'); + all([ + program.isLoadedPath(10), + program.isLoadedAbsolute(20), + program.isLoadedJSAbsolute(30) + ]).then(([path, absolute, jsabsolute]) => { + assert.equal(path, 10, 'JSM files resolved from path work'); + assert.equal(absolute, 20, 'JSM files resolved from full resource:// work'); + assert.equal(jsabsolute, 30, 'JS files resolved from full resource:// work'); + }).then(done, console.error); + + }).then(null, console.error); +}; + +exports['test native Loader with mappings'] = function (assert, done) { + all([ + getJSON('/fixtures/native-addon-test/expectedmap.json'), + getJSON('/fixtures/native-addon-test/package.json') + ]).then(([expectedMap, manifest]) => { + + // Override dummy module and point it to `test-math` to see if the + // require is pulling from the mapping + expectedMap['./index.js']['./dir/dummy'] = './dir/a.js'; + + let rootURI = root + '/fixtures/native-addon-test/'; + let loader = Loader({ + paths: makePaths(rootURI), + rootURI: rootURI, + manifest: manifest, + requireMap: expectedMap, + isNative: true + }); + + let program = main(loader); + assert.equal(program.dummyModule, 'dir/a', + 'The lookup uses the information given in the mapping'); + + testLoader(program, assert); + unload(loader); + done(); + }).then(null, (reason) => console.error(reason)); +}; + +exports['test native Loader without mappings'] = function (assert, done) { + getJSON('/fixtures/native-addon-test/package.json').then(manifest => { + let rootURI = root + '/fixtures/native-addon-test/'; + let loader = Loader({ + paths: makePaths(rootURI), + rootURI: rootURI, + manifest: manifest, + isNative: true + }); + + let program = main(loader); + testLoader(program, assert); + unload(loader); + done(); + }).then(null, (reason) => console.error(reason)); +}; + +function testLoader (program, assert) { + // Test 'main' entries + // no relative custom main `lib/index.js` + assert.equal(program.customMainModule, 'custom entry file', + 'a node_module dependency correctly uses its `main` entry in manifest'); + // relative custom main `./lib/index.js` + assert.equal(program.customMainModuleRelative, 'custom entry file relative', + 'a node_module dependency correctly uses its `main` entry in manifest with relative ./'); + // implicit './index.js' + assert.equal(program.defaultMain, 'default main', + 'a node_module dependency correctly defautls to index.js for main'); + + // Test directory exports + assert.equal(program.directoryDefaults, 'utils', + '`require`ing a directory defaults to dir/index.js'); + assert.equal(program.directoryMain, 'main from new module', + '`require`ing a directory correctly loads the `main` entry and not index.js'); + assert.equal(program.resolvesJSoverDir, 'dir/a', + '`require`ing "a" resolves "a.js" over "a/index.js"'); + + // Test dependency's dependencies + assert.ok(program.math.add, + 'correctly defaults to index.js of a module'); + assert.equal(program.math.add(10, 5), 15, + 'node dependencies correctly include their own dependencies'); + assert.equal(program.math.subtract(10, 5), 5, + 'node dependencies correctly include their own dependencies'); + assert.equal(program.mathInRelative.subtract(10, 5), 5, + 'relative modules can also include node dependencies'); + + // Test SDK natives + assert.ok(program.promise.defer, 'main entry can include SDK modules with no deps'); + assert.ok(program.promise.resolve, 'main entry can include SDK modules with no deps'); + assert.ok(program.eventCore.on, 'main entry can include SDK modules that have dependencies'); + assert.ok(program.eventCore.off, 'main entry can include SDK modules that have dependencies'); + + // Test JSMs + assert.ok(program.promisejsm.defer, 'can require JSM files in path'); + assert.equal(program.localJSM.test, 'this is a jsm', + 'can require relative JSM files'); + + // Other tests + assert.equal(program.areModulesCached, true, + 'modules are correctly cached'); + assert.equal(program.testJSON.dependencies['test-math'], '*', + 'correctly requires JSON files'); +} + +function getJSON (uri) { + return readURI(root + uri).then(manifest => JSON.parse(manifest)); +} + +function makePaths (uri) { + // Uses development SDK modules if overloaded in loader + let sdkPaths = testOptions.paths ? testOptions.paths[''] : 'resource://gre/modules/commonjs/'; + return { + './': uri, + 'sdk/': sdkPaths + 'sdk/', + 'toolkit/': sdkPaths + 'toolkit/', + 'modules/': 'resource://gre/modules/' + }; +} + +function loadAddon (uri, map) { + let rootURI = root + uri; + getJSON(uri + '/package.json').then(manifest => { + let loader = Loader({ + paths: makePaths(rootURI), + rootURI: rootURI, + manifest: manifest, + isNative: true, + modules: { + '@test/options': testOptions + } + }); + let program = main(loader); + }).then(null, console.error); +} + +require('test').run(exports); diff --git a/addon-sdk-1.16/test/test-net-url.js b/addon-sdk-1.16/test/test-net-url.js new file mode 100644 index 00000000..6ce2a328 --- /dev/null +++ b/addon-sdk-1.16/test/test-net-url.js @@ -0,0 +1,212 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const { readURI, readURISync } = require("sdk/net/url"); +const data = require("./fixtures"); + +const utf8text = "Hello, ゼロ!"; +const latin1text = "Hello, ゼロ!"; + +const dataURIutf8 = "data:text/plain;charset=utf-8," + encodeURIComponent(utf8text); +const dataURIlatin1 = "data:text/plain;charset=ISO-8859-1," + escape(latin1text); +const chromeURI = "chrome://global-platform/locale/accessible.properties"; + +exports["test async readURI"] = function(assert, done) { + let content = ""; + + readURI(data.url("test-net-url.txt")).then(function(data) { + content = data; + assert.equal(content, utf8text, "The URL content is loaded properly"); + done(); + }, function() { + assert.fail("should not reject"); + done(); + }) + + assert.equal(content, "", "The URL content is not load yet"); +} + +exports["test sync readURI"] = function(assert) { + let content = ""; + + readURI(data.url("test-net-url.txt"), { sync: true }).then(function(data) { + content = data; + }, function() { + assert.fail("should not reject"); + }) + + assert.equal(content, utf8text, "The URL content is loaded properly"); +} + +exports["test readURISync"] = function(assert) { + let content = readURISync(data.url("test-net-url.txt")); + + assert.equal(content, utf8text, "The URL content is loaded properly"); +} + +exports["test async readURI with ISO-8859-1 charset"] = function(assert, done) { + let content = ""; + + readURI(data.url("test-net-url.txt"), { charset : "ISO-8859-1"}).then(function(data) { + content = data; + assert.equal(content, latin1text, "The URL content is loaded properly"); + done(); + }, function() { + assert.fail("should not reject"); + done(); + }) + + assert.equal(content, "", "The URL content is not load yet"); +} + +exports["test sync readURI with ISO-8859-1 charset"] = function(assert) { + let content = ""; + + readURI(data.url("test-net-url.txt"), { + sync: true, + charset: "ISO-8859-1" + }).then(function(data) { + content = data; + }, function() { + assert.fail("should not reject"); + }) + + assert.equal(content, latin1text, "The URL content is loaded properly"); +} + +exports["test readURISync with ISO-8859-1 charset"] = function(assert) { + let content = readURISync(data.url("test-net-url.txt"), "ISO-8859-1"); + + assert.equal(content, latin1text, "The URL content is loaded properly"); +} + +exports["test async readURI with not existing file"] = function(assert, done) { + readURI(data.url("test-net-url-fake.txt")).then(function(data) { + assert.fail("should not resolve"); + done(); + }, function(reason) { + assert.ok(reason.indexOf("Failed to read:") === 0); + done(); + }) +} + +exports["test sync readURI with not existing file"] = function(assert) { + readURI(data.url("test-net-url-fake.txt"), { sync: true }).then(function(data) { + assert.fail("should not resolve"); + }, function(reason) { + assert.ok(reason.indexOf("Failed to read:") === 0); + }) +} + +exports["test readURISync with not existing file"] = function(assert) { + assert.throws(function() { + readURISync(data.url("test-net-url-fake.txt")); + }, /NS_ERROR_FILE_NOT_FOUND/); +} + +exports["test async readURI with data URI"] = function(assert, done) { + let content = ""; + + readURI(dataURIutf8).then(function(data) { + content = data; + assert.equal(content, utf8text, "The URL content is loaded properly"); + done(); + }, function() { + assert.fail("should not reject"); + done(); + }) + + assert.equal(content, "", "The URL content is not load yet"); +} + +exports["test sync readURI with data URI"] = function(assert) { + let content = ""; + + readURI(dataURIutf8, { sync: true }).then(function(data) { + content = data; + }, function() { + assert.fail("should not reject"); + }) + + assert.equal(content, utf8text, "The URL content is loaded properly"); +} + +exports["test readURISync with data URI"] = function(assert) { + let content = readURISync(dataURIutf8); + + assert.equal(content, utf8text, "The URL content is loaded properly"); +} + +exports["test async readURI with data URI and ISO-8859-1 charset"] = function(assert, done) { + let content = ""; + + readURI(dataURIlatin1, { charset : "ISO-8859-1"}).then(function(data) { + content = unescape(data); + assert.equal(content, latin1text, "The URL content is loaded properly"); + done(); + }, function() { + assert.fail("should not reject"); + done(); + }) + + assert.equal(content, "", "The URL content is not load yet"); +} + +exports["test sync readURI with data URI and ISO-8859-1 charset"] = function(assert) { + let content = ""; + + readURI(dataURIlatin1, { + sync: true, + charset: "ISO-8859-1" + }).then(function(data) { + content = unescape(data); + }, function() { + assert.fail("should not reject"); + }) + + assert.equal(content, latin1text, "The URL content is loaded properly"); +} + +exports["test readURISync with data URI and ISO-8859-1 charset"] = function(assert) { + let content = unescape(readURISync(dataURIlatin1, "ISO-8859-1")); + + assert.equal(content, latin1text, "The URL content is loaded properly"); +} + +exports["test readURISync with chrome URI"] = function(assert) { + let content = readURISync(chromeURI); + + assert.ok(content, "The URL content is loaded properly"); +} + +exports["test async readURI with chrome URI"] = function(assert, done) { + let content = ""; + + readURI(chromeURI).then(function(data) { + content = data; + assert.equal(content, readURISync(chromeURI), "The URL content is loaded properly"); + done(); + }, function() { + assert.fail("should not reject"); + done(); + }) + + assert.equal(content, "", "The URL content is not load yet"); +} + +exports["test sync readURI with chrome URI"] = function(assert) { + let content = ""; + + readURI(chromeURI, { sync: true }).then(function(data) { + content = data; + }, function() { + assert.fail("should not reject"); + }) + + assert.equal(content, readURISync(chromeURI), "The URL content is loaded properly"); +} + +require("test").run(exports) diff --git a/addon-sdk-1.16/test/test-node-os.js b/addon-sdk-1.16/test/test-node-os.js new file mode 100644 index 00000000..4ac2eb3a --- /dev/null +++ b/addon-sdk-1.16/test/test-node-os.js @@ -0,0 +1,33 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +let os = require("node/os"); +let system = require("sdk/system"); + +exports["test os"] = function (assert) { + assert.equal(os.tmpdir(), system.pathFor("TmpD"), "os.tmpdir() matches temp dir"); + assert.ok(os.endianness() === "BE" || os.endianness() === "LE", "os.endianness is BE or LE"); + + assert.ok(os.arch().length > 0, "os.arch() returns a value"); + assert.equal(typeof os.arch(), "string", "os.arch() returns a string"); + assert.ok(os.type().length > 0, "os.type() returns a value"); + assert.equal(typeof os.type(), "string", "os.type() returns a string"); + assert.ok(os.platform().length > 0, "os.platform() returns a value"); + assert.equal(typeof os.platform(), "string", "os.platform() returns a string"); + + assert.ok(os.release().length > 0, "os.release() returns a value"); + assert.equal(typeof os.release(), "string", "os.release() returns a string"); + assert.ok(os.hostname().length > 0, "os.hostname() returns a value"); + assert.equal(typeof os.hostname(), "string", "os.hostname() returns a string"); + assert.ok(os.EOL === "\n" || os.EOL === "\r\n", "os.EOL returns a correct EOL char"); + + assert.deepEqual(os.loadavg(), [0, 0, 0], "os.loadavg() returns an array of 0s"); + + ["uptime", "totalmem", "freemem", "cpus"].forEach(method => { + assert.throws(() => os[method](), "os." + method + " throws"); + }); +}; + +require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-notifications.js b/addon-sdk-1.16/test/test-notifications.js new file mode 100644 index 00000000..d154e390 --- /dev/null +++ b/addon-sdk-1.16/test/test-notifications.js @@ -0,0 +1,93 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const { Loader } = require('sdk/test/loader'); + +exports.testOnClick = function (assert) { + let [loader, mockAlertServ] = makeLoader(module); + let notifs = loader.require("sdk/notifications"); + let data = "test data"; + let opts = { + onClick: function (clickedData) { + assert.equal(this, notifs, "|this| should be notifications module"); + assert.equal(clickedData, data, + "data passed to onClick should be correct"); + }, + data: data, + title: "test title", + text: "test text", + iconURL: "test icon URL" + }; + notifs.notify(opts); + mockAlertServ.click(); + loader.unload(); +}; + +exports['test:numbers and URLs in options'] = function(assert) { + let [loader] = makeLoader(module); + let notifs = loader.require('sdk/notifications'); + let opts = { + title: 123, + text: 45678, + // must use in-loader `sdk/url` module for the validation type check to work + iconURL: loader.require('sdk/url').URL('data:image/png,blah') + }; + try { + notifs.notify(opts); + assert.pass('using numbers and URLs in options works'); + } catch (e) { + assert.fail('using numbers and URLs in options must not throw'); + } + loader.unload(); +} + +exports['test:new tag, dir and lang options'] = function(assert) { + let [loader] = makeLoader(module); + let notifs = loader.require('sdk/notifications'); + let opts = { + title: 'best', + tag: 'tagging', + lang: 'en' + }; + + try { + opts.dir = 'ttb'; + notifs.notify(opts); + assert.fail('`dir` option must not accept TopToBottom direction.'); + } catch (e) { + assert.equal(e.message, + '`dir` option must be one of: "auto", "ltr" or "rtl".'); + } + + try { + opts.dir = 'rtl'; + notifs.notify(opts); + assert.pass('`dir` option accepts "rtl" direction.'); + } catch (e) { + assert.fail('`dir` option must accept "rtl" direction.'); + } + + loader.unload(); +} + +// Returns [loader, mockAlertService]. +function makeLoader(module) { + let loader = Loader(module); + let mockAlertServ = { + showAlertNotification: function (imageUrl, title, text, textClickable, + cookie, alertListener, name) { + this._cookie = cookie; + this._alertListener = alertListener; + }, + click: function () { + this._alertListener.observe(null, "alertclickcallback", this._cookie); + } + }; + loader.require("sdk/notifications"); + let scope = loader.sandbox("sdk/notifications"); + scope.notify = mockAlertServ.showAlertNotification.bind(mockAlertServ); + return [loader, mockAlertServ]; +} + +require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-object.js b/addon-sdk-1.16/test/test-object.js new file mode 100644 index 00000000..128cb010 --- /dev/null +++ b/addon-sdk-1.16/test/test-object.js @@ -0,0 +1,36 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +'use strict'; + +const { merge, extend, has, each } = require('sdk/util/object'); + +let o = { + 'paper': 0, + 'rock': 1, + 'scissors': 2 +}; + +//exports.testMerge = function(assert) {} +//exports.testExtend = function(assert) {} + +exports.testHas = function(assert) { + assert.equal(has(o, 'paper'), true, 'has correctly finds key'); + assert.equal(has(o, 'rock'), true, 'has correctly finds key'); + assert.equal(has(o, 'scissors'), true, 'has correctly finds key'); + assert.equal(has(o, 'nope'), false, 'has correctly does not find key'); + assert.equal(has(o, '__proto__'), false, 'has correctly does not find key'); + assert.equal(has(o, 'isPrototypeOf'), false, 'has correctly does not find key'); +}; + +exports.testEach = function(assert) { + var keys = new Set(); + each(o, function (value, key, object) { + keys.add(key); + assert.equal(o[key], value, 'Key and value pairs passed in'); + assert.equal(o, object, 'Object passed in'); + }); + assert.equal(keys.size, 3, 'All keys have been iterated upon'); +}; + +require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-packaging.js b/addon-sdk-1.16/test/test-packaging.js new file mode 100644 index 00000000..6721633e --- /dev/null +++ b/addon-sdk-1.16/test/test-packaging.js @@ -0,0 +1,46 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + "use strict"; + +var options = require("@loader/options"); + +exports.testPackaging = function(assert) { + assert.equal(options.metadata.description, + "Add-on development made easy.", + "packaging metadata should be available"); + try { + options.metadata.description = 'new description'; + assert.fail('should not have been able to set options.metadata property'); + } + catch (e) {} + + assert.equal(options.metadata.description, + "Add-on development made easy.", + "packaging metadata should be frozen"); + + assert.equal(options.metadata.permissions['private-browsing'], undefined, + "private browsing metadata should be undefined"); + assert.equal(options.metadata['private-browsing'], undefined, + "private browsing metadata should be be frozen"); + assert.equal(options['private-browsing'], undefined, + "private browsing metadata should be be frozen"); + + try { + options.metadata['private-browsing'] = true; + assert.fail('should not have been able to set options.metadata property'); + } + catch(e) {} + assert.equal(options.metadata['private-browsing'], undefined, + "private browsing metadata should be be frozen"); + + try { + options.metadata.permissions['private-browsing'] = true; + assert.fail('should not have been able to set options.metadata.permissions property'); + } + catch (e) {} + assert.equal(options.metadata.permissions['private-browsing'], undefined, + "private browsing metadata should be be frozen"); +}; + +require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-page-mod.js b/addon-sdk-1.16/test/test-page-mod.js new file mode 100644 index 00000000..6e494bcb --- /dev/null +++ b/addon-sdk-1.16/test/test-page-mod.js @@ -0,0 +1,1202 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +"use strict"; + +const { PageMod } = require("sdk/page-mod"); +const testPageMod = require("./pagemod-test-helpers").testPageMod; +const { Loader } = require('sdk/test/loader'); +const tabs = require("sdk/tabs"); +const timer = require("sdk/timers"); +const { Cc, Ci, Cu } = require("chrome"); +const { open, getFrames, getMostRecentBrowserWindow } = require('sdk/window/utils'); +const windowUtils = require('sdk/deprecated/window-utils'); +const { getTabContentWindow, getActiveTab, setTabURL, openTab, closeTab } = require('sdk/tabs/utils'); +const xulApp = require("sdk/system/xul-app"); +const { isPrivateBrowsingSupported } = require('sdk/self'); +const { isPrivate } = require('sdk/private-browsing'); +const { openWebpage } = require('./private-browsing/helper'); +const { isTabPBSupported, isWindowPBSupported, isGlobalPBSupported } = require('sdk/private-browsing/utils'); +const promise = require("sdk/core/promise"); +const { pb } = require('./private-browsing/helper'); +const { URL } = require("sdk/url"); + +const { waitUntil } = require("sdk/test/utils"); +const data = require("./fixtures"); + +const testPageURI = data.url("test.html"); + +// The following adds Debugger constructor to the global namespace. +const { addDebuggerToGlobal } = + Cu.import('resource://gre/modules/jsdebugger.jsm', {}); +addDebuggerToGlobal(this); + +function Isolate(worker) { + return "(" + worker + ")()"; +} + +/* Tests for the PageMod APIs */ + +exports.testPageMod1 = function(assert, done) { + let mods = testPageMod(assert, done, "about:", [{ + include: /about:/, + contentScriptWhen: 'end', + contentScript: 'new ' + function WorkerScope() { + window.document.body.setAttribute("JEP-107", "worked"); + }, + onAttach: function() { + assert.equal(this, mods[0], "The 'this' object is the page mod."); + } + }], + function(win, done) { + assert.equal( + win.document.body.getAttribute("JEP-107"), + "worked", + "PageMod.onReady test" + ); + done(); + } + ); +}; + +exports.testPageMod2 = function(assert, done) { + testPageMod(assert, done, "about:", [{ + include: "about:*", + contentScript: [ + 'new ' + function contentScript() { + window.AUQLUE = function() { return 42; } + try { + window.AUQLUE() + } + catch(e) { + throw new Error("PageMod scripts executed in order"); + } + document.documentElement.setAttribute("first", "true"); + }, + 'new ' + function contentScript() { + document.documentElement.setAttribute("second", "true"); + } + ] + }], function(win, done) { + assert.equal(win.document.documentElement.getAttribute("first"), + "true", + "PageMod test #2: first script has run"); + assert.equal(win.document.documentElement.getAttribute("second"), + "true", + "PageMod test #2: second script has run"); + assert.equal("AUQLUE" in win, false, + "PageMod test #2: scripts get a wrapped window"); + done(); + }); +}; + +exports.testPageModIncludes = function(assert, done) { + var asserts = []; + function createPageModTest(include, expectedMatch) { + // Create an 'onload' test function... + asserts.push(function(test, win) { + var matches = include in win.localStorage; + assert.ok(expectedMatch ? matches : !matches, + "'" + include + "' match test, expected: " + expectedMatch); + }); + // ...and corresponding PageMod options + return { + include: include, + contentScript: 'new ' + function() { + self.on("message", function(msg) { + window.localStorage[msg] = true; + }); + }, + // The testPageMod callback with test assertions is called on 'end', + // and we want this page mod to be attached before it gets called, + // so we attach it on 'start'. + contentScriptWhen: 'start', + onAttach: function(worker) { + worker.postMessage(this.include[0]); + } + }; + } + + testPageMod(assert, done, testPageURI, [ + createPageModTest("*", false), + createPageModTest("*.google.com", false), + createPageModTest("resource:*", true), + createPageModTest("resource:", false), + createPageModTest(testPageURI, true) + ], + function (win, done) { + waitUntil(function () win.localStorage[testPageURI], + testPageURI + " page-mod to be executed") + .then(function () { + asserts.forEach(function(fn) { + fn(assert, win); + }); + done(); + }); + } + ); +}; + +exports.testPageModErrorHandling = function(assert) { + assert.throws(function() { + new PageMod(); + }, + /The `include` option must always contain atleast one rule/, + "PageMod() throws when 'include' option is not specified."); +}; + +/* Tests for internal functions. */ +exports.testCommunication1 = function(assert, done) { + let workerDone = false, + callbackDone = null; + + testPageMod(assert, done, "about:", [{ + include: "about:*", + contentScriptWhen: 'end', + contentScript: 'new ' + function WorkerScope() { + self.on('message', function(msg) { + document.body.setAttribute('JEP-107', 'worked'); + self.postMessage(document.body.getAttribute('JEP-107')); + }) + }, + onAttach: function(worker) { + worker.on('error', function(e) { + assert.fail('Errors where reported'); + }); + worker.on('message', function(value) { + assert.equal( + "worked", + value, + "test comunication" + ); + workerDone = true; + if (callbackDone) + callbackDone(); + }); + worker.postMessage('do it!') + } + }], + function(win, done) { + (callbackDone = function() { + if (workerDone) { + assert.equal( + 'worked', + win.document.body.getAttribute('JEP-107'), + 'attribute should be modified' + ); + done(); + } + })(); + } + ); +}; + +exports.testCommunication2 = function(assert, done) { + let callbackDone = null, + window; + + testPageMod(assert, done, "about:license", [{ + include: "about:*", + contentScriptWhen: 'start', + contentScript: 'new ' + function WorkerScope() { + document.documentElement.setAttribute('AUQLUE', 42); + window.addEventListener('load', function listener() { + self.postMessage('onload'); + }, false); + self.on("message", function() { + self.postMessage(document.documentElement.getAttribute("test")) + }); + }, + onAttach: function(worker) { + worker.on('error', function(e) { + assert.fail('Errors where reported'); + }); + worker.on('message', function(msg) { + if ('onload' == msg) { + assert.equal( + '42', + window.document.documentElement.getAttribute('AUQLUE'), + 'PageMod scripts executed in order' + ); + window.document.documentElement.setAttribute('test', 'changes in window'); + worker.postMessage('get window.test') + } else { + assert.equal( + 'changes in window', + msg, + 'PageMod test #2: second script has run' + ) + callbackDone(); + } + }); + } + }], + function(win, done) { + window = win; + callbackDone = done; + } + ); +}; + +exports.testEventEmitter = function(assert, done) { + let workerDone = false, + callbackDone = null; + + testPageMod(assert, done, "about:", [{ + include: "about:*", + contentScript: 'new ' + function WorkerScope() { + self.port.on('addon-to-content', function(data) { + self.port.emit('content-to-addon', data); + }); + }, + onAttach: function(worker) { + worker.on('error', function(e) { + assert.fail('Errors were reported : '+e); + }); + worker.port.on('content-to-addon', function(value) { + assert.equal( + "worked", + value, + "EventEmitter API works!" + ); + if (callbackDone) + callbackDone(); + else + workerDone = true; + }); + worker.port.emit('addon-to-content', 'worked'); + } + }], + function(win, done) { + if (workerDone) + done(); + else + callbackDone = done; + } + ); +}; + +// Execute two concurrent page mods on same document to ensure that their +// JS contexts are different +exports.testMixedContext = function(assert, done) { + let doneCallback = null; + let messages = 0; + let modObject = { + include: "data:text/html;charset=utf-8,", + contentScript: 'new ' + function WorkerScope() { + // Both scripts will execute this, + // context is shared if one script see the other one modification. + let isContextShared = "sharedAttribute" in document; + self.postMessage(isContextShared); + document.sharedAttribute = true; + }, + onAttach: function(w) { + w.on("message", function (isContextShared) { + if (isContextShared) { + assert.fail("Page mod contexts are mixed."); + doneCallback(); + } + else if (++messages == 2) { + assert.pass("Page mod contexts are different."); + doneCallback(); + } + }); + } + }; + testPageMod(assert, done, "data:text/html;charset=utf-8,", [modObject, modObject], + function(win, done) { + doneCallback = done; + } + ); +}; + +exports.testHistory = function(assert, done) { + // We need a valid url in order to have a working History API. + // (i.e do not work on data: or about: pages) + // Test bug 679054. + let url = data.url("test-page-mod.html"); + let callbackDone = null; + testPageMod(assert, done, url, [{ + include: url, + contentScriptWhen: 'end', + contentScript: 'new ' + function WorkerScope() { + history.pushState({}, "", "#"); + history.replaceState({foo: "bar"}, "", "#"); + self.postMessage(history.state); + }, + onAttach: function(worker) { + worker.on('message', function (data) { + assert.equal(JSON.stringify(data), JSON.stringify({foo: "bar"}), + "History API works!"); + callbackDone(); + }); + } + }], + function(win, done) { + callbackDone = done; + } + ); +}; + +exports.testRelatedTab = function(assert, done) { + let tab; + let pageMod = new PageMod({ + include: "about:*", + onAttach: function(worker) { + assert.ok(!!worker.tab, "Worker.tab exists"); + assert.equal(tab, worker.tab, "Worker.tab is valid"); + pageMod.destroy(); + tab.close(done); + } + }); + + tabs.open({ + url: "about:", + onOpen: function onOpen(t) { + tab = t; + } + }); +}; + +exports.testRelatedTabNoRequireTab = function(assert, done) { + let loader = Loader(module); + let tab; + let url = "data:text/html;charset=utf-8," + encodeURI("Test related worker tab 2"); + let { PageMod } = loader.require("sdk/page-mod"); + let pageMod = new PageMod({ + include: url, + onAttach: function(worker) { + assert.equal(worker.tab.url, url, "Worker.tab.url is valid"); + worker.tab.close(function() { + pageMod.destroy(); + loader.unload(); + done(); + }); + } + }); + + tabs.open(url); +}; + +exports.testRelatedTabNoOtherReqs = function(assert, done) { + let loader = Loader(module); + let { PageMod } = loader.require("sdk/page-mod"); + let pageMod = new PageMod({ + include: "about:blank?testRelatedTabNoOtherReqs", + onAttach: function(worker) { + assert.ok(!!worker.tab, "Worker.tab exists"); + pageMod.destroy(); + worker.tab.close(function() { + worker.destroy(); + loader.unload(); + done(); + }); + } + }); + + tabs.open({ + url: "about:blank?testRelatedTabNoOtherReqs" + }); +}; + +exports.testWorksWithExistingTabs = function(assert, done) { + let url = "data:text/html;charset=utf-8," + encodeURI("Test unique document"); + let { PageMod } = require("sdk/page-mod"); + tabs.open({ + url: url, + onReady: function onReady(tab) { + let pageModOnExisting = new PageMod({ + include: url, + attachTo: ["existing", "top", "frame"], + onAttach: function(worker) { + assert.ok(!!worker.tab, "Worker.tab exists"); + assert.equal(tab, worker.tab, "A worker has been created on this existing tab"); + + timer.setTimeout(function() { + pageModOnExisting.destroy(); + pageModOffExisting.destroy(); + tab.close(done); + }, 0); + } + }); + + let pageModOffExisting = new PageMod({ + include: url, + onAttach: function(worker) { + assert.fail("pageModOffExisting page-mod should not have attached to anything"); + } + }); + } + }); +}; + +exports.testExistingFrameDoesntMatchInclude = function(assert, done) { + let iframeURL = 'data:text/html;charset=utf-8,UNIQUE-TEST-STRING-42'; + let iframe = ' - - diff --git a/addon-sdk-1.16/test/fixtures/test-iframe.js b/addon-sdk-1.16/test/fixtures/test-iframe.js deleted file mode 100644 index c7f83451..00000000 --- a/addon-sdk-1.16/test/fixtures/test-iframe.js +++ /dev/null @@ -1,13 +0,0 @@ - -var count = 0; - -setTimeout(function() { - window.addEventListener("message", function(msg) { - if (++count > 1) { - self.postMessage(msg.data); - } - else msg.source.postMessage(msg.data, '*'); - }); - - document.getElementById('inner').src = iframePath; -}, 0); diff --git a/addon-sdk-1.16/test/fixtures/test-message-manager.js b/addon-sdk-1.16/test/fixtures/test-message-manager.js deleted file mode 100644 index d647bd8f..00000000 --- a/addon-sdk-1.16/test/fixtures/test-message-manager.js +++ /dev/null @@ -1,6 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -const TEST_VALUE = 11; - diff --git a/addon-sdk-1.16/test/fixtures/test-net-url.txt b/addon-sdk-1.16/test/fixtures/test-net-url.txt deleted file mode 100644 index 9f8166e6..00000000 --- a/addon-sdk-1.16/test/fixtures/test-net-url.txt +++ /dev/null @@ -1 +0,0 @@ -Hello, ゼロ! \ No newline at end of file diff --git a/addon-sdk-1.16/test/fixtures/test-page-mod.html b/addon-sdk-1.16/test/fixtures/test-page-mod.html deleted file mode 100644 index 901abefc..00000000 --- a/addon-sdk-1.16/test/fixtures/test-page-mod.html +++ /dev/null @@ -1,12 +0,0 @@ - - - - - Page Mod test - - -

Lorem ipsum dolor sit amet.

- - diff --git a/addon-sdk-1.16/test/fixtures/test-page-worker.html b/addon-sdk-1.16/test/fixtures/test-page-worker.html deleted file mode 100644 index 85264034..00000000 --- a/addon-sdk-1.16/test/fixtures/test-page-worker.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - Page Worker test - - -

Lorem ipsum dolor sit amet.

- - diff --git a/addon-sdk-1.16/test/fixtures/test-page-worker.js b/addon-sdk-1.16/test/fixtures/test-page-worker.js deleted file mode 100644 index 11108702..00000000 --- a/addon-sdk-1.16/test/fixtures/test-page-worker.js +++ /dev/null @@ -1,29 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - - -// get title directly -self.postMessage(["equal", document.title, "Page Worker test", - "Correct page title accessed directly"]); - -// get

directly -let p = document.getElementById("paragraph"); -self.postMessage(["ok", !!p, "

can be accessed directly"]); -self.postMessage(["equal", p.firstChild.nodeValue, - "Lorem ipsum dolor sit amet.", - "Correct text node expected"]); - -// Modify page -let div = document.createElement("div"); -div.setAttribute("id", "block"); -div.appendChild(document.createTextNode("Test text created")); -document.body.appendChild(div); - -// Check back the modification -div = document.getElementById("block"); -self.postMessage(["ok", !!div, "

can be accessed directly"]); -self.postMessage(["equal", div.firstChild.nodeValue, - "Test text created", "Correct text node expected"]); -self.postMessage(["done"]); - diff --git a/addon-sdk-1.16/test/fixtures/test-sidebar-addon-global.html b/addon-sdk-1.16/test/fixtures/test-sidebar-addon-global.html deleted file mode 100644 index cfa07395..00000000 --- a/addon-sdk-1.16/test/fixtures/test-sidebar-addon-global.html +++ /dev/null @@ -1,10 +0,0 @@ - -SIDEBAR TEST diff --git a/addon-sdk-1.16/test/fixtures/test-trusted-document.html b/addon-sdk-1.16/test/fixtures/test-trusted-document.html deleted file mode 100644 index c31e055c..00000000 --- a/addon-sdk-1.16/test/fixtures/test-trusted-document.html +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - Worker test - - -

Lorem ipsum dolor sit amet.

- - - diff --git a/addon-sdk-1.16/test/fixtures/test.html b/addon-sdk-1.16/test/fixtures/test.html deleted file mode 100644 index 181e85f9..00000000 --- a/addon-sdk-1.16/test/fixtures/test.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - foo - - -

bar

- - diff --git a/addon-sdk-1.16/test/fixtures/testLocalXhr.json b/addon-sdk-1.16/test/fixtures/testLocalXhr.json deleted file mode 100644 index 0967ef42..00000000 --- a/addon-sdk-1.16/test/fixtures/testLocalXhr.json +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/addon-sdk-1.16/test/loader/fixture.js b/addon-sdk-1.16/test/loader/fixture.js deleted file mode 100644 index ebf91abb..00000000 --- a/addon-sdk-1.16/test/loader/fixture.js +++ /dev/null @@ -1,7 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -exports.foo = foo; -exports.bar = 2; -print('testing'); diff --git a/addon-sdk-1.16/test/modules/add.js b/addon-sdk-1.16/test/modules/add.js deleted file mode 100644 index 54729340..00000000 --- a/addon-sdk-1.16/test/modules/add.js +++ /dev/null @@ -1,9 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -define('modules/add', function () { - return function (a, b) { - return a + b; - }; -}); diff --git a/addon-sdk-1.16/test/modules/async1.js b/addon-sdk-1.16/test/modules/async1.js deleted file mode 100644 index b7b60fdc..00000000 --- a/addon-sdk-1.16/test/modules/async1.js +++ /dev/null @@ -1,14 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -define(['./traditional2', './async2'], function () { - var traditional2 = require('./traditional2'); - return { - name: 'async1', - traditional1Name: traditional2.traditional1Name, - traditional2Name: traditional2.name, - async2Name: require('./async2').name, - async2Traditional2Name: require('./async2').traditional2Name - }; -}); diff --git a/addon-sdk-1.16/test/modules/async2.js b/addon-sdk-1.16/test/modules/async2.js deleted file mode 100644 index 802fb250..00000000 --- a/addon-sdk-1.16/test/modules/async2.js +++ /dev/null @@ -1,8 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -define(['./traditional2', 'exports'], function (traditional2, exports) { - exports.name = 'async2'; - exports.traditional2Name = traditional2.name; -}); diff --git a/addon-sdk-1.16/test/modules/badExportAndReturn.js b/addon-sdk-1.16/test/modules/badExportAndReturn.js deleted file mode 100644 index 35a5359f..00000000 --- a/addon-sdk-1.16/test/modules/badExportAndReturn.js +++ /dev/null @@ -1,10 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -// This is a bad module, it asks for exports but also returns a value from -// the define defintion function. -define(['exports'], function (exports) { - return 'badExportAndReturn'; -}); - diff --git a/addon-sdk-1.16/test/modules/badFirst.js b/addon-sdk-1.16/test/modules/badFirst.js deleted file mode 100644 index fdb03bd4..00000000 --- a/addon-sdk-1.16/test/modules/badFirst.js +++ /dev/null @@ -1,9 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -define(['./badSecond'], function (badSecond) { - return { - name: 'badFirst' - }; -}); diff --git a/addon-sdk-1.16/test/modules/badSecond.js b/addon-sdk-1.16/test/modules/badSecond.js deleted file mode 100644 index 8321a854..00000000 --- a/addon-sdk-1.16/test/modules/badSecond.js +++ /dev/null @@ -1,8 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -var first = require('./badFirst'); - -exports.name = 'badSecond'; -exports.badFirstName = first.name; diff --git a/addon-sdk-1.16/test/modules/blue.js b/addon-sdk-1.16/test/modules/blue.js deleted file mode 100644 index 14ab1493..00000000 --- a/addon-sdk-1.16/test/modules/blue.js +++ /dev/null @@ -1,9 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -define(function () { - return { - name: 'blue' - }; -}); diff --git a/addon-sdk-1.16/test/modules/castor.js b/addon-sdk-1.16/test/modules/castor.js deleted file mode 100644 index 9209623b..00000000 --- a/addon-sdk-1.16/test/modules/castor.js +++ /dev/null @@ -1,10 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -define(['exports', './pollux'], function(exports, pollux) { - exports.name = 'castor'; - exports.getPolluxName = function () { - return pollux.name; - }; -}); diff --git a/addon-sdk-1.16/test/modules/cheetah.js b/addon-sdk-1.16/test/modules/cheetah.js deleted file mode 100644 index 37d12bfc..00000000 --- a/addon-sdk-1.16/test/modules/cheetah.js +++ /dev/null @@ -1,9 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -define(function () { - return function () { - return 'cheetah'; - }; -}); diff --git a/addon-sdk-1.16/test/modules/color.js b/addon-sdk-1.16/test/modules/color.js deleted file mode 100644 index 0d946bd9..00000000 --- a/addon-sdk-1.16/test/modules/color.js +++ /dev/null @@ -1,7 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -define({ - type: 'color' -}); diff --git a/addon-sdk-1.16/test/modules/dupe.js b/addon-sdk-1.16/test/modules/dupe.js deleted file mode 100644 index f87fa555..00000000 --- a/addon-sdk-1.16/test/modules/dupe.js +++ /dev/null @@ -1,15 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -define({ - name: 'dupe' -}); - -// This is wrong and should not be allowed. Only one call to -// define per file. -define([], function () { - return { - name: 'dupe2' - }; -}); diff --git a/addon-sdk-1.16/test/modules/dupeNested.js b/addon-sdk-1.16/test/modules/dupeNested.js deleted file mode 100644 index 703948ca..00000000 --- a/addon-sdk-1.16/test/modules/dupeNested.js +++ /dev/null @@ -1,15 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - - -define(function () { - // This is wrong and should not be allowed. - define('dupeNested2', { - name: 'dupeNested2' - }); - - return { - name: 'dupeNested' - }; -}); diff --git a/addon-sdk-1.16/test/modules/dupeSetExports.js b/addon-sdk-1.16/test/modules/dupeSetExports.js deleted file mode 100644 index 12a1be22..00000000 --- a/addon-sdk-1.16/test/modules/dupeSetExports.js +++ /dev/null @@ -1,8 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -define({name: "dupeSetExports"}); - -// so this should cause a failure -module.setExports("no no no"); diff --git a/addon-sdk-1.16/test/modules/exportsEquals.js b/addon-sdk-1.16/test/modules/exportsEquals.js deleted file mode 100644 index e176316f..00000000 --- a/addon-sdk-1.16/test/modules/exportsEquals.js +++ /dev/null @@ -1,5 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -module.exports = 4; diff --git a/addon-sdk-1.16/test/modules/green.js b/addon-sdk-1.16/test/modules/green.js deleted file mode 100644 index ce2d134b..00000000 --- a/addon-sdk-1.16/test/modules/green.js +++ /dev/null @@ -1,10 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -define('modules/green', ['./color'], function (color) { - return { - name: 'green', - parentType: color.type - }; -}); diff --git a/addon-sdk-1.16/test/modules/lion.js b/addon-sdk-1.16/test/modules/lion.js deleted file mode 100644 index 9c3ac3bf..00000000 --- a/addon-sdk-1.16/test/modules/lion.js +++ /dev/null @@ -1,7 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -define(function(require) { - return 'lion'; -}); diff --git a/addon-sdk-1.16/test/modules/orange.js b/addon-sdk-1.16/test/modules/orange.js deleted file mode 100644 index 900a32b0..00000000 --- a/addon-sdk-1.16/test/modules/orange.js +++ /dev/null @@ -1,10 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -define(['./color'], function (color) { - return { - name: 'orange', - parentType: color.type - }; -}); diff --git a/addon-sdk-1.16/test/modules/pollux.js b/addon-sdk-1.16/test/modules/pollux.js deleted file mode 100644 index 61cf66f9..00000000 --- a/addon-sdk-1.16/test/modules/pollux.js +++ /dev/null @@ -1,10 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -define(['exports', './castor'], function(exports, castor) { - exports.name = 'pollux'; - exports.getCastorName = function () { - return castor.name; - }; -}); diff --git a/addon-sdk-1.16/test/modules/red.js b/addon-sdk-1.16/test/modules/red.js deleted file mode 100644 index c47b8e92..00000000 --- a/addon-sdk-1.16/test/modules/red.js +++ /dev/null @@ -1,16 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -define(function (require) { - // comment fake-outs for require finding. - // require('bad1'); - return { - name: 'red', - parentType: require('./color').type - }; - - /* - require('bad2'); - */ -}); diff --git a/addon-sdk-1.16/test/modules/setExports.js b/addon-sdk-1.16/test/modules/setExports.js deleted file mode 100644 index 495c301c..00000000 --- a/addon-sdk-1.16/test/modules/setExports.js +++ /dev/null @@ -1,5 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -module.setExports(5); diff --git a/addon-sdk-1.16/test/modules/subtract.js b/addon-sdk-1.16/test/modules/subtract.js deleted file mode 100644 index 06e1053a..00000000 --- a/addon-sdk-1.16/test/modules/subtract.js +++ /dev/null @@ -1,9 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -define(function () { - return function (a, b) { - return a - b; - } -}); diff --git a/addon-sdk-1.16/test/modules/tiger.js b/addon-sdk-1.16/test/modules/tiger.js deleted file mode 100644 index 80479d01..00000000 --- a/addon-sdk-1.16/test/modules/tiger.js +++ /dev/null @@ -1,8 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -define(function (require, exports) { - exports.name = 'tiger'; - exports.type = require('./types/cat').type; -}); diff --git a/addon-sdk-1.16/test/modules/traditional1.js b/addon-sdk-1.16/test/modules/traditional1.js deleted file mode 100644 index 28af8ef3..00000000 --- a/addon-sdk-1.16/test/modules/traditional1.js +++ /dev/null @@ -1,12 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -exports.name = 'traditional1' - -var async1 = require('./async1'); - -exports.traditional2Name = async1.traditional2Name; -exports.traditional1Name = async1.traditional1Name; -exports.async2Name = async1.async2Name; -exports.async2Traditional2Name = async1.async2Traditional2Name; diff --git a/addon-sdk-1.16/test/modules/traditional2.js b/addon-sdk-1.16/test/modules/traditional2.js deleted file mode 100644 index 67b64eec..00000000 --- a/addon-sdk-1.16/test/modules/traditional2.js +++ /dev/null @@ -1,6 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -exports.name = 'traditional2'; -exports.traditional1Name = require('./traditional1').name; diff --git a/addon-sdk-1.16/test/modules/types/cat.js b/addon-sdk-1.16/test/modules/types/cat.js deleted file mode 100644 index 41513d68..00000000 --- a/addon-sdk-1.16/test/modules/types/cat.js +++ /dev/null @@ -1,5 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -exports.type = 'cat'; diff --git a/addon-sdk-1.16/test/pagemod-test-helpers.js b/addon-sdk-1.16/test/pagemod-test-helpers.js deleted file mode 100644 index 8bcd3ebd..00000000 --- a/addon-sdk-1.16/test/pagemod-test-helpers.js +++ /dev/null @@ -1,64 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const {Cc,Ci} = require("chrome"); -const timer = require("sdk/timers"); -const xulApp = require("sdk/system/xul-app"); -const { Loader } = require("sdk/test/loader"); -const { openTab, getBrowserForTab, closeTab } = require("sdk/tabs/utils"); - -/** - * A helper function that creates a PageMod, then opens the specified URL - * and checks the effect of the page mod on 'onload' event via testCallback. - */ -exports.testPageMod = function testPageMod(assert, done, testURL, pageModOptions, - testCallback, timeout) { - if (!xulApp.versionInRange(xulApp.platformVersion, "1.9.3a3", "*") && - !xulApp.versionInRange(xulApp.platformVersion, "1.9.2.7", "1.9.2.*")) { - assert.pass("Note: not testing PageMod, as it doesn't work on this platform version"); - return null; - } - - var wm = Cc['@mozilla.org/appshell/window-mediator;1'] - .getService(Ci.nsIWindowMediator); - var browserWindow = wm.getMostRecentWindow("navigator:browser"); - if (!browserWindow) { - assert.pass("page-mod tests: could not find the browser window, so " + - "will not run. Use -a firefox to run the pagemod tests.") - return null; - } - - let loader = Loader(module); - let pageMod = loader.require("sdk/page-mod"); - - var pageMods = [new pageMod.PageMod(opts) for each(opts in pageModOptions)]; - - let newTab = openTab(browserWindow, testURL, { - inBackground: false - }); - var b = getBrowserForTab(newTab); - - function onPageLoad() { - b.removeEventListener("load", onPageLoad, true); - // Delay callback execute as page-mod content scripts may be executed on - // load event. So page-mod actions may not be already done. - // If we delay even more contentScriptWhen:'end', we may want to modify - // this code again. - timer.setTimeout(testCallback, 0, - b.contentWindow.wrappedJSObject, - function () { - pageMods.forEach(function(mod) mod.destroy()); - // XXX leaks reported if we don't close the tab? - closeTab(newTab); - loader.unload(); - done(); - } - ); - } - b.addEventListener("load", onPageLoad, true); - - return pageMods; -} diff --git a/addon-sdk-1.16/test/private-browsing/global.js b/addon-sdk-1.16/test/private-browsing/global.js deleted file mode 100644 index 89d7da4e..00000000 --- a/addon-sdk-1.16/test/private-browsing/global.js +++ /dev/null @@ -1,239 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -'use strict'; - -const timer = require("sdk/timers"); -const { LoaderWithHookedConsole, deactivate, pb, pbUtils } = require("./helper"); -const tabs = require("sdk/tabs"); -const { getMostRecentBrowserWindow, isWindowPrivate } = require('sdk/window/utils'); -const { set: setPref } = require("sdk/preferences/service"); -const DEPRECATE_PREF = "devtools.errorconsole.deprecation_warnings"; - -exports["test activate private mode via handler"] = function(assert, done) { - function onReady(tab) { - if (tab.url == "about:robots") - tab.close(function() pb.activate()); - } - function cleanup(tab) { - if (tab.url == "about:") { - tabs.removeListener("ready", cleanup); - tab.close(function onClose() { - done(); - }); - } - } - - tabs.on("ready", onReady); - pb.once("start", function onStart() { - assert.pass("private mode was activated"); - pb.deactivate(); - }); - pb.once("stop", function onStop() { - assert.pass("private mode was deactivated"); - tabs.removeListener("ready", onReady); - tabs.on("ready", cleanup); - }); - tabs.once("open", function onOpen() { - tabs.open("about:robots"); - }); - tabs.open("about:"); -}; - -// tests that isActive has the same value as the private browsing service -// expects -exports.testGetIsActive = function (assert) { - assert.equal(pb.isActive, false, - "private-browsing.isActive is correct without modifying PB service"); - assert.equal(pb.isPrivate(), false, - "private-browsing.sPrivate() is correct without modifying PB service"); - - pb.once("start", function() { - assert.ok(pb.isActive, - "private-browsing.isActive is correct after modifying PB service"); - assert.ok(pb.isPrivate(), - "private-browsing.sPrivate() is correct after modifying PB service"); - // Switch back to normal mode. - pb.deactivate(); - }); - pb.activate(); - - pb.once("stop", function() { - assert.ok(!pb.isActive, - "private-browsing.isActive is correct after modifying PB service"); - assert.ok(!pb.isPrivate(), - "private-browsing.sPrivate() is correct after modifying PB service"); - test.done(); - }); -}; - -exports.testStart = function(assert, done) { - pb.on("start", function onStart() { - assert.equal(this, pb, "`this` should be private-browsing module"); - assert.ok(pbUtils.getMode(), - 'private mode is active when "start" event is emitted'); - assert.ok(pb.isActive, - '`isActive` is `true` when "start" event is emitted'); - assert.ok(pb.isPrivate(), - '`isPrivate` is `true` when "start" event is emitted'); - pb.removeListener("start", onStart); - deactivate(done); - }); - pb.activate(); -}; - -exports.testStop = function(assert, done) { - pb.once("stop", function onStop() { - assert.equal(this, pb, "`this` should be private-browsing module"); - assert.equal(pbUtils.getMode(), false, - "private mode is disabled when stop event is emitted"); - assert.equal(pb.isActive, false, - "`isActive` is `false` when stop event is emitted"); - assert.equal(pb.isPrivate(), false, - "`isPrivate()` is `false` when stop event is emitted"); - done(); - }); - pb.activate(); - pb.once("start", function() { - pb.deactivate(); - }); -}; - -exports.testBothListeners = function(assert, done) { - let stop = false; - let start = false; - - function onStop() { - assert.equal(stop, false, - "stop callback must be called only once"); - assert.equal(pbUtils.getMode(), false, - "private mode is disabled when stop event is emitted"); - assert.equal(pb.isActive, false, - "`isActive` is `false` when stop event is emitted"); - assert.equal(pb.isPrivate(), false, - "`isPrivate()` is `false` when stop event is emitted"); - - pb.on("start", finish); - pb.removeListener("start", onStart); - pb.removeListener("start", onStart2); - pb.activate(); - stop = true; - } - - function onStart() { - assert.equal(false, start, - "stop callback must be called only once"); - assert.ok(pbUtils.getMode(), - "private mode is active when start event is emitted"); - assert.ok(pb.isActive, - "`isActive` is `true` when start event is emitted"); - assert.ok(pb.isPrivate(), - "`isPrivate()` is `true` when start event is emitted"); - - pb.on("stop", onStop); - pb.deactivate(); - start = true; - } - - function onStart2() { - assert.ok(start, "start listener must be called already"); - assert.equal(false, stop, "stop callback must not be called yet"); - } - - function finish() { - assert.ok(pbUtils.getMode(), true, - "private mode is active when start event is emitted"); - assert.ok(pb.isActive, - "`isActive` is `true` when start event is emitted"); - assert.ok(pb.isPrivate(), - "`isPrivate()` is `true` when start event is emitted"); - - pb.removeListener("start", finish); - pb.removeListener("stop", onStop); - - pb.deactivate(); - pb.once("stop", function () { - assert.equal(pbUtils.getMode(), false); - assert.equal(pb.isActive, false); - assert.equal(pb.isPrivate(), false); - - done(); - }); - } - - pb.on("start", onStart); - pb.on("start", onStart2); - pb.activate(); -}; - -exports.testAutomaticUnload = function(assert, done) { - setPref(DEPRECATE_PREF, true); - - // Create another private browsing instance and unload it - let { loader, errors } = LoaderWithHookedConsole(module); - let pb2 = loader.require("sdk/private-browsing"); - let called = false; - pb2.on("start", function onStart() { - called = true; - assert.fail("should not be called:x"); - }); - loader.unload(); - - // Then switch to private mode in order to check that the previous instance - // is correctly destroyed - pb.once("start", function onStart() { - timer.setTimeout(function () { - assert.ok(!called, - "First private browsing instance is destroyed and inactive"); - // Must reset to normal mode, so that next test starts with it. - deactivate(function() { - assert.ok(errors.length, 0, "should have been 1 deprecation error"); - done(); - }); - }, 0); - }); - - pb.activate(); -}; - -exports.testUnloadWhileActive = function(assert, done) { - let called = false; - let { loader, errors } = LoaderWithHookedConsole(module); - let pb2 = loader.require("sdk/private-browsing"); - let ul = loader.require("sdk/system/unload"); - - let unloadHappened = false; - ul.when(function() { - unloadHappened = true; - timer.setTimeout(function() { - pb.deactivate(); - }); - }); - pb2.once("start", function() { - loader.unload(); - }); - pb2.once("stop", function() { - called = true; - assert.ok(unloadHappened, "the unload event should have already occurred."); - assert.fail("stop should not have been fired"); - }); - pb.once("stop", function() { - assert.ok(!called, "stop was not called on unload"); - assert.ok(errors.length, 2, "should have been 2 deprecation errors"); - done(); - }); - - pb.activate(); -}; - -exports.testIgnoreWindow = function(assert, done) { - let window = getMostRecentBrowserWindow(); - - pb.once('start', function() { - assert.ok(isWindowPrivate(window), 'window is private'); - assert.ok(!pbUtils.ignoreWindow(window), 'window is not ignored'); - pb.once('stop', done); - pb.deactivate(); - }); - pb.activate(); -}; diff --git a/addon-sdk-1.16/test/private-browsing/helper.js b/addon-sdk-1.16/test/private-browsing/helper.js deleted file mode 100644 index cf4d5a01..00000000 --- a/addon-sdk-1.16/test/private-browsing/helper.js +++ /dev/null @@ -1,100 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -'use strict'; - -const { Loader } = require('sdk/test/loader'); - -const { loader } = LoaderWithHookedConsole(module); - -const pb = loader.require('sdk/private-browsing'); -const pbUtils = loader.require('sdk/private-browsing/utils'); -const xulApp = require("sdk/system/xul-app"); -const { open: openWindow, getMostRecentBrowserWindow } = require('sdk/window/utils'); -const { openTab, getTabContentWindow, getActiveTab, setTabURL, closeTab } = require('sdk/tabs/utils'); -const promise = require("sdk/core/promise"); -const windowHelpers = require('sdk/window/helpers'); -const events = require("sdk/system/events"); - -function LoaderWithHookedConsole(module) { - let globals = {}; - let errors = []; - - globals.console = Object.create(console, { - error: { - value: function(e) { - errors.push(e); - if (!/DEPRECATED:/.test(e)) { - console.error(e); - } - } - } - }); - - let loader = Loader(module, globals); - - return { - loader: loader, - errors: errors - } -} - -function deactivate(callback) { - if (pbUtils.isGlobalPBSupported) { - if (callback) - pb.once('stop', callback); - pb.deactivate(); - } -} -exports.deactivate = deactivate; - -exports.pb = pb; -exports.pbUtils = pbUtils; -exports.LoaderWithHookedConsole = LoaderWithHookedConsole; - -exports.openWebpage = function openWebpage(url, enablePrivate) { - if (xulApp.is("Fennec")) { - let chromeWindow = getMostRecentBrowserWindow(); - let rawTab = openTab(chromeWindow, url, { - isPrivate: enablePrivate - }); - - return { - ready: promise.resolve(getTabContentWindow(rawTab)), - close: function () { - closeTab(rawTab); - // Returns a resolved promise as there is no need to wait - return promise.resolve(); - } - }; - } - else { - let win = openWindow(null, { - features: { - private: enablePrivate - } - }); - let deferred = promise.defer(); - - // Wait for delayed startup code to be executed, in order to ensure - // that the window is really ready - events.on("browser-delayed-startup-finished", function onReady({subject}) { - if (subject == win) { - events.off("browser-delayed-startup-finished", onReady); - deferred.resolve(win); - - let rawTab = getActiveTab(win); - setTabURL(rawTab, url); - deferred.resolve(getTabContentWindow(rawTab)); - } - }, true); - - return { - ready: deferred.promise, - close: function () { - return windowHelpers.close(win); - } - }; - } - return null; -} diff --git a/addon-sdk-1.16/test/private-browsing/tabs.js b/addon-sdk-1.16/test/private-browsing/tabs.js deleted file mode 100644 index 726a370a..00000000 --- a/addon-sdk-1.16/test/private-browsing/tabs.js +++ /dev/null @@ -1,27 +0,0 @@ -'use strict'; - -const { Ci } = require('chrome'); -const { openTab, closeTab } = require('sdk/tabs/utils'); -const { browserWindows } = require('sdk/windows'); -const { getOwnerWindow } = require('sdk/private-browsing/window/utils'); -const { isPrivate } = require('sdk/private-browsing'); - -exports.testIsPrivateOnTab = function(assert) { - let window = browserWindows.activeWindow; - - let chromeWindow = getOwnerWindow(window); - - assert.ok(chromeWindow instanceof Ci.nsIDOMWindow, 'associated window is found'); - assert.ok(!isPrivate(chromeWindow), 'the top level window is not private'); - - let rawTab = openTab(chromeWindow, 'data:text/html,

Hi!

', { - isPrivate: true - }); - - // test that the tab is private - assert.ok(rawTab.browser.docShell.QueryInterface(Ci.nsILoadContext).usePrivateBrowsing); - assert.ok(isPrivate(rawTab.browser.contentWindow)); - assert.ok(isPrivate(rawTab.browser)); - - closeTab(rawTab); -}; diff --git a/addon-sdk-1.16/test/private-browsing/windows.js b/addon-sdk-1.16/test/private-browsing/windows.js deleted file mode 100644 index a302643a..00000000 --- a/addon-sdk-1.16/test/private-browsing/windows.js +++ /dev/null @@ -1,139 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -'use strict'; - -const { pb, pbUtils } = require('./helper'); -const { onFocus, openDialog, open } = require('sdk/window/utils'); -const { open: openPromise, close, focus, promise } = require('sdk/window/helpers'); -const { isPrivate } = require('sdk/private-browsing'); -const { browserWindows: windows } = require('sdk/windows'); -const { defer } = require('sdk/core/promise'); -const tabs = require('sdk/tabs'); - -// test openDialog() from window/utils with private option -// test isActive state in pwpb case -// test isPrivate on ChromeWindow -exports.testPerWindowPrivateBrowsingGetter = function(assert, done) { - let win = openDialog({ - private: true - }); - - promise(win, 'DOMContentLoaded').then(function onload() { - assert.equal(pbUtils.getMode(win), - true, 'Newly opened window is in PB mode'); - assert.ok(isPrivate(win), 'isPrivate(window) is true'); - assert.equal(pb.isActive, false, 'PB mode is not active'); - - close(win).then(function() { - assert.equal(pb.isActive, false, 'PB mode is not active'); - done(); - }); - }); -} - -// test open() from window/utils with private feature -// test isActive state in pwpb case -// test isPrivate on ChromeWindow -exports.testPerWindowPrivateBrowsingGetter = function(assert, done) { - let win = open('chrome://browser/content/browser.xul', { - features: { - private: true - } - }); - - promise(win, 'DOMContentLoaded').then(function onload() { - assert.equal(pbUtils.getMode(win), - true, 'Newly opened window is in PB mode'); - assert.ok(isPrivate(win), 'isPrivate(window) is true'); - assert.equal(pb.isActive, false, 'PB mode is not active'); - - close(win).then(function() { - assert.equal(pb.isActive, false, 'PB mode is not active'); - done(); - }); - }); -} - -exports.testIsPrivateOnWindowOpen = function(assert, done) { - windows.open({ - isPrivate: true, - onOpen: function(window) { - assert.equal(isPrivate(window), false, 'isPrivate for a window is true when it should be'); - assert.equal(isPrivate(window.tabs[0]), false, 'isPrivate for a tab is false when it should be'); - window.close(done); - } - }); -} - -exports.testIsPrivateOnWindowOpenFromPrivate = function(assert, done) { - // open a private window - openPromise(null, { - features: { - private: true, - chrome: true, - titlebar: true, - toolbar: true - } - }).then(focus).then(function(window) { - let { promise, resolve } = defer(); - - assert.equal(isPrivate(window), true, 'the only open window is private'); - - windows.open({ - url: 'about:blank', - onOpen: function(w) { - assert.equal(isPrivate(w), false, 'new test window is not private'); - w.close(function() resolve(window)); - } - }); - - return promise; - }).then(close). - then(done, assert.fail); -}; - -exports.testOpenTabWithPrivateWindow = function(assert, done) { - function start() { - openPromise(null, { - features: { - private: true, - toolbar: true - } - }).then(focus).then(function(window) { - let { promise, resolve } = defer(); - assert.equal(isPrivate(window), true, 'the focused window is private'); - - tabs.open({ - url: 'about:blank', - onOpen: function(tab) { - assert.equal(isPrivate(tab), false, 'the opened tab is not private'); - // not closing this tab on purpose.. for now... - // we keep this tab open because we closed all windows - // and must keep a non-private window open at end of this test for next ones. - resolve(window); - } - }); - - return promise; - }).then(close).then(done, assert.fail); - } - - (function closeWindows() { - if (windows.length > 0) { - return windows.activeWindow.close(closeWindows); - } - assert.pass('all pre test windows have been closed'); - return start(); - })() -}; - -exports.testIsPrivateOnWindowOff = function(assert, done) { - windows.open({ - onOpen: function(window) { - assert.equal(isPrivate(window), false, 'isPrivate for a window is false when it should be'); - assert.equal(isPrivate(window.tabs[0]), false, 'isPrivate for a tab is false when it should be'); - window.close(done); - } - }) -} diff --git a/addon-sdk-1.16/test/sidebar/utils.js b/addon-sdk-1.16/test/sidebar/utils.js deleted file mode 100644 index 80c3af91..00000000 --- a/addon-sdk-1.16/test/sidebar/utils.js +++ /dev/null @@ -1,73 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -'use strict'; - -module.metadata = { - 'engines': { - 'Firefox': '*' - } -}; - -const { Cu } = require('chrome'); -const { getMostRecentBrowserWindow } = require('sdk/window/utils'); -const { fromIterator } = require('sdk/util/array'); - -const BUILTIN_SIDEBAR_MENUITEMS = exports.BUILTIN_SIDEBAR_MENUITEMS = [ - 'menu_socialSidebar', - 'menu_historySidebar', - 'menu_bookmarksSidebar' -]; - -function isSidebarShowing(window) { - window = window || getMostRecentBrowserWindow(); - let sidebar = window.document.getElementById('sidebar-box'); - return !sidebar.hidden; -} -exports.isSidebarShowing = isSidebarShowing; - -function getSidebarMenuitems(window) { - window = window || getMostRecentBrowserWindow(); - return fromIterator(window.document.querySelectorAll('#viewSidebarMenu menuitem')); -} -exports.getSidebarMenuitems = getSidebarMenuitems; - -function getExtraSidebarMenuitems() { - let menuitems = getSidebarMenuitems(); - return menuitems.filter(function(mi) { - return BUILTIN_SIDEBAR_MENUITEMS.indexOf(mi.getAttribute('id')) < 0; - }); -} -exports.getExtraSidebarMenuitems = getExtraSidebarMenuitems; - -function makeID(id) { - return 'jetpack-sidebar-' + id; -} -exports.makeID = makeID; - -function simulateCommand(ele) { - let window = ele.ownerDocument.defaultView; - let { document } = window; - var evt = document.createEvent('XULCommandEvent'); - evt.initCommandEvent('command', true, true, window, - 0, false, false, false, false, null); - ele.dispatchEvent(evt); -} -exports.simulateCommand = simulateCommand; - -function simulateClick(ele) { - let window = ele.ownerDocument.defaultView; - let { document } = window; - let evt = document.createEvent('MouseEvents'); - evt.initMouseEvent('click', true, true, window, - 0, 0, 0, 0, 0, false, false, false, false, 0, null); - ele.dispatchEvent(evt); -} -exports.simulateClick = simulateClick; - -// OSX and Windows exhibit different behaviors when 'checked' is false, -// so compare against the consistent 'true'. See bug 894809. -function isChecked(node) { - return node.getAttribute('checked') === 'true'; -}; -exports.isChecked = isChecked; diff --git a/addon-sdk-1.16/test/tabs/test-fennec-tabs.js b/addon-sdk-1.16/test/tabs/test-fennec-tabs.js deleted file mode 100644 index e579ebba..00000000 --- a/addon-sdk-1.16/test/tabs/test-fennec-tabs.js +++ /dev/null @@ -1,584 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -'use strict'; - -const { Cc, Ci } = require('chrome'); -const { Loader, LoaderWithHookedConsole } = require('sdk/test/loader'); -const timer = require('sdk/timers'); -const tabs = require('sdk/tabs'); -const windows = require('sdk/windows'); -const { set: setPref } = require("sdk/preferences/service"); -const DEPRECATE_PREF = "devtools.errorconsole.deprecation_warnings"; - -const tabsLen = tabs.length; -const URL = 'data:text/html;charset=utf-8,#title#'; - -// Fennec error message dispatched on all currently unimplement tab features, -// that match LoaderWithHookedConsole messages object pattern -const ERR_FENNEC_MSG = { - type: "error", - msg: "This method is not yet supported by Fennec" -}; - -// TEST: tab unloader -exports.testAutomaticDestroy = function(assert, done) { - let called = false; - - let loader2 = Loader(module); - let loader3 = Loader(module); - let tabs2 = loader2.require('sdk/tabs'); - let tabs3 = loader3.require('sdk/tabs'); - let tabs2Len = tabs2.length; - - tabs2.on('open', function onOpen(tab) { - assert.fail("an onOpen listener was called that should not have been"); - called = true; - }); - tabs2.on('ready', function onReady(tab) { - assert.fail("an onReady listener was called that should not have been"); - called = true; - }); - tabs2.on('select', function onSelect(tab) { - assert.fail("an onSelect listener was called that should not have been"); - called = true; - }); - tabs2.on('close', function onClose(tab) { - assert.fail("an onClose listener was called that should not have been"); - called = true; - }); - loader2.unload(); - - tabs3.on('open', function onOpen(tab) { - assert.pass("an onOpen listener was called for tabs3"); - - tab.on('ready', function onReady(tab) { - assert.fail("an onReady listener was called that should not have been"); - called = true; - }); - tab.on('select', function onSelect(tab) { - assert.fail("an onSelect listener was called that should not have been"); - called = true; - }); - tab.on('close', function onClose(tab) { - assert.fail("an onClose listener was called that should not have been"); - called = true; - }); - }); - tabs3.open(URL.replace(/#title#/, 'tabs3')); - loader3.unload(); - - // Fire a tab event and ensure that the destroyed tab is inactive - tabs.once('open', function(tab) { - assert.pass('tabs.once("open") works!'); - - assert.equal(tabs2Len, tabs2.length, "tabs2 length was not changed"); - assert.equal(tabs.length, (tabs2.length+2), "tabs.length > tabs2.length"); - - tab.once('ready', function() { - assert.pass('tab.once("ready") works!'); - - tab.once('close', function() { - assert.pass('tab.once("close") works!'); - - timer.setTimeout(function () { - assert.ok(!called, "Unloaded tab module is destroyed and inactive"); - - // end test - done(); - }); - }); - - tab.close(); - }); - }); - - tabs.open('data:text/html;charset=utf-8,foo'); -}; - -// TEST: tab properties -exports.testTabProperties = function(assert, done) { - setPref(DEPRECATE_PREF, true); - let { loader, messages } = LoaderWithHookedConsole(); - let tabs = loader.require('sdk/tabs'); - - let url = "data:text/html;charset=utf-8,foofoo"; - let tabsLen = tabs.length; - tabs.open({ - url: url, - onReady: function(tab) { - assert.equal(tab.title, "foo", "title of the new tab matches"); - assert.equal(tab.url, url, "URL of the new tab matches"); - assert.ok(tab.favicon, "favicon of the new tab is not empty"); - // TODO: remove need for this test by implementing the favicon feature - assert.equal(messages[0].msg, - "tab.favicon is deprecated, and " + - "currently favicon helpers are not yet supported " + - "by Fennec", - "favicon logs an error for now"); - assert.equal(tab.style, null, "style of the new tab matches"); - assert.equal(tab.index, tabsLen, "index of the new tab matches"); - assert.notEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches"); - assert.notEqual(tab.id, null, "a tab object always has an id property"); - - tab.close(function() { - loader.unload(); - - // end test - done(); - }); - } - }); -}; - -// TEST: tabs iterator and length property -exports.testTabsIteratorAndLength = function(assert, done) { - let newTabs = []; - let startCount = 0; - for each (let t in tabs) startCount++; - - assert.equal(startCount, tabs.length, "length property is correct"); - - let url = "data:text/html;charset=utf-8,testTabsIteratorAndLength"; - tabs.open({url: url, onOpen: function(tab) newTabs.push(tab)}); - tabs.open({url: url, onOpen: function(tab) newTabs.push(tab)}); - tabs.open({ - url: url, - onOpen: function(tab) { - let count = 0; - for each (let t in tabs) count++; - assert.equal(count, startCount + 3, "iterated tab count matches"); - assert.equal(startCount + 3, tabs.length, "iterated tab count matches length property"); - - let newTabsLength = newTabs.length; - newTabs.forEach(function(t) t.close(function() { - if (--newTabsLength > 0) return; - - tab.close(done); - })); - } - }); -}; - -// TEST: tab.url setter -exports.testTabLocation = function(assert, done) { - let url1 = "data:text/html;charset=utf-8,foo"; - let url2 = "data:text/html;charset=utf-8,bar"; - - tabs.on('ready', function onReady(tab) { - if (tab.url != url2) - return; - - tabs.removeListener('ready', onReady); - assert.pass("tab loaded the correct url"); - - tab.close(done); - }); - - tabs.open({ - url: url1, - onOpen: function(tab) { - tab.url = url2; - } - }); -}; - -// TEST: tab.move() -exports.testTabMove = function(assert, done) { - let { loader, messages } = LoaderWithHookedConsole(); - let tabs = loader.require('sdk/tabs'); - - let url = "data:text/html;charset=utf-8,testTabMove"; - - tabs.open({ - url: url, - onOpen: function(tab1) { - assert.ok(tab1.index >= 0, "opening a tab returns a tab w/ valid index"); - - tabs.open({ - url: url, - onOpen: function(tab) { - let i = tab.index; - assert.ok(tab.index > tab1.index, "2nd tab has valid index"); - tab.index = 0; - assert.equal(tab.index, i, "tab index after move matches"); - assert.equal(JSON.stringify(messages), - JSON.stringify([ERR_FENNEC_MSG]), - "setting tab.index logs error"); - // end test - tab1.close(function() tab.close(function() { - loader.unload(); - done(); - })); - } - }); - } - }); -}; - -// TEST: open tab with default options -exports.testTabsOpen_alt = function(assert, done) { - let { loader, messages } = LoaderWithHookedConsole(); - let tabs = loader.require('sdk/tabs'); - let url = "data:text/html;charset=utf-8,default"; - - tabs.open({ - url: url, - onReady: function(tab) { - assert.equal(tab.url, url, "URL of the new tab matches"); - assert.equal(tabs.activeTab, tab, "URL of active tab in the current window matches"); - assert.equal(tab.isPinned, false, "The new tab is not pinned"); - assert.equal(messages.length, 1, "isPinned logs error"); - - // end test - tab.close(function() { - loader.unload(); - done(); - }); - } - }); -}; - -// TEST: open pinned tab -exports.testOpenPinned_alt = function(assert, done) { - let { loader, messages } = LoaderWithHookedConsole(); - let tabs = loader.require('sdk/tabs'); - let url = "about:blank"; - - tabs.open({ - url: url, - isPinned: true, - onOpen: function(tab) { - assert.equal(tab.isPinned, false, "The new tab is pinned"); - // We get two error message: one for tabs.open's isPinned argument - // and another one for tab.isPinned - assert.equal(JSON.stringify(messages), - JSON.stringify([ERR_FENNEC_MSG, ERR_FENNEC_MSG]), - "isPinned logs error"); - - // end test - tab.close(function() { - loader.unload(); - done(); - }); - } - }); -}; - -// TEST: pin/unpin opened tab -exports.testPinUnpin_alt = function(assert, done) { - let { loader, messages } = LoaderWithHookedConsole(); - let tabs = loader.require('sdk/tabs'); - let url = "data:text/html;charset=utf-8,default"; - - tabs.open({ - url: url, - onOpen: function(tab) { - tab.pin(); - assert.equal(tab.isPinned, false, "The tab was pinned correctly"); - assert.equal(JSON.stringify(messages), - JSON.stringify([ERR_FENNEC_MSG, ERR_FENNEC_MSG]), - "tab.pin() logs error"); - - // Clear console messages for the following test - messages.length = 0; - - tab.unpin(); - assert.equal(tab.isPinned, false, "The tab was unpinned correctly"); - assert.equal(JSON.stringify(messages), - JSON.stringify([ERR_FENNEC_MSG, ERR_FENNEC_MSG]), - "tab.unpin() logs error"); - - // end test - tab.close(function() { - loader.unload(); - done(); - }); - } - }); -}; - -// TEST: open tab in background -exports.testInBackground = function(assert, done) { - let activeUrl = tabs.activeTab.url; - let url = "data:text/html;charset=utf-8,background"; - let window = windows.browserWindows.activeWindow; - tabs.once('ready', function onReady(tab) { - assert.equal(tabs.activeTab.url, activeUrl, "URL of active tab has not changed"); - assert.equal(tab.url, url, "URL of the new background tab matches"); - assert.equal(windows.browserWindows.activeWindow, window, "a new window was not opened"); - assert.notEqual(tabs.activeTab.url, url, "URL of active tab is not the new URL"); - - // end test - tab.close(done); - }); - - tabs.open({ - url: url, - inBackground: true - }); -}; - -// TEST: open tab in new window -exports.testOpenInNewWindow = function(assert, done) { - let url = "data:text/html;charset=utf-8,newwindow"; - let window = windows.browserWindows.activeWindow; - - tabs.open({ - url: url, - inNewWindow: true, - onReady: function(tab) { - assert.equal(windows.browserWindows.length, 1, "a new window was not opened"); - assert.equal(windows.browserWindows.activeWindow, window, "old window is active"); - assert.equal(tab.url, url, "URL of the new tab matches"); - assert.equal(tabs.activeTab, tab, "tab is the activeTab"); - - tab.close(done); - } - }); -}; - -// TEST: onOpen event handler -exports.testTabsEvent_onOpen = function(assert, done) { - let url = URL.replace('#title#', 'testTabsEvent_onOpen'); - let eventCount = 0; - - // add listener via property assignment - function listener1(tab) { - eventCount++; - }; - tabs.on('open', listener1); - - // add listener via collection add - tabs.on('open', function listener2(tab) { - assert.equal(++eventCount, 2, "both listeners notified"); - tabs.removeListener('open', listener1); - tabs.removeListener('open', listener2); - - // ends test - tab.close(done); - }); - - tabs.open(url); -}; - -// TEST: onClose event handler -exports.testTabsEvent_onClose = function(assert, done) { - let url = "data:text/html;charset=utf-8,onclose"; - let eventCount = 0; - - // add listener via property assignment - function listener1(tab) { - eventCount++; - } - tabs.on('close', listener1); - - // add listener via collection add - tabs.on('close', function listener2(tab) { - assert.equal(++eventCount, 2, "both listeners notified"); - tabs.removeListener('close', listener1); - tabs.removeListener('close', listener2); - - // end test - done(); - }); - - tabs.on('ready', function onReady(tab) { - tabs.removeListener('ready', onReady); - tab.close(); - }); - - tabs.open(url); -}; - -// TEST: onClose event handler when a window is closed -exports.testTabsEvent_onCloseWindow = function(assert, done) { - let closeCount = 0, individualCloseCount = 0; - function listener() { - closeCount++; - } - tabs.on('close', listener); - - // One tab is already open with the window - let openTabs = 0; - function testCasePossiblyLoaded(tab) { - tab.close(function() { - if (++openTabs == 3) { - tabs.removeListener("close", listener); - - assert.equal(closeCount, 3, "Correct number of close events received"); - assert.equal(individualCloseCount, 3, - "Each tab with an attached onClose listener received a close " + - "event when the window was closed"); - - done(); - } - }); - } - - tabs.open({ - url: "data:text/html;charset=utf-8,tab2", - onOpen: testCasePossiblyLoaded, - onClose: function() individualCloseCount++ - }); - - tabs.open({ - url: "data:text/html;charset=utf-8,tab3", - onOpen: testCasePossiblyLoaded, - onClose: function() individualCloseCount++ - }); - - tabs.open({ - url: "data:text/html;charset=utf-8,tab4", - onOpen: testCasePossiblyLoaded, - onClose: function() individualCloseCount++ - }); -}; - -// TEST: onReady event handler -exports.testTabsEvent_onReady = function(assert, done) { - let url = "data:text/html;charset=utf-8,onready"; - let eventCount = 0; - - // add listener via property assignment - function listener1(tab) { - eventCount++; - }; - tabs.on('ready', listener1); - - // add listener via collection add - tabs.on('ready', function listener2(tab) { - assert.equal(++eventCount, 2, "both listeners notified"); - tabs.removeListener('ready', listener1); - tabs.removeListener('ready', listener2); - - // end test - tab.close(done); - }); - - tabs.open(url); -}; - -// TEST: onActivate event handler -exports.testTabsEvent_onActivate = function(assert, done) { - let url = "data:text/html;charset=utf-8,onactivate"; - let eventCount = 0; - - // add listener via property assignment - function listener1(tab) { - eventCount++; - }; - tabs.on('activate', listener1); - - // add listener via collection add - tabs.on('activate', function listener2(tab) { - assert.equal(++eventCount, 2, "both listeners notified"); - assert.equal(tab, tabs.activeTab, 'the active tab is correct'); - tabs.removeListener('activate', listener1); - tabs.removeListener('activate', listener2); - - // end test - tab.close(done); - }); - - tabs.open(url); -}; - -// TEST: onDeactivate event handler -exports.testTabsEvent_onDeactivate = function(assert, done) { - let url = "data:text/html;charset=utf-8,ondeactivate"; - let eventCount = 0; - - // add listener via property assignment - function listener1(tab) { - eventCount++; - }; - tabs.on('deactivate', listener1); - - // add listener via collection add - tabs.on('deactivate', function listener2(tab) { - assert.equal(++eventCount, 2, 'both listeners notified'); - assert.notEqual(tab, tabs.activeTab, 'the active tab is not the deactivated tab'); - tabs.removeListener('deactivate', listener1); - tabs.removeListener('deactivate', listener2); - - // end test - tab.close(done); - }); - - tabs.on('activate', function onActivate(tab) { - tabs.removeListener('activate', onActivate); - tabs.open("data:text/html;charset=utf-8,foo"); - tab.close(); - }); - - tabs.open(url); -}; - -// TEST: per-tab event handlers -exports.testPerTabEvents = function(assert, done) { - let eventCount = 0; - - tabs.open({ - url: "data:text/html;charset=utf-8,foo", - onOpen: function(tab) { - // add listener via property assignment - function listener1() { - eventCount++; - }; - tab.on('ready', listener1); - - // add listener via collection add - tab.on('ready', function listener2() { - assert.equal(eventCount, 1, "both listeners notified"); - tab.removeListener('ready', listener1); - tab.removeListener('ready', listener2); - - // end test - tab.close(done); - }); - } - }); -}; - -exports.testUniqueTabIds = function(assert, done) { - var tabs = require('sdk/tabs'); - var tabIds = {}; - var steps = [ - function (index) { - tabs.open({ - url: "data:text/html;charset=utf-8,foo", - onOpen: function(tab) { - tabIds['tab1'] = tab.id; - next(index); - } - }); - }, - function (index) { - tabs.open({ - url: "data:text/html;charset=utf-8,bar", - onOpen: function(tab) { - tabIds['tab2'] = tab.id; - next(index); - } - }); - }, - function (index) { - assert.notEqual(tabIds.tab1, tabIds.tab2, "Tab ids should be unique."); - done(); - } - ]; - - function next(index) { - if (index === steps.length) { - return; - } - let fn = steps[index]; - index++; - fn(index); - } - - next(0); -} - -require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/tabs/test-firefox-tabs.js b/addon-sdk-1.16/test/tabs/test-firefox-tabs.js deleted file mode 100644 index 18c7e557..00000000 --- a/addon-sdk-1.16/test/tabs/test-firefox-tabs.js +++ /dev/null @@ -1,993 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -'use strict'; - -const { Cc, Ci } = require('chrome'); -const { Loader } = require('sdk/test/loader'); -const timer = require('sdk/timers'); -const { getOwnerWindow } = require('sdk/private-browsing/window/utils'); -const { windows, onFocus, getMostRecentBrowserWindow } = require('sdk/window/utils'); -const { open, focus, close } = require('sdk/window/helpers'); -const tabs = require('sdk/tabs'); -const { browserWindows } = require('sdk/windows'); -const { set: setPref } = require("sdk/preferences/service"); -const DEPRECATE_PREF = "devtools.errorconsole.deprecation_warnings"; - -const base64png = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAA"; - -// Bug 682681 - tab.title should never be empty -exports.testBug682681_aboutURI = function(assert, done) { - let url = 'chrome://browser/locale/tabbrowser.properties'; - let stringBundle = Cc["@mozilla.org/intl/stringbundle;1"]. - getService(Ci.nsIStringBundleService). - createBundle(url); - let emptyTabTitle = stringBundle.GetStringFromName('tabs.emptyTabTitle'); - - tabs.on('ready', function onReady(tab) { - tabs.removeListener('ready', onReady); - - assert.equal(tab.title, - emptyTabTitle, - "title of about: tab is not blank"); - - tab.close(done); - }); - - // open a about: url - tabs.open({ - url: "about:blank", - inBackground: true - }); -}; - -// related to Bug 682681 -exports.testTitleForDataURI = function(assert, done) { - tabs.open({ - url: "data:text/html;charset=utf-8,tab", - inBackground: true, - onReady: function(tab) { - assert.equal(tab.title, "tab", "data: title is not Connecting..."); - tab.close(done); - } - }); -}; - -// TEST: 'BrowserWindow' instance creation on tab 'activate' event -// See bug 648244: there was a infinite loop. -exports.testBrowserWindowCreationOnActivate = function(assert, done) { - let windows = require("sdk/windows").browserWindows; - let gotActivate = false; - - tabs.once('activate', function onActivate(eventTab) { - assert.ok(windows.activeWindow, "Is able to fetch activeWindow"); - gotActivate = true; - }); - - open().then(function(window) { - assert.ok(gotActivate, "Received activate event before openBrowserWindow's callback is called"); - close(window).then(done); - }); -} - -// TEST: tab unloader -exports.testAutomaticDestroy = function(assert, done) { - // Create a second tab instance that we will destroy - let called = false; - - let loader = Loader(module); - let tabs2 = loader.require("sdk/tabs"); - tabs2.on('open', function onOpen(tab) { - called = true; - }); - - loader.unload(); - - // Fire a tab event and ensure that the destroyed tab is inactive - tabs.once('open', function (tab) { - timer.setTimeout(function () { - assert.ok(!called, "Unloaded tab module is destroyed and inactive"); - tab.close(done); - }); - }); - tabs.open("data:text/html;charset=utf-8,foo"); -}; - -exports.testTabPropertiesInNewWindow = function(assert, done) { - let warning = "DEPRECATED: tab.favicon is deprecated, please use require(\"sdk/places/favicon\").getFavicon instead.\n" - const { LoaderWithFilteredConsole } = require("sdk/test/loader"); - let loader = LoaderWithFilteredConsole(module, function(type, message) { - if (type == "error" && message.substring(0, warning.length) == warning) - return false; - return true; - }); - - let tabs = loader.require('sdk/tabs'); - let { getOwnerWindow } = loader.require('sdk/private-browsing/window/utils'); - - let count = 0; - function onReadyOrLoad (tab) { - if (count++) { - close(getOwnerWindow(tab)).then(done); - } - } - - let url = "data:text/html;charset=utf-8,foofoo"; - tabs.open({ - inNewWindow: true, - url: url, - onReady: function(tab) { - assert.equal(tab.title, "foo", "title of the new tab matches"); - assert.equal(tab.url, url, "URL of the new tab matches"); - assert.ok(tab.favicon, "favicon of the new tab is not empty"); - assert.equal(tab.style, null, "style of the new tab matches"); - assert.equal(tab.index, 0, "index of the new tab matches"); - assert.notEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches"); - assert.notEqual(tab.id, null, "a tab object always has an id property."); - - onReadyOrLoad(tab); - }, - onLoad: function(tab) { - assert.equal(tab.title, "foo", "title of the new tab matches"); - assert.equal(tab.url, url, "URL of the new tab matches"); - assert.ok(tab.favicon, "favicon of the new tab is not empty"); - assert.equal(tab.style, null, "style of the new tab matches"); - assert.equal(tab.index, 0, "index of the new tab matches"); - assert.notEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches"); - assert.notEqual(tab.id, null, "a tab object always has an id property."); - - onReadyOrLoad(tab); - } - }); -}; - -exports.testTabPropertiesInSameWindow = function(assert, done) { - let warning = "DEPRECATED: tab.favicon is deprecated, please use require(\"sdk/places/favicon\").getFavicon instead.\n" - const { LoaderWithFilteredConsole } = require("sdk/test/loader"); - let loader = LoaderWithFilteredConsole(module, function(type, message) { - if (type == "error" && message.substring(0, warning.length) == warning) - return false; - return true; - }); - - let tabs = loader.require('sdk/tabs'); - - // Get current count of tabs so we know the index of the - // new tab, bug 893846 - let tabCount = tabs.length; - let count = 0; - function onReadyOrLoad (tab) { - if (count++) { - tab.close(done); - } - } - - let url = "data:text/html;charset=utf-8,foofoo"; - tabs.open({ - url: url, - onReady: function(tab) { - assert.equal(tab.title, "foo", "title of the new tab matches"); - assert.equal(tab.url, url, "URL of the new tab matches"); - assert.ok(tab.favicon, "favicon of the new tab is not empty"); - assert.equal(tab.style, null, "style of the new tab matches"); - assert.equal(tab.index, tabCount, "index of the new tab matches"); - assert.notEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches"); - assert.notEqual(tab.id, null, "a tab object always has an id property."); - - onReadyOrLoad(tab); - }, - onLoad: function(tab) { - assert.equal(tab.title, "foo", "title of the new tab matches"); - assert.equal(tab.url, url, "URL of the new tab matches"); - assert.ok(tab.favicon, "favicon of the new tab is not empty"); - assert.equal(tab.style, null, "style of the new tab matches"); - assert.equal(tab.index, tabCount, "index of the new tab matches"); - assert.notEqual(tab.getThumbnail(), null, "thumbnail of the new tab matches"); - assert.notEqual(tab.id, null, "a tab object always has an id property."); - - onReadyOrLoad(tab); - } - }); -}; - -// TEST: tab properties -exports.testTabContentTypeAndReload = function(assert, done) { - open().then(focus).then(function(window) { - let url = "data:text/html;charset=utf-8,foofoo"; - let urlXML = "data:text/xml;charset=utf-8,bar"; - tabs.open({ - url: url, - onReady: function(tab) { - if (tab.url === url) { - assert.equal(tab.contentType, "text/html"); - tab.url = urlXML; - } - else { - assert.equal(tab.contentType, "text/xml"); - close(window).then(done); - } - } - }); - }); -}; - -// TEST: tabs iterator and length property -exports.testTabsIteratorAndLength = function(assert, done) { - open(null, { features: { chrome: true, toolbar: true } }).then(focus).then(function(window) { - let startCount = 0; - for each (let t in tabs) startCount++; - assert.equal(startCount, tabs.length, "length property is correct"); - let url = "data:text/html;charset=utf-8,default"; - - tabs.open(url); - tabs.open(url); - tabs.open({ - url: url, - onOpen: function(tab) { - let count = 0; - for each (let t in tabs) count++; - assert.equal(count, startCount + 3, "iterated tab count matches"); - assert.equal(startCount + 3, tabs.length, "iterated tab count matches length property"); - - close(window).then(done); - } - }); - }); -}; - -// TEST: tab.url setter -exports.testTabLocation = function(assert, done) { - open().then(focus).then(function(window) { - let url1 = "data:text/html;charset=utf-8,foo"; - let url2 = "data:text/html;charset=utf-8,bar"; - - tabs.on('ready', function onReady(tab) { - if (tab.url != url2) - return; - tabs.removeListener('ready', onReady); - assert.pass("tab.load() loaded the correct url"); - close(window).then(done); - }); - - tabs.open({ - url: url1, - onOpen: function(tab) { - tab.url = url2 - } - }); - }); -}; - -// TEST: tab.close() -exports.testTabClose = function(assert, done) { - let url = "data:text/html;charset=utf-8,foo"; - - assert.notEqual(tabs.activeTab.url, url, "tab is not the active tab"); - tabs.on('ready', function onReady(tab) { - tabs.removeListener('ready', onReady); - assert.equal(tabs.activeTab.url, tab.url, "tab is now the active tab"); - let secondOnCloseCalled = false; - - // Bug 699450: Multiple calls to tab.close should not throw - tab.close(function() secondOnCloseCalled = true); - try { - tab.close(function () { - assert.ok(secondOnCloseCalled, - "The immediate second call to tab.close gots its callback fired"); - assert.notEqual(tabs.activeTab.url, url, "tab is no longer the active tab"); - - done(); - }); - } - catch(e) { - assert.fail("second call to tab.close() thrown an exception: " + e); - } - assert.notEqual(tabs.activeTab.url, url, "tab is no longer the active tab"); - }); - - tabs.open(url); -}; - -// TEST: tab.move() -exports.testTabMove = function(assert, done) { - open().then(focus).then(function(window) { - let url = "data:text/html;charset=utf-8,foo"; - - tabs.open({ - url: url, - onOpen: function(tab) { - assert.equal(tab.index, 1, "tab index before move matches"); - tab.index = 0; - assert.equal(tab.index, 0, "tab index after move matches"); - close(window).then(done); - } - }); - }); -}; - -// TEST: open tab with default options -exports.testOpen = function(assert, done) { - let url = "data:text/html;charset=utf-8,default"; - tabs.open({ - url: url, - onReady: function(tab) { - assert.equal(tab.url, url, "URL of the new tab matches"); - assert.equal(tab.isPinned, false, "The new tab is not pinned"); - - tab.close(done); - } - }); -}; - -// TEST: opening a pinned tab -exports.testOpenPinned = function(assert, done) { - let url = "data:text/html;charset=utf-8,default"; - tabs.open({ - url: url, - isPinned: true, - onOpen: function(tab) { - assert.equal(tab.isPinned, true, "The new tab is pinned"); - tab.close(done); - } - }); -}; - -// TEST: pin/unpin opened tab -exports.testPinUnpin = function(assert, done) { - let url = "data:text/html;charset=utf-8,default"; - tabs.open({ - url: url, - inBackground: true, - onOpen: function(tab) { - tab.pin(); - assert.equal(tab.isPinned, true, "The tab was pinned correctly"); - tab.unpin(); - assert.equal(tab.isPinned, false, "The tab was unpinned correctly"); - tab.close(done); - } - }); -} - -// TEST: open tab in background -exports.testInBackground = function(assert, done) { - let window = getMostRecentBrowserWindow(); - let activeUrl = tabs.activeTab.url; - let url = "data:text/html;charset=utf-8,background"; - assert.equal(activeWindow, window, "activeWindow matches this window"); - tabs.on('ready', function onReady(tab) { - tabs.removeListener('ready', onReady); - assert.equal(tabs.activeTab.url, activeUrl, "URL of active tab has not changed"); - assert.equal(tab.url, url, "URL of the new background tab matches"); - assert.equal(activeWindow, window, "a new window was not opened"); - assert.notEqual(tabs.activeTab.url, url, "URL of active tab is not the new URL"); - tab.close(done); - }); - - tabs.open({ - url: url, - inBackground: true - }); -} - -// TEST: open tab in new window -exports.testOpenInNewWindow = function(assert, done) { - let startWindowCount = windows().length; - - let url = "data:text/html;charset=utf-8,testOpenInNewWindow"; - tabs.open({ - url: url, - inNewWindow: true, - onReady: function(tab) { - let newWindow = getOwnerWindow(tab); - assert.equal(windows().length, startWindowCount + 1, "a new window was opened"); - - onFocus(newWindow).then(function() { - assert.equal(activeWindow, newWindow, "new window is active"); - assert.equal(tab.url, url, "URL of the new tab matches"); - assert.equal(newWindow.content.location, url, "URL of new tab in new window matches"); - assert.equal(tabs.activeTab.url, url, "URL of activeTab matches"); - - close(newWindow).then(done); - }, assert.fail).then(null, assert.fail); - } - }); - -} - -// Test tab.open inNewWindow + onOpen combination -exports.testOpenInNewWindowOnOpen = function(assert, done) { - let startWindowCount = windows().length; - - let url = "data:text/html;charset=utf-8,newwindow"; - tabs.open({ - url: url, - inNewWindow: true, - onOpen: function(tab) { - let newWindow = getOwnerWindow(tab); - - onFocus(newWindow).then(function() { - assert.equal(windows().length, startWindowCount + 1, "a new window was opened"); - assert.equal(activeWindow, newWindow, "new window is active"); - - close(newWindow).then(done); - }); - } - }); -}; - -// TEST: onOpen event handler -exports.testTabsEvent_onOpen = function(assert, done) { - openBrowserWindow(function(window, browser) { - let url = "data:text/html;charset=utf-8,1"; - let eventCount = 0; - - // add listener via property assignment - function listener1(tab) { - eventCount++; - }; - tabs.on('open', listener1); - - // add listener via collection add - tabs.on('open', function listener2(tab) { - assert.equal(++eventCount, 2, "both listeners notified"); - tabs.removeListener('open', listener1); - tabs.removeListener('open', listener2); - close(window).then(done); - }); - - tabs.open(url); - }); -}; - -// TEST: onClose event handler -exports.testTabsEvent_onClose = function(assert, done) { - openBrowserWindow(function(window, browser) { - let url = "data:text/html;charset=utf-8,onclose"; - let eventCount = 0; - - // add listener via property assignment - function listener1(tab) { - eventCount++; - } - tabs.on('close', listener1); - - // add listener via collection add - tabs.on('close', function listener2(tab) { - assert.equal(++eventCount, 2, "both listeners notified"); - tabs.removeListener('close', listener1); - tabs.removeListener('close', listener2); - close(window).then(done); - }); - - tabs.on('ready', function onReady(tab) { - tabs.removeListener('ready', onReady); - tab.close(); - }); - - tabs.open(url); - }); -}; - -// TEST: onClose event handler when a window is closed -exports.testTabsEvent_onCloseWindow = function(assert, done) { - let closeCount = 0; - let individualCloseCount = 0; - - openBrowserWindow(function(window) { - tabs.on("close", function listener() { - if (++closeCount == 4) { - tabs.removeListener("close", listener); - } - }); - - function endTest() { - if (++individualCloseCount < 3) { - return; - } - - assert.equal(closeCount, 4, "Correct number of close events received"); - assert.equal(individualCloseCount, 3, - "Each tab with an attached onClose listener received a close " + - "event when the window was closed"); - - done(); - } - - // One tab is already open with the window - let openTabs = 1; - function testCasePossiblyLoaded() { - if (++openTabs == 4) { - window.close(); - } - } - - tabs.open({ - url: "data:text/html;charset=utf-8,tab2", - onOpen: testCasePossiblyLoaded, - onClose: endTest - }); - - tabs.open({ - url: "data:text/html;charset=utf-8,tab3", - onOpen: testCasePossiblyLoaded, - onClose: endTest - }); - - tabs.open({ - url: "data:text/html;charset=utf-8,tab4", - onOpen: testCasePossiblyLoaded, - onClose: endTest - }); - }); -} - -// TEST: onReady event handler -exports.testTabsEvent_onReady = function(assert, done) { - openBrowserWindow(function(window, browser) { - let url = "data:text/html;charset=utf-8,onready"; - let eventCount = 0; - - // add listener via property assignment - function listener1(tab) { - eventCount++; - }; - tabs.on('ready', listener1); - - // add listener via collection add - tabs.on('ready', function listener2(tab) { - assert.equal(++eventCount, 2, "both listeners notified"); - tabs.removeListener('ready', listener1); - tabs.removeListener('ready', listener2); - close(window).then(done); - }); - - tabs.open(url); - }); -}; - -// TEST: onActivate event handler -exports.testTabsEvent_onActivate = function(assert, done) { - openBrowserWindow(function(window, browser) { - let url = "data:text/html;charset=utf-8,onactivate"; - let eventCount = 0; - - // add listener via property assignment - function listener1(tab) { - eventCount++; - }; - tabs.on('activate', listener1); - - // add listener via collection add - tabs.on('activate', function listener2(tab) { - assert.equal(++eventCount, 2, "both listeners notified"); - tabs.removeListener('activate', listener1); - tabs.removeListener('activate', listener2); - close(window).then(done); - }); - - tabs.open(url); - }); -}; - -// onDeactivate event handler -exports.testTabsEvent_onDeactivate = function(assert, done) { - openBrowserWindow(function(window, browser) { - let url = "data:text/html;charset=utf-8,ondeactivate"; - let eventCount = 0; - - // add listener via property assignment - function listener1(tab) { - eventCount++; - }; - tabs.on('deactivate', listener1); - - // add listener via collection add - tabs.on('deactivate', function listener2(tab) { - assert.equal(++eventCount, 2, "both listeners notified"); - tabs.removeListener('deactivate', listener1); - tabs.removeListener('deactivate', listener2); - close(window).then(done); - }); - - tabs.on('open', function onOpen(tab) { - tabs.removeListener('open', onOpen); - tabs.open("data:text/html;charset=utf-8,foo"); - }); - - tabs.open(url); - }); -}; - -// pinning -exports.testTabsEvent_pinning = function(assert, done) { - openBrowserWindow(function(window, browser) { - let url = "data:text/html;charset=utf-8,1"; - - tabs.on('open', function onOpen(tab) { - tabs.removeListener('open', onOpen); - tab.pin(); - }); - - tabs.on('pinned', function onPinned(tab) { - tabs.removeListener('pinned', onPinned); - assert.ok(tab.isPinned, "notified tab is pinned"); - tab.unpin(); - }); - - tabs.on('unpinned', function onUnpinned(tab) { - tabs.removeListener('unpinned', onUnpinned); - assert.ok(!tab.isPinned, "notified tab is not pinned"); - close(window).then(done); - }); - - tabs.open(url); - }); -}; - -// TEST: per-tab event handlers -exports.testPerTabEvents = function(assert, done) { - openBrowserWindow(function(window, browser) { - let eventCount = 0; - - tabs.open({ - url: "data:text/html;charset=utf-8,foo", - onOpen: function(tab) { - // add listener via property assignment - function listener1() { - eventCount++; - }; - tab.on('ready', listener1); - - // add listener via collection add - tab.on('ready', function listener2() { - assert.equal(eventCount, 1, "both listeners notified"); - tab.removeListener('ready', listener1); - tab.removeListener('ready', listener2); - close(window).then(done); - }); - } - }); - }); -}; - -exports.testAttachOnOpen = function (assert, done) { - // Take care that attach has to be called on tab ready and not on tab open. - openBrowserWindow(function(window, browser) { - tabs.open({ - url: "data:text/html;charset=utf-8,foobar", - onOpen: function (tab) { - let worker = tab.attach({ - contentScript: 'self.postMessage(document.location.href); ', - onMessage: function (msg) { - assert.equal(msg, "about:blank", - "Worker document url is about:blank on open"); - worker.destroy(); - close(window).then(done); - } - }); - } - }); - - }); -} - -exports.testAttachOnMultipleDocuments = function (assert, done) { - // Example of attach that process multiple tab documents - openBrowserWindow(function(window, browser) { - let firstLocation = "data:text/html;charset=utf-8,foobar"; - let secondLocation = "data:text/html;charset=utf-8,bar"; - let thirdLocation = "data:text/html;charset=utf-8,fox"; - let onReadyCount = 0; - let worker1 = null; - let worker2 = null; - let detachEventCount = 0; - - tabs.open({ - url: firstLocation, - onReady: function (tab) { - onReadyCount++; - if (onReadyCount == 1) { - worker1 = tab.attach({ - contentScript: 'self.on("message", ' + - ' function () self.postMessage(document.location.href)' + - ');', - onMessage: function (msg) { - assert.equal(msg, firstLocation, - "Worker url is equal to the 1st document"); - tab.url = secondLocation; - }, - onDetach: function () { - detachEventCount++; - assert.pass("Got worker1 detach event"); - assert.throws(function () { - worker1.postMessage("ex-1"); - }, - /Couldn't find the worker/, - "postMessage throw because worker1 is destroyed"); - checkEnd(); - } - }); - worker1.postMessage("new-doc-1"); - } - else if (onReadyCount == 2) { - - worker2 = tab.attach({ - contentScript: 'self.on("message", ' + - ' function () self.postMessage(document.location.href)' + - ');', - onMessage: function (msg) { - assert.equal(msg, secondLocation, - "Worker url is equal to the 2nd document"); - tab.url = thirdLocation; - }, - onDetach: function () { - detachEventCount++; - assert.pass("Got worker2 detach event"); - assert.throws(function () { - worker2.postMessage("ex-2"); - }, - /Couldn't find the worker/, - "postMessage throw because worker2 is destroyed"); - checkEnd(); - } - }); - worker2.postMessage("new-doc-2"); - } - else if (onReadyCount == 3) { - tab.close(); - } - } - }); - - function checkEnd() { - if (detachEventCount != 2) - return; - - assert.pass("Got all detach events"); - - close(window).then(done); - } - - }); -} - - -exports.testAttachWrappers = function (assert, done) { - // Check that content script has access to wrapped values by default - openBrowserWindow(function(window, browser) { - let document = "data:text/html;charset=utf-8,"; - let count = 0; - - tabs.open({ - url: document, - onReady: function (tab) { - let worker = tab.attach({ - contentScript: 'try {' + - ' self.postMessage(!("globalJSVar" in window));' + - ' self.postMessage(typeof window.globalJSVar == "undefined");' + - '} catch(e) {' + - ' self.postMessage(e.message);' + - '}', - onMessage: function (msg) { - assert.equal(msg, true, "Worker has wrapped objects ("+count+")"); - if (count++ == 1) - close(window).then(done); - } - }); - } - }); - - }); -} - -/* -// We do not offer unwrapped access to DOM since bug 601295 landed -// See 660780 to track progress of unwrap feature -exports.testAttachUnwrapped = function (assert, done) { - // Check that content script has access to unwrapped values through unsafeWindow - openBrowserWindow(function(window, browser) { - let document = "data:text/html;charset=utf-8,"; - let count = 0; - - tabs.open({ - url: document, - onReady: function (tab) { - let worker = tab.attach({ - contentScript: 'try {' + - ' self.postMessage(unsafeWindow.globalJSVar);' + - '} catch(e) {' + - ' self.postMessage(e.message);' + - '}', - onMessage: function (msg) { - assert.equal(msg, true, "Worker has access to javascript content globals ("+count+")"); - close(window).then(done); - } - }); - } - }); - - }); -} -*/ - -exports['test window focus changes active tab'] = function(assert, done) { - let url1 = "data:text/html;charset=utf-8," + encodeURIComponent("test window focus changes active tab

Window #1"); - - let win1 = openBrowserWindow(function() { - assert.pass("window 1 is open"); - - let win2 = openBrowserWindow(function() { - assert.pass("window 2 is open"); - - focus(win2).then(function() { - tabs.on("activate", function onActivate(tab) { - tabs.removeListener("activate", onActivate); - assert.pass("activate was called on windows focus change."); - assert.equal(tab.url, url1, 'the activated tab url is correct'); - - close(win2).then(function() { - assert.pass('window 2 was closed'); - return close(win1); - }).then(done); - }); - - win1.focus(); - }); - }, "data:text/html;charset=utf-8,test window focus changes active tab

Window #2"); - }, url1); -}; - -exports['test ready event on new window tab'] = function(assert, done) { - let uri = encodeURI("data:text/html;charset=utf-8,Waiting for ready event!"); - - require("sdk/tabs").on("ready", function onReady(tab) { - if (tab.url === uri) { - require("sdk/tabs").removeListener("ready", onReady); - assert.pass("ready event was emitted"); - close(window).then(done); - } - }); - - let window = openBrowserWindow(function(){}, uri); -}; - -exports['test unique tab ids'] = function(assert, done) { - var windows = require('sdk/windows').browserWindows; - var { all, defer } = require('sdk/core/promise'); - - function openWindow() { - // console.log('in openWindow'); - let deferred = defer(); - let win = windows.open({ - url: "data:text/html;charset=utf-8,foo", - }); - - win.on('open', function(window) { - assert.ok(window.tabs.length); - assert.ok(window.tabs.activeTab); - assert.ok(window.tabs.activeTab.id); - deferred.resolve({ - id: window.tabs.activeTab.id, - win: win - }); - }); - - return deferred.promise; - } - - var one = openWindow(), two = openWindow(); - all([one, two]).then(function(results) { - assert.notEqual(results[0].id, results[1].id, "tab Ids should not be equal."); - results[0].win.close(); - results[1].win.close(); - done(); - }); -} - -// related to Bug 671305 -exports.testOnLoadEventWithDOM = function(assert, done) { - let count = 0; - let title = 'testOnLoadEventWithDOM'; - - // open a about: url - tabs.open({ - url: 'data:text/html;charset=utf-8,' + title + '', - inBackground: true, - onLoad: function(tab) { - assert.equal(tab.title, title, 'tab passed in as arg, load called'); - - if (++count > 1) { - assert.pass('onLoad event called on reload'); - tab.close(done); - } - else { - assert.pass('first onLoad event occured'); - tab.reload(); - } - } - }); -}; - -// related to Bug 671305 -exports.testOnLoadEventWithImage = function(assert, done) { - let count = 0; - - tabs.open({ - url: base64png, - inBackground: true, - onLoad: function(tab) { - if (++count > 1) { - assert.pass('onLoad event called on reload with image'); - tab.close(done); - } - else { - assert.pass('first onLoad event occured'); - tab.reload(); - } - } - }); -}; - -exports.testFaviconGetterDeprecation = function (assert, done) { - setPref(DEPRECATE_PREF, true); - const { LoaderWithHookedConsole } = require("sdk/test/loader"); - let { loader, messages } = LoaderWithHookedConsole(module); - let tabs = loader.require('sdk/tabs'); - - tabs.open({ - url: 'data:text/html;charset=utf-8,', - onOpen: function (tab) { - let favicon = tab.favicon; - assert.ok(messages.length === 1, 'only one error is dispatched'); - assert.ok(messages[0].type, 'error', 'the console message is an error'); - - let msg = messages[0].msg; - assert.ok(msg.indexOf('tab.favicon is deprecated') !== -1, - 'message contains the given message'); - tab.close(done); - loader.unload(); - } - }); -} - -/******************* helpers *********************/ - -// Helper for getting the active window -this.__defineGetter__("activeWindow", function activeWindow() { - return Cc["@mozilla.org/appshell/window-mediator;1"]. - getService(Ci.nsIWindowMediator). - getMostRecentWindow("navigator:browser"); -}); - -// Utility function to open a new browser window. -function openBrowserWindow(callback, url) { - let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"]. - getService(Ci.nsIWindowWatcher); - let urlString = Cc["@mozilla.org/supports-string;1"]. - createInstance(Ci.nsISupportsString); - urlString.data = url; - let window = ww.openWindow(null, "chrome://browser/content/browser.xul", - "_blank", "chrome,all,dialog=no", urlString); - - if (callback) { - window.addEventListener("load", function onLoad(event) { - if (event.target && event.target.defaultView == window) { - window.removeEventListener("load", onLoad, true); - let browsers = window.document.getElementsByTagName("tabbrowser"); - try { - timer.setTimeout(function () { - callback(window, browsers[0]); - }, 10); - } - catch (e) { - console.exception(e); - } - } - }, true); - } - - return window; -} - -require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-addon-installer.js b/addon-sdk-1.16/test/test-addon-installer.js deleted file mode 100644 index 3857f2cd..00000000 --- a/addon-sdk-1.16/test/test-addon-installer.js +++ /dev/null @@ -1,179 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -"use strict"; - -const { Cc, Ci, Cu } = require("chrome"); -const AddonInstaller = require("sdk/addon/installer"); -const { on, off } = require("sdk/system/events"); -const { setTimeout } = require("sdk/timers"); -const tmp = require("sdk/test/tmp-file"); -const system = require("sdk/system"); -const fixtures = require("./fixtures"); - -const testFolderURL = module.uri.split('test-addon-installer.js')[0]; -const ADDON_URL = testFolderURL + "fixtures/addon-install-unit-test@mozilla.com.xpi"; -const ADDON_PATH = tmp.createFromURL(ADDON_URL); - -exports["test Install"] = function (assert, done) { - - // Save all events distpatched by bootstrap.js of the installed addon - let events = []; - function eventsObserver({ data }) { - events.push(data); - } - on("addon-install-unit-test", eventsObserver); - - // Install the test addon - AddonInstaller.install(ADDON_PATH).then( - function onInstalled(id) { - assert.equal(id, "addon-install-unit-test@mozilla.com", "`id` is valid"); - - // Now uninstall it - AddonInstaller.uninstall(id).then(function () { - // Ensure that bootstrap.js methods of the addon have been called - // successfully and in the right order - let expectedEvents = ["install", "startup", "shutdown", "uninstall"]; - assert.equal(JSON.stringify(events), - JSON.stringify(expectedEvents), - "addon's bootstrap.js functions have been called"); - - off("addon-install-unit-test", eventsObserver); - done(); - }); - }, - function onFailure(code) { - assert.fail("Install failed: "+code); - off("addon-install-unit-test", eventsObserver); - done(); - } - ); -}; - -exports["test Failing Install With Invalid Path"] = function (assert, done) { - AddonInstaller.install("invalid-path").then( - function onInstalled(id) { - assert.fail("Unexpected success"); - done(); - }, - function onFailure(code) { - assert.equal(code, AddonInstaller.ERROR_FILE_ACCESS, - "Got expected error code"); - done(); - } - ); -}; - -exports["test Failing Install With Invalid File"] = function (assert, done) { - let directory = system.pathFor("ProfD"); - AddonInstaller.install(directory).then( - function onInstalled(id) { - assert.fail("Unexpected success"); - done(); - }, - function onFailure(code) { - assert.equal(code, AddonInstaller.ERROR_CORRUPT_FILE, - "Got expected error code"); - done(); - } - ); -} - -exports["test Update"] = function (assert, done) { - // Save all events distpatched by bootstrap.js of the installed addon - let events = []; - let iteration = 1; - let eventsObserver = ({data}) => events.push(data); - on("addon-install-unit-test", eventsObserver); - - function onInstalled(id) { - let prefix = "[" + iteration + "] "; - assert.equal(id, "addon-install-unit-test@mozilla.com", - prefix + "`id` is valid"); - - // On 2nd and 3rd iteration, we receive uninstall events from the last - // previously installed addon - let expectedEvents = - iteration == 1 - ? ["install", "startup"] - : ["shutdown", "uninstall", "install", "startup"]; - assert.equal(JSON.stringify(events), - JSON.stringify(expectedEvents), - prefix + "addon's bootstrap.js functions have been called"); - - if (iteration++ < 3) { - next(); - } - else { - events = []; - AddonInstaller.uninstall(id).then(function() { - let expectedEvents = ["shutdown", "uninstall"]; - assert.equal(JSON.stringify(events), - JSON.stringify(expectedEvents), - prefix + "addon's bootstrap.js functions have been called"); - - off("addon-install-unit-test", eventsObserver); - done(); - }); - } - } - function onFailure(code) { - assert.fail("Install failed: "+code); - off("addon-install-unit-test", eventsObserver); - done(); - } - - function next() { - events = []; - AddonInstaller.install(ADDON_PATH).then(onInstalled, onFailure); - } - - next(); -}; - -exports['test Uninstall failure'] = function (assert, done) { - AddonInstaller.uninstall('invalid-addon-path').then( - () => assert.fail('Addon uninstall should not resolve successfully'), - () => assert.pass('Addon correctly rejected invalid uninstall') - ).then(done, assert.fail); -}; - -exports['test Addon Disable and Enable'] = function (assert, done) { - let ensureActive = (addonId) => AddonInstaller.isActive(addonId).then(state => { - assert.equal(state, true, 'Addon should be enabled by default'); - return addonId; - }); - let ensureInactive = (addonId) => AddonInstaller.isActive(addonId).then(state => { - assert.equal(state, false, 'Addon should be disabled after disabling'); - return addonId; - }); - - AddonInstaller.install(ADDON_PATH) - .then(ensureActive) - .then(AddonInstaller.enable) // should do nothing, yet not fail - .then(ensureActive) - .then(AddonInstaller.disable) - .then(ensureInactive) - .then(AddonInstaller.disable) // should do nothing, yet not fail - .then(ensureInactive) - .then(AddonInstaller.enable) - .then(ensureActive) - .then(AddonInstaller.uninstall) - .then(done, assert.fail); -}; - -exports['test Disable failure'] = function (assert, done) { - AddonInstaller.disable('not-an-id').then( - () => assert.fail('Addon disable should not resolve successfully'), - () => assert.pass('Addon correctly rejected invalid disable') - ).then(done, assert.fail); -}; - -exports['test Enable failure'] = function (assert, done) { - AddonInstaller.enable('not-an-id').then( - () => assert.fail('Addon enable should not resolve successfully'), - () => assert.pass('Addon correctly rejected invalid enable') - ).then(done, assert.fail); -}; - -require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-addon-window.js b/addon-sdk-1.16/test/test-addon-window.js deleted file mode 100644 index eda1ae7d..00000000 --- a/addon-sdk-1.16/test/test-addon-window.js +++ /dev/null @@ -1,22 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -'use strict'; - -let { Loader } = require('sdk/test/loader'); - -exports.testReady = function(assert, done) { - let loader = Loader(module); - let { ready, window } = loader.require('sdk/addon/window'); - let windowIsReady = false; - - ready.then(function() { - assert.equal(windowIsReady, false, 'ready promise was resolved only once'); - windowIsReady = true; - - loader.unload(); - done(); - }).then(null, assert.fail); -} - -require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-api-utils.js b/addon-sdk-1.16/test/test-api-utils.js deleted file mode 100644 index a1239f60..00000000 --- a/addon-sdk-1.16/test/test-api-utils.js +++ /dev/null @@ -1,316 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -const apiUtils = require("sdk/deprecated/api-utils"); - -exports.testValidateOptionsEmpty = function (assert) { - let val = apiUtils.validateOptions(null, {}); - - assert.deepEqual(val, {}); - - val = apiUtils.validateOptions(null, { foo: {} }); - assert.deepEqual(val, {}); - - val = apiUtils.validateOptions({}, {}); - assert.deepEqual(val, {}); - - val = apiUtils.validateOptions({}, { foo: {} }); - assert.deepEqual(val, {}); -}; - -exports.testValidateOptionsNonempty = function (assert) { - let val = apiUtils.validateOptions({ foo: 123 }, {}); - assert.deepEqual(val, {}); - - val = apiUtils.validateOptions({ foo: 123, bar: 456 }, - { foo: {}, bar: {}, baz: {} }); - - assert.deepEqual(val, { foo: 123, bar: 456 }); -}; - -exports.testValidateOptionsMap = function (assert) { - let val = apiUtils.validateOptions({ foo: 3, bar: 2 }, { - foo: { map: function (v) v * v }, - bar: { map: function (v) undefined } - }); - assert.deepEqual(val, { foo: 9, bar: undefined }); -}; - -exports.testValidateOptionsMapException = function (assert) { - let val = apiUtils.validateOptions({ foo: 3 }, { - foo: { map: function () { throw new Error(); }} - }); - assert.deepEqual(val, { foo: 3 }); -}; - -exports.testValidateOptionsOk = function (assert) { - let val = apiUtils.validateOptions({ foo: 3, bar: 2, baz: 1 }, { - foo: { ok: function (v) v }, - bar: { ok: function (v) v } - }); - assert.deepEqual(val, { foo: 3, bar: 2 }); - - assert.throws( - function () apiUtils.validateOptions({ foo: 2, bar: 2 }, { - bar: { ok: function (v) v > 2 } - }), - /^The option "bar" is invalid/, - "ok should raise exception on invalid option" - ); - - assert.throws( - function () apiUtils.validateOptions(null, { foo: { ok: function (v) v }}), - /^The option "foo" is invalid/, - "ok should raise exception on invalid option" - ); -}; - -exports.testValidateOptionsIs = function (assert) { - let opts = { - array: [], - boolean: true, - func: function () {}, - nul: null, - number: 1337, - object: {}, - string: "foo", - undef1: undefined - }; - let requirements = { - array: { is: ["array"] }, - boolean: { is: ["boolean"] }, - func: { is: ["function"] }, - nul: { is: ["null"] }, - number: { is: ["number"] }, - object: { is: ["object"] }, - string: { is: ["string"] }, - undef1: { is: ["undefined"] }, - undef2: { is: ["undefined"] } - }; - let val = apiUtils.validateOptions(opts, requirements); - assert.deepEqual(val, opts); - - assert.throws( - function () apiUtils.validateOptions(null, { - foo: { is: ["object", "number"] } - }), - /^The option "foo" must be one of the following types: object, number/, - "Invalid type should raise exception" - ); -}; - -exports.testValidateOptionsIsWithExportedValue = function (assert) { - let { string, number, boolean, object } = apiUtils; - - let opts = { - boolean: true, - number: 1337, - object: {}, - string: "foo" - }; - let requirements = { - string: { is: string }, - number: { is: number }, - boolean: { is: boolean }, - object: { is: object } - }; - let val = apiUtils.validateOptions(opts, requirements); - assert.deepEqual(val, opts); - - // Test the types are optional by default - val = apiUtils.validateOptions({foo: 'bar'}, requirements); - assert.deepEqual(val, {}); -}; - -exports.testValidateOptionsIsWithEither = function (assert) { - let { string, number, boolean, either } = apiUtils; - let text = { is: either(string, number) }; - - let requirements = { - text: text, - boolOrText: { is: either(text, boolean) } - }; - - let val = apiUtils.validateOptions({text: 12}, requirements); - assert.deepEqual(val, {text: 12}); - - val = apiUtils.validateOptions({text: "12"}, requirements); - assert.deepEqual(val, {text: "12"}); - - val = apiUtils.validateOptions({boolOrText: true}, requirements); - assert.deepEqual(val, {boolOrText: true}); - - val = apiUtils.validateOptions({boolOrText: "true"}, requirements); - assert.deepEqual(val, {boolOrText: "true"}); - - val = apiUtils.validateOptions({boolOrText: 1}, requirements); - assert.deepEqual(val, {boolOrText: 1}); - - assert.throws( - () => apiUtils.validateOptions({text: true}, requirements), - /^The option "text" must be one of the following types/, - "Invalid type should raise exception" - ); - - assert.throws( - () => apiUtils.validateOptions({boolOrText: []}, requirements), - /^The option "boolOrText" must be one of the following types/, - "Invalid type should raise exception" - ); -}; - -exports.testValidateOptionsWithRequiredAndOptional = function (assert) { - let { string, number, required, optional } = apiUtils; - - let opts = { - number: 1337, - string: "foo" - }; - - let requirements = { - string: required(string), - number: number - }; - - let val = apiUtils.validateOptions(opts, requirements); - assert.deepEqual(val, opts); - - val = apiUtils.validateOptions({string: "foo"}, requirements); - assert.deepEqual(val, {string: "foo"}); - - assert.throws( - () => apiUtils.validateOptions({number: 10}, requirements), - /^The option "string" must be one of the following types/, - "Invalid type should raise exception" - ); - - // Makes string optional - requirements.string = optional(requirements.string); - - val = apiUtils.validateOptions({number: 10}, requirements), - assert.deepEqual(val, {number: 10}); - -}; - - - -exports.testValidateOptionsWithExportedValue = function (assert) { - let { string, number, boolean, object } = apiUtils; - - let opts = { - boolean: true, - number: 1337, - object: {}, - string: "foo" - }; - let requirements = { - string: string, - number: number, - boolean: boolean, - object: object - }; - let val = apiUtils.validateOptions(opts, requirements); - assert.deepEqual(val, opts); - - // Test the types are optional by default - val = apiUtils.validateOptions({foo: 'bar'}, requirements); - assert.deepEqual(val, {}); -}; - - -exports.testValidateOptionsMapIsOk = function (assert) { - let [map, is, ok] = [false, false, false]; - let val = apiUtils.validateOptions({ foo: 1337 }, { - foo: { - map: function (v) v.toString(), - is: ["string"], - ok: function (v) v.length > 0 - } - }); - assert.deepEqual(val, { foo: "1337" }); - - let requirements = { - foo: { - is: ["object"], - ok: function () assert.fail("is should have caused us to throw by now") - } - }; - assert.throws( - function () apiUtils.validateOptions(null, requirements), - /^The option "foo" must be one of the following types: object/, - "is should be used before ok is called" - ); -}; - -exports.testValidateOptionsErrorMsg = function (assert) { - assert.throws( - function () apiUtils.validateOptions(null, { - foo: { ok: function (v) v, msg: "foo!" } - }), - /^foo!/, - "ok should raise exception with customized message" - ); -}; - -exports.testValidateMapWithMissingKey = function (assert) { - let val = apiUtils.validateOptions({ }, { - foo: { - map: function (v) v || "bar" - } - }); - assert.deepEqual(val, { foo: "bar" }); - - val = apiUtils.validateOptions({ }, { - foo: { - map: function (v) { throw "bar" } - } - }); - assert.deepEqual(val, { }); -}; - -exports.testValidateMapWithMissingKeyAndThrown = function (assert) { - let val = apiUtils.validateOptions({}, { - bar: { - map: function(v) { throw "bar" } - }, - baz: { - map: function(v) "foo" - } - }); - assert.deepEqual(val, { baz: "foo" }); -}; - -exports.testAddIterator = function testAddIterator (assert) { - let obj = {}; - let keys = ["foo", "bar", "baz"]; - let vals = [1, 2, 3]; - let keysVals = [["foo", 1], ["bar", 2], ["baz", 3]]; - apiUtils.addIterator( - obj, - function keysValsGen() { - for each (let keyVal in keysVals) - yield keyVal; - } - ); - - let keysItr = []; - for (let key in obj) - keysItr.push(key); - - assert.equal(keysItr.length, keys.length, - "the keys iterator returns the correct number of items"); - for (let i = 0; i < keys.length; i++) - assert.equal(keysItr[i], keys[i], "the key is correct"); - - let valsItr = []; - for each (let val in obj) - valsItr.push(val); - assert.equal(valsItr.length, vals.length, - "the vals iterator returns the correct number of items"); - for (let i = 0; i < vals.length; i++) - assert.equal(valsItr[i], vals[i], "the val is correct"); - -}; - -require('test').run(exports); diff --git a/addon-sdk-1.16/test/test-array.js b/addon-sdk-1.16/test/test-array.js deleted file mode 100644 index 8a9cb440..00000000 --- a/addon-sdk-1.16/test/test-array.js +++ /dev/null @@ -1,103 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -'use strict' - -const array = require('sdk/util/array'); - -exports.testHas = function(assert) { - var testAry = [1, 2, 3]; - assert.equal(array.has([1, 2, 3], 1), true); - assert.equal(testAry.length, 3); - assert.equal(testAry[0], 1); - assert.equal(testAry[1], 2); - assert.equal(testAry[2], 3); - assert.equal(array.has(testAry, 2), true); - assert.equal(array.has(testAry, 3), true); - assert.equal(array.has(testAry, 4), false); - assert.equal(array.has(testAry, '1'), false); -}; -exports.testHasAny = function(assert) { - var testAry = [1, 2, 3]; - assert.equal(array.hasAny([1, 2, 3], [1]), true); - assert.equal(array.hasAny([1, 2, 3], [1, 5]), true); - assert.equal(array.hasAny([1, 2, 3], [5, 1]), true); - assert.equal(array.hasAny([1, 2, 3], [5, 2]), true); - assert.equal(array.hasAny([1, 2, 3], [5, 3]), true); - assert.equal(array.hasAny([1, 2, 3], [5, 4]), false); - assert.equal(testAry.length, 3); - assert.equal(testAry[0], 1); - assert.equal(testAry[1], 2); - assert.equal(testAry[2], 3); - assert.equal(array.hasAny(testAry, [2]), true); - assert.equal(array.hasAny(testAry, [3]), true); - assert.equal(array.hasAny(testAry, [4]), false); - assert.equal(array.hasAny(testAry), false); - assert.equal(array.hasAny(testAry, '1'), false); -}; - -exports.testAdd = function(assert) { - var testAry = [1]; - assert.equal(array.add(testAry, 1), false); - assert.equal(testAry.length, 1); - assert.equal(testAry[0], 1); - assert.equal(array.add(testAry, 2), true); - assert.equal(testAry.length, 2); - assert.equal(testAry[0], 1); - assert.equal(testAry[1], 2); -}; - -exports.testRemove = function(assert) { - var testAry = [1, 2]; - assert.equal(array.remove(testAry, 3), false); - assert.equal(testAry.length, 2); - assert.equal(testAry[0], 1); - assert.equal(testAry[1], 2); - assert.equal(array.remove(testAry, 2), true); - assert.equal(testAry.length, 1); - assert.equal(testAry[0], 1); -}; - -exports.testFlatten = function(assert) { - assert.equal(array.flatten([1, 2, 3]).length, 3); - assert.equal(array.flatten([1, [2, 3]]).length, 3); - assert.equal(array.flatten([1, [2, [3]]]).length, 3); - assert.equal(array.flatten([[1], [[2, [3]]]]).length, 3); -}; - -exports.testUnique = function(assert) { - var Class = function () {}; - var A = {}; - var B = new Class(); - var C = [ 1, 2, 3 ]; - var D = {}; - var E = new Class(); - - assert.deepEqual(array.unique([1,2,3,1,2]), [1,2,3]); - assert.deepEqual(array.unique([1,1,1,4,9,5,5]), [1,4,9,5]); - assert.deepEqual(array.unique([A, A, A, B, B, D]), [A,B,D]); - assert.deepEqual(array.unique([A, D, A, E, E, D, A, A, C]), [A, D, E, C]) -}; - -exports.testUnion = function(assert) { - var Class = function () {}; - var A = {}; - var B = new Class(); - var C = [ 1, 2, 3 ]; - var D = {}; - var E = new Class(); - - assert.deepEqual(array.union([1, 2, 3],[7, 1, 2]), [1, 2, 3, 7]); - assert.deepEqual(array.union([1, 1, 1, 4, 9, 5, 5], [10, 1, 5]), [1, 4, 9, 5, 10]); - assert.deepEqual(array.union([A, B], [A, D]), [A, B, D]); - assert.deepEqual(array.union([A, D], [A, E], [E, D, A], [A, C]), [A, D, E, C]); -}; - -exports.testFind = function(assert) { - let isOdd = (x) => x % 2; - assert.equal(array.find([2, 4, 5, 7, 8, 9], isOdd), 5); - assert.equal(array.find([2, 4, 6, 8], isOdd), undefined); - assert.equal(array.find([2, 4, 6, 8], isOdd, null), null); -}; - -require('test').run(exports); diff --git a/addon-sdk-1.16/test/test-base64.js b/addon-sdk-1.16/test/test-base64.js deleted file mode 100644 index 6ca75002..00000000 --- a/addon-sdk-1.16/test/test-base64.js +++ /dev/null @@ -1,75 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const base64 = require("sdk/base64"); - -const text = "Awesome!"; -const b64text = "QXdlc29tZSE="; - -const utf8text = "✓ à la mode"; -const b64utf8text = "4pyTIMOgIGxhIG1vZGU="; - -exports["test base64.encode"] = function (assert) { - assert.equal(base64.encode(text), b64text, "encode correctly") -} - -exports["test base64.decode"] = function (assert) { - assert.equal(base64.decode(b64text), text, "decode correctly") -} - -exports["test base64.encode Unicode"] = function (assert) { - - assert.equal(base64.encode(utf8text, "utf-8"), b64utf8text, - "encode correctly Unicode strings.") -} - -exports["test base64.decode Unicode"] = function (assert) { - - assert.equal(base64.decode(b64utf8text, "utf-8"), utf8text, - "decode correctly Unicode strings.") -} - -exports["test base64.encode with wrong charset"] = function (assert) { - - assert.throws(function() { - base64.encode(utf8text, "utf-16"); - }, "The charset argument can be only 'utf-8'"); - - assert.throws(function() { - base64.encode(utf8text, ""); - }, "The charset argument can be only 'utf-8'"); - - assert.throws(function() { - base64.encode(utf8text, 8); - }, "The charset argument can be only 'utf-8'"); - -} - -exports["test base64.decode with wrong charset"] = function (assert) { - - assert.throws(function() { - base64.decode(utf8text, "utf-16"); - }, "The charset argument can be only 'utf-8'"); - - assert.throws(function() { - base64.decode(utf8text, ""); - }, "The charset argument can be only 'utf-8'"); - - assert.throws(function() { - base64.decode(utf8text, 8); - }, "The charset argument can be only 'utf-8'"); - -} - -exports["test encode/decode Unicode without utf-8 as charset"] = function (assert) { - - assert.notEqual(base64.decode(base64.encode(utf8text)), utf8text, - "Unicode strings needs 'utf-8' charset" - ); - -} - -require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-browser-events.js b/addon-sdk-1.16/test/test-browser-events.js deleted file mode 100644 index b4d8a1b0..00000000 --- a/addon-sdk-1.16/test/test-browser-events.js +++ /dev/null @@ -1,101 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -module.metadata = { - engines: { - "Firefox": "*" - } -}; - -const { Loader } = require("sdk/test/loader"); -const { open, getMostRecentBrowserWindow, getOuterId } = require("sdk/window/utils"); -const { setTimeout } = require("sdk/timers"); - -exports["test browser events"] = function(assert, done) { - let loader = Loader(module); - let { events } = loader.require("sdk/browser/events"); - let { on, off } = loader.require("sdk/event/core"); - let actual = []; - - on(events, "data", function handler(e) { - actual.push(e); - if (e.type === "load") window.close(); - if (e.type === "close") { - // Unload the module so that all listeners set by observer are removed. - - let [ ready, load, close ] = actual; - - assert.equal(ready.type, "DOMContentLoaded"); - assert.equal(ready.target, window, "window ready"); - - assert.equal(load.type, "load"); - assert.equal(load.target, window, "window load"); - - assert.equal(close.type, "close"); - assert.equal(close.target, window, "window load"); - - // Note: If window is closed right after this GC won't have time - // to claim loader and there for this listener, there for it's safer - // to remove listener. - off(events, "data", handler); - loader.unload(); - done(); - } - }); - - // Open window and close it to trigger observers. - let window = open(); -}; - -exports["test browser events ignore other wins"] = function(assert, done) { - let loader = Loader(module); - let { events: windowEvents } = loader.require("sdk/window/events"); - let { events: browserEvents } = loader.require("sdk/browser/events"); - let { on, off } = loader.require("sdk/event/core"); - let actualBrowser = []; - let actualWindow = []; - - function browserEventHandler(e) actualBrowser.push(e) - on(browserEvents, "data", browserEventHandler); - on(windowEvents, "data", function handler(e) { - actualWindow.push(e); - // Delay close so that if "load" is also emitted on `browserEvents` - // `browserEventHandler` will be invoked. - if (e.type === "load") setTimeout(window.close); - if (e.type === "close") { - assert.deepEqual(actualBrowser, [], "browser events were not triggered"); - let [ open, ready, load, close ] = actualWindow; - - assert.equal(open.type, "open"); - assert.equal(open.target, window, "window is open"); - - - - assert.equal(ready.type, "DOMContentLoaded"); - assert.equal(ready.target, window, "window ready"); - - assert.equal(load.type, "load"); - assert.equal(load.target, window, "window load"); - - assert.equal(close.type, "close"); - assert.equal(close.target, window, "window load"); - - - // Note: If window is closed right after this GC won't have time - // to claim loader and there for this listener, there for it's safer - // to remove listener. - off(windowEvents, "data", handler); - off(browserEvents, "data", browserEventHandler); - loader.unload(); - done(); - } - }); - - // Open window and close it to trigger observers. - let window = open("data:text/html,not a browser"); -}; - -require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-buffer.js b/addon-sdk-1.16/test/test-buffer.js deleted file mode 100644 index 1e3379e0..00000000 --- a/addon-sdk-1.16/test/test-buffer.js +++ /dev/null @@ -1,566 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - - -/* - * Many of these tests taken from Joyent's Node - * https://github.com/joyent/node/blob/master/test/simple/test-buffer.js - */ - -// Copyright Joyent, Inc. and other Node contributors. -// -// Permission is hereby granted, free of charge, to any person obtaining a -// copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to permit -// persons to whom the Software is furnished to do so, subject to the -// following conditions: -// -// The above copyright notice and this permission notice shall be included -// in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS -// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN -// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, -// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE -// USE OR OTHER DEALINGS IN THE SOFTWARE. - -const { Buffer, TextEncoder, TextDecoder } = require('sdk/io/buffer'); -const { safeMerge } = require('sdk/util/object'); - -const ENCODINGS = ['utf-8', 'utf-16le', 'utf-16be']; - -exports.testBufferMain = function (assert) { - let b = Buffer('abcdef'); - - // try to create 0-length buffers - new Buffer(''); - new Buffer(0); - // test encodings supported by node; - // this is different than what node supports, details - // in buffer.js - ENCODINGS.forEach(enc => { - new Buffer('', enc); - assert.pass('Creating a buffer with ' + enc + ' does not throw'); - }); - - ENCODINGS.forEach(function(encoding) { - // Does not work with utf8 - if (encoding === 'utf-8') return; - var b = new Buffer(10); - b.write('あいうえお', encoding); - assert.equal(b.toString(encoding), 'あいうえお', - 'encode and decodes buffer with ' + encoding); - }); - - // invalid encoding for Buffer.toString - assert.throws(() => { - b.toString('invalid'); - }, TypeError, 'invalid encoding for Buffer.toString'); - - // try to toString() a 0-length slice of a buffer, both within and without the - // valid buffer range - assert.equal(new Buffer('abc').toString('utf8', 0, 0), '', - 'toString 0-length buffer, valid range'); - assert.equal(new Buffer('abc').toString('utf8', -100, -100), '', - 'toString 0-length buffer, invalid range'); - assert.equal(new Buffer('abc').toString('utf8', 100, 100), '', - 'toString 0-length buffer, invalid range'); - - // try toString() with a object as a encoding - assert.equal(new Buffer('abc').toString({toString: function() { - return 'utf8'; - }}), 'abc', 'toString with object as an encoding'); - - // test for buffer overrun - var buf = new Buffer([0, 0, 0, 0, 0]); // length: 5 - var sub = buf.slice(0, 4); // length: 4 - var written = sub.write('12345', 'utf8'); - assert.equal(written, 4, 'correct bytes written in slice'); - assert.equal(buf[4], 0, 'correct origin buffer value'); - - // Check for fractional length args, junk length args, etc. - // https://github.com/joyent/node/issues/1758 - Buffer(3.3).toString(); // throws bad argument error in commit 43cb4ec - assert.equal(Buffer(-1).length, 0); - assert.equal(Buffer(NaN).length, 0); - assert.equal(Buffer(3.3).length, 3); - assert.equal(Buffer({length: 3.3}).length, 3); - assert.equal(Buffer({length: 'BAM'}).length, 0); - - // Make sure that strings are not coerced to numbers. - assert.equal(Buffer('99').length, 2); - assert.equal(Buffer('13.37').length, 5); -}; - -exports.testIsEncoding = function (assert) { - ENCODINGS.map(encoding => { - assert.ok(Buffer.isEncoding(encoding), - 'Buffer.isEncoding ' + encoding + ' truthy'); - }); - ['not-encoding', undefined, null, 100, {}].map(encoding => { - assert.ok(!Buffer.isEncoding(encoding), - 'Buffer.isEncoding ' + encoding + ' falsy'); - }); -}; - -exports.testBufferCopy = function (assert) { - // counter to ensure unique value is always copied - var cntr = 0; - var b = Buffer(1024); // safe constructor - - assert.strictEqual(1024, b.length); - b[0] = -1; - assert.strictEqual(b[0], 255); - - var shimArray = []; - for (var i = 0; i < 1024; i++) { - b[i] = i % 256; - shimArray[i] = i % 256; - } - - compareBuffers(assert, b, shimArray, 'array notation'); - - var c = new Buffer(512); - assert.strictEqual(512, c.length); - // copy 512 bytes, from 0 to 512. - b.fill(++cntr); - c.fill(++cntr); - var copied = b.copy(c, 0, 0, 512); - assert.strictEqual(512, copied, - 'copied ' + copied + ' bytes from b into c'); - - compareBuffers(assert, b, c, 'copied to other buffer'); - - // copy c into b, without specifying sourceEnd - b.fill(++cntr); - c.fill(++cntr); - var copied = c.copy(b, 0, 0); - assert.strictEqual(c.length, copied, - 'copied ' + copied + ' bytes from c into b w/o sourceEnd'); - compareBuffers(assert, b, c, - 'copied to other buffer without specifying sourceEnd'); - - // copy c into b, without specifying sourceStart - b.fill(++cntr); - c.fill(++cntr); - var copied = c.copy(b, 0); - assert.strictEqual(c.length, copied, - 'copied ' + copied + ' bytes from c into b w/o sourceStart'); - compareBuffers(assert, b, c, - 'copied to other buffer without specifying sourceStart'); - - // copy longer buffer b to shorter c without targetStart - b.fill(++cntr); - c.fill(++cntr); - - var copied = b.copy(c); - assert.strictEqual(c.length, copied, - 'copied ' + copied + ' bytes from b into c w/o targetStart'); - compareBuffers(assert, b, c, - 'copy long buffer to shorter buffer without targetStart'); - - // copy starting near end of b to c - b.fill(++cntr); - c.fill(++cntr); - var copied = b.copy(c, 0, b.length - Math.floor(c.length / 2)); - assert.strictEqual(Math.floor(c.length / 2), copied, - 'copied ' + copied + ' bytes from end of b into beg. of c'); - - let successStatus = true; - for (var i = 0; i < Math.floor(c.length / 2); i++) { - if (b[b.length - Math.floor(c.length / 2) + i] !== c[i]) - successStatus = false; - } - - for (var i = Math.floor(c.length /2) + 1; i < c.length; i++) { - if (c[c.length-1] !== c[i]) - successStatus = false; - } - assert.ok(successStatus, - 'Copied bytes from end of large buffer into beginning of small buffer'); - - // try to copy 513 bytes, and check we don't overrun c - b.fill(++cntr); - c.fill(++cntr); - var copied = b.copy(c, 0, 0, 513); - assert.strictEqual(c.length, copied, - 'copied ' + copied + ' bytes from b trying to overrun c'); - compareBuffers(assert, b, c, - 'copying to buffer that would overflow'); - - // copy 768 bytes from b into b - b.fill(++cntr); - b.fill(++cntr, 256); - var copied = b.copy(b, 0, 256, 1024); - assert.strictEqual(768, copied, - 'copied ' + copied + ' bytes from b into b'); - - compareBuffers(assert, b, shimArray.map(()=>cntr), - 'copy partial buffer to itself'); - - // copy string longer than buffer length (failure will segfault) - var bb = new Buffer(10); - bb.fill('hello crazy world'); - - // copy throws at negative sourceStart - assert.throws(function() { - Buffer(5).copy(Buffer(5), 0, -1); - }, RangeError, 'buffer copy throws at negative sourceStart'); - - // check sourceEnd resets to targetEnd if former is greater than the latter - b.fill(++cntr); - c.fill(++cntr); - var copied = b.copy(c, 0, 0, 1025); - assert.strictEqual(copied, c.length, - 'copied ' + copied + ' bytes from b into c'); - compareBuffers(assert, b, c, 'copying should reset sourceEnd if targetEnd if sourceEnd > targetEnd'); - - // throw with negative sourceEnd - assert.throws(function() { - b.copy(c, 0, 0, -1); - }, RangeError, 'buffer copy throws at negative sourceEnd'); - - // when sourceStart is greater than sourceEnd, zero copied - assert.equal(b.copy(c, 0, 100, 10), 0); - - // when targetStart > targetLength, zero copied - assert.equal(b.copy(c, 512, 0, 10), 0); - - // try to copy 0 bytes worth of data into an empty buffer - b.copy(new Buffer(0), 0, 0, 0); - - // try to copy 0 bytes past the end of the target buffer - b.copy(new Buffer(0), 1, 1, 1); - b.copy(new Buffer(1), 1, 1, 1); - - // try to copy 0 bytes from past the end of the source buffer - b.copy(new Buffer(1), 0, 2048, 2048); -}; - -exports.testBufferWrite = function (assert) { - let b = Buffer(1024); - b.fill(0); - - assert.throws(() => { - b.write('test string', 0, 5, 'invalid'); - }, TypeError, 'invalid encoding with buffer write throws'); - // try to write a 0-length string beyond the end of b - assert.throws(function() { - b.write('', 2048); - }, RangeError, 'writing a 0-length string beyond buffer throws'); - // throw when writing to negative offset - assert.throws(function() { - b.write('a', -1); - }, RangeError, 'writing negative offset on buffer throws'); - - // throw when writing past bounds from the pool - assert.throws(function() { - b.write('a', 2048); - }, RangeError, 'writing past buffer bounds from pool throws'); - - // testing for smart defaults and ability to pass string values as offset - - // previous write API was the following: - // write(string, encoding, offset, length) - // this is planned on being removed in node v0.13, - // we will not support it - var writeTest = new Buffer('abcdes'); - writeTest.write('n', 'utf8'); -// writeTest.write('o', 'utf8', '1'); - writeTest.write('d', '2', 'utf8'); - writeTest.write('e', 3, 'utf8'); -// writeTest.write('j', 'utf8', 4); - assert.equal(writeTest.toString(), 'nbdees', - 'buffer write API alternative syntax works'); -}; - -exports.testBufferWriteEncoding = function (assert) { - - // Node #1210 Test UTF-8 string includes null character - var buf = new Buffer('\0'); - assert.equal(buf.length, 1); - buf = new Buffer('\0\0'); - assert.equal(buf.length, 2); - - buf = new Buffer(2); - var written = buf.write(''); // 0byte - assert.equal(written, 0); - written = buf.write('\0'); // 1byte (v8 adds null terminator) - assert.equal(written, 1); - written = buf.write('a\0'); // 1byte * 2 - assert.equal(written, 2); - // TODO, these tests write 0, possibly due to character encoding -/* - written = buf.write('あ'); // 3bytes - assert.equal(written, 0); - written = buf.write('\0あ'); // 1byte + 3bytes - assert.equal(written, 1); -*/ - written = buf.write('\0\0あ'); // 1byte * 2 + 3bytes - buf = new Buffer(10); - written = buf.write('あいう'); // 3bytes * 3 (v8 adds null terminator) - assert.equal(written, 9); - written = buf.write('あいう\0'); // 3bytes * 3 + 1byte - assert.equal(written, 10); -}; - -exports.testBufferWriteWithMaxLength = function (assert) { - // Node #243 Test write() with maxLength - var buf = new Buffer(4); - buf.fill(0xFF); - var written = buf.write('abcd', 1, 2, 'utf8'); - assert.equal(written, 2); - assert.equal(buf[0], 0xFF); - assert.equal(buf[1], 0x61); - assert.equal(buf[2], 0x62); - assert.equal(buf[3], 0xFF); - - buf.fill(0xFF); - written = buf.write('abcd', 1, 4); - assert.equal(written, 3); - assert.equal(buf[0], 0xFF); - assert.equal(buf[1], 0x61); - assert.equal(buf[2], 0x62); - assert.equal(buf[3], 0x63); - - buf.fill(0xFF); - // Ignore legacy API - /* - written = buf.write('abcd', 'utf8', 1, 2); // legacy style - console.log(buf); - assert.equal(written, 2); - assert.equal(buf[0], 0xFF); - assert.equal(buf[1], 0x61); - assert.equal(buf[2], 0x62); - assert.equal(buf[3], 0xFF); - */ -}; - -exports.testBufferSlice = function (assert) { - var asciiString = 'hello world'; - var offset = 100; - var b = Buffer(1024); - b.fill(0); - - for (var i = 0; i < asciiString.length; i++) { - b[i] = asciiString.charCodeAt(i); - } - var asciiSlice = b.toString('utf8', 0, asciiString.length); - assert.equal(asciiString, asciiSlice); - - var written = b.write(asciiString, offset, 'utf8'); - assert.equal(asciiString.length, written); - asciiSlice = b.toString('utf8', offset, offset + asciiString.length); - assert.equal(asciiString, asciiSlice); - - var sliceA = b.slice(offset, offset + asciiString.length); - var sliceB = b.slice(offset, offset + asciiString.length); - compareBuffers(assert, sliceA, sliceB, - 'slicing is idempotent'); - - let sliceTest = true; - for (var j = 0; j < 100; j++) { - var slice = b.slice(100, 150); - if (50 !== slice.length) - sliceTest = false; - for (var i = 0; i < 50; i++) { - if (b[100 + i] !== slice[i]) - sliceTest = false; - } - } - assert.ok(sliceTest, 'massive slice runs do not affect buffer'); - - // Single argument slice - let testBuf = new Buffer('abcde'); - assert.equal('bcde', testBuf.slice(1).toString(), 'single argument slice'); - - // slice(0,0).length === 0 - assert.equal(0, Buffer('hello').slice(0, 0).length, 'slice(0,0) === 0'); - - var buf = new Buffer('0123456789'); - assert.equal(buf.slice(-10, 10), '0123456789', 'buffer slice range correct'); - assert.equal(buf.slice(-20, 10), '0123456789', 'buffer slice range correct'); - assert.equal(buf.slice(-20, -10), '', 'buffer slice range correct'); - assert.equal(buf.slice(0, -1), '012345678', 'buffer slice range correct'); - assert.equal(buf.slice(2, -2), '234567', 'buffer slice range correct'); - assert.equal(buf.slice(0, 65536), '0123456789', 'buffer slice range correct'); - assert.equal(buf.slice(65536, 0), '', 'buffer slice range correct'); - - let sliceTest = true; - for (var i = 0, s = buf.toString(); i < buf.length; ++i) { - if (buf.slice(-i) != s.slice(-i)) sliceTest = false; - if (buf.slice(0, -i) != s.slice(0, -i)) sliceTest = false; - } - assert.ok(sliceTest, 'buffer.slice should be consistent'); - - // Make sure modifying a sliced buffer, affects original and vice versa - b.fill(0); - let sliced = b.slice(0, 10); - let babyslice = sliced.slice(0, 5); - - for (let i = 0; i < sliced.length; i++) - sliced[i] = 'jetpack'.charAt(i); - - compareBuffers(assert, b, sliced, - 'modifying sliced buffer affects original'); - - compareBuffers(assert, b, babyslice, - 'modifying sliced buffer affects child-sliced buffer'); - - for (let i = 0; i < sliced.length; i++) - b[i] = 'odinmonkey'.charAt(i); - - compareBuffers(assert, b, sliced, - 'modifying original buffer affects sliced'); - - compareBuffers(assert, b, babyslice, - 'modifying original buffer affects grandchild sliced buffer'); -}; - -exports.testSlicingParents = function (assert) { - let root = Buffer(5); - let child = root.slice(0, 4); - let grandchild = child.slice(0, 3); - - assert.equal(root.parent, undefined, 'a new buffer should not have a parent'); - - // make sure a zero length slice doesn't set the .parent attribute - assert.equal(root.slice(0,0).parent, undefined, - '0-length slice should not have a parent'); - - assert.equal(child.parent, root, - 'a valid slice\'s parent should be the original buffer (child)'); - - assert.equal(grandchild.parent, root, - 'a valid slice\'s parent should be the original buffer (grandchild)'); -}; - -exports.testIsBuffer = function (assert) { - let buffer = new Buffer('content', 'utf8'); - assert.ok(Buffer.isBuffer(buffer), 'isBuffer truthy on buffers'); - assert.ok(!Buffer.isBuffer({}), 'isBuffer falsy on objects'); - assert.ok(!Buffer.isBuffer(new Uint8Array()), - 'isBuffer falsy on Uint8Array'); - assert.ok(Buffer.isBuffer(buffer.slice(0)), 'Buffer#slice should be a new buffer'); -}; - -exports.testBufferConcat = function (assert) { - let zero = []; - let one = [ new Buffer('asdf') ]; - let long = []; - for (let i = 0; i < 10; i++) long.push(new Buffer('asdf')); - - let flatZero = Buffer.concat(zero); - let flatOne = Buffer.concat(one); - let flatLong = Buffer.concat(long); - let flatLongLen = Buffer.concat(long, 40); - - assert.equal(flatZero.length, 0); - assert.equal(flatOne.toString(), 'asdf'); - assert.equal(flatOne, one[0]); - assert.ok(flatLong.toString(), (new Array(10+1).join('asdf'))); - assert.equal(flatLongLen.toString(), (new Array(10+1).join('asdf'))); -}; - -exports.testBufferByteLength = function (assert) { - let str = '\u00bd + \u00bc = \u00be'; - assert.equal(Buffer.byteLength(str), 12, - 'correct byteLength of string'); - - assert.equal(14, Buffer.byteLength('Il était tué')); - assert.equal(14, Buffer.byteLength('Il était tué', 'utf8')); - // We do not currently support these encodings - /* - ['ucs2', 'ucs-2', 'utf16le', 'utf-16le'].forEach(function(encoding) { - assert.equal(24, Buffer.byteLength('Il était tué', encoding)); - }); - assert.equal(12, Buffer.byteLength('Il était tué', 'ascii')); - assert.equal(12, Buffer.byteLength('Il était tué', 'binary')); - */ -}; - -exports.testTextEncoderDecoder = function (assert) { - assert.ok(TextEncoder, 'TextEncoder exists'); - assert.ok(TextDecoder, 'TextDecoder exists'); -}; - -exports.testOverflowedBuffers = function (assert) { - assert.throws(function() { - new Buffer(0xFFFFFFFF); - }, RangeError, 'correctly throws buffer overflow'); - - assert.throws(function() { - new Buffer(0xFFFFFFFFF); - }, RangeError, 'correctly throws buffer overflow'); - - assert.throws(function() { - var buf = new Buffer(8); - buf.readFloatLE(0xffffffff); - }, RangeError, 'correctly throws buffer overflow with readFloatLE'); - - assert.throws(function() { - var buf = new Buffer(8); - buf.writeFloatLE(0.0, 0xffffffff); - }, RangeError, 'correctly throws buffer overflow with writeFloatLE'); - - //ensure negative values can't get past offset - assert.throws(function() { - var buf = new Buffer(8); - buf.readFloatLE(-1); - }, RangeError, 'correctly throws with readFloatLE negative values'); - - assert.throws(function() { - var buf = new Buffer(8); - buf.writeFloatLE(0.0, -1); - }, RangeError, 'correctly throws with writeFloatLE with negative values'); - - assert.throws(function() { - var buf = new Buffer(8); - buf.readFloatLE(-1); - }, RangeError, 'correctly throws with readFloatLE with negative values'); -}; - -exports.testReadWriteDataTypeErrors = function (assert) { - var buf = new Buffer(0); - assert.throws(function() { buf.readUInt8(0); }, RangeError, - 'readUInt8(0) throws'); - assert.throws(function() { buf.readInt8(0); }, RangeError, - 'readInt8(0) throws'); - - [16, 32].forEach(function(bits) { - var buf = new Buffer(bits / 8 - 1); - assert.throws(function() { buf['readUInt' + bits + 'BE'](0); }, - RangeError, - 'readUInt' + bits + 'BE'); - - assert.throws(function() { buf['readUInt' + bits + 'LE'](0); }, - RangeError, - 'readUInt' + bits + 'LE'); - - assert.throws(function() { buf['readInt' + bits + 'BE'](0); }, - RangeError, - 'readInt' + bits + 'BE()'); - - assert.throws(function() { buf['readInt' + bits + 'LE'](0); }, - RangeError, - 'readInt' + bits + 'LE()'); - }); -}; - -safeMerge(exports, require('./buffers/test-write-types')); -safeMerge(exports, require('./buffers/test-read-types')); - -function compareBuffers (assert, buf1, buf2, message) { - let status = true; - for (let i = 0; i < Math.min(buf1.length, buf2.length); i++) { - if (buf1[i] !== buf2[i]) - status = false; - } - assert.ok(status, 'buffer successfully copied: ' + message); -} -require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-byte-streams.js b/addon-sdk-1.16/test/test-byte-streams.js deleted file mode 100644 index 24302dc6..00000000 --- a/addon-sdk-1.16/test/test-byte-streams.js +++ /dev/null @@ -1,169 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -const byteStreams = require("sdk/io/byte-streams"); -const file = require("sdk/io/file"); -const { pathFor } = require("sdk/system"); -const { Loader } = require("sdk/test/loader"); - -const STREAM_CLOSED_ERROR = new RegExp("The stream is closed and cannot be used."); - -// This should match the constant of the same name in byte-streams.js. -const BUFFER_BYTE_LEN = 0x8000; - -exports.testWriteRead = function (assert) { - let fname = dataFileFilename(); - - // Write a small string less than the stream's buffer size... - let str = "exports.testWriteRead data!"; - let stream = open(assert, fname, true); - assert.ok(!stream.closed, "stream.closed after open should be false"); - stream.write(str); - stream.close(); - assert.ok(stream.closed, "Stream should be closed after stream.close"); - assert.throws(function () stream.write("This shouldn't be written!"), - STREAM_CLOSED_ERROR, - "stream.write after close should raise error"); - - // ... and read it. - stream = open(assert, fname); - assert.equal(stream.read(), str, - "stream.read should return string written"); - assert.equal(stream.read(), "", - "stream.read at EOS should return empty string"); - stream.close(); - assert.ok(stream.closed, "Stream should be closed after stream.close"); - assert.throws(function () stream.read(), - STREAM_CLOSED_ERROR, - "stream.read after close should raise error"); - - file.remove(fname); -}; - -// Write a big string many times the size of the stream's buffer and read it. -exports.testWriteReadBig = function (assert) { - let str = ""; - let bufLen = BUFFER_BYTE_LEN; - let fileSize = bufLen * 10; - for (let i = 0; i < fileSize; i++) - str += i % 10; - let fname = dataFileFilename(); - let stream = open(assert, fname, true); - stream.write(str); - stream.close(); - stream = open(assert, fname); - assert.equal(stream.read(), str, - "stream.read should return string written"); - stream.close(); - file.remove(fname); -}; - -// The same, but write and read in chunks. -exports.testWriteReadChunks = function (assert) { - let str = ""; - let bufLen = BUFFER_BYTE_LEN; - let fileSize = bufLen * 10; - for (let i = 0; i < fileSize; i++) - str += i % 10; - let fname = dataFileFilename(); - let stream = open(assert, fname, true); - let i = 0; - while (i < str.length) { - // Use a chunk length that spans buffers. - let chunk = str.substr(i, bufLen + 1); - stream.write(chunk); - i += bufLen + 1; - } - stream.close(); - stream = open(assert, fname); - let readStr = ""; - bufLen = BUFFER_BYTE_LEN; - let readLen = bufLen + 1; - do { - var frag = stream.read(readLen); - readStr += frag; - } while (frag); - stream.close(); - assert.equal(readStr, str, - "stream.write and read in chunks should work as expected"); - file.remove(fname); -}; - -exports.testReadLengths = function (assert) { - let fname = dataFileFilename(); - let str = "exports.testReadLengths data!"; - let stream = open(assert, fname, true); - stream.write(str); - stream.close(); - - stream = open(assert, fname); - assert.equal(stream.read(str.length * 1000), str, - "stream.read with big byte length should return string " + - "written"); - stream.close(); - - stream = open(assert, fname); - assert.equal(stream.read(0), "", - "string.read with zero byte length should return empty " + - "string"); - stream.close(); - - stream = open(assert, fname); - assert.equal(stream.read(-1), "", - "string.read with negative byte length should return " + - "empty string"); - stream.close(); - - file.remove(fname); -}; - -exports.testTruncate = function (assert) { - let fname = dataFileFilename(); - let str = "exports.testReadLengths data!"; - let stream = open(assert, fname, true); - stream.write(str); - stream.close(); - - stream = open(assert, fname); - assert.equal(stream.read(), str, - "stream.read should return string written"); - stream.close(); - - stream = open(assert, fname, true); - stream.close(); - - stream = open(assert, fname); - assert.equal(stream.read(), "", - "stream.read after truncate should be empty"); - stream.close(); - - file.remove(fname); -}; - -exports.testUnload = function (assert) { - let loader = Loader(module); - let file = loader.require("sdk/io/file"); - - let filename = dataFileFilename("temp-b"); - let stream = file.open(filename, "wb"); - - loader.unload(); - assert.ok(stream.closed, "Stream should be closed after module unload"); -}; - -// Returns the name of a file that should be used to test writing and reading. -function dataFileFilename() { - return file.join(pathFor("ProfD"), "test-byte-streams-data"); -} - -// Opens and returns the given file and ensures it's of the correct class. -function open(assert, filename, forWriting) { - let stream = file.open(filename, forWriting ? "wb" : "b"); - let klass = forWriting ? "ByteWriter" : "ByteReader"; - assert.ok(stream instanceof byteStreams[klass], - "Opened stream should be a " + klass); - return stream; -} - -require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-chrome.js b/addon-sdk-1.16/test/test-chrome.js deleted file mode 100644 index 7d0addae..00000000 --- a/addon-sdk-1.16/test/test-chrome.js +++ /dev/null @@ -1,84 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -'use strict'; - -let chrome = require('chrome'); - -const FIXTURES_URL = module.uri.substr(0, module.uri.lastIndexOf('/') + 1) + - 'fixtures/chrome-worker/' - -exports['test addEventListener'] = function(assert, done) { - let uri = FIXTURES_URL + 'addEventListener.js'; - - let worker = new chrome.ChromeWorker(uri); - worker.addEventListener('message', function(event) { - assert.equal(event.data, 'Hello', 'message received'); - worker.terminate(); - done(); - }); -}; - -exports['test onmessage'] = function(assert, done) { - let uri = FIXTURES_URL + 'onmessage.js'; - - let worker = new chrome.ChromeWorker(uri); - worker.onmessage = function(event) { - assert.equal(event.data, 'ok', 'message received'); - worker.terminate(); - done(); - }; - worker.postMessage('ok'); -}; - -exports['test setTimeout'] = function(assert, done) { - let uri = FIXTURES_URL + 'setTimeout.js'; - - let worker = new chrome.ChromeWorker(uri); - worker.onmessage = function(event) { - assert.equal(event.data, 'ok', 'setTimeout fired'); - worker.terminate(); - done(); - }; -}; - -exports['test jsctypes'] = function(assert, done) { - let uri = FIXTURES_URL + 'jsctypes.js'; - - let worker = new chrome.ChromeWorker(uri); - worker.onmessage = function(event) { - assert.equal(event.data, 'function', 'ctypes.open is a function'); - worker.terminate(); - done(); - }; -}; - -exports['test XMLHttpRequest'] = function(assert, done) { - let uri = FIXTURES_URL + 'xhr.js'; - - let worker = new chrome.ChromeWorker(uri); - worker.onmessage = function(event) { - assert.equal(event.data, 'ok', 'XMLHttpRequest works'); - worker.terminate(); - done(); - }; -}; - -exports['test onerror'] = function(assert, done) { - let uri = FIXTURES_URL + 'onerror.js'; - - let worker = new chrome.ChromeWorker(uri); - worker.onerror = function(event) { - assert.equal(event.filename, uri, 'event reports the correct uri'); - assert.equal(event.lineno, 6, 'event reports the correct line number'); - assert.equal(event.target, worker, 'event reports the correct worker'); - assert.ok(event.message.match(/ok/), - 'event contains the exception message'); - // Call preventDefault in order to avoid being displayed in JS console. - event.preventDefault(); - worker.terminate(); - done(); - }; -}; - -require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-clipboard.js b/addon-sdk-1.16/test/test-clipboard.js deleted file mode 100644 index 52ef9a5e..00000000 --- a/addon-sdk-1.16/test/test-clipboard.js +++ /dev/null @@ -1,222 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -"use strict"; - -require("sdk/clipboard"); - -const { Cc, Ci } = require("chrome"); - -const imageTools = Cc["@mozilla.org/image/tools;1"]. - getService(Ci.imgITools); - -const io = Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService); - -const base64png = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYA" + - "AABzenr0AAAASUlEQVRYhe3O0QkAIAwD0eyqe3Q993AQ3cBSUKpygfsNTy" + - "N5ugbQpK0BAADgP0BRDWXWlwEAAAAAgPsA3rzDaAAAAHgPcGrpgAnzQ2FG" + - "bWRR9AAAAABJRU5ErkJggg%3D%3D"; - -const base64jpeg = "data:image/jpeg;base64,%2F9j%2F4AAQSkZJRgABAQAAAQABAAD%2F" + - "2wBDAAMCAgICAgMCAgIDAwMDBAYEBAQEBAgGBgUGCQgKCgkICQkKDA8MCg" + - "sOCwkJDRENDg8QEBEQCgwSExIQEw8QEBD%2F2wBDAQMDAwQDBAgEBAgQCw" + - "kLEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ" + - "EBAQEBAQEBD%2FwAARCAAgACADAREAAhEBAxEB%2F8QAHwAAAQUBAQEBAQ" + - "EAAAAAAAAAAAECAwQFBgcICQoL%2F8QAtRAAAgEDAwIEAwUFBAQAAAF9AQ" + - "IDAAQRBRIhMUEGE1FhByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRol" + - "JicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eX" + - "qDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJ" + - "ytLT1NXW19jZ2uHi4%2BTl5ufo6erx8vP09fb3%2BPn6%2F8QAHwEAAwEB" + - "AQEBAQEBAQAAAAAAAAECAwQFBgcICQoL%2F8QAtREAAgECBAQDBAcFBAQA" + - "AQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHBCSMzUvAVYnLRChYkNO" + - "El8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0" + - "dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6ws" + - "PExcbHyMnK0tPU1dbX2Nna4uPk5ebn6Onq8vP09fb3%2BPn6%2F9oADAMB" + - "AAIRAxEAPwD5Kr8kP9CwoA5f4m%2F8iRqX%2FbH%2FANHJXr5F%2FwAjCn" + - "8%2F%2FSWfnnir%2FwAkji%2F%2B4f8A6dgeD1%2BiH8bn1BX5If6FmFqW" + - "pXtveyQwzbUXGBtB7D2r9l4U4UyjMsoo4rFUeacua75pLaUktFJLZH5NxN" + - "xNmmX5pVw2Gq8sI8tlyxe8U3q03uzD8S3dxqOi3NneSeZDJs3LgDOHBHI5" + - "6gV%2BkcG%2BH%2FDmJzuhSq4e8XzfbqfyS%2FvH5rx1xTm2MyDEUa1W8X" + - "yXXLFbTi%2BkThv7B0r%2FAJ9f%2FH2%2Fxr90%2FwCIVcI%2F9An%2FAJ" + - "Uq%2FwDyZ%2FO%2F16v%2FADfgv8j0r%2FhZvgj%2FAKDf%2FktN%2FwDE" + - "V%2Fnr%2FYWYf8%2B%2Fxj%2Fmf3R%2FxFXhH%2FoL%2FwDKdX%2F5Azrv" + - "xLouo3D3lne%2BZDJja3luM4GDwRnqDX9LeH%2FBud4nhzD1aVC8Xz%2Fa" + - "h%2Fz8l%2FePx%2FinjrIMZm1WtRxF4vls%2BSa2jFdYlDUdRsp7OSKKbc" + - "7YwNpHce1fqfCvCub5bm9HFYqjywjzXfNF7xklopN7s%2BC4l4lyvMMrq4" + - "fD1bzfLZcsltJPqktkYlfsZ%2BUnBV%2FnufVnXaD%2FAMgqD%2FgX%2Fo" + - "Rr%2BxvCr%2FkkcJ%2F3E%2F8ATsz5%2FHfx5fL8kX6%2FQjkCgD%2F%2F" + - "2Q%3D%3D"; - -const canvasHTML = "data:text/html," + encodeURIComponent( - "\ - \ - \ - \ - " -); - -function comparePixelImages(imageA, imageB, callback) { - let tabs = require("sdk/tabs"); - - tabs.open({ - url: canvasHTML, - - onReady: function onReady(tab) { - let worker = tab.attach({ - contentScript: "new " + function() { - let canvas = document.querySelector("canvas"); - let context = canvas.getContext("2d"); - - self.port.on("draw-image", function(imageURI) { - let img = new Image(); - - img.onload = function() { - context.drawImage(this, 0, 0); - - let pixels = Array.join(context.getImageData(0, 0, 32, 32).data); - self.port.emit("image-pixels", pixels); - } - - img.src = imageURI; - }); - } - }); - - let compared = ""; - - worker.port.on("image-pixels", function (pixels) { - if (!compared) { - compared = pixels; - this.emit("draw-image", imageB); - } else { - callback(compared === pixels); - tab.close() - } - }); - - worker.port.emit("draw-image", imageA); - } - }); -} - - -// Test the typical use case, setting & getting with no flavors specified -exports["test With No Flavor"] = function(assert) { - var contents = "hello there"; - var flavor = "text"; - var fullFlavor = "text/unicode"; - var clip = require("sdk/clipboard"); - - // Confirm we set the clipboard - assert.ok(clip.set(contents)); - - // Confirm flavor is set - assert.equal(clip.currentFlavors[0], flavor); - - // Confirm we set the clipboard - assert.equal(clip.get(), contents); - - // Confirm we can get the clipboard using the flavor - assert.equal(clip.get(flavor), contents); - - // Confirm we can still get the clipboard using the full flavor - assert.equal(clip.get(fullFlavor), contents); -}; - -// Test the slightly less common case where we specify the flavor -exports["test With Flavor"] = function(assert) { - var contents = "hello there"; - var contentsText = "hello there"; - var flavor = "html"; - var fullFlavor = "text/html"; - var unicodeFlavor = "text"; - var unicodeFullFlavor = "text/unicode"; - var clip = require("sdk/clipboard"); - - assert.ok(clip.set(contents, flavor)); - - assert.equal(clip.currentFlavors[0], unicodeFlavor); - assert.equal(clip.currentFlavors[1], flavor); - assert.equal(clip.get(), contentsText); - assert.equal(clip.get(flavor), contents); - assert.equal(clip.get(fullFlavor), contents); - assert.equal(clip.get(unicodeFlavor), contentsText); - assert.equal(clip.get(unicodeFullFlavor), contentsText); -}; - -// Test that the typical case still works when we specify the flavor to set -exports["test With Redundant Flavor"] = function(assert) { - var contents = "hello there"; - var flavor = "text"; - var fullFlavor = "text/unicode"; - var clip = require("sdk/clipboard"); - - assert.ok(clip.set(contents, flavor)); - assert.equal(clip.currentFlavors[0], flavor); - assert.equal(clip.get(), contents); - assert.equal(clip.get(flavor), contents); - assert.equal(clip.get(fullFlavor), contents); -}; - -exports["test Not In Flavor"] = function(assert) { - var contents = "hello there"; - var flavor = "html"; - var clip = require("sdk/clipboard"); - - assert.ok(clip.set(contents)); - // If there's nothing on the clipboard with this flavor, should return null - assert.equal(clip.get(flavor), null); -}; - -exports["test Set Image"] = function(assert) { - var clip = require("sdk/clipboard"); - var flavor = "image"; - var fullFlavor = "image/png"; - - assert.ok(clip.set(base64png, flavor), "clipboard set"); - assert.equal(clip.currentFlavors[0], flavor, "flavor is set"); -}; - -exports["test Get Image"] = function(assert, done) { - var clip = require("sdk/clipboard"); - - clip.set(base64png, "image"); - - var contents = clip.get(); - - comparePixelImages(base64png, contents, function (areEquals) { - assert.ok(areEquals, - "Image gets from clipboard equals to image sets to the clipboard"); - - done(); - }); -} - -exports["test Set Image Type Not Supported"] = function(assert) { - var clip = require("sdk/clipboard"); - var flavor = "image"; - - assert.throws(function () { - clip.set(base64jpeg, flavor); - }, "Invalid flavor for image/jpeg"); - -}; - -// Notice that `imageTools.decodeImageData`, used by `clipboard.set` method for -// images, write directly to the javascript console the error in case the image -// is corrupt, even if the error is catched. -// -// See: http://mxr.mozilla.org/mozilla-central/source/image/src/Decoder.cpp#136 -exports["test Set Image Type Wrong Data"] = function(assert) { - var clip = require("sdk/clipboard"); - var flavor = "image"; - - var wrongPNG = "data:image/png" + base64jpeg.substr(15); - - assert.throws(function () { - clip.set(wrongPNG, flavor); - }, "Unable to decode data given in a valid image."); -}; - -require("test").run(exports) diff --git a/addon-sdk-1.16/test/test-collection.js b/addon-sdk-1.16/test/test-collection.js deleted file mode 100644 index d723c14c..00000000 --- a/addon-sdk-1.16/test/test-collection.js +++ /dev/null @@ -1,128 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -"use strict"; - -const collection = require("sdk/util/collection"); - -exports.testAddRemove = function (assert) { - let coll = new collection.Collection(); - compare(assert, coll, []); - addRemove(assert, coll, [], false); -}; - -exports.testAddRemoveBackingArray = function (assert) { - let items = ["foo"]; - let coll = new collection.Collection(items); - compare(assert, coll, items); - addRemove(assert, coll, items, true); - - items = ["foo", "bar"]; - coll = new collection.Collection(items); - compare(assert, coll, items); - addRemove(assert, coll, items, true); -}; - -exports.testProperty = function (assert) { - let obj = makeObjWithCollProp(); - compare(assert, obj.coll, []); - addRemove(assert, obj.coll, [], false); - - // Test single-value set. - let items = ["foo"]; - obj.coll = items[0]; - compare(assert, obj.coll, items); - addRemove(assert, obj.coll, items, false); - - // Test array set. - items = ["foo", "bar"]; - obj.coll = items; - compare(assert, obj.coll, items); - addRemove(assert, obj.coll, items, false); -}; - -exports.testPropertyBackingArray = function (assert) { - let items = ["foo"]; - let obj = makeObjWithCollProp(items); - compare(assert, obj.coll, items); - addRemove(assert, obj.coll, items, true); - - items = ["foo", "bar"]; - obj = makeObjWithCollProp(items); - compare(assert, obj.coll, items); - addRemove(assert, obj.coll, items, true); -}; - -// Adds some values to coll and then removes them. initialItems is an array -// containing coll's initial items. isBacking is true if initialItems is coll's -// backing array; the point is that updates to coll should affect initialItems -// if that's the case. -function addRemove(assert, coll, initialItems, isBacking) { - let items = isBacking ? initialItems : initialItems.slice(0); - let numInitialItems = items.length; - - // Test add(val). - let numInsertions = 5; - for (let i = 0; i < numInsertions; i++) { - compare(assert, coll, items); - coll.add(i); - if (!isBacking) - items.push(i); - } - compare(assert, coll, items); - - // Add the items we just added to make sure duplicates aren't added. - for (let i = 0; i < numInsertions; i++) - coll.add(i); - compare(assert, coll, items); - - // Test remove(val). Do a kind of shuffled remove. Remove item 1, then - // item 0, 3, 2, 5, 4, ... - for (let i = 0; i < numInsertions; i++) { - let val = i % 2 ? i - 1 : - i === numInsertions - 1 ? i : i + 1; - coll.remove(val); - if (!isBacking) - items.splice(items.indexOf(val), 1); - compare(assert, coll, items); - } - assert.equal(coll.length, numInitialItems, - "All inserted items should be removed"); - - // Remove the items we just removed. coll should be unchanged. - for (let i = 0; i < numInsertions; i++) - coll.remove(i); - compare(assert, coll, items); - - // Test add and remove([val1, val2]). - let newItems = [0, 1]; - coll.add(newItems); - compare(assert, coll, isBacking ? items : items.concat(newItems)); - coll.remove(newItems); - compare(assert, coll, items); - assert.equal(coll.length, numInitialItems, - "All inserted items should be removed"); -} - -// Asserts that the items in coll are the items of array. -function compare(assert, coll, array) { - assert.equal(coll.length, array.length, - "Collection length should be correct"); - let numItems = 0; - for (let item in coll) { - assert.equal(item, array[numItems], "Items should be equal"); - numItems++; - } - assert.equal(numItems, array.length, - "Number of items in iteration should be correct"); -} - -// Returns a new object with a collection property named "coll". backingArray, -// if defined, will create the collection with that backing array. -function makeObjWithCollProp(backingArray) { - let obj = {}; - collection.addCollectionProperty(obj, "coll", backingArray); - return obj; -} - -require("sdk/test").run(exports); diff --git a/addon-sdk-1.16/test/test-commonjs-test-adapter.js b/addon-sdk-1.16/test/test-commonjs-test-adapter.js deleted file mode 100644 index 936bea91..00000000 --- a/addon-sdk-1.16/test/test-commonjs-test-adapter.js +++ /dev/null @@ -1,11 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -exports["test custom `Assert`'s"] = require("./commonjs-test-adapter/asserts"); - -// Disabling this check since it is not yet supported by jetpack. -// if (module == require.main) - require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-content-events.js b/addon-sdk-1.16/test/test-content-events.js deleted file mode 100644 index dd0bbf91..00000000 --- a/addon-sdk-1.16/test/test-content-events.js +++ /dev/null @@ -1,135 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const { Loader } = require("sdk/test/loader"); -const { getMostRecentBrowserWindow, getInnerId } = require("sdk/window/utils"); -const { openTab, closeTab, getBrowserForTab } = require("sdk/tabs/utils"); -const { defer } = require("sdk/core/promise"); -const { curry, identity, partial } = require("sdk/lang/functional"); - -let when = curry(function(options, tab) { - let type = options.type || options; - let capture = options.capture || false; - let target = getBrowserForTab(tab); - let { promise, resolve } = defer(); - - target.addEventListener(type, function handler(event) { - if (!event.target.defaultView.frameElement) { - target.removeEventListener(type, handler, capture); - resolve(tab); - } - }, capture); - - return promise; -}); - -let use = function(value) function() value; - - -let open = curry(function(url, window) openTab(window, url)); -let close = function(tab) { - let promise = when("pagehide", tab); - closeTab(tab); - return promise; -} - -exports["test multiple tabs"] = function(assert, done) { - let loader = Loader(module); - let { events } = loader.require("sdk/content/events"); - let { on, off } = loader.require("sdk/event/core"); - let actual = []; - - on(events, "data", handler); - function handler ({type, target, timeStamp}) { - eventFilter(type, target, () => { - actual.push(type + " -> " + target.URL) - }); - } - - let window = getMostRecentBrowserWindow(); - let firstTab = open("data:text/html,first-tab", window); - - when("pageshow", firstTab). - then(close). - then(use(window)). - then(open("data:text/html,second-tab")). - then(when("pageshow")). - then(close). - then(function() { - assert.deepEqual(actual, [ - "document-element-inserted -> data:text/html,first-tab", - "DOMContentLoaded -> data:text/html,first-tab", - "load -> data:text/html,first-tab", - "pageshow -> data:text/html,first-tab", - "document-element-inserted -> data:text/html,second-tab", - "DOMContentLoaded -> data:text/html,second-tab", - "load -> data:text/html,second-tab", - "pageshow -> data:text/html,second-tab" - ], "all events dispatche as expeced") - }, function(reason) { - assert.fail(Error(reason)); - }).then(function() { - loader.unload(); - off(events, "data", handler); - done(); - }); -}; - -exports["test nested frames"] = function(assert, done) { - let loader = Loader(module); - let { events } = loader.require("sdk/content/events"); - let { on, off } = loader.require("sdk/event/core"); - let actual = []; - on(events, "data", handler); - function handler ({type, target, timeStamp}) { - eventFilter(type, target, () => { - actual.push(type + " -> " + target.URL) - }); - } - - let window = getMostRecentBrowserWindow(); - let uri = encodeURI("data:text/html, -

- -

- - A targetted link. - -

- -

- -

- -

- - A link with no ID and an anchor, used by PredicateContext tests. - -

- -

- -

- -

- -

- -

- -

- -

-

This content is editable.

-

- - diff --git a/addon-sdk-1.16/test/test-context-menu.js b/addon-sdk-1.16/test/test-context-menu.js deleted file mode 100644 index f2b1c3c0..00000000 --- a/addon-sdk-1.16/test/test-context-menu.js +++ /dev/null @@ -1,4045 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - 'use strict'; - -let { Cc, Ci } = require("chrome"); - -require("sdk/context-menu"); - -const { Loader } = require('sdk/test/loader'); -const timer = require("sdk/timers"); -const { merge } = require("sdk/util/object"); - -// These should match the same constants in the module. -const ITEM_CLASS = "addon-context-menu-item"; -const SEPARATOR_CLASS = "addon-context-menu-separator"; -const OVERFLOW_THRESH_DEFAULT = 10; -const OVERFLOW_THRESH_PREF = - "extensions.addon-sdk.context-menu.overflowThreshold"; -const OVERFLOW_MENU_CLASS = "addon-content-menu-overflow-menu"; -const OVERFLOW_POPUP_CLASS = "addon-content-menu-overflow-popup"; - -const TEST_DOC_URL = module.uri.replace(/\.js$/, ".html"); -const data = require("./fixtures"); - -// Tests that when present the separator is placed before the separator from -// the old context-menu module -exports.testSeparatorPosition = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - // Create the old separator - let oldSeparator = test.contextMenuPopup.ownerDocument.createElement("menuseparator"); - oldSeparator.id = "jetpack-context-menu-separator"; - test.contextMenuPopup.appendChild(oldSeparator); - - // Create an item. - let item = new loader.cm.Item({ label: "item" }); - - test.showMenu(null, function (popup) { - assert.equal(test.contextMenuSeparator.nextSibling.nextSibling, oldSeparator, - "New separator should appear before the old one"); - test.contextMenuPopup.removeChild(oldSeparator); - test.done(); - }); -}; - -// Destroying items that were previously created should cause them to be absent -// from the menu. -exports.testConstructDestroy = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - // Create an item. - let item = new loader.cm.Item({ label: "item" }); - assert.equal(item.parentMenu, loader.cm.contentContextMenu, - "item's parent menu should be correct"); - - test.showMenu(null, function (popup) { - - // It should be present when the menu is shown. - test.checkMenu([item], [], []); - popup.hidePopup(); - - // Destroy the item. Multiple destroys should be harmless. - item.destroy(); - item.destroy(); - test.showMenu(null, function (popup) { - - // It should be removed from the menu. - test.checkMenu([item], [], [item]); - test.done(); - }); - }); -}; - - -// Destroying an item twice should not cause an error. -exports.testDestroyTwice = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let item = new loader.cm.Item({ label: "item" }); - item.destroy(); - item.destroy(); - - test.pass("Destroying an item twice should not cause an error."); - test.done(); -}; - - -// CSS selector contexts should cause their items to be present in the menu -// when the menu is invoked on nodes that match the selectors. -exports.testSelectorContextMatch = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let item = new loader.cm.Item({ - label: "item", - data: "item", - context: loader.cm.SelectorContext("img") - }); - - test.withTestDoc(function (window, doc) { - test.showMenu(doc.getElementById("image"), function (popup) { - test.checkMenu([item], [], []); - test.done(); - }); - }); -}; - - -// CSS selector contexts should cause their items to be present in the menu -// when the menu is invoked on nodes that have ancestors that match the -// selectors. -exports.testSelectorAncestorContextMatch = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let item = new loader.cm.Item({ - label: "item", - data: "item", - context: loader.cm.SelectorContext("a[href]") - }); - - test.withTestDoc(function (window, doc) { - test.showMenu(doc.getElementById("span-link"), function (popup) { - test.checkMenu([item], [], []); - test.done(); - }); - }); -}; - - -// CSS selector contexts should cause their items to be absent from the menu -// when the menu is not invoked on nodes that match or have ancestors that -// match the selectors. -exports.testSelectorContextNoMatch = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let item = new loader.cm.Item({ - label: "item", - data: "item", - context: loader.cm.SelectorContext("img") - }); - - test.showMenu(null, function (popup) { - test.checkMenu([item], [item], []); - test.done(); - }); -}; - - -// Page contexts should cause their items to be present in the menu when the -// menu is not invoked on an active element. -exports.testPageContextMatch = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let items = [ - new loader.cm.Item({ - label: "item 0" - }), - new loader.cm.Item({ - label: "item 1", - context: undefined - }), - new loader.cm.Item({ - label: "item 2", - context: loader.cm.PageContext() - }), - new loader.cm.Item({ - label: "item 3", - context: [loader.cm.PageContext()] - }) - ]; - - test.showMenu(null, function (popup) { - test.checkMenu(items, [], []); - test.done(); - }); -}; - - -// Page contexts should cause their items to be absent from the menu when the -// menu is invoked on an active element. -exports.testPageContextNoMatch = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let items = [ - new loader.cm.Item({ - label: "item 0" - }), - new loader.cm.Item({ - label: "item 1", - context: undefined - }), - new loader.cm.Item({ - label: "item 2", - context: loader.cm.PageContext() - }), - new loader.cm.Item({ - label: "item 3", - context: [loader.cm.PageContext()] - }) - ]; - - test.withTestDoc(function (window, doc) { - test.showMenu(doc.getElementById("image"), function (popup) { - test.checkMenu(items, items, []); - test.done(); - }); - }); -}; - - -// Selection contexts should cause items to appear when a selection exists. -exports.testSelectionContextMatch = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let item = loader.cm.Item({ - label: "item", - context: loader.cm.SelectionContext() - }); - - test.withTestDoc(function (window, doc) { - window.getSelection().selectAllChildren(doc.body); - test.showMenu(null, function (popup) { - test.checkMenu([item], [], []); - test.done(); - }); - }); -}; - - -// Selection contexts should cause items to appear when a selection exists in -// a text field. -exports.testSelectionContextMatchInTextField = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let item = loader.cm.Item({ - label: "item", - context: loader.cm.SelectionContext() - }); - - test.withTestDoc(function (window, doc) { - let textfield = doc.getElementById("textfield"); - textfield.setSelectionRange(0, textfield.value.length); - test.showMenu(textfield, function (popup) { - test.checkMenu([item], [], []); - test.done(); - }); - }); -}; - - -// Selection contexts should not cause items to appear when a selection does -// not exist in a text field. -exports.testSelectionContextNoMatchInTextField = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let item = loader.cm.Item({ - label: "item", - context: loader.cm.SelectionContext() - }); - - test.withTestDoc(function (window, doc) { - let textfield = doc.getElementById("textfield"); - textfield.setSelectionRange(0, 0); - test.showMenu(textfield, function (popup) { - test.checkMenu([item], [item], []); - test.done(); - }); - }); -}; - - -// Selection contexts should not cause items to appear when a selection does -// not exist. -exports.testSelectionContextNoMatch = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let item = loader.cm.Item({ - label: "item", - context: loader.cm.SelectionContext() - }); - - test.showMenu(null, function (popup) { - test.checkMenu([item], [item], []); - test.done(); - }); -}; - - -// Selection contexts should cause items to appear when a selection exists even -// for newly opened pages -exports.testSelectionContextInNewTab = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let item = loader.cm.Item({ - label: "item", - context: loader.cm.SelectionContext() - }); - - test.withTestDoc(function (window, doc) { - let link = doc.getElementById("targetlink"); - link.click(); - - test.delayedEventListener(this.tabBrowser, "load", function () { - let browser = test.tabBrowser.selectedBrowser; - let window = browser.contentWindow; - let doc = browser.contentDocument; - window.getSelection().selectAllChildren(doc.body); - - test.showMenu(null, function (popup) { - test.checkMenu([item], [], []); - popup.hidePopup(); - - test.tabBrowser.removeTab(test.tabBrowser.selectedTab); - test.tabBrowser.selectedTab = test.tab; - - test.showMenu(null, function (popup) { - test.checkMenu([item], [item], []); - test.done(); - }); - }); - }, true); - }); -}; - - -// Selection contexts should work when right clicking a form button -exports.testSelectionContextButtonMatch = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let item = loader.cm.Item({ - label: "item", - context: loader.cm.SelectionContext() - }); - - test.withTestDoc(function (window, doc) { - window.getSelection().selectAllChildren(doc.body); - let button = doc.getElementById("button"); - test.showMenu(button, function (popup) { - test.checkMenu([item], [], []); - test.done(); - }); - }); -}; - - -//Selection contexts should work when right clicking a form button -exports.testSelectionContextButtonNoMatch = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let item = loader.cm.Item({ - label: "item", - context: loader.cm.SelectionContext() - }); - - test.withTestDoc(function (window, doc) { - let button = doc.getElementById("button"); - test.showMenu(button, function (popup) { - test.checkMenu([item], [item], []); - test.done(); - }); - }); -}; - - -// URL contexts should cause items to appear on pages that match. -exports.testURLContextMatch = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let items = [ - loader.cm.Item({ - label: "item 0", - context: loader.cm.URLContext(TEST_DOC_URL) - }), - loader.cm.Item({ - label: "item 1", - context: loader.cm.URLContext([TEST_DOC_URL, "*.bogus.com"]) - }), - loader.cm.Item({ - label: "item 2", - context: loader.cm.URLContext([new RegExp(".*\\.html")]) - }) - ]; - - test.withTestDoc(function (window, doc) { - test.showMenu(null, function (popup) { - test.checkMenu(items, [], []); - test.done(); - }); - }); -}; - - -// URL contexts should not cause items to appear on pages that do not match. -exports.testURLContextNoMatch = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let items = [ - loader.cm.Item({ - label: "item 0", - context: loader.cm.URLContext("*.bogus.com") - }), - loader.cm.Item({ - label: "item 1", - context: loader.cm.URLContext(["*.bogus.com", "*.gnarly.com"]) - }), - loader.cm.Item({ - label: "item 2", - context: loader.cm.URLContext([new RegExp(".*\\.js")]) - }) - ]; - - test.withTestDoc(function (window, doc) { - test.showMenu(null, function (popup) { - test.checkMenu(items, items, []); - test.done(); - }); - }); -}; - - -// Removing a non-matching URL context after its item is created and the page is -// loaded should cause the item's content script to be evaluated when the -// context menu is next opened. -exports.testURLContextRemove = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let shouldBeEvaled = false; - let context = loader.cm.URLContext("*.bogus.com"); - let item = loader.cm.Item({ - label: "item", - context: context, - contentScript: 'self.postMessage("ok"); self.on("context", function () true);', - onMessage: function (msg) { - assert.ok(shouldBeEvaled, - "content script should be evaluated when expected"); - assert.equal(msg, "ok", "Should have received the right message"); - shouldBeEvaled = false; - } - }); - - test.withTestDoc(function (window, doc) { - test.showMenu(null, function (popup) { - test.checkMenu([item], [item], []); - - item.context.remove(context); - - shouldBeEvaled = true; - - test.hideMenu(function () { - test.showMenu(null, function (popup) { - test.checkMenu([item], [], []); - - assert.ok(!shouldBeEvaled, - "content script should have been evaluated"); - - test.hideMenu(function () { - // Shouldn't get evaluated again - test.showMenu(null, function (popup) { - test.checkMenu([item], [], []); - test.done(); - }); - }); - }); - }); - }); - }); -}; - -// Loading a new page in the same tab should correctly start a new worker for -// any content scripts -exports.testPageReload = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let item = loader.cm.Item({ - label: "Item", - contentScript: "var doc = document; self.on('context', function(node) doc.body.getAttribute('showItem') == 'true');" - }); - - test.withTestDoc(function (window, doc) { - // Set a flag on the document that the item uses - doc.body.setAttribute("showItem", "true"); - - test.showMenu(null, function (popup) { - // With the attribute true the item should be visible in the menu - test.checkMenu([item], [], []); - test.hideMenu(function() { - let browser = this.tabBrowser.getBrowserForTab(this.tab) - test.delayedEventListener(browser, "load", function() { - test.delayedEventListener(browser, "load", function() { - window = browser.contentWindow; - doc = window.document; - - // Set a flag on the document that the item uses - doc.body.setAttribute("showItem", "false"); - - test.showMenu(null, function (popup) { - // In the new document with the attribute false the item should be - // hidden, but if the contentScript hasn't been reloaded it will - // still see the old value - test.checkMenu([item], [item], []); - - test.done(); - }); - }, true); - browser.loadURI(TEST_DOC_URL, null, null); - }, true); - // Required to make sure we load a new page in history rather than - // just reloading the current page which would unload it - browser.loadURI("about:blank", null, null); - }); - }); - }); -}; - -// Closing a page after it's been used with a worker should cause the worker -// to be destroyed -/*exports.testWorkerDestroy = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let loadExpected = false; - - let item = loader.cm.Item({ - label: "item", - contentScript: 'self.postMessage("loaded"); self.on("detach", function () { console.log("saw detach"); self.postMessage("detach") });', - onMessage: function (msg) { - console.log("Saw " + msg) - switch (msg) { - case "loaded": - assert.ok(loadExpected, "Should have seen the load event at the right time"); - loadExpected = false; - break; - case "detach": - test.done(); - break; - } - } - }); - - test.withTestDoc(function (window, doc) { - loadExpected = true; - test.showMenu(null, function (popup) { - assert.ok(!loadExpected, "Should have seen a message"); - - test.checkMenu([item], [], []); - - test.closeTab(); - }); - }); -};*/ - - -// Content contexts that return true should cause their items to be present -// in the menu. -exports.testContentContextMatch = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let item = new loader.cm.Item({ - label: "item", - contentScript: 'self.on("context", function () true);' - }); - - test.showMenu(null, function (popup) { - test.checkMenu([item], [], []); - test.done(); - }); -}; - - -// Content contexts that return false should cause their items to be absent -// from the menu. -exports.testContentContextNoMatch = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let item = new loader.cm.Item({ - label: "item", - contentScript: 'self.on("context", function () false);' - }); - - test.showMenu(null, function (popup) { - test.checkMenu([item], [item], []); - test.done(); - }); -}; - - -// Content contexts that return undefined should cause their items to be absent -// from the menu. -exports.testContentContextUndefined = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let item = new loader.cm.Item({ - label: "item", - contentScript: 'self.on("context", function () {});' - }); - - test.showMenu(null, function (popup) { - test.checkMenu([item], [item], []); - test.done(); - }); -}; - - -// Content contexts that return an empty string should cause their items to be -// absent from the menu and shouldn't wipe the label -exports.testContentContextEmptyString = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let item = new loader.cm.Item({ - label: "item", - contentScript: 'self.on("context", function () "");' - }); - - test.showMenu(null, function (popup) { - test.checkMenu([item], [item], []); - assert.equal(item.label, "item", "Label should still be correct"); - test.done(); - }); -}; - - -// If any content contexts returns true then their items should be present in -// the menu. -exports.testMultipleContentContextMatch1 = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let item = new loader.cm.Item({ - label: "item", - contentScript: 'self.on("context", function () true); ' + - 'self.on("context", function () false);', - onMessage: function() { - test.fail("Should not have called the second context listener"); - } - }); - - test.showMenu(null, function (popup) { - test.checkMenu([item], [], []); - test.done(); - }); -}; - - -// If any content contexts returns true then their items should be present in -// the menu. -exports.testMultipleContentContextMatch2 = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let item = new loader.cm.Item({ - label: "item", - contentScript: 'self.on("context", function () false); ' + - 'self.on("context", function () true);' - }); - - test.showMenu(null, function (popup) { - test.checkMenu([item], [], []); - test.done(); - }); -}; - - -// If any content contexts returns a string then their items should be present -// in the menu. -exports.testMultipleContentContextString1 = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let item = new loader.cm.Item({ - label: "item", - contentScript: 'self.on("context", function () "new label"); ' + - 'self.on("context", function () false);' - }); - - test.showMenu(null, function (popup) { - test.checkMenu([item], [], []); - assert.equal(item.label, "new label", "Label should have changed"); - test.done(); - }); -}; - - -// If any content contexts returns a string then their items should be present -// in the menu. -exports.testMultipleContentContextString2 = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let item = new loader.cm.Item({ - label: "item", - contentScript: 'self.on("context", function () false); ' + - 'self.on("context", function () "new label");' - }); - - test.showMenu(null, function (popup) { - test.checkMenu([item], [], []); - assert.equal(item.label, "new label", "Label should have changed"); - test.done(); - }); -}; - - -// If many content contexts returns a string then the first should take effect -exports.testMultipleContentContextString3 = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let item = new loader.cm.Item({ - label: "item", - contentScript: 'self.on("context", function () "new label 1"); ' + - 'self.on("context", function () "new label 2");' - }); - - test.showMenu(null, function (popup) { - test.checkMenu([item], [], []); - assert.equal(item.label, "new label 1", "Label should have changed"); - test.done(); - }); -}; - - -// Content contexts that return true should cause their items to be present -// in the menu when context clicking an active element. -exports.testContentContextMatchActiveElement = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let items = [ - new loader.cm.Item({ - label: "item 1", - contentScript: 'self.on("context", function () true);' - }), - new loader.cm.Item({ - label: "item 2", - context: undefined, - contentScript: 'self.on("context", function () true);' - }), - // These items will always be hidden by the declarative usage of PageContext - new loader.cm.Item({ - label: "item 3", - context: loader.cm.PageContext(), - contentScript: 'self.on("context", function () true);' - }), - new loader.cm.Item({ - label: "item 4", - context: [loader.cm.PageContext()], - contentScript: 'self.on("context", function () true);' - }) - ]; - - test.withTestDoc(function (window, doc) { - test.showMenu(doc.getElementById("image"), function (popup) { - test.checkMenu(items, [items[2], items[3]], []); - test.done(); - }); - }); -}; - - -// Content contexts that return false should cause their items to be absent -// from the menu when context clicking an active element. -exports.testContentContextNoMatchActiveElement = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let items = [ - new loader.cm.Item({ - label: "item 1", - contentScript: 'self.on("context", function () false);' - }), - new loader.cm.Item({ - label: "item 2", - context: undefined, - contentScript: 'self.on("context", function () false);' - }), - // These items will always be hidden by the declarative usage of PageContext - new loader.cm.Item({ - label: "item 3", - context: loader.cm.PageContext(), - contentScript: 'self.on("context", function () false);' - }), - new loader.cm.Item({ - label: "item 4", - context: [loader.cm.PageContext()], - contentScript: 'self.on("context", function () false);' - }) - ]; - - test.withTestDoc(function (window, doc) { - test.showMenu(doc.getElementById("image"), function (popup) { - test.checkMenu(items, items, []); - test.done(); - }); - }); -}; - - -// Content contexts that return undefined should cause their items to be absent -// from the menu when context clicking an active element. -exports.testContentContextNoMatchActiveElement = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let items = [ - new loader.cm.Item({ - label: "item 1", - contentScript: 'self.on("context", function () {});' - }), - new loader.cm.Item({ - label: "item 2", - context: undefined, - contentScript: 'self.on("context", function () {});' - }), - // These items will always be hidden by the declarative usage of PageContext - new loader.cm.Item({ - label: "item 3", - context: loader.cm.PageContext(), - contentScript: 'self.on("context", function () {});' - }), - new loader.cm.Item({ - label: "item 4", - context: [loader.cm.PageContext()], - contentScript: 'self.on("context", function () {});' - }) - ]; - - test.withTestDoc(function (window, doc) { - test.showMenu(doc.getElementById("image"), function (popup) { - test.checkMenu(items, items, []); - test.done(); - }); - }); -}; - - -// Content contexts that return a string should cause their items to be present -// in the menu and the items' labels to be updated. -exports.testContentContextMatchString = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let item = new loader.cm.Item({ - label: "first label", - contentScript: 'self.on("context", function () "second label");' - }); - - test.showMenu(null, function (popup) { - test.checkMenu([item], [], []); - assert.equal(item.label, "second label", - "item's label should be updated"); - test.done(); - }); -}; - - -// Ensure that contentScriptFile is working correctly -exports.testContentScriptFile = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - // Reject remote files - assert.throws(function() { - new loader.cm.Item({ - label: "item", - contentScriptFile: "http://mozilla.com/context-menu.js" - }); - }, - new RegExp("The 'contentScriptFile' option must be a local file URL " + - "or an array of local file URLs."), - "Item throws when contentScriptFile is a remote URL"); - - // But accept files from data folder - let item = new loader.cm.Item({ - label: "item", - contentScriptFile: data.url("test-context-menu.js") - }); - - test.showMenu(null, function (popup) { - test.checkMenu([item], [], []); - test.done(); - }); -}; - - -// The args passed to context listeners should be correct. -exports.testContentContextArgs = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - let callbacks = 0; - - let item = new loader.cm.Item({ - label: "item", - contentScript: 'self.on("context", function (node) {' + - ' self.postMessage(node.tagName);' + - ' return false;' + - '});', - onMessage: function (tagName) { - assert.equal(tagName, "HTML", "node should be an HTML element"); - if (++callbacks == 2) test.done(); - } - }); - - test.showMenu(null, function () { - if (++callbacks == 2) test.done(); - }); -}; - -// Multiple contexts imply intersection, not union, and content context -// listeners should not be called if all declarative contexts are not current. -exports.testMultipleContexts = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let item = new loader.cm.Item({ - label: "item", - context: [loader.cm.SelectorContext("a[href]"), loader.cm.PageContext()], - contentScript: 'self.on("context", function () self.postMessage());', - onMessage: function () { - test.fail("Context listener should not be called"); - } - }); - - test.withTestDoc(function (window, doc) { - test.showMenu(doc.getElementById("span-link"), function (popup) { - test.checkMenu([item], [item], []); - test.done(); - }); - }); -}; - -// Once a context is removed, it should no longer cause its item to appear. -exports.testRemoveContext = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let ctxt = loader.cm.SelectorContext("img"); - let item = new loader.cm.Item({ - label: "item", - context: ctxt - }); - - test.withTestDoc(function (window, doc) { - test.showMenu(doc.getElementById("image"), function (popup) { - - // The item should be present at first. - test.checkMenu([item], [], []); - popup.hidePopup(); - - // Remove the img context and check again. - item.context.remove(ctxt); - test.showMenu(doc.getElementById("image"), function (popup) { - test.checkMenu([item], [item], []); - test.done(); - }); - }); - }); -}; - - -// Lots of items should overflow into the overflow submenu. -exports.testOverflow = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let items = []; - for (let i = 0; i < OVERFLOW_THRESH_DEFAULT + 1; i++) { - let item = new loader.cm.Item({ label: "item " + i }); - items.push(item); - } - - test.showMenu(null, function (popup) { - test.checkMenu(items, [], []); - test.done(); - }); -}; - - -// Module unload should cause all items to be removed. -exports.testUnload = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let item = new loader.cm.Item({ label: "item" }); - - test.showMenu(null, function (popup) { - - // The menu should contain the item. - test.checkMenu([item], [], []); - popup.hidePopup(); - - // Unload the module. - loader.unload(); - test.showMenu(null, function (popup) { - - // The item should be removed from the menu. - test.checkMenu([item], [], [item]); - test.done(); - }); - }); -}; - - -// Using multiple module instances to add items without causing overflow should -// work OK. Assumes OVERFLOW_THRESH_DEFAULT >= 2. -exports.testMultipleModulesAdd = function (assert, done) { - let test = new TestHelper(assert, done); - let loader0 = test.newLoader(); - let loader1 = test.newLoader(); - - // Use each module to add an item, then unload each module in turn. - let item0 = new loader0.cm.Item({ label: "item 0" }); - let item1 = new loader1.cm.Item({ label: "item 1" }); - - test.showMenu(null, function (popup) { - - // The menu should contain both items. - test.checkMenu([item0, item1], [], []); - popup.hidePopup(); - - // Unload the first module. - loader0.unload(); - test.showMenu(null, function (popup) { - - // The first item should be removed from the menu. - test.checkMenu([item0, item1], [], [item0]); - popup.hidePopup(); - - // Unload the second module. - loader1.unload(); - test.showMenu(null, function (popup) { - - // Both items should be removed from the menu. - test.checkMenu([item0, item1], [], [item0, item1]); - test.done(); - }); - }); - }); -}; - - -// Using multiple module instances to add items causing overflow should work OK. -exports.testMultipleModulesAddOverflow = function (assert, done) { - let test = new TestHelper(assert, done); - let loader0 = test.newLoader(); - let loader1 = test.newLoader(); - - // Use module 0 to add OVERFLOW_THRESH_DEFAULT items. - let items0 = []; - for (let i = 0; i < OVERFLOW_THRESH_DEFAULT; i++) { - let item = new loader0.cm.Item({ label: "item 0 " + i }); - items0.push(item); - } - - // Use module 1 to add one item. - let item1 = new loader1.cm.Item({ label: "item 1" }); - - let allItems = items0.concat(item1); - - test.showMenu(null, function (popup) { - - // The menu should contain all items in overflow. - test.checkMenu(allItems, [], []); - popup.hidePopup(); - - // Unload the first module. - loader0.unload(); - test.showMenu(null, function (popup) { - - // The first items should be removed from the menu, which should not - // overflow. - test.checkMenu(allItems, [], items0); - popup.hidePopup(); - - // Unload the second module. - loader1.unload(); - test.showMenu(null, function (popup) { - - // All items should be removed from the menu. - test.checkMenu(allItems, [], allItems); - test.done(); - }); - }); - }); -}; - - -// Using multiple module instances to modify the menu without causing overflow -// should work OK. This test creates two loaders and: -// loader0 create item -> loader1 create item -> loader0.unload -> -// loader1.unload -exports.testMultipleModulesDiffContexts1 = function (assert, done) { - let test = new TestHelper(assert, done); - let loader0 = test.newLoader(); - let loader1 = test.newLoader(); - - let item0 = new loader0.cm.Item({ - label: "item 0", - context: loader0.cm.SelectorContext("img") - }); - - let item1 = new loader1.cm.Item({ label: "item 1" }); - - test.showMenu(null, function (popup) { - - // The menu should contain item1. - test.checkMenu([item0, item1], [item0], []); - popup.hidePopup(); - - // Unload module 0. - loader0.unload(); - test.showMenu(null, function (popup) { - - // item0 should be removed from the menu. - test.checkMenu([item0, item1], [], [item0]); - popup.hidePopup(); - - // Unload module 1. - loader1.unload(); - test.showMenu(null, function (popup) { - - // Both items should be removed from the menu. - test.checkMenu([item0, item1], [], [item0, item1]); - test.done(); - }); - }); - }); -}; - - -// Using multiple module instances to modify the menu without causing overflow -// should work OK. This test creates two loaders and: -// loader1 create item -> loader0 create item -> loader0.unload -> -// loader1.unload -exports.testMultipleModulesDiffContexts2 = function (assert, done) { - let test = new TestHelper(assert, done); - let loader0 = test.newLoader(); - let loader1 = test.newLoader(); - - let item1 = new loader1.cm.Item({ label: "item 1" }); - - let item0 = new loader0.cm.Item({ - label: "item 0", - context: loader0.cm.SelectorContext("img") - }); - - test.showMenu(null, function (popup) { - - // The menu should contain item1. - test.checkMenu([item0, item1], [item0], []); - popup.hidePopup(); - - // Unload module 0. - loader0.unload(); - test.showMenu(null, function (popup) { - - // item0 should be removed from the menu. - test.checkMenu([item0, item1], [], [item0]); - popup.hidePopup(); - - // Unload module 1. - loader1.unload(); - test.showMenu(null, function (popup) { - - // Both items should be removed from the menu. - test.checkMenu([item0, item1], [], [item0, item1]); - test.done(); - }); - }); - }); -}; - - -// Using multiple module instances to modify the menu without causing overflow -// should work OK. This test creates two loaders and: -// loader0 create item -> loader1 create item -> loader1.unload -> -// loader0.unload -exports.testMultipleModulesDiffContexts3 = function (assert, done) { - let test = new TestHelper(assert, done); - let loader0 = test.newLoader(); - let loader1 = test.newLoader(); - - let item0 = new loader0.cm.Item({ - label: "item 0", - context: loader0.cm.SelectorContext("img") - }); - - let item1 = new loader1.cm.Item({ label: "item 1" }); - - test.showMenu(null, function (popup) { - - // The menu should contain item1. - test.checkMenu([item0, item1], [item0], []); - popup.hidePopup(); - - // Unload module 1. - loader1.unload(); - test.showMenu(null, function (popup) { - - // item1 should be removed from the menu. - test.checkMenu([item0, item1], [item0], [item1]); - popup.hidePopup(); - - // Unload module 0. - loader0.unload(); - test.showMenu(null, function (popup) { - - // Both items should be removed from the menu. - test.checkMenu([item0, item1], [], [item0, item1]); - test.done(); - }); - }); - }); -}; - - -// Using multiple module instances to modify the menu without causing overflow -// should work OK. This test creates two loaders and: -// loader1 create item -> loader0 create item -> loader1.unload -> -// loader0.unload -exports.testMultipleModulesDiffContexts4 = function (assert, done) { - let test = new TestHelper(assert, done); - let loader0 = test.newLoader(); - let loader1 = test.newLoader(); - - let item1 = new loader1.cm.Item({ label: "item 1" }); - - let item0 = new loader0.cm.Item({ - label: "item 0", - context: loader0.cm.SelectorContext("img") - }); - - test.showMenu(null, function (popup) { - - // The menu should contain item1. - test.checkMenu([item0, item1], [item0], []); - popup.hidePopup(); - - // Unload module 1. - loader1.unload(); - test.showMenu(null, function (popup) { - - // item1 should be removed from the menu. - test.checkMenu([item0, item1], [item0], [item1]); - popup.hidePopup(); - - // Unload module 0. - loader0.unload(); - test.showMenu(null, function (popup) { - - // Both items should be removed from the menu. - test.checkMenu([item0, item1], [], [item0, item1]); - test.done(); - }); - }); - }); -}; - - -// Test interactions between a loaded module, unloading another module, and the -// menu separator and overflow submenu. -exports.testMultipleModulesAddRemove = function (assert, done) { - let test = new TestHelper(assert, done); - let loader0 = test.newLoader(); - let loader1 = test.newLoader(); - - let item = new loader0.cm.Item({ label: "item" }); - - test.showMenu(null, function (popup) { - - // The menu should contain the item. - test.checkMenu([item], [], []); - popup.hidePopup(); - - // Remove the item. - item.destroy(); - test.showMenu(null, function (popup) { - - // The item should be removed from the menu. - test.checkMenu([item], [], [item]); - popup.hidePopup(); - - // Unload module 1. - loader1.unload(); - test.showMenu(null, function (popup) { - - // There shouldn't be any errors involving the menu separator or - // overflow submenu. - test.checkMenu([item], [], [item]); - test.done(); - }); - }); - }); -}; - - -// Checks that the order of menu items is correct when adding/removing across -// multiple modules. All items from a single module should remain in a group -exports.testMultipleModulesOrder = function (assert, done) { - let test = new TestHelper(assert, done); - let loader0 = test.newLoader(); - let loader1 = test.newLoader(); - - // Use each module to add an item, then unload each module in turn. - let item0 = new loader0.cm.Item({ label: "item 0" }); - let item1 = new loader1.cm.Item({ label: "item 1" }); - - test.showMenu(null, function (popup) { - - // The menu should contain both items. - test.checkMenu([item0, item1], [], []); - popup.hidePopup(); - - let item2 = new loader0.cm.Item({ label: "item 2" }); - - test.showMenu(null, function (popup) { - - // The new item should be grouped with the same items from loader0. - test.checkMenu([item0, item2, item1], [], []); - popup.hidePopup(); - - let item3 = new loader1.cm.Item({ label: "item 3" }); - - test.showMenu(null, function (popup) { - - // Same again - test.checkMenu([item0, item2, item1, item3], [], []); - test.done(); - }); - }); - }); -}; - - -// Checks that the order of menu items is correct when adding/removing across -// multiple modules when overflowing. All items from a single module should -// remain in a group -exports.testMultipleModulesOrderOverflow = function (assert, done) { - let test = new TestHelper(assert, done); - let loader0 = test.newLoader(); - let loader1 = test.newLoader(); - - let prefs = loader0.loader.require("sdk/preferences/service"); - prefs.set(OVERFLOW_THRESH_PREF, 0); - - // Use each module to add an item, then unload each module in turn. - let item0 = new loader0.cm.Item({ label: "item 0" }); - let item1 = new loader1.cm.Item({ label: "item 1" }); - - test.showMenu(null, function (popup) { - - // The menu should contain both items. - test.checkMenu([item0, item1], [], []); - popup.hidePopup(); - - let item2 = new loader0.cm.Item({ label: "item 2" }); - - test.showMenu(null, function (popup) { - - // The new item should be grouped with the same items from loader0. - test.checkMenu([item0, item2, item1], [], []); - popup.hidePopup(); - - let item3 = new loader1.cm.Item({ label: "item 3" }); - - test.showMenu(null, function (popup) { - - // Same again - test.checkMenu([item0, item2, item1, item3], [], []); - test.done(); - }); - }); - }); -}; - - -// Checks that if a module's items are all hidden then the overflow menu doesn't -// get hidden -exports.testMultipleModulesOverflowHidden = function (assert, done) { - let test = new TestHelper(assert, done); - let loader0 = test.newLoader(); - let loader1 = test.newLoader(); - - let prefs = loader0.loader.require("sdk/preferences/service"); - prefs.set(OVERFLOW_THRESH_PREF, 0); - - // Use each module to add an item, then unload each module in turn. - let item0 = new loader0.cm.Item({ label: "item 0" }); - let item1 = new loader1.cm.Item({ - label: "item 1", - context: loader1.cm.SelectorContext("a") - }); - - test.showMenu(null, function (popup) { - // One should be hidden - test.checkMenu([item0, item1], [item1], []); - test.done(); - }); -}; - - -// Checks that if a module's items are all hidden then the overflow menu doesn't -// get hidden (reverse order to above) -exports.testMultipleModulesOverflowHidden2 = function (assert, done) { - let test = new TestHelper(assert, done); - let loader0 = test.newLoader(); - let loader1 = test.newLoader(); - - let prefs = loader0.loader.require("sdk/preferences/service"); - prefs.set(OVERFLOW_THRESH_PREF, 0); - - // Use each module to add an item, then unload each module in turn. - let item0 = new loader0.cm.Item({ - label: "item 0", - context: loader0.cm.SelectorContext("a") - }); - let item1 = new loader1.cm.Item({ label: "item 1" }); - - test.showMenu(null, function (popup) { - // One should be hidden - test.checkMenu([item0, item1], [item0], []); - test.done(); - }); -}; - - -// Checks that we don't overflow if there are more items than the overflow -// threshold but not all of them are visible -exports.testOverflowIgnoresHidden = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let prefs = loader.loader.require("sdk/preferences/service"); - prefs.set(OVERFLOW_THRESH_PREF, 2); - - let allItems = [ - new loader.cm.Item({ - label: "item 0" - }), - new loader.cm.Item({ - label: "item 1" - }), - new loader.cm.Item({ - label: "item 2", - context: loader.cm.SelectorContext("a") - }) - ]; - - test.showMenu(null, function (popup) { - // One should be hidden - test.checkMenu(allItems, [allItems[2]], []); - test.done(); - }); -}; - - -// Checks that we don't overflow if there are more items than the overflow -// threshold but not all of them are visible -exports.testOverflowIgnoresHiddenMultipleModules1 = function (assert, done) { - let test = new TestHelper(assert, done); - let loader0 = test.newLoader(); - let loader1 = test.newLoader(); - - let prefs = loader0.loader.require("sdk/preferences/service"); - prefs.set(OVERFLOW_THRESH_PREF, 2); - - let allItems = [ - new loader0.cm.Item({ - label: "item 0" - }), - new loader0.cm.Item({ - label: "item 1" - }), - new loader1.cm.Item({ - label: "item 2", - context: loader1.cm.SelectorContext("a") - }), - new loader1.cm.Item({ - label: "item 3", - context: loader1.cm.SelectorContext("a") - }) - ]; - - test.showMenu(null, function (popup) { - // One should be hidden - test.checkMenu(allItems, [allItems[2], allItems[3]], []); - test.done(); - }); -}; - - -// Checks that we don't overflow if there are more items than the overflow -// threshold but not all of them are visible -exports.testOverflowIgnoresHiddenMultipleModules2 = function (assert, done) { - let test = new TestHelper(assert, done); - let loader0 = test.newLoader(); - let loader1 = test.newLoader(); - - let prefs = loader0.loader.require("sdk/preferences/service"); - prefs.set(OVERFLOW_THRESH_PREF, 2); - - let allItems = [ - new loader0.cm.Item({ - label: "item 0" - }), - new loader0.cm.Item({ - label: "item 1", - context: loader0.cm.SelectorContext("a") - }), - new loader1.cm.Item({ - label: "item 2" - }), - new loader1.cm.Item({ - label: "item 3", - context: loader1.cm.SelectorContext("a") - }) - ]; - - test.showMenu(null, function (popup) { - // One should be hidden - test.checkMenu(allItems, [allItems[1], allItems[3]], []); - test.done(); - }); -}; - - -// Checks that we don't overflow if there are more items than the overflow -// threshold but not all of them are visible -exports.testOverflowIgnoresHiddenMultipleModules3 = function (assert, done) { - let test = new TestHelper(assert, done); - let loader0 = test.newLoader(); - let loader1 = test.newLoader(); - - let prefs = loader0.loader.require("sdk/preferences/service"); - prefs.set(OVERFLOW_THRESH_PREF, 2); - - let allItems = [ - new loader0.cm.Item({ - label: "item 0", - context: loader0.cm.SelectorContext("a") - }), - new loader0.cm.Item({ - label: "item 1", - context: loader0.cm.SelectorContext("a") - }), - new loader1.cm.Item({ - label: "item 2" - }), - new loader1.cm.Item({ - label: "item 3" - }) - ]; - - test.showMenu(null, function (popup) { - // One should be hidden - test.checkMenu(allItems, [allItems[0], allItems[1]], []); - test.done(); - }); -}; - - -// Tests that we transition between overflowing to non-overflowing to no items -// and back again -exports.testOverflowTransition = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let prefs = loader.loader.require("sdk/preferences/service"); - prefs.set(OVERFLOW_THRESH_PREF, 2); - - let pItems = [ - new loader.cm.Item({ - label: "item 0", - context: loader.cm.SelectorContext("p") - }), - new loader.cm.Item({ - label: "item 1", - context: loader.cm.SelectorContext("p") - }) - ]; - - let aItems = [ - new loader.cm.Item({ - label: "item 2", - context: loader.cm.SelectorContext("a") - }), - new loader.cm.Item({ - label: "item 3", - context: loader.cm.SelectorContext("a") - }) - ]; - - let allItems = pItems.concat(aItems); - - test.withTestDoc(function (window, doc) { - test.showMenu(doc.getElementById("link"), function (popup) { - // The menu should contain all items and will overflow - test.checkMenu(allItems, [], []); - popup.hidePopup(); - - test.showMenu(doc.getElementById("text"), function (popup) { - // Only contains hald the items and will not overflow - test.checkMenu(allItems, aItems, []); - popup.hidePopup(); - - test.showMenu(null, function (popup) { - // None of the items will be visible - test.checkMenu(allItems, allItems, []); - popup.hidePopup(); - - test.showMenu(doc.getElementById("text"), function (popup) { - // Only contains hald the items and will not overflow - test.checkMenu(allItems, aItems, []); - popup.hidePopup(); - - test.showMenu(doc.getElementById("link"), function (popup) { - // The menu should contain all items and will overflow - test.checkMenu(allItems, [], []); - popup.hidePopup(); - - test.showMenu(null, function (popup) { - // None of the items will be visible - test.checkMenu(allItems, allItems, []); - popup.hidePopup(); - - test.showMenu(doc.getElementById("link"), function (popup) { - // The menu should contain all items and will overflow - test.checkMenu(allItems, [], []); - test.done(); - }); - }); - }); - }); - }); - }); - }); - }); -}; - - -// An item's command listener should work. -exports.testItemCommand = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let item = new loader.cm.Item({ - label: "item", - data: "item data", - contentScript: 'self.on("click", function (node, data) {' + - ' self.postMessage({' + - ' tagName: node.tagName,' + - ' data: data' + - ' });' + - '});', - onMessage: function (data) { - assert.equal(this, item, "`this` inside onMessage should be item"); - assert.equal(data.tagName, "HTML", "node should be an HTML element"); - assert.equal(data.data, item.data, "data should be item data"); - test.done(); - } - }); - - test.showMenu(null, function (popup) { - test.checkMenu([item], [], []); - let elt = test.getItemElt(popup, item); - - // create a command event - let evt = elt.ownerDocument.createEvent('Event'); - evt.initEvent('command', true, true); - elt.dispatchEvent(evt); - }); -}; - - -// A menu's click listener should work and receive bubbling 'command' events from -// sub-items appropriately. This also tests menus and ensures that when a CSS -// selector context matches the clicked node's ancestor, the matching ancestor -// is passed to listeners as the clicked node. -exports.testMenuCommand = function (assert, done) { - // Create a top-level menu, submenu, and item, like this: - // topMenu -> submenu -> item - // Click the item and make sure the click bubbles. - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let item = new loader.cm.Item({ - label: "submenu item", - data: "submenu item data", - context: loader.cm.SelectorContext("a"), - }); - - let submenu = new loader.cm.Menu({ - label: "submenu", - context: loader.cm.SelectorContext("a"), - items: [item] - }); - - let topMenu = new loader.cm.Menu({ - label: "top menu", - contentScript: 'self.on("click", function (node, data) {' + - ' self.postMessage({' + - ' tagName: node.tagName,' + - ' data: data' + - ' });' + - '});', - onMessage: function (data) { - assert.equal(this, topMenu, "`this` inside top menu should be menu"); - assert.equal(data.tagName, "A", "Clicked node should be anchor"); - assert.equal(data.data, item.data, - "Clicked item data should be correct"); - test.done(); - }, - items: [submenu], - context: loader.cm.SelectorContext("a") - }); - - test.withTestDoc(function (window, doc) { - test.showMenu(doc.getElementById("span-link"), function (popup) { - test.checkMenu([topMenu], [], []); - let topMenuElt = test.getItemElt(popup, topMenu); - let topMenuPopup = topMenuElt.firstChild; - let submenuElt = test.getItemElt(topMenuPopup, submenu); - let submenuPopup = submenuElt.firstChild; - let itemElt = test.getItemElt(submenuPopup, item); - - // create a command event - let evt = itemElt.ownerDocument.createEvent('Event'); - evt.initEvent('command', true, true); - itemElt.dispatchEvent(evt); - }); - }); -}; - - -// Click listeners should work when multiple modules are loaded. -exports.testItemCommandMultipleModules = function (assert, done) { - let test = new TestHelper(assert, done); - let loader0 = test.newLoader(); - let loader1 = test.newLoader(); - - let item0 = loader0.cm.Item({ - label: "loader 0 item", - contentScript: 'self.on("click", self.postMessage);', - onMessage: function () { - test.fail("loader 0 item should not emit click event"); - } - }); - let item1 = loader1.cm.Item({ - label: "loader 1 item", - contentScript: 'self.on("click", self.postMessage);', - onMessage: function () { - test.pass("loader 1 item clicked as expected"); - test.done(); - } - }); - - test.showMenu(null, function (popup) { - test.checkMenu([item0, item1], [], []); - let item1Elt = test.getItemElt(popup, item1); - - // create a command event - let evt = item1Elt.ownerDocument.createEvent('Event'); - evt.initEvent('command', true, true); - item1Elt.dispatchEvent(evt); - }); -}; - - - - -// An item's click listener should work. -exports.testItemClick = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let item = new loader.cm.Item({ - label: "item", - data: "item data", - contentScript: 'self.on("click", function (node, data) {' + - ' self.postMessage({' + - ' tagName: node.tagName,' + - ' data: data' + - ' });' + - '});', - onMessage: function (data) { - assert.equal(this, item, "`this` inside onMessage should be item"); - assert.equal(data.tagName, "HTML", "node should be an HTML element"); - assert.equal(data.data, item.data, "data should be item data"); - test.done(); - } - }); - - test.showMenu(null, function (popup) { - test.checkMenu([item], [], []); - let elt = test.getItemElt(popup, item); - elt.click(); - }); -}; - - -// A menu's click listener should work and receive bubbling clicks from -// sub-items appropriately. This also tests menus and ensures that when a CSS -// selector context matches the clicked node's ancestor, the matching ancestor -// is passed to listeners as the clicked node. -exports.testMenuClick = function (assert, done) { - // Create a top-level menu, submenu, and item, like this: - // topMenu -> submenu -> item - // Click the item and make sure the click bubbles. - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let item = new loader.cm.Item({ - label: "submenu item", - data: "submenu item data", - context: loader.cm.SelectorContext("a"), - }); - - let submenu = new loader.cm.Menu({ - label: "submenu", - context: loader.cm.SelectorContext("a"), - items: [item] - }); - - let topMenu = new loader.cm.Menu({ - label: "top menu", - contentScript: 'self.on("click", function (node, data) {' + - ' self.postMessage({' + - ' tagName: node.tagName,' + - ' data: data' + - ' });' + - '});', - onMessage: function (data) { - assert.equal(this, topMenu, "`this` inside top menu should be menu"); - assert.equal(data.tagName, "A", "Clicked node should be anchor"); - assert.equal(data.data, item.data, - "Clicked item data should be correct"); - test.done(); - }, - items: [submenu], - context: loader.cm.SelectorContext("a") - }); - - test.withTestDoc(function (window, doc) { - test.showMenu(doc.getElementById("span-link"), function (popup) { - test.checkMenu([topMenu], [], []); - let topMenuElt = test.getItemElt(popup, topMenu); - let topMenuPopup = topMenuElt.firstChild; - let submenuElt = test.getItemElt(topMenuPopup, submenu); - let submenuPopup = submenuElt.firstChild; - let itemElt = test.getItemElt(submenuPopup, item); - itemElt.click(); - }); - }); -}; - -// Click listeners should work when multiple modules are loaded. -exports.testItemClickMultipleModules = function (assert, done) { - let test = new TestHelper(assert, done); - let loader0 = test.newLoader(); - let loader1 = test.newLoader(); - - let item0 = loader0.cm.Item({ - label: "loader 0 item", - contentScript: 'self.on("click", self.postMessage);', - onMessage: function () { - test.fail("loader 0 item should not emit click event"); - } - }); - let item1 = loader1.cm.Item({ - label: "loader 1 item", - contentScript: 'self.on("click", self.postMessage);', - onMessage: function () { - test.pass("loader 1 item clicked as expected"); - test.done(); - } - }); - - test.showMenu(null, function (popup) { - test.checkMenu([item0, item1], [], []); - let item1Elt = test.getItemElt(popup, item1); - item1Elt.click(); - }); -}; - - -// Adding a separator to a submenu should work OK. -exports.testSeparator = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let menu = new loader.cm.Menu({ - label: "submenu", - items: [new loader.cm.Separator()] - }); - - test.showMenu(null, function (popup) { - test.checkMenu([menu], [], []); - test.done(); - }); -}; - - -// The parentMenu option should work -exports.testParentMenu = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let menu = new loader.cm.Menu({ - label: "submenu", - items: [loader.cm.Item({ label: "item 1" })], - parentMenu: loader.cm.contentContextMenu - }); - - let item = loader.cm.Item({ - label: "item 2", - parentMenu: menu, - }); - - assert.equal(menu.items[1], item, "Item should be in the sub menu"); - - test.showMenu(null, function (popup) { - test.checkMenu([menu], [], []); - test.done(); - }); -}; - - -// Existing context menu modifications should apply to new windows. -exports.testNewWindow = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let item = new loader.cm.Item({ label: "item" }); - - test.withNewWindow(function () { - test.showMenu(null, function (popup) { - test.checkMenu([item], [], []); - test.done(); - }); - }); -}; - - -// When a new window is opened, items added by an unloaded module should not -// be present in the menu. -exports.testNewWindowMultipleModules = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - let item = new loader.cm.Item({ label: "item" }); - - test.showMenu(null, function (popup) { - test.checkMenu([item], [], []); - popup.hidePopup(); - loader.unload(); - test.withNewWindow(function () { - test.showMenu(null, function (popup) { - test.checkMenu([item], [], [item]); - test.done(); - }); - }); - }); -}; - - -// Existing context menu modifications should not apply to new private windows. -exports.testNewPrivateWindow = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let item = new loader.cm.Item({ label: "item" }); - - test.showMenu(null, function (popup) { - test.checkMenu([item], [], []); - popup.hidePopup(); - - test.withNewPrivateWindow(function () { - test.showMenu(null, function (popup) { - test.checkMenu([], [], []); - test.done(); - }); - }); - }); -}; - - -// Existing context menu modifications should apply to new private windows when -// private browsing support is enabled. -exports.testNewPrivateEnabledWindow = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newPrivateLoader(); - - let item = new loader.cm.Item({ label: "item" }); - - test.showMenu(null, function (popup) { - test.checkMenu([item], [], []); - popup.hidePopup(); - - test.withNewPrivateWindow(function () { - test.showMenu(null, function (popup) { - test.checkMenu([item], [], []); - test.done(); - }); - }); - }); -}; - - -// Existing context menu modifications should apply to new private windows when -// private browsing support is enabled unless unloaded. -exports.testNewPrivateEnabledWindowUnloaded = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newPrivateLoader(); - - let item = new loader.cm.Item({ label: "item" }); - - test.showMenu(null, function (popup) { - test.checkMenu([item], [], []); - popup.hidePopup(); - - loader.unload(); - - test.withNewPrivateWindow(function () { - test.showMenu(null, function (popup) { - test.checkMenu([], [], []); - test.done(); - }); - }); - }); -}; - - -// Items in the context menu should be sorted according to locale. -exports.testSorting = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - // Make an unsorted items list. It'll look like this: - // item 1, item 0, item 3, item 2, item 5, item 4, ... - let items = []; - for (let i = 0; i < OVERFLOW_THRESH_DEFAULT; i += 2) { - items.push(new loader.cm.Item({ label: "item " + (i + 1) })); - items.push(new loader.cm.Item({ label: "item " + i })); - } - - test.showMenu(null, function (popup) { - test.checkMenu(items, [], []); - test.done(); - }); -}; - - -// Items in the overflow menu should be sorted according to locale. -exports.testSortingOverflow = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - // Make an unsorted items list. It'll look like this: - // item 1, item 0, item 3, item 2, item 5, item 4, ... - let items = []; - for (let i = 0; i < OVERFLOW_THRESH_DEFAULT * 2; i += 2) { - items.push(new loader.cm.Item({ label: "item " + (i + 1) })); - items.push(new loader.cm.Item({ label: "item " + i })); - } - - test.showMenu(null, function (popup) { - test.checkMenu(items, [], []); - test.done(); - }); -}; - - -// Multiple modules shouldn't interfere with sorting. -exports.testSortingMultipleModules = function (assert, done) { - let test = new TestHelper(assert, done); - let loader0 = test.newLoader(); - let loader1 = test.newLoader(); - - let items0 = []; - let items1 = []; - for (let i = 0; i < OVERFLOW_THRESH_DEFAULT; i++) { - if (i % 2) { - let item = new loader0.cm.Item({ label: "item " + i }); - items0.push(item); - } - else { - let item = new loader1.cm.Item({ label: "item " + i }); - items1.push(item); - } - } - let allItems = items0.concat(items1); - - test.showMenu(null, function (popup) { - - // All items should be present and sorted. - test.checkMenu(allItems, [], []); - popup.hidePopup(); - loader0.unload(); - loader1.unload(); - test.showMenu(null, function (popup) { - - // All items should be removed. - test.checkMenu(allItems, [], allItems); - test.done(); - }); - }); -}; - - -// Content click handlers and context handlers should be able to communicate, -// i.e., they're eval'ed in the same worker and sandbox. -exports.testContentCommunication = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let item = new loader.cm.Item({ - label: "item", - contentScript: 'var potato;' + - 'self.on("context", function () {' + - ' potato = "potato";' + - ' return true;' + - '});' + - 'self.on("click", function () {' + - ' self.postMessage(potato);' + - '});', - }); - - item.on("message", function (data) { - assert.equal(data, "potato", "That's a lot of potatoes!"); - test.done(); - }); - - test.showMenu(null, function (popup) { - test.checkMenu([item], [], []); - let elt = test.getItemElt(popup, item); - elt.click(); - }); -}; - - -// When the context menu is invoked on a tab that was already open when the -// module was loaded, it should contain the expected items and content workers -// should function as expected. -exports.testLoadWithOpenTab = function (assert, done) { - let test = new TestHelper(assert, done); - test.withTestDoc(function (window, doc) { - let loader = test.newLoader(); - let item = new loader.cm.Item({ - label: "item", - contentScript: - 'self.on("click", function () self.postMessage("click"));', - onMessage: function (msg) { - if (msg === "click") - test.done(); - } - }); - test.showMenu(null, function (popup) { - test.checkMenu([item], [], []); - test.getItemElt(popup, item).click(); - }); - }); -}; - -// Bug 732716: Ensure that the node given in `click` event works fine -// (i.e. is correctly wrapped) -exports.testDrawImageOnClickNode = function (assert, done) { - let test = new TestHelper(assert, done); - test.withTestDoc(function (window, doc) { - let loader = test.newLoader(); - let item = new loader.cm.Item({ - label: "item", - context: loader.cm.SelectorContext("img"), - contentScript: "new " + function() { - self.on("click", function (img, data) { - let ctx = document.createElement("canvas").getContext("2d"); - ctx.drawImage(img, 1, 1, 1, 1); - self.postMessage("done"); - }); - }, - onMessage: function (msg) { - if (msg === "done") - test.done(); - } - }); - test.showMenu(doc.getElementById("image"), function (popup) { - test.checkMenu([item], [], []); - test.getItemElt(popup, item).click(); - }); - }); -}; - - -// Setting an item's label before the menu is ever shown should correctly change -// its label. -exports.testSetLabelBeforeShow = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let items = [ - new loader.cm.Item({ label: "a" }), - new loader.cm.Item({ label: "b" }) - ] - items[0].label = "z"; - assert.equal(items[0].label, "z"); - - test.showMenu(null, function (popup) { - test.checkMenu(items, [], []); - test.done(); - }); -}; - - -// Setting an item's label after the menu is shown should correctly change its -// label. -exports.testSetLabelAfterShow = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let items = [ - new loader.cm.Item({ label: "a" }), - new loader.cm.Item({ label: "b" }) - ]; - - test.showMenu(null, function (popup) { - test.checkMenu(items, [], []); - popup.hidePopup(); - - items[0].label = "z"; - assert.equal(items[0].label, "z"); - test.showMenu(null, function (popup) { - test.checkMenu(items, [], []); - test.done(); - }); - }); -}; - - -// Setting an item's label before the menu is ever shown should correctly change -// its label. -exports.testSetLabelBeforeShowOverflow = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let prefs = loader.loader.require("sdk/preferences/service"); - prefs.set(OVERFLOW_THRESH_PREF, 0); - - let items = [ - new loader.cm.Item({ label: "a" }), - new loader.cm.Item({ label: "b" }) - ] - items[0].label = "z"; - assert.equal(items[0].label, "z"); - - test.showMenu(null, function (popup) { - test.checkMenu(items, [], []); - test.done(); - }); -}; - - -// Setting an item's label after the menu is shown should correctly change its -// label. -exports.testSetLabelAfterShowOverflow = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let prefs = loader.loader.require("sdk/preferences/service"); - prefs.set(OVERFLOW_THRESH_PREF, 0); - - let items = [ - new loader.cm.Item({ label: "a" }), - new loader.cm.Item({ label: "b" }) - ]; - - test.showMenu(null, function (popup) { - test.checkMenu(items, [], []); - popup.hidePopup(); - - items[0].label = "z"; - assert.equal(items[0].label, "z"); - test.showMenu(null, function (popup) { - test.checkMenu(items, [], []); - test.done(); - }); - }); -}; - - -// Setting the label of an item in a Menu should work. -exports.testSetLabelMenuItem = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let menu = loader.cm.Menu({ - label: "menu", - items: [loader.cm.Item({ label: "a" })] - }); - menu.items[0].label = "z"; - - assert.equal(menu.items[0].label, "z"); - - test.showMenu(null, function (popup) { - test.checkMenu([menu], [], []); - test.done(); - }); -}; - - -// Menu.addItem() should work. -exports.testMenuAddItem = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let menu = loader.cm.Menu({ - label: "menu", - items: [ - loader.cm.Item({ label: "item 0" }) - ] - }); - menu.addItem(loader.cm.Item({ label: "item 1" })); - menu.addItem(loader.cm.Item({ label: "item 2" })); - - assert.equal(menu.items.length, 3, - "menu should have correct number of items"); - for (let i = 0; i < 3; i++) { - assert.equal(menu.items[i].label, "item " + i, - "item label should be correct"); - assert.equal(menu.items[i].parentMenu, menu, - "item's parent menu should be correct"); - } - - test.showMenu(null, function (popup) { - test.checkMenu([menu], [], []); - test.done(); - }); -}; - - -// Adding the same item twice to a menu should work as expected. -exports.testMenuAddItemTwice = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let menu = loader.cm.Menu({ - label: "menu", - items: [] - }); - let subitem = loader.cm.Item({ label: "item 1" }) - menu.addItem(subitem); - menu.addItem(loader.cm.Item({ label: "item 0" })); - menu.addItem(subitem); - - assert.equal(menu.items.length, 2, - "menu should have correct number of items"); - for (let i = 0; i < 2; i++) { - assert.equal(menu.items[i].label, "item " + i, - "item label should be correct"); - } - - test.showMenu(null, function (popup) { - test.checkMenu([menu], [], []); - test.done(); - }); -}; - - -// Menu.removeItem() should work. -exports.testMenuRemoveItem = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let subitem = loader.cm.Item({ label: "item 1" }); - let menu = loader.cm.Menu({ - label: "menu", - items: [ - loader.cm.Item({ label: "item 0" }), - subitem, - loader.cm.Item({ label: "item 2" }) - ] - }); - - // Removing twice should be harmless. - menu.removeItem(subitem); - menu.removeItem(subitem); - - assert.equal(subitem.parentMenu, null, - "item's parent menu should be correct"); - - assert.equal(menu.items.length, 2, - "menu should have correct number of items"); - assert.equal(menu.items[0].label, "item 0", - "item label should be correct"); - assert.equal(menu.items[1].label, "item 2", - "item label should be correct"); - - test.showMenu(null, function (popup) { - test.checkMenu([menu], [], []); - test.done(); - }); -}; - - -// Adding an item currently contained in one menu to another menu should work. -exports.testMenuItemSwap = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let subitem = loader.cm.Item({ label: "item" }); - let menu0 = loader.cm.Menu({ - label: "menu 0", - items: [subitem] - }); - let menu1 = loader.cm.Menu({ - label: "menu 1", - items: [] - }); - menu1.addItem(subitem); - - assert.equal(menu0.items.length, 0, - "menu should have correct number of items"); - - assert.equal(menu1.items.length, 1, - "menu should have correct number of items"); - assert.equal(menu1.items[0].label, "item", - "item label should be correct"); - - assert.equal(subitem.parentMenu, menu1, - "item's parent menu should be correct"); - - test.showMenu(null, function (popup) { - test.checkMenu([menu0, menu1], [menu0], []); - test.done(); - }); -}; - - -// Destroying an item should remove it from its parent menu. -exports.testMenuItemDestroy = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let subitem = loader.cm.Item({ label: "item" }); - let menu = loader.cm.Menu({ - label: "menu", - items: [subitem] - }); - subitem.destroy(); - - assert.equal(menu.items.length, 0, - "menu should have correct number of items"); - assert.equal(subitem.parentMenu, null, - "item's parent menu should be correct"); - - test.showMenu(null, function (popup) { - test.checkMenu([menu], [menu], []); - test.done(); - }); -}; - - -// Setting Menu.items should work. -exports.testMenuItemsSetter = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let menu = loader.cm.Menu({ - label: "menu", - items: [ - loader.cm.Item({ label: "old item 0" }), - loader.cm.Item({ label: "old item 1" }) - ] - }); - menu.items = [ - loader.cm.Item({ label: "new item 0" }), - loader.cm.Item({ label: "new item 1" }), - loader.cm.Item({ label: "new item 2" }) - ]; - - assert.equal(menu.items.length, 3, - "menu should have correct number of items"); - for (let i = 0; i < 3; i++) { - assert.equal(menu.items[i].label, "new item " + i, - "item label should be correct"); - assert.equal(menu.items[i].parentMenu, menu, - "item's parent menu should be correct"); - } - - test.showMenu(null, function (popup) { - test.checkMenu([menu], [], []); - test.done(); - }); -}; - - -// Setting Item.data should work. -exports.testItemDataSetter = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let item = loader.cm.Item({ label: "old item 0", data: "old" }); - item.data = "new"; - - assert.equal(item.data, "new", "item should have correct data"); - - test.showMenu(null, function (popup) { - test.checkMenu([item], [], []); - test.done(); - }); -}; - - -// Open the test doc, load the module, make sure items appear when context- -// clicking the iframe. -exports.testAlreadyOpenIframe = function (assert, done) { - let test = new TestHelper(assert, done); - test.withTestDoc(function (window, doc) { - let loader = test.newLoader(); - let item = new loader.cm.Item({ - label: "item" - }); - test.showMenu(doc.getElementById("iframe"), function (popup) { - test.checkMenu([item], [], []); - test.done(); - }); - }); -}; - - -// Tests that a missing label throws an exception -exports.testItemNoLabel = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - try { - new loader.cm.Item({}); - assert.ok(false, "Should have seen exception"); - } - catch (e) { - assert.ok(true, "Should have seen exception"); - } - - try { - new loader.cm.Item({ label: null }); - assert.ok(false, "Should have seen exception"); - } - catch (e) { - assert.ok(true, "Should have seen exception"); - } - - try { - new loader.cm.Item({ label: undefined }); - assert.ok(false, "Should have seen exception"); - } - catch (e) { - assert.ok(true, "Should have seen exception"); - } - - try { - new loader.cm.Item({ label: "" }); - assert.ok(false, "Should have seen exception"); - } - catch (e) { - assert.ok(true, "Should have seen exception"); - } - - test.done(); -} - - -// Tests that items can have an empty data property -exports.testItemNoData = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - function checkData(data) { - assert.equal(data, undefined, "Data should be undefined"); - } - - let item1 = new loader.cm.Item({ - label: "item 1", - contentScript: 'self.on("click", function(node, data) self.postMessage(data))', - onMessage: checkData - }); - let item2 = new loader.cm.Item({ - label: "item 2", - data: null, - contentScript: 'self.on("click", function(node, data) self.postMessage(data))', - onMessage: checkData - }); - let item3 = new loader.cm.Item({ - label: "item 3", - data: undefined, - contentScript: 'self.on("click", function(node, data) self.postMessage(data))', - onMessage: checkData - }); - - assert.equal(item1.data, undefined, "Should be no defined data"); - assert.equal(item2.data, null, "Should be no defined data"); - assert.equal(item3.data, undefined, "Should be no defined data"); - - test.showMenu(null, function (popup) { - test.checkMenu([item1, item2, item3], [], []); - - let itemElt = test.getItemElt(popup, item1); - itemElt.click(); - - test.hideMenu(function() { - test.showMenu(null, function (popup) { - let itemElt = test.getItemElt(popup, item2); - itemElt.click(); - - test.hideMenu(function() { - test.showMenu(null, function (popup) { - let itemElt = test.getItemElt(popup, item3); - itemElt.click(); - - test.done(); - }); - }); - }); - }); - }); -} - - -// Tests that items without an image don't attempt to show one -exports.testItemNoImage = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let item1 = new loader.cm.Item({ label: "item 1" }); - let item2 = new loader.cm.Item({ label: "item 2", image: null }); - let item3 = new loader.cm.Item({ label: "item 3", image: undefined }); - - assert.equal(item1.image, undefined, "Should be no defined image"); - assert.equal(item2.image, null, "Should be no defined image"); - assert.equal(item3.image, undefined, "Should be no defined image"); - - test.showMenu(null, function (popup) { - test.checkMenu([item1, item2, item3], [], []); - - test.done(); - }); -} - - -// Test image support. -exports.testItemImage = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let imageURL = data.url("moz_favicon.ico"); - let item = new loader.cm.Item({ label: "item", image: imageURL }); - let menu = new loader.cm.Menu({ label: "menu", image: imageURL, items: [ - loader.cm.Item({ label: "subitem" }) - ]}); - assert.equal(item.image, imageURL, "Should have set the image correctly"); - assert.equal(menu.image, imageURL, "Should have set the image correctly"); - - test.showMenu(null, function (popup) { - test.checkMenu([item, menu], [], []); - - let imageURL2 = data.url("dummy.ico"); - item.image = imageURL2; - menu.image = imageURL2; - assert.equal(item.image, imageURL2, "Should have set the image correctly"); - assert.equal(menu.image, imageURL2, "Should have set the image correctly"); - test.checkMenu([item, menu], [], []); - - item.image = null; - menu.image = null; - assert.equal(item.image, null, "Should have set the image correctly"); - assert.equal(menu.image, null, "Should have set the image correctly"); - test.checkMenu([item, menu], [], []); - - test.done(); - }); -}; - -// Test image URL validation. -exports.testItemImageValidURL = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - assert.throws(function(){ - new loader.cm.Item({ - label: "item 1", - image: "foo" - }) - }, /Image URL validation failed/ - ); - - assert.throws(function(){ - new loader.cm.Item({ - label: "item 2", - image: false - }) - }, /Image URL validation failed/ - ); - - assert.throws(function(){ - new loader.cm.Item({ - label: "item 3", - image: 0 - }) - }, /Image URL validation failed/ - ); - - let imageURL = data.url("moz_favicon.ico"); - let item4 = new loader.cm.Item({ label: "item 4", image: imageURL }); - let item5 = new loader.cm.Item({ label: "item 5", image: null }); - let item6 = new loader.cm.Item({ label: "item 6", image: undefined }); - - assert.equal(item4.image, imageURL, "Should be proper image URL"); - assert.equal(item5.image, null, "Should be null image"); - assert.equal(item6.image, undefined, "Should be undefined image"); - - test.done(); -}; - - -// Menu.destroy should destroy the item tree rooted at that menu. -exports.testMenuDestroy = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let menu = loader.cm.Menu({ - label: "menu", - items: [ - loader.cm.Item({ label: "item 0" }), - loader.cm.Menu({ - label: "item 1", - items: [ - loader.cm.Item({ label: "subitem 0" }), - loader.cm.Item({ label: "subitem 1" }), - loader.cm.Item({ label: "subitem 2" }) - ] - }), - loader.cm.Item({ label: "item 2" }) - ] - }); - menu.destroy(); - - /*let numRegistryEntries = 0; - loader.globalScope.browserManager.browserWins.forEach(function (bwin) { - for (let itemID in bwin.items) - numRegistryEntries++; - }); - assert.equal(numRegistryEntries, 0, "All items should be unregistered.");*/ - - test.showMenu(null, function (popup) { - test.checkMenu([menu], [], [menu]); - test.done(); - }); -}; - -// Checks that if a menu contains sub items that are hidden then the menu is -// hidden too. Also checks that content scripts and contexts work for sub items. -exports.testSubItemContextNoMatchHideMenu = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let items = [ - loader.cm.Menu({ - label: "menu 1", - items: [ - loader.cm.Item({ - label: "subitem 1", - context: loader.cm.SelectorContext(".foo") - }) - ] - }), - loader.cm.Menu({ - label: "menu 2", - items: [ - loader.cm.Item({ - label: "subitem 2", - contentScript: 'self.on("context", function () false);' - }) - ] - }), - loader.cm.Menu({ - label: "menu 3", - items: [ - loader.cm.Item({ - label: "subitem 3", - context: loader.cm.SelectorContext(".foo") - }), - loader.cm.Item({ - label: "subitem 4", - contentScript: 'self.on("context", function () false);' - }) - ] - }) - ]; - - test.showMenu(null, function (popup) { - test.checkMenu(items, items, []); - test.done(); - }); -}; - - -// Checks that if a menu contains a combination of hidden and visible sub items -// then the menu is still visible too. -exports.testSubItemContextMatch = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let hiddenItems = [ - loader.cm.Item({ - label: "subitem 3", - context: loader.cm.SelectorContext(".foo") - }), - loader.cm.Item({ - label: "subitem 6", - contentScript: 'self.on("context", function () false);' - }) - ]; - - let items = [ - loader.cm.Menu({ - label: "menu 1", - items: [ - loader.cm.Item({ - label: "subitem 1", - context: loader.cm.URLContext(TEST_DOC_URL) - }) - ] - }), - loader.cm.Menu({ - label: "menu 2", - items: [ - loader.cm.Item({ - label: "subitem 2", - contentScript: 'self.on("context", function () true);' - }) - ] - }), - loader.cm.Menu({ - label: "menu 3", - items: [ - hiddenItems[0], - loader.cm.Item({ - label: "subitem 4", - contentScript: 'self.on("context", function () true);' - }) - ] - }), - loader.cm.Menu({ - label: "menu 4", - items: [ - loader.cm.Item({ - label: "subitem 5", - context: loader.cm.URLContext(TEST_DOC_URL) - }), - hiddenItems[1] - ] - }), - loader.cm.Menu({ - label: "menu 5", - items: [ - loader.cm.Item({ - label: "subitem 7", - context: loader.cm.URLContext(TEST_DOC_URL) - }), - loader.cm.Item({ - label: "subitem 8", - contentScript: 'self.on("context", function () true);' - }) - ] - }) - ]; - - test.withTestDoc(function (window, doc) { - test.showMenu(null, function (popup) { - test.checkMenu(items, hiddenItems, []); - test.done(); - }); - }); -}; - - -// Child items should default to visible, not to PageContext -exports.testSubItemDefaultVisible = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let items = [ - loader.cm.Menu({ - label: "menu 1", - context: loader.cm.SelectorContext("img"), - items: [ - loader.cm.Item({ - label: "subitem 1" - }), - loader.cm.Item({ - label: "subitem 2", - context: loader.cm.SelectorContext("img") - }), - loader.cm.Item({ - label: "subitem 3", - context: loader.cm.SelectorContext("a") - }) - ] - }) - ]; - - // subitem 3 will be hidden - let hiddenItems = [items[0].items[2]]; - - test.withTestDoc(function (window, doc) { - test.showMenu(doc.getElementById("image"), function (popup) { - test.checkMenu(items, hiddenItems, []); - test.done(); - }); - }); -}; - -// Tests that the click event on sub menuitem -// tiggers the click event for the sub menuitem and the parent menu -exports.testSubItemClick = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let state = 0; - - let items = [ - loader.cm.Menu({ - label: "menu 1", - items: [ - loader.cm.Item({ - label: "subitem 1", - data: "foobar", - contentScript: 'self.on("click", function (node, data) {' + - ' self.postMessage({' + - ' tagName: node.tagName,' + - ' data: data' + - ' });' + - '});', - onMessage: function(msg) { - assert.equal(msg.tagName, "HTML", "should have seen the right node"); - assert.equal(msg.data, "foobar", "should have seen the right data"); - assert.equal(state, 0, "should have seen the event at the right time"); - state++; - } - }) - ], - contentScript: 'self.on("click", function (node, data) {' + - ' self.postMessage({' + - ' tagName: node.tagName,' + - ' data: data' + - ' });' + - '});', - onMessage: function(msg) { - assert.equal(msg.tagName, "HTML", "should have seen the right node"); - assert.equal(msg.data, "foobar", "should have seen the right data"); - assert.equal(state, 1, "should have seen the event at the right time"); - - test.done(); - } - }) - ]; - - test.withTestDoc(function (window, doc) { - test.showMenu(null, function (popup) { - test.checkMenu(items, [], []); - - let topMenuElt = test.getItemElt(popup, items[0]); - let topMenuPopup = topMenuElt.firstChild; - let itemElt = test.getItemElt(topMenuPopup, items[0].items[0]); - itemElt.click(); - }); - }); -}; - -// Tests that the command event on sub menuitem -// tiggers the click event for the sub menuitem and the parent menu -exports.testSubItemCommand = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let state = 0; - - let items = [ - loader.cm.Menu({ - label: "menu 1", - items: [ - loader.cm.Item({ - label: "subitem 1", - data: "foobar", - contentScript: 'self.on("click", function (node, data) {' + - ' self.postMessage({' + - ' tagName: node.tagName,' + - ' data: data' + - ' });' + - '});', - onMessage: function(msg) { - assert.equal(msg.tagName, "HTML", "should have seen the right node"); - assert.equal(msg.data, "foobar", "should have seen the right data"); - assert.equal(state, 0, "should have seen the event at the right time"); - state++; - } - }) - ], - contentScript: 'self.on("click", function (node, data) {' + - ' self.postMessage({' + - ' tagName: node.tagName,' + - ' data: data' + - ' });' + - '});', - onMessage: function(msg) { - assert.equal(msg.tagName, "HTML", "should have seen the right node"); - assert.equal(msg.data, "foobar", "should have seen the right data"); - assert.equal(state, 1, "should have seen the event at the right time"); - state++ - - test.done(); - } - }) - ]; - - test.withTestDoc(function (window, doc) { - test.showMenu(null, function (popup) { - test.checkMenu(items, [], []); - - let topMenuElt = test.getItemElt(popup, items[0]); - let topMenuPopup = topMenuElt.firstChild; - let itemElt = test.getItemElt(topMenuPopup, items[0].items[0]); - - // create a command event - let evt = itemElt.ownerDocument.createEvent('Event'); - evt.initEvent('command', true, true); - itemElt.dispatchEvent(evt); - }); - }); -}; - -// Tests that opening a context menu for an outer frame when an inner frame -// has a selection doesn't activate the SelectionContext -exports.testSelectionInInnerFrameNoMatch = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let state = 0; - - let items = [ - loader.cm.Item({ - label: "test item", - context: loader.cm.SelectionContext() - }) - ]; - - test.withTestDoc(function (window, doc) { - let frame = doc.getElementById("iframe"); - frame.contentWindow.getSelection().selectAllChildren(frame.contentDocument.body); - - test.showMenu(null, function (popup) { - test.checkMenu(items, items, []); - test.done(); - }); - }); -}; - -// Tests that opening a context menu for an inner frame when the inner frame -// has a selection does activate the SelectionContext -exports.testSelectionInInnerFrameMatch = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let state = 0; - - let items = [ - loader.cm.Item({ - label: "test item", - context: loader.cm.SelectionContext() - }) - ]; - - test.withTestDoc(function (window, doc) { - let frame = doc.getElementById("iframe"); - frame.contentWindow.getSelection().selectAllChildren(frame.contentDocument.body); - - test.showMenu(frame.contentDocument.getElementById("text"), function (popup) { - test.checkMenu(items, [], []); - test.done(); - }); - }); -}; - -// Tests that opening a context menu for an inner frame when the outer frame -// has a selection doesn't activate the SelectionContext -exports.testSelectionInOuterFrameNoMatch = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let state = 0; - - let items = [ - loader.cm.Item({ - label: "test item", - context: loader.cm.SelectionContext() - }) - ]; - - test.withTestDoc(function (window, doc) { - let frame = doc.getElementById("iframe"); - window.getSelection().selectAllChildren(doc.body); - - test.showMenu(frame.contentDocument.getElementById("text"), function (popup) { - test.checkMenu(items, items, []); - test.done(); - }); - }); -}; - - -// Test that the return value of the predicate function determines if -// item is shown -exports.testPredicateContextControl = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let itemTrue = loader.cm.Item({ - label: "visible", - context: loader.cm.PredicateContext(function () { return true; }) - }); - - let itemFalse = loader.cm.Item({ - label: "hidden", - context: loader.cm.PredicateContext(function () { return false; }) - }); - - test.showMenu(null, function (popup) { - test.checkMenu([itemTrue, itemFalse], [itemFalse], []); - test.done(); - }); -}; - -// Test that the data object has the correct document type -exports.testPredicateContextDocumentType = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let items = [loader.cm.Item({ - label: "item", - context: loader.cm.PredicateContext(function (data) { - assert.equal(data.documentType, 'text/html'); - return true; - }) - })]; - - test.withTestDoc(function (window, doc) { - test.showMenu(null, function (popup) { - test.checkMenu(items, [], []); - test.done(); - }); - }); -}; - -// Test that the data object has the correct document URL -exports.testPredicateContextDocumentURL = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let items = [loader.cm.Item({ - label: "item", - context: loader.cm.PredicateContext(function (data) { - assert.equal(data.documentURL, TEST_DOC_URL); - return true; - }) - })]; - - test.withTestDoc(function (window, doc) { - test.showMenu(null, function (popup) { - test.checkMenu(items, [], []); - test.done(); - }); - }); -}; - - -// Test that the data object has the correct element name -exports.testPredicateContextTargetName = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let items = [loader.cm.Item({ - label: "item", - context: loader.cm.PredicateContext(function (data) { - assert.strictEqual(data.targetName, "input"); - return true; - }) - })]; - - test.withTestDoc(function (window, doc) { - test.showMenu(doc.getElementById("button"), function (popup) { - test.checkMenu(items, [], []); - test.done(); - }); - }); -}; - - -// Test that the data object has the correct ID -exports.testPredicateContextTargetIDSet = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let items = [loader.cm.Item({ - label: "item", - context: loader.cm.PredicateContext(function (data) { - assert.strictEqual(data.targetID, "button"); - return true; - }) - })]; - - test.withTestDoc(function (window, doc) { - test.showMenu(doc.getElementById("button"), function (popup) { - test.checkMenu(items, [], []); - test.done(); - }); - }); -}; - -// Test that the data object has the correct ID -exports.testPredicateContextTargetIDNotSet = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let items = [loader.cm.Item({ - label: "item", - context: loader.cm.PredicateContext(function (data) { - assert.strictEqual(data.targetID, null); - return true; - }) - })]; - - test.withTestDoc(function (window, doc) { - test.showMenu(doc.getElementsByClassName("predicate-test-a")[0], function (popup) { - test.checkMenu(items, [], []); - test.done(); - }); - }); -}; - -// Test that the data object is showing editable correctly for regular text inputs -exports.testPredicateContextTextBoxIsEditable = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let items = [loader.cm.Item({ - label: "item", - context: loader.cm.PredicateContext(function (data) { - assert.strictEqual(data.isEditable, true); - return true; - }) - })]; - - test.withTestDoc(function (window, doc) { - test.showMenu(doc.getElementById("textbox"), function (popup) { - test.checkMenu(items, [], []); - test.done(); - }); - }); -}; - -// Test that the data object is showing editable correctly for readonly text inputs -exports.testPredicateContextReadonlyTextBoxIsNotEditable = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let items = [loader.cm.Item({ - label: "item", - context: loader.cm.PredicateContext(function (data) { - assert.strictEqual(data.isEditable, false); - return true; - }) - })]; - - test.withTestDoc(function (window, doc) { - test.showMenu(doc.getElementById("readonly-textbox"), function (popup) { - test.checkMenu(items, [], []); - test.done(); - }); - }); -}; - -// Test that the data object is showing editable correctly for disabled text inputs -exports.testPredicateContextDisabledTextBoxIsNotEditable = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let items = [loader.cm.Item({ - label: "item", - context: loader.cm.PredicateContext(function (data) { - assert.strictEqual(data.isEditable, false); - return true; - }) - })]; - - test.withTestDoc(function (window, doc) { - test.showMenu(doc.getElementById("disabled-textbox"), function (popup) { - test.checkMenu(items, [], []); - test.done(); - }); - }); -}; - -// Test that the data object is showing editable correctly for text areas -exports.testPredicateContextTextAreaIsEditable = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let items = [loader.cm.Item({ - label: "item", - context: loader.cm.PredicateContext(function (data) { - assert.strictEqual(data.isEditable, true); - return true; - }) - })]; - - test.withTestDoc(function (window, doc) { - test.showMenu(doc.getElementById("textfield"), function (popup) { - test.checkMenu(items, [], []); - test.done(); - }); - }); -}; - -// Test that non-text inputs are not considered editable -exports.testPredicateContextButtonIsNotEditable = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let items = [loader.cm.Item({ - label: "item", - context: loader.cm.PredicateContext(function (data) { - assert.strictEqual(data.isEditable, false); - return true; - }) - })]; - - test.withTestDoc(function (window, doc) { - test.showMenu(doc.getElementById("button"), function (popup) { - test.checkMenu(items, [], []); - test.done(); - }); - }); -}; - - -// Test that the data object is showing editable correctly -exports.testPredicateContextNonInputIsNotEditable = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let items = [loader.cm.Item({ - label: "item", - context: loader.cm.PredicateContext(function (data) { - assert.strictEqual(data.isEditable, false); - return true; - }) - })]; - - test.withTestDoc(function (window, doc) { - test.showMenu(doc.getElementById("image"), function (popup) { - test.checkMenu(items, [], []); - test.done(); - }); - }); -}; - - -// Test that the data object is showing editable correctly for HTML contenteditable elements -exports.testPredicateContextEditableElement = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let items = [loader.cm.Item({ - label: "item", - context: loader.cm.PredicateContext(function (data) { - assert.strictEqual(data.isEditable, true); - return true; - }) - })]; - - test.withTestDoc(function (window, doc) { - test.showMenu(doc.getElementById("editable"), function (popup) { - test.checkMenu(items, [], []); - test.done(); - }); - }); -}; - - -// Test that the data object does not have a selection when there is none -exports.testPredicateContextNoSelectionInPage = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let items = [loader.cm.Item({ - label: "item", - context: loader.cm.PredicateContext(function (data) { - assert.strictEqual(data.selectionText, null); - return true; - }) - })]; - - test.withTestDoc(function (window, doc) { - test.showMenu(null, function (popup) { - test.checkMenu(items, [], []); - test.done(); - }); - }); -}; - -// Test that the data object includes the selected page text -exports.testPredicateContextSelectionInPage = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let items = [loader.cm.Item({ - label: "item", - context: loader.cm.PredicateContext(function (data) { - // since we might get whitespace - assert.ok(data.selectionText && data.selectionText.search(/^\s*Some text.\s*$/) != -1, - 'Expected "Some text.", got "' + data.selectionText + '"'); - return true; - }) - })]; - - test.withTestDoc(function (window, doc) { - window.getSelection().selectAllChildren(doc.getElementById("text")); - test.showMenu(null, function (popup) { - test.checkMenu(items, [], []); - test.done(); - }); - }); -}; - -// Test that the data object includes the selected input text -exports.testPredicateContextSelectionInTextBox = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let items = [loader.cm.Item({ - label: "item", - context: loader.cm.PredicateContext(function (data) { - // since we might get whitespace - assert.strictEqual(data.selectionText, "t v"); - return true; - }) - })]; - - test.withTestDoc(function (window, doc) { - let textbox = doc.getElementById("textbox"); - textbox.focus(); - textbox.setSelectionRange(3, 6); - test.showMenu(textbox, function (popup) { - test.checkMenu(items, [], []); - test.done(); - }); - }); -}; - -// Test that the data object has the correct src for an image -exports.testPredicateContextTargetSrcSet = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - let image; - - let items = [loader.cm.Item({ - label: "item", - context: loader.cm.PredicateContext(function (data) { - assert.strictEqual(data.srcURL, image.src); - return true; - }) - })]; - - test.withTestDoc(function (window, doc) { - image = doc.getElementById("image"); - test.showMenu(image, function (popup) { - test.checkMenu(items, [], []); - test.done(); - }); - }); -}; - -// Test that the data object has no src for a link -exports.testPredicateContextTargetSrcNotSet = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let items = [loader.cm.Item({ - label: "item", - context: loader.cm.PredicateContext(function (data) { - assert.strictEqual(data.srcURL, null); - return true; - }) - })]; - - test.withTestDoc(function (window, doc) { - test.showMenu(doc.getElementById("link"), function (popup) { - test.checkMenu(items, [], []); - test.done(); - }); - }); -}; - - -// Test that the data object has the correct link set -exports.testPredicateContextTargetLinkSet = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - let image; - - let items = [loader.cm.Item({ - label: "item", - context: loader.cm.PredicateContext(function (data) { - assert.strictEqual(data.linkURL, TEST_DOC_URL + "#test"); - return true; - }) - })]; - - test.withTestDoc(function (window, doc) { - test.showMenu(doc.getElementsByClassName("predicate-test-a")[0], function (popup) { - test.checkMenu(items, [], []); - test.done(); - }); - }); -}; - -// Test that the data object has no link for an image -exports.testPredicateContextTargetLinkNotSet = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let items = [loader.cm.Item({ - label: "item", - context: loader.cm.PredicateContext(function (data) { - assert.strictEqual(data.linkURL, null); - return true; - }) - })]; - - test.withTestDoc(function (window, doc) { - test.showMenu(doc.getElementById("image"), function (popup) { - test.checkMenu(items, [], []); - test.done(); - }); - }); -}; - -// Test that the data object has the value for an input textbox -exports.testPredicateContextTargetValueSet = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - let image; - - let items = [loader.cm.Item({ - label: "item", - context: loader.cm.PredicateContext(function (data) { - assert.strictEqual(data.value, "test value"); - return true; - }) - })]; - - test.withTestDoc(function (window, doc) { - test.showMenu(doc.getElementById("textbox"), function (popup) { - test.checkMenu(items, [], []); - test.done(); - }); - }); -}; - -// Test that the data object has no value for an image -exports.testPredicateContextTargetValueNotSet = function (assert, done) { - let test = new TestHelper(assert, done); - let loader = test.newLoader(); - - let items = [loader.cm.Item({ - label: "item", - context: loader.cm.PredicateContext(function (data) { - assert.strictEqual(data.value, null); - return true; - }) - })]; - - test.withTestDoc(function (window, doc) { - test.showMenu(doc.getElementById("image"), function (popup) { - test.checkMenu(items, [], []); - test.done(); - }); - }); -}; - - -// NO TESTS BELOW THIS LINE! /////////////////////////////////////////////////// - -// This makes it easier to run tests by handling things like opening the menu, -// opening new windows, making assertions, etc. Methods on |test| can be called -// on instances of this class. Don't forget to call done() to end the test! -// WARNING: This looks up items in popups by comparing labels, so don't give two -// items the same label. -function TestHelper(assert, done) { - this.assert = assert; - this.end = done; - this.loaders = []; - this.browserWindow = Cc["@mozilla.org/appshell/window-mediator;1"]. - getService(Ci.nsIWindowMediator). - getMostRecentWindow("navigator:browser"); - this.overflowThreshValue = require("sdk/preferences/service"). - get(OVERFLOW_THRESH_PREF, OVERFLOW_THRESH_DEFAULT); -} - -TestHelper.prototype = { - get contextMenuPopup() { - return this.browserWindow.document.getElementById("contentAreaContextMenu"); - }, - - get contextMenuSeparator() { - return this.browserWindow.document.querySelector("." + SEPARATOR_CLASS); - }, - - get overflowPopup() { - return this.browserWindow.document.querySelector("." + OVERFLOW_POPUP_CLASS); - }, - - get overflowSubmenu() { - return this.browserWindow.document.querySelector("." + OVERFLOW_MENU_CLASS); - }, - - get tabBrowser() { - return this.browserWindow.gBrowser; - }, - - // Methods on the wrapped test can be called on this object. - __noSuchMethod__: function (methodName, args) { - this.assert[methodName].apply(this.assert, args); - }, - - // Asserts that elt, a DOM element representing item, looks OK. - checkItemElt: function (elt, item) { - let itemType = this.getItemType(item); - - switch (itemType) { - case "Item": - this.assert.equal(elt.localName, "menuitem", - "Item DOM element should be a xul:menuitem"); - if (typeof(item.data) === "string") { - this.assert.equal(elt.getAttribute("value"), item.data, - "Item should have correct data"); - } - break - case "Menu": - this.assert.equal(elt.localName, "menu", - "Menu DOM element should be a xul:menu"); - let subPopup = elt.firstChild; - this.assert.ok(subPopup, "xul:menu should have a child"); - this.assert.equal(subPopup.localName, "menupopup", - "xul:menu's first child should be a menupopup"); - break; - case "Separator": - this.assert.equal(elt.localName, "menuseparator", - "Separator DOM element should be a xul:menuseparator"); - break; - } - - if (itemType === "Item" || itemType === "Menu") { - this.assert.equal(elt.getAttribute("label"), item.label, - "Item should have correct title"); - if (typeof(item.image) === "string") { - this.assert.equal(elt.getAttribute("image"), item.image, - "Item should have correct image"); - if (itemType === "Menu") - this.assert.ok(elt.classList.contains("menu-iconic"), - "Menus with images should have the correct class") - else - this.assert.ok(elt.classList.contains("menuitem-iconic"), - "Items with images should have the correct class") - } - else { - this.assert.ok(!elt.getAttribute("image"), - "Item should not have image"); - this.assert.ok(!elt.classList.contains("menu-iconic") && !elt.classList.contains("menuitem-iconic"), - "The iconic classes should not be present") - } - } - }, - - // Asserts that the context menu looks OK given the arguments. presentItems - // are items that have been added to the menu. absentItems are items that - // shouldn't match the current context. removedItems are items that have been - // removed from the menu. - checkMenu: function (presentItems, absentItems, removedItems) { - // Count up how many top-level items there are - let total = 0; - for (let item of presentItems) { - if (absentItems.indexOf(item) < 0 && removedItems.indexOf(item) < 0) - total++; - } - - let separator = this.contextMenuSeparator; - if (total == 0) { - this.assert.ok(!separator || separator.hidden, - "separator should not be present"); - } - else { - this.assert.ok(separator && !separator.hidden, - "separator should be present"); - } - - let mainNodes = this.browserWindow.document.querySelectorAll("#contentAreaContextMenu > ." + ITEM_CLASS); - let overflowNodes = this.browserWindow.document.querySelectorAll("." + OVERFLOW_POPUP_CLASS + " > ." + ITEM_CLASS); - - this.assert.ok(mainNodes.length == 0 || overflowNodes.length == 0, - "Should only see nodes at the top level or in overflow"); - - let overflow = this.overflowSubmenu; - if (this.shouldOverflow(total)) { - this.assert.ok(overflow && !overflow.hidden, - "overflow menu should be present"); - this.assert.equal(mainNodes.length, 0, - "should be no items in the main context menu"); - } - else { - this.assert.ok(!overflow || overflow.hidden, - "overflow menu should not be present"); - // When visible nodes == 0 they could be in overflow or top level - if (total > 0) { - this.assert.equal(overflowNodes.length, 0, - "should be no items in the overflow context menu"); - } - } - - // Iterate over wherever the nodes have ended up - let nodes = mainNodes.length ? mainNodes : overflowNodes; - this.checkNodes(nodes, presentItems, absentItems, removedItems) - let pos = 0; - }, - - // Recurses through the item hierarchy of presentItems comparing it to the - // node hierarchy of nodes. Any items in removedItems will be skipped (so - // should not exist in the XUL), any items in absentItems must exist and be - // hidden - checkNodes: function (nodes, presentItems, absentItems, removedItems) { - let pos = 0; - for (let item of presentItems) { - // Removed items shouldn't be in the list - if (removedItems.indexOf(item) >= 0) - continue; - - if (nodes.length <= pos) { - this.assert.ok(false, "Not enough nodes"); - return; - } - - let hidden = absentItems.indexOf(item) >= 0; - - this.checkItemElt(nodes[pos], item); - this.assert.equal(nodes[pos].hidden, hidden, - "hidden should be set correctly"); - - // The contents of hidden menus doesn't matter so much - if (!hidden && this.getItemType(item) == "Menu") { - this.assert.equal(nodes[pos].firstChild.localName, "menupopup", - "menu XUL should contain a menupopup"); - this.checkNodes(nodes[pos].firstChild.childNodes, item.items, absentItems, removedItems); - } - - if (pos > 0) - this.assert.equal(nodes[pos].previousSibling, nodes[pos - 1], - "nodes should all be in the same group"); - pos++; - } - - this.assert.equal(nodes.length, pos, - "should have checked all the XUL nodes"); - }, - - // Attaches an event listener to node. The listener is automatically removed - // when it's fired (so it's assumed it will fire), and callback is called - // after a short delay. Since the module we're testing relies on the same - // event listeners to do its work, this is to give them a little breathing - // room before callback runs. Inside callback |this| is this object. - // Optionally you can pass a function to test if the event is the event you - // want. - delayedEventListener: function (node, event, callback, useCapture, isValid) { - const self = this; - node.addEventListener(event, function handler(evt) { - if (isValid && !isValid(evt)) - return; - node.removeEventListener(event, handler, useCapture); - timer.setTimeout(function () { - try { - callback.call(self, evt); - } - catch (err) { - self.assert.fail(err); - self.end(); - } - }, 20); - }, useCapture); - }, - - // Call to finish the test. - done: function () { - const self = this; - function commonDone() { - this.closeTab(); - - while (this.loaders.length) { - this.loaders[0].unload(); - } - - require("sdk/preferences/service").set(OVERFLOW_THRESH_PREF, self.overflowThreshValue); - - this.end(); - } - - function closeBrowserWindow() { - if (this.oldBrowserWindow) { - this.delayedEventListener(this.browserWindow, "unload", commonDone, - false); - this.browserWindow.close(); - this.browserWindow = this.oldBrowserWindow; - delete this.oldBrowserWindow; - } - else { - commonDone.call(this); - } - }; - - if (this.contextMenuPopup.state == "closed") { - closeBrowserWindow.call(this); - } - else { - this.delayedEventListener(this.contextMenuPopup, "popuphidden", - function () closeBrowserWindow.call(this), - false); - this.contextMenuPopup.hidePopup(); - } - }, - - closeTab: function() { - if (this.tab) { - this.tabBrowser.removeTab(this.tab); - this.tabBrowser.selectedTab = this.oldSelectedTab; - this.tab = null; - } - }, - - // Returns the DOM element in popup corresponding to item. - // WARNING: The element is found by comparing labels, so don't give two items - // the same label. - getItemElt: function (popup, item) { - let nodes = popup.childNodes; - for (let i = nodes.length - 1; i >= 0; i--) { - if (this.getItemType(item) === "Separator") { - if (nodes[i].localName === "menuseparator") - return nodes[i]; - } - else if (nodes[i].getAttribute("label") === item.label) - return nodes[i]; - } - return null; - }, - - // Returns "Item", "Menu", or "Separator". - getItemType: function (item) { - // Could use instanceof here, but that would require accessing the loader - // that created the item, and I don't want to A) somehow search through the - // this.loaders list to find it, and B) assume there are any live loaders at - // all. - return /^\[object (Item|Menu|Separator)/.exec(item.toString())[1]; - }, - - // Returns a wrapper around a new loader: { loader, cm, unload, globalScope }. - // loader is a Cuddlefish sandboxed loader, cm is the context menu module, - // globalScope is the context menu module's global scope, and unload is a - // function that unloads the loader and associated resources. - newLoader: function () { - const self = this; - let loader = Loader(module); - let wrapper = { - loader: loader, - cm: loader.require("sdk/context-menu"), - globalScope: loader.sandbox("sdk/context-menu"), - unload: function () { - loader.unload(); - let idx = self.loaders.indexOf(wrapper); - if (idx < 0) - throw new Error("Test error: tried to unload nonexistent loader"); - self.loaders.splice(idx, 1); - } - }; - this.loaders.push(wrapper); - return wrapper; - }, - - // As above but the loader has private-browsing support enabled. - newPrivateLoader: function() { - let base = require("@loader/options"); - - // Clone current loader's options adding the private-browsing permission - let options = merge({}, base, { - metadata: merge({}, base.metadata || {}, { - permissions: merge({}, base.metadata.permissions || {}, { - 'private-browsing': true - }) - }) - }); - - const self = this; - let loader = Loader(module, null, options); - let wrapper = { - loader: loader, - cm: loader.require("sdk/context-menu"), - globalScope: loader.sandbox("sdk/context-menu"), - unload: function () { - loader.unload(); - let idx = self.loaders.indexOf(wrapper); - if (idx < 0) - throw new Error("Test error: tried to unload nonexistent loader"); - self.loaders.splice(idx, 1); - } - }; - this.loaders.push(wrapper); - return wrapper; - }, - - // Returns true if the count crosses the overflow threshold. - shouldOverflow: function (count) { - return count > - (this.loaders.length ? - this.loaders[0].loader.require("sdk/preferences/service"). - get(OVERFLOW_THRESH_PREF, OVERFLOW_THRESH_DEFAULT) : - OVERFLOW_THRESH_DEFAULT); - }, - - // Opens the context menu on the current page. If targetNode is null, the - // menu is opened in the top-left corner. onShowncallback is passed the - // popup. - showMenu: function(targetNode, onshownCallback) { - function sendEvent() { - this.delayedEventListener(this.browserWindow, "popupshowing", - function (e) { - let popup = e.target; - onshownCallback.call(this, popup); - }, false); - - let rect = targetNode ? - targetNode.getBoundingClientRect() : - { left: 0, top: 0, width: 0, height: 0 }; - let contentWin = targetNode ? targetNode.ownerDocument.defaultView - : this.browserWindow.content; - contentWin. - QueryInterface(Ci.nsIInterfaceRequestor). - getInterface(Ci.nsIDOMWindowUtils). - sendMouseEvent("contextmenu", - rect.left + (rect.width / 2), - rect.top + (rect.height / 2), - 2, 1, 0); - } - - // If a new tab or window has not yet been opened, open a new tab now. For - // some reason using the tab already opened when the test starts causes - // leaks. See bug 566351 for details. - if (!targetNode && !this.oldSelectedTab && !this.oldBrowserWindow) { - this.oldSelectedTab = this.tabBrowser.selectedTab; - this.tab = this.tabBrowser.addTab("about:blank"); - let browser = this.tabBrowser.getBrowserForTab(this.tab); - - this.delayedEventListener(browser, "load", function () { - this.tabBrowser.selectedTab = this.tab; - sendEvent.call(this); - }, true); - } - else - sendEvent.call(this); - }, - - hideMenu: function(onhiddenCallback) { - this.delayedEventListener(this.browserWindow, "popuphidden", onhiddenCallback); - - this.contextMenuPopup.hidePopup(); - }, - - // Opens a new browser window. The window will be closed automatically when - // done() is called. - withNewWindow: function (onloadCallback) { - let win = this.browserWindow.OpenBrowserWindow(); - this.delayedEventListener(win, "load", onloadCallback, true); - this.oldBrowserWindow = this.browserWindow; - this.browserWindow = win; - }, - - // Opens a new private browser window. The window will be closed - // automatically when done() is called. - withNewPrivateWindow: function (onloadCallback) { - let win = this.browserWindow.OpenBrowserWindow({private: true}); - this.delayedEventListener(win, "load", onloadCallback, true); - this.oldBrowserWindow = this.browserWindow; - this.browserWindow = win; - }, - - // Opens a new tab with our test page in the current window. The tab will - // be closed automatically when done() is called. - withTestDoc: function (onloadCallback) { - this.oldSelectedTab = this.tabBrowser.selectedTab; - this.tab = this.tabBrowser.addTab(TEST_DOC_URL); - let browser = this.tabBrowser.getBrowserForTab(this.tab); - - this.delayedEventListener(browser, "load", function () { - this.tabBrowser.selectedTab = this.tab; - onloadCallback.call(this, browser.contentWindow, browser.contentDocument); - }, true, function(evt) { - return evt.target.location == TEST_DOC_URL; - }); - } -}; - -require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-cortex.js b/addon-sdk-1.16/test/test-cortex.js deleted file mode 100644 index 6040ab41..00000000 --- a/addon-sdk-1.16/test/test-cortex.js +++ /dev/null @@ -1,120 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -var Cortex = require("sdk/deprecated/cortex").Cortex; - -exports["test property changes propagate"] = function (assert) { - var source = { - _foo: "secret", - get foo() { - return this._foo; - }, - set foo(value) { - this._foo = value; - }, - get getOnly() { - return this._foo; - }, - set setOnly(value) { - this._setOnly = value; - }, - bar: "public", - method: function method(a, b) { - return this._foo + a + b - } - }; - var fixture = Cortex(source); - - assert.ok(!('_foo' in fixture), - "properties that start with `_` are omitted"); - assert.equal(fixture.foo, "secret", "get accessor alias works"); - fixture.foo = "new secret"; - assert.equal(fixture.foo, "new secret", "set accessor alias works"); - assert.equal(source.foo, "new secret", "accessor delegates to the source"); - assert.equal(fixture.bar, "public", "data property alias works"); - fixture.bar = "bar"; - assert.equal(source.bar, "bar", "data property change propagates"); - source.bar = "foo" - assert.equal(fixture.bar, "foo", "data property change probagets back"); - assert.equal(fixture.method("a", "b"), "new secretab", - "public methods are callable"); - assert.equal(fixture.method.call({ _foo: "test" }, " a,", "b"), - "new secret a,b", - "`this` pseudo-variable can not be passed through call."); - assert.equal(fixture.method.apply({ _foo: "test" }, [" a,", "b"]), - "new secret a,b", - "`this` pseudo-variable can not be passed through apply."); - assert.equal(fixture.getOnly, source._foo, - "getter returned property of wrapped object"); - fixture.setOnly = 'bar' - assert.equal(source._setOnly, 'bar', "setter modified wrapped object") -}; - - -exports["test immunity of inheritance"] = function(assert) { - function Type() {} - Type.prototype = { - constructor: Type, - _bar: 2, - bar: 3, - get_Foo: function getFoo() { - return this._foo; - } - } - var source = Object.create(Type.prototype, { - _foo: { value: 'secret' }, - getBar: { value: function get_Bar() { - return this.bar - }}, - get_Bar: { value: function getBar() { - return this._bar - }} - }); - - var fixture = Cortex(source); - - assert.ok(Cortex({}, null, Type.prototype) instanceof Type, - "if custom prototype is providede cortex will inherit from it"); - assert.ok(fixture instanceof Type, - "if no prototype is given cortex inherits from object's prototype"); - - source.bar += 1; - assert.notEqual(fixture.bar, source.bar, - "chages of properties don't propagate to non-aliases"); - assert.equal(fixture.getBar(), source.bar, - "methods accessing public properties are bound to the source"); - - fixture._bar += 1; - assert.notEqual(fixture._bar, source._bar, - "changes of non aliased properties don't propagate"); - assert.equal(fixture.get_Bar(), source._bar, - "methods accessing privates are bound to the source"); - assert.notEqual(fixture.get_Foo(), source._foo, - "prototoype methods are not bound to the source"); -} - -exports["test customized public properties"] = function(assert) { - var source = { - _a: 'a', - b: 'b', - get: function get(name) { - return this[name]; - } - }; - - var fixture = Cortex(source, ['_a', 'get']); - fixture._a += "#change"; - - - assert.ok(!("b" in fixture), "non-public own property is not defined"); - assert.equal(fixture.get("b"), source.b, - "public methods preserve access to the private properties"); - assert.equal(fixture._a, source._a, - "custom public property changes propagate"); -} - -//if (require.main == module) - require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-cuddlefish.js b/addon-sdk-1.16/test/test-cuddlefish.js deleted file mode 100644 index 2d8bb610..00000000 --- a/addon-sdk-1.16/test/test-cuddlefish.js +++ /dev/null @@ -1,47 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -'use strict'; - -const { Loader, Require, unload, override } = require('sdk/loader/cuddlefish'); -const packaging = require('@loader/options'); - -exports['test loader'] = function(assert) { - var prints = []; - function print(message) { - prints.push(message); - } - - let loader = Loader(override(packaging, { - globals: { - print: print, - foo: 1 - } - })); - let require = Require(loader, module); - - var fixture = require('./loader/fixture'); - - assert.equal(fixture.foo, 1, 'custom globals must work.'); - assert.equal(fixture.bar, 2, 'exports are set'); - - assert.equal(prints[0], 'testing', 'global print must be injected.'); - - var unloadsCalled = ''; - - require("sdk/system/unload").when(function(reason) { - assert.equal(reason, 'test', 'unload reason is passed'); - unloadsCalled += 'a'; - }); - require('sdk/system/unload.js').when(function() { - unloadsCalled += 'b'; - }); - - unload(loader, 'test'); - - assert.equal(unloadsCalled, 'ba', - 'loader.unload() must call listeners in LIFO order.'); -}; - -require('test').run(exports); diff --git a/addon-sdk-1.16/test/test-deprecate.js b/addon-sdk-1.16/test/test-deprecate.js deleted file mode 100644 index ce306b15..00000000 --- a/addon-sdk-1.16/test/test-deprecate.js +++ /dev/null @@ -1,160 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -'use strict'; - -const deprecate = require("sdk/util/deprecate"); -const { LoaderWithHookedConsole } = require("sdk/test/loader"); -const { get, set } = require("sdk/preferences/service"); -const PREFERENCE = "devtools.errorconsole.deprecation_warnings"; - -exports["test Deprecate Usage"] = function testDeprecateUsage(assert) { - set(PREFERENCE, true); - let { loader, messages } = LoaderWithHookedConsole(module); - let deprecate = loader.require("sdk/util/deprecate"); - - function functionIsDeprecated() { - deprecate.deprecateUsage("foo"); - } - - functionIsDeprecated(); - - assert.equal(messages.length, 1, "only one error is dispatched"); - assert.equal(messages[0].type, "error", "the console message is an error"); - - let msg = messages[0].msg; - - assert.ok(msg.indexOf("foo") !== -1, - "message contains the given message"); - assert.ok(msg.indexOf("functionIsDeprecated") !== -1, - "message contains name of the caller function"); - assert.ok(msg.indexOf(module.uri) !== -1, - "message contains URI of the caller module"); - - loader.unload(); -} - -exports["test Deprecate Function"] = function testDeprecateFunction(assert) { - set(PREFERENCE, true); - let { loader, messages } = LoaderWithHookedConsole(module); - let deprecate = loader.require("sdk/util/deprecate"); - - let self = {}; - let arg1 = "foo"; - let arg2 = {}; - - function originalFunction(a1, a2) { - assert.equal(this, self); - assert.equal(a1, arg1); - assert.equal(a2, arg2); - }; - - let deprecateFunction = deprecate.deprecateFunction(originalFunction, - "bar"); - - deprecateFunction.call(self, arg1, arg2); - - assert.equal(messages.length, 1, "only one error is dispatched"); - assert.equal(messages[0].type, "error", "the console message is an error"); - - let msg = messages[0].msg; - assert.ok(msg.indexOf("bar") !== -1, "message contains the given message"); - assert.ok(msg.indexOf("testDeprecateFunction") !== -1, - "message contains name of the caller function"); - assert.ok(msg.indexOf(module.uri) !== -1, - "message contains URI of the caller module"); - - loader.unload(); -} - -exports.testDeprecateEvent = function(assert, done) { - set(PREFERENCE, true); - let { loader, messages } = LoaderWithHookedConsole(module); - let deprecate = loader.require("sdk/util/deprecate"); - - let { on, emit } = loader.require('sdk/event/core'); - let testObj = {}; - testObj.on = deprecate.deprecateEvent(on.bind(null, testObj), 'BAD', ['fire']); - - testObj.on('fire', function() { - testObj.on('water', function() { - assert.equal(messages.length, 1, "only one error is dispatched"); - loader.unload(); - done(); - }) - assert.equal(messages.length, 1, "only one error is dispatched"); - emit(testObj, 'water'); - }); - assert.equal(messages.length, 1, "only one error is dispatched"); - assert.equal(messages[0].type, "error", "the console message is an error"); - let msg = messages[0].msg; - assert.ok(msg.indexOf("BAD") !== -1, "message contains the given message"); - assert.ok(msg.indexOf("deprecateEvent") !== -1, - "message contains name of the caller function"); - assert.ok(msg.indexOf(module.uri) !== -1, - "message contains URI of the caller module"); - - emit(testObj, 'fire'); -} - -exports.testDeprecateSettingToggle = function (assert, done) { - let { loader, messages } = LoaderWithHookedConsole(module); - let deprecate = loader.require("sdk/util/deprecate"); - - function fn () { deprecate.deprecateUsage("foo"); } - - set(PREFERENCE, false); - fn(); - assert.equal(messages.length, 0, 'no deprecation warnings'); - - set(PREFERENCE, true); - fn(); - assert.equal(messages.length, 1, 'deprecation warnings when toggled'); - - set(PREFERENCE, false); - fn(); - assert.equal(messages.length, 1, 'no new deprecation warnings'); - done(); -}; - -exports.testDeprecateSetting = function (assert, done) { - // Set devtools.errorconsole.deprecation_warnings to false - set(PREFERENCE, false); - - let { loader, messages } = LoaderWithHookedConsole(module); - let deprecate = loader.require("sdk/util/deprecate"); - - // deprecateUsage test - function functionIsDeprecated() { - deprecate.deprecateUsage("foo"); - } - functionIsDeprecated(); - - assert.equal(messages.length, 0, - "no errors dispatched on deprecateUsage"); - - // deprecateFunction test - function originalFunction() {}; - - let deprecateFunction = deprecate.deprecateFunction(originalFunction, - "bar"); - deprecateFunction(); - - assert.equal(messages.length, 0, - "no errors dispatched on deprecateFunction"); - - // deprecateEvent - let { on, emit } = loader.require('sdk/event/core'); - let testObj = {}; - testObj.on = deprecate.deprecateEvent(on.bind(null, testObj), 'BAD', ['fire']); - - testObj.on('fire', () => { - assert.equal(messages.length, 0, - "no errors dispatched on deprecateEvent"); - done(); - }); - - emit(testObj, 'fire'); -} -require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-deprecated-list.js b/addon-sdk-1.16/test/test-deprecated-list.js deleted file mode 100644 index beae09f6..00000000 --- a/addon-sdk-1.16/test/test-deprecated-list.js +++ /dev/null @@ -1,200 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -'use strict'; - -const { List } = require('sdk/deprecated/list'); - -function assertList(assert, array, list) { - for (let i = 0, l = array.length; i < l; i++) { - assert.equal( - array.length, - list.length, - 'list must contain same amount of elements as array' - ); - assert.equal( - 'List(' + array + ')', - list + '', - 'toString must output array like result' - ); - assert.ok(i in list, 'must contain element with index: ' + i); - assert.equal( - array[i], - list[i], - 'element with index: ' + i + ' should match' - ); - } -} - -exports['test:test for'] = function(assert) { - let fixture = List(3, 2, 1); - - assert.equal(3, fixture.length, 'length is 3'); - let i = 0; - for (let key in fixture) { - assert.equal(i++, key, 'key should match'); - } -}; - -exports['test:test for each'] = function(assert) { - let fixture = new List(3, 2, 1); - - assert.equal(3, fixture.length, 'length is 3'); - let i = 3; - for (let value of fixture) { - assert.equal(i--, value, 'value should match'); - } -}; - -exports['test:test for of'] = function(assert) { - let fixture = new List(3, 2, 1); - - assert.equal(3, fixture.length, 'length is 3'); - let i = 3; - for (let value of fixture) { - assert.equal(i--, value, 'value should match'); - } -}; - -exports['test:test toString'] = function(assert) { - let fixture = List(3, 2, 1); - - assert.equal( - 'List(3,2,1)', - fixture + '', - 'toString must output array like result' - ) -}; - -exports['test:test constructor with apply'] = function(assert) { - let array = ['a', 'b', 'c']; - let fixture = List.apply(null, array); - - assert.equal( - 3, - fixture.length, - 'should have applied arguments' - ); -}; - -exports['test:direct element access'] = function(assert) { - let array = [1, 'foo', 2, 'bar', {}, 'bar', function a() {}, assert, 1]; - let fixture = List.apply(null, array); - array.splice(5, 1); - array.splice(7, 1); - - assert.equal( - array.length, - fixture.length, - 'list should omit duplicate elements' - ); - - assert.equal( - 'List(' + array + ')', - fixture.toString(), - 'elements should not be rearranged' - ); - - for (let key in array) { - assert.ok(key in fixture,'should contain key for index:' + key); - assert.equal(array[key], fixture[key], 'values should match for: ' + key); - } -}; - -exports['test:removing adding elements'] = function(assert) { - let array = [1, 'foo', 2, 'bar', {}, 'bar', function a() {}, assert, 1]; - let fixture = List.compose({ - add: function() this._add.apply(this, arguments), - remove: function() this._remove.apply(this, arguments), - clear: function() this._clear() - }).apply(null, array); - array.splice(5, 1); - array.splice(7, 1); - - assertList(assert, array, fixture); - - array.splice(array.indexOf(2), 1); - fixture.remove(2); - assertList(assert, array, fixture); - - array.splice(array.indexOf('foo'), 1); - fixture.remove('foo'); - array.splice(array.indexOf(1), 1); - fixture.remove(1); - array.push('foo'); - fixture.add('foo'); - assertList(assert, array, fixture); - - array.splice(0); - fixture.clear(0); - assertList(assert, array, fixture); - - array.push(1, 'foo', 2, 'bar', 3); - fixture.add(1); - fixture.add('foo'); - fixture.add(2); - fixture.add('bar'); - fixture.add(3); - - assertList(assert, array, fixture); -}; - -exports['test: remove does not leave invalid numerical properties'] = function(assert) { - let fixture = List.compose({ - remove: function() this._remove.apply(this, arguments), - }).apply(null, [1, 2, 3]); - - fixture.remove(1); - assert.equal(fixture[fixture.length], undefined); -} - -exports['test:add list item from Iterator'] = function(assert) { - let array = [1, 2, 3, 4], sum = 0, added = false; - - let fixture = List.compose({ - add: function() this._add.apply(this, arguments), - }).apply(null, array); - - for (let item of fixture) { - sum += item; - - if (!added) { - fixture.add(5); - added = true; - } - } - - assert.equal(sum, 1 + 2 + 3 + 4, 'sum = 1 + 2 + 3 + 4'); -}; - -exports['test:remove list item from Iterator'] = function(assert) { - let array = [1, 2, 3, 4], sum = 0; - - let fixture = List.compose({ - remove: function() this._remove.apply(this, arguments), - }).apply(null, array); - - for (let item of fixture) { - sum += item; - fixture.remove(item); - } - - assert.equal(sum, 1 + 2 + 3 + 4, 'sum = 1 + 2 + 3 + 4'); -}; - -exports['test:clear list from Iterator'] = function(assert) { - let array = [1, 2, 3, 4], sum = 0; - - let fixture = List.compose({ - clear: function() this._clear() - }).apply(null, array); - - for (let item of fixture) { - sum += item; - fixture.clear(); - } - - assert.equal(sum, 1 + 2 + 3 + 4, 'sum = 1 + 2 + 3 + 4'); -}; - -require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-diffpatcher.js b/addon-sdk-1.16/test/test-diffpatcher.js deleted file mode 100644 index 47126c93..00000000 --- a/addon-sdk-1.16/test/test-diffpatcher.js +++ /dev/null @@ -1,8 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -exports["test diffpatcher"] = require("diffpatcher/test/index"); -require("sdk/test").run(exports); diff --git a/addon-sdk-1.16/test/test-disposable.js b/addon-sdk-1.16/test/test-disposable.js deleted file mode 100644 index 95b438d4..00000000 --- a/addon-sdk-1.16/test/test-disposable.js +++ /dev/null @@ -1,222 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -"use strict"; - -const { Loader } = require("sdk/test/loader"); -const { Class } = require("sdk/core/heritage"); -const { Cc, Ci, Cu } = require("chrome"); -const { setTimeout } = require("sdk/timers"); - -exports["test disposables are desposed on unload"] = function(assert) { - let loader = Loader(module); - let { Disposable } = loader.require("sdk/core/disposable"); - - let arg1 = {} - let arg2 = 2 - let disposals = 0 - - let Foo = Class({ - extends: Disposable, - setup: function setup(a, b) { - assert.equal(a, arg1, - "arguments passed to constructur is passed to setup"); - assert.equal(b, arg2, - "second argument is also passed to a setup"); - assert.ok(this instanceof Foo, - "this is an instance in the scope of the setup method"); - - this.fooed = true - }, - dispose: function dispose() { - assert.equal(this.fooed, true, "attribute was set") - this.fooed = false - disposals = disposals + 1 - } - }) - - let foo1 = Foo(arg1, arg2) - let foo2 = Foo(arg1, arg2) - - loader.unload(); - - assert.equal(disposals, 2, "both instances were disposed") -} - -exports["test destroyed windows dispose before unload"] = function(assert) { - let loader = Loader(module); - let { Disposable } = loader.require("sdk/core/disposable"); - - let arg1 = {} - let arg2 = 2 - let disposals = 0 - - let Foo = Class({ - extends: Disposable, - setup: function setup(a, b) { - assert.equal(a, arg1, - "arguments passed to constructur is passed to setup"); - assert.equal(b, arg2, - "second argument is also passed to a setup"); - assert.ok(this instanceof Foo, - "this is an instance in the scope of the setup method"); - - this.fooed = true - }, - dispose: function dispose() { - assert.equal(this.fooed, true, "attribute was set") - this.fooed = false - disposals = disposals + 1 - } - }) - - let foo1 = Foo(arg1, arg2) - let foo2 = Foo(arg1, arg2) - - foo1.destroy(); - assert.equal(disposals, 1, "destroy disposes instance") - - loader.unload(); - - assert.equal(disposals, 2, "unload disposes alive instances") -} - -exports["test disposables are GC-able"] = function(assert, done) { - let loader = Loader(module); - let { Disposable } = loader.require("sdk/core/disposable"); - - let arg1 = {} - let arg2 = 2 - let disposals = 0 - - let Foo = Class({ - extends: Disposable, - setup: function setup(a, b) { - assert.equal(a, arg1, - "arguments passed to constructur is passed to setup"); - assert.equal(b, arg2, - "second argument is also passed to a setup"); - assert.ok(this instanceof Foo, - "this is an instance in the scope of the setup method"); - - this.fooed = true - }, - dispose: function dispose() { - assert.equal(this.fooed, true, "attribute was set") - this.fooed = false - disposals = disposals + 1 - } - }) - - let foo1 = Foo(arg1, arg2) - let foo2 = Foo(arg1, arg2) - - let foo1 = null - let foo2 = null - - Cu.schedulePreciseGC(function() { - loader.unload(); - assert.equal(disposals, 0, "GC removed dispose listeners"); - done(); - }); -} - - -exports["test loader unloads do not affect other loaders"] = function(assert) { - let loader1 = Loader(module); - let loader2 = Loader(module); - let { Disposable: Disposable1 } = loader1.require("sdk/core/disposable"); - let { Disposable: Disposable2 } = loader2.require("sdk/core/disposable"); - - let arg1 = {} - let arg2 = 2 - let disposals = 0 - - let Foo1 = Class({ - extends: Disposable1, - dispose: function dispose() { - disposals = disposals + 1; - } - }); - - let Foo2 = Class({ - extends: Disposable2, - dispose: function dispose() { - disposals = disposals + 1; - } - }); - - let foo1 = Foo1(arg1, arg2); - let foo2 = Foo2(arg1, arg2); - - assert.equal(disposals, 0, "no destroy calls"); - - loader1.unload(); - - assert.equal(disposals, 1, "1 destroy calls"); - - loader2.unload(); - - assert.equal(disposals, 2, "2 destroy calls"); -} - -exports["test disposables that throw"] = function(assert) { - let loader = Loader(module); - let { Disposable } = loader.require("sdk/core/disposable"); - - let disposals = 0 - - let Foo = Class({ - extends: Disposable, - setup: function setup(a, b) { - throw Error("Boom!") - }, - dispose: function dispose() { - disposals = disposals + 1 - } - }) - - assert.throws(function() { - let foo1 = Foo() - }, /Boom/, "disposable constructors may throw"); - - loader.unload(); - - assert.equal(disposals, 0, "no disposal if constructor threw"); -} - -exports["test multiple destroy"] = function(assert) { - let loader = Loader(module); - let { Disposable } = loader.require("sdk/core/disposable"); - - let disposals = 0 - - let Foo = Class({ - extends: Disposable, - dispose: function dispose() { - disposals = disposals + 1 - } - }) - - let foo1 = Foo(); - let foo2 = Foo(); - let foo3 = Foo(); - - assert.equal(disposals, 0, "no disposals yet"); - - foo1.destroy(); - assert.equal(disposals, 1, "disposed properly"); - foo1.destroy(); - assert.equal(disposals, 1, "didn't attempt to dispose twice"); - - foo2.destroy(); - assert.equal(disposals, 2, "other instances still dispose fine"); - foo2.destroy(); - assert.equal(disposals, 2, "but not twice"); - - loader.unload(); - - assert.equal(disposals, 3, "unload only disposed the remaining instance"); -} - -require('test').run(exports); diff --git a/addon-sdk-1.16/test/test-dom.js b/addon-sdk-1.16/test/test-dom.js deleted file mode 100644 index 0b50a763..00000000 --- a/addon-sdk-1.16/test/test-dom.js +++ /dev/null @@ -1,88 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const events = require("sdk/dom/events"); -const { activeBrowserWindow: { document } } = require("sdk/deprecated/window-utils"); -const window = document.window; -/* -exports["test on / emit"] = function (assert, done) { - let element = document.createElement("div"); - events.on(element, "click", function listener(event) { - assert.equal(event.target, element, "event has correct target"); - events.removeListener(element, "click", listener); - done(); - }); - - events.emit(element, "click", { - category: "MouseEvents", - settings: [ - true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null - ] - }); -}; - -exports["test remove"] = function (assert, done) { - let element = document.createElement("span"); - let l1 = 0; - let l2 = 0; - let options = { - category: "MouseEvents", - settings: [ - true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null - ] - }; - - events.on(element, "click", function listener1(event) { - l1 ++; - assert.equal(event.target, element, "event has correct target"); - events.removeListener(element, "click", listener1); - }); - - events.on(element, "click", function listener2(event) { - l2 ++; - if (l1 < l2) { - assert.equal(l1, 1, "firs listener was called and then romeved"); - events.removeListener(element, "click", listener2); - done(); - } - events.emit(element, "click", options); - }); - - events.emit(element, "click", options); -}; - -exports["test once"] = function (assert, done) { - let element = document.createElement("h1"); - let l1 = 0; - let l2 = 0; - let options = { - category: "MouseEvents", - settings: [ - true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null - ] - }; - - - events.once(element, "click", function listener(event) { - assert.equal(event.target, element, "event target is a correct element"); - l1 ++; - }); - - events.on(element, "click", function listener(event) { - l2 ++; - if (l2 > 3) { - events.removeListener(element, "click", listener); - assert.equal(event.target, element, "event has correct target"); - assert.equal(l1, 1, "once was called only once"); - done(); - } - events.emit(element, "click", options); - }); - - events.emit(element, "click", options); -} -*/ -require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-environment.js b/addon-sdk-1.16/test/test-environment.js deleted file mode 100644 index 8475d244..00000000 --- a/addon-sdk-1.16/test/test-environment.js +++ /dev/null @@ -1,50 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -'use strict'; - -const { env } = require('sdk/system/environment'); -const { Cc, Ci } = require('chrome'); -const { get, set, exists } = Cc['@mozilla.org/process/environment;1']. - getService(Ci.nsIEnvironment); - -exports['test exists'] = function(assert) { - assert.equal('PATH' in env, exists('PATH'), - 'PATH environment variable is defined'); - assert.equal('FOO1' in env, exists('FOO1'), - 'FOO1 environment variable is not defined'); - set('FOO1', 'foo'); - assert.equal('FOO1' in env, true, - 'FOO1 environment variable was set'); - set('FOO1', null); - assert.equal('FOO1' in env, false, - 'FOO1 environment variable was unset'); -}; - -exports['test get'] = function(assert) { - assert.equal(env.PATH, get('PATH'), 'PATH env variable matches'); - assert.equal(env.BAR2, undefined, 'BAR2 env variable is not defined'); - set('BAR2', 'bar'); - assert.equal(env.BAR2, 'bar', 'BAR2 env variable was set'); - set('BAR2', null); - assert.equal(env.BAR2, undefined, 'BAR2 env variable was unset'); -}; - -exports['test set'] = function(assert) { - assert.equal(get('BAZ3'), '', 'BAZ3 env variable is not set'); - assert.equal(env.BAZ3, undefined, 'BAZ3 is not set'); - env.BAZ3 = 'baz'; - assert.equal(env.BAZ3, get('BAZ3'), 'BAZ3 env variable is set'); - assert.equal(get('BAZ3'), 'baz', 'BAZ3 env variable was set to "baz"'); -}; - -exports['test unset'] = function(assert) { - env.BLA4 = 'bla'; - assert.equal(env.BLA4, 'bla', 'BLA4 env variable is set'); - assert.equal(delete env.BLA4, true, 'BLA4 env variable is removed'); - assert.equal(env.BLA4, undefined, 'BLA4 env variable is unset'); - assert.equal('BLA4' in env, false, 'BLA4 env variable no longer exists' ); -}; - -require('test').run(exports); diff --git a/addon-sdk-1.16/test/test-errors.js b/addon-sdk-1.16/test/test-errors.js deleted file mode 100644 index 7ebc888c..00000000 --- a/addon-sdk-1.16/test/test-errors.js +++ /dev/null @@ -1,72 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -"use strict"; - -const errors = require("sdk/deprecated/errors"); - -exports.testCatchAndLog = function(assert) { - var caught = []; - function dummyLog(e) { caught.push(e); } - - var wrapped = errors.catchAndLog(function(x) { - throw Error("blah" + x + this); - }, "boop", dummyLog); - - assert.equal(wrapped.call("hi", 1), "boop", - "exceptions should be trapped, def. resp. returned"); - assert.equal(caught.length, 1, - "logging function should be called"); - assert.equal(caught[0].message, "blah1hi", - "args and this should be passed to wrapped func"); -}; - -exports.testCatchAndLogProps = function(assert) { - var caught = []; - function dummyLog(e) { caught.push(e); } - - var thing = { - foo: function(x) { throw Error("nowai" + x); }, - bar: function() { throw Error("blah"); }, - baz: function() { throw Error("fnarg"); } - }; - - errors.catchAndLogProps(thing, "foo", "ugh", dummyLog); - - assert.equal(thing.foo(1), "ugh", - "props should be wrapped"); - assert.equal(caught.length, 1, - "logging function should be called"); - assert.equal(caught[0].message, "nowai1", - "args should be passed to wrapped func"); - assert.throws(function() { thing.bar(); }, - /blah/, - "non-wrapped props should be wrapped"); - - errors.catchAndLogProps(thing, ["bar", "baz"], "err", dummyLog); - assert.ok((thing.bar() == thing.baz()) && - (thing.bar() == "err"), - "multiple props should be wrapped if array passed in"); -}; - -exports.testCatchAndReturn = function(assert) { - var wrapped = errors.catchAndReturn(function(x) { - if (x == 1) - return "one"; - if (x == 2) - throw new Error("two"); - return this + x; - }); - - assert.equal(wrapped(1).returnValue, "one", - "arg should be passed; return value should be returned"); - assert.ok(wrapped(2).exception, "exception should be returned"); - assert.equal(wrapped(2).exception.message, "two", "message is correct"); - assert.ok(wrapped(2).exception.fileName.indexOf("test-errors.js") != -1, - "filename is present"); - assert.ok(wrapped(2).exception.stack, "stack is available"); - assert.equal(wrapped.call("hi", 3).returnValue, "hi3", - "`this` should be set correctly"); -}; - -require("sdk/test").run(exports); diff --git a/addon-sdk-1.16/test/test-event-core.js b/addon-sdk-1.16/test/test-event-core.js deleted file mode 100644 index ecee12bd..00000000 --- a/addon-sdk-1.16/test/test-event-core.js +++ /dev/null @@ -1,245 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -'use strict'; - -const { on, once, off, emit, count } = require('sdk/event/core'); -const { LoaderWithHookedConsole } = require("sdk/test/loader"); - -exports['test add a listener'] = function(assert) { - let events = [ { name: 'event#1' }, 'event#2' ]; - let target = { name: 'target' }; - - on(target, 'message', function(message) { - assert.equal(this, target, 'this is a target object'); - assert.equal(message, events.shift(), 'message is emitted event'); - }); - emit(target, 'message', events[0]); - emit(target, 'message', events[0]); -}; - -exports['test that listener is unique per type'] = function(assert) { - let actual = [] - let target = {} - function listener() { actual.push(1) } - on(target, 'message', listener); - on(target, 'message', listener); - on(target, 'message', listener); - on(target, 'foo', listener); - on(target, 'foo', listener); - - emit(target, 'message'); - assert.deepEqual([ 1 ], actual, 'only one message listener added'); - emit(target, 'foo'); - assert.deepEqual([ 1, 1 ], actual, 'same listener added for other event'); -}; - -exports['test event type matters'] = function(assert) { - let target = { name: 'target' } - on(target, 'message', function() { - assert.fail('no event is expected'); - }); - on(target, 'done', function() { - assert.pass('event is emitted'); - }); - emit(target, 'foo') - emit(target, 'done'); -}; - -exports['test all arguments are pasesd'] = function(assert) { - let foo = { name: 'foo' }, bar = 'bar'; - let target = { name: 'target' }; - on(target, 'message', function(a, b) { - assert.equal(a, foo, 'first argument passed'); - assert.equal(b, bar, 'second argument passed'); - }); - emit(target, 'message', foo, bar); -}; - -exports['test no side-effects in emit'] = function(assert) { - let target = { name: 'target' }; - on(target, 'message', function() { - assert.pass('first listener is called'); - on(target, 'message', function() { - assert.fail('second listener is called'); - }); - }); - emit(target, 'message'); -}; - -exports['test can remove next listener'] = function(assert) { - let target = { name: 'target' }; - function fail() assert.fail('Listener should be removed'); - - on(target, 'data', function() { - assert.pass('first litener called'); - off(target, 'data', fail); - }); - on(target, 'data', fail); - - emit(target, 'data', 'hello'); -}; - -exports['test order of propagation'] = function(assert) { - let actual = []; - let target = { name: 'target' }; - on(target, 'message', function() { actual.push(1); }); - on(target, 'message', function() { actual.push(2); }); - on(target, 'message', function() { actual.push(3); }); - emit(target, 'message'); - assert.deepEqual([ 1, 2, 3 ], actual, 'called in order they were added'); -}; - -exports['test remove a listener'] = function(assert) { - let target = { name: 'target' }; - let actual = []; - on(target, 'message', function listener() { - actual.push(1); - on(target, 'message', function() { - off(target, 'message', listener); - actual.push(2); - }) - }); - - emit(target, 'message'); - assert.deepEqual([ 1 ], actual, 'first listener called'); - emit(target, 'message'); - assert.deepEqual([ 1, 1, 2 ], actual, 'second listener called'); - - emit(target, 'message'); - assert.deepEqual([ 1, 1, 2, 2, 2 ], actual, 'first listener removed'); -}; - -exports['test remove all listeners for type'] = function(assert) { - let actual = []; - let target = { name: 'target' } - on(target, 'message', function() { actual.push(1); }); - on(target, 'message', function() { actual.push(2); }); - on(target, 'message', function() { actual.push(3); }); - on(target, 'bar', function() { actual.push('b') }); - off(target, 'message'); - - emit(target, 'message'); - emit(target, 'bar'); - - assert.deepEqual([ 'b' ], actual, 'all message listeners were removed'); -}; - -exports['test remove all listeners'] = function(assert) { - let actual = []; - let target = { name: 'target' } - on(target, 'message', function() { actual.push(1); }); - on(target, 'message', function() { actual.push(2); }); - on(target, 'message', function() { actual.push(3); }); - on(target, 'bar', function() { actual.push('b') }); - off(target); - - emit(target, 'message'); - emit(target, 'bar'); - - assert.deepEqual([], actual, 'all listeners events were removed'); -}; - -exports['test falsy arguments are fine'] = function(assert) { - let type, listener, actual = []; - let target = { name: 'target' } - on(target, 'bar', function() { actual.push(0) }); - - off(target, 'bar', listener); - emit(target, 'bar'); - assert.deepEqual([ 0 ], actual, '3rd bad ard will keep listeners'); - - off(target, type); - emit(target, 'bar'); - assert.deepEqual([ 0, 0 ], actual, '2nd bad arg will keep listener'); - - off(target, type, listener); - emit(target, 'bar'); - assert.deepEqual([ 0, 0, 0 ], actual, '2nd&3rd bad args will keep listener'); -}; - -exports['test error handling'] = function(assert) { - let target = Object.create(null); - let error = Error('boom!'); - - on(target, 'message', function() { throw error; }) - on(target, 'error', function(boom) { - assert.equal(boom, error, 'thrown exception causes error event'); - }); - emit(target, 'message'); -}; - -exports['test unhandled exceptions'] = function(assert) { - let exceptions = []; - let { loader, messages } = LoaderWithHookedConsole(module); - - let { emit, on } = loader.require('sdk/event/core'); - let target = {}; - let boom = Error('Boom!'); - let drax = Error('Draax!!'); - - on(target, 'message', function() { throw boom; }); - - emit(target, 'message'); - assert.equal(messages.length, 1, 'Got the first exception'); - assert.equal(messages[0].type, 'exception', 'The console message is exception'); - assert.ok(~String(messages[0].msg).indexOf('Boom!'), - 'unhandled exception is logged'); - - on(target, 'error', function() { throw drax; }); - emit(target, 'message'); - assert.equal(messages.length, 2, 'Got the second exception'); - assert.equal(messages[1].type, 'exception', 'The console message is exception'); - assert.ok(~String(messages[1].msg).indexOf('Draax!'), - 'error in error handler is logged'); -}; - -exports['test unhandled errors'] = function(assert) { - let exceptions = []; - let { loader, messages } = LoaderWithHookedConsole(module); - - let { emit, on } = loader.require('sdk/event/core'); - let target = {}; - let boom = Error('Boom!'); - - emit(target, 'error', boom); - assert.equal(messages.length, 1, 'Error was logged'); - assert.equal(messages[0].type, 'exception', 'The console message is exception'); - assert.ok(~String(messages[0].msg).indexOf('Boom!'), - 'unhandled exception is logged'); -}; - - -exports['test count'] = function(assert) { - let target = {}; - - assert.equal(count(target, 'foo'), 0, 'no listeners for "foo" events'); - on(target, 'foo', function() {}); - assert.equal(count(target, 'foo'), 1, 'listener registered'); - on(target, 'foo', function() {}, 2, 'another listener registered'); - off(target) - assert.equal(count(target, 'foo'), 0, 'listeners unregistered'); -}; - -exports['test listen to all events'] = function(assert) { - let actual = []; - let target = {}; - - on(target, 'foo', message => actual.push(message)); - on(target, '*', (type, ...message) => { - actual.push([type].concat(message)); - }); - - emit(target, 'foo', 'hello'); - assert.equal(actual[0], 'hello', - 'non-wildcard listeners still work'); - assert.deepEqual(actual[1], ['foo', 'hello'], - 'wildcard listener called'); - - emit(target, 'bar', 'goodbye'); - assert.deepEqual(actual[2], ['bar', 'goodbye'], - 'wildcard listener called for unbound event name'); -}; - -require('test').run(exports); diff --git a/addon-sdk-1.16/test/test-event-target.js b/addon-sdk-1.16/test/test-event-target.js deleted file mode 100644 index 972906ea..00000000 --- a/addon-sdk-1.16/test/test-event-target.js +++ /dev/null @@ -1,205 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -'use strict'; - -const { emit } = require('sdk/event/core'); -const { EventTarget } = require('sdk/event/target'); -const { Loader } = require('sdk/test/loader'); - -exports['test add a listener'] = function(assert) { - let events = [ { name: 'event#1' }, 'event#2' ]; - let target = EventTarget(); - - target.on('message', function(message) { - assert.equal(this, target, 'this is a target object'); - assert.equal(message, events.shift(), 'message is emitted event'); - }); - - emit(target, 'message', events[0]); - emit(target, 'message', events[0]); -}; - -exports['test pass in listeners'] = function(assert) { - let actual = [ ]; - let target = EventTarget({ - onMessage: function onMessage(message) { - assert.equal(this, target, 'this is an event target'); - actual.push(1); - }, - onFoo: null, - onbla: function() { - assert.fail('`onbla` is not supposed to be called'); - } - }); - target.on('message', function(message) { - assert.equal(this, target, 'this is an event target'); - actual.push(2); - }); - - emit(target, 'message'); - emit(target, 'missing'); - - assert.deepEqual([ 1, 2 ], actual, 'all listeners trigerred in right order'); -}; - -exports['test that listener is unique per type'] = function(assert) { - let actual = [] - let target = EventTarget(); - function listener() { actual.push(1) } - target.on('message', listener); - target.on('message', listener); - target.on('message', listener); - target.on('foo', listener); - target.on('foo', listener); - - emit(target, 'message'); - assert.deepEqual([ 1 ], actual, 'only one message listener added'); - emit(target, 'foo'); - assert.deepEqual([ 1, 1 ], actual, 'same listener added for other event'); -}; - -exports['test event type matters'] = function(assert) { - let target = EventTarget(); - target.on('message', function() { - assert.fail('no event is expected'); - }); - target.on('done', function() { - assert.pass('event is emitted'); - }); - - emit(target, 'foo'); - emit(target, 'done'); -}; - -exports['test all arguments are pasesd'] = function(assert) { - let foo = { name: 'foo' }, bar = 'bar'; - let target = EventTarget(); - target.on('message', function(a, b) { - assert.equal(a, foo, 'first argument passed'); - assert.equal(b, bar, 'second argument passed'); - }); - emit(target, 'message', foo, bar); -}; - -exports['test no side-effects in emit'] = function(assert) { - let target = EventTarget(); - target.on('message', function() { - assert.pass('first listener is called'); - target.on('message', function() { - assert.fail('second listener is called'); - }); - }); - emit(target, 'message'); -}; - -exports['test order of propagation'] = function(assert) { - let actual = []; - let target = EventTarget(); - target.on('message', function() { actual.push(1); }); - target.on('message', function() { actual.push(2); }); - target.on('message', function() { actual.push(3); }); - emit(target, 'message'); - assert.deepEqual([ 1, 2, 3 ], actual, 'called in order they were added'); -}; - -exports['test remove a listener'] = function(assert) { - let target = EventTarget(); - let actual = []; - target.on('message', function listener() { - actual.push(1); - target.on('message', function() { - target.removeListener('message', listener); - actual.push(2); - }) - }); - - target.off('message'); // must do nothing. - emit(target, 'message'); - assert.deepEqual([ 1 ], actual, 'first listener called'); - emit(target, 'message'); - assert.deepEqual([ 1, 1, 2 ], actual, 'second listener called'); - emit(target, 'message'); - assert.deepEqual([ 1, 1, 2, 2, 2 ], actual, 'first listener removed'); -}; - -exports['test error handling'] = function(assert) { - let target = EventTarget(); - let error = Error('boom!'); - - target.on('message', function() { throw error; }) - target.on('error', function(boom) { - assert.equal(boom, error, 'thrown exception causes error event'); - }); - emit(target, 'message'); -}; - -exports['test unhandled errors'] = function(assert) { - let exceptions = []; - let loader = Loader(module); - let { emit } = loader.require('sdk/event/core'); - let { EventTarget } = loader.require('sdk/event/target'); - Object.defineProperties(loader.sandbox('sdk/event/core'), { - console: { value: { - exception: function(e) { - exceptions.push(e); - } - }} - }); - let target = EventTarget(); - let boom = Error('Boom!'); - let drax = Error('Draax!!'); - - target.on('message', function() { throw boom; }); - - emit(target, 'message'); - assert.ok(~String(exceptions[0]).indexOf('Boom!'), - 'unhandled exception is logged'); - - target.on('error', function() { throw drax; }); - emit(target, 'message'); - assert.ok(~String(exceptions[1]).indexOf('Draax!'), - 'error in error handler is logged'); -}; - -exports['test target is chainable'] = function (assert, done) { - let loader = Loader(module); - let exceptions = []; - let { EventTarget } = loader.require('sdk/event/target'); - let { emit } = loader.require('sdk/event/core'); - Object.defineProperties(loader.sandbox('sdk/event/core'), { - console: { value: { - exception: function(e) { - exceptions.push(e); - } - }} - }); - - let emitter = EventTarget(); - let boom = Error('Boom'); - let onceCalled = 0; - - emitter.once('oneTime', function () { - assert.equal(++onceCalled, 1, 'once event called only once'); - }).on('data', function (message) { - assert.equal(message, 'message', 'handles event'); - emit(emitter, 'oneTime'); - emit(emitter, 'data2', 'message2'); - }).on('phony', function () { - assert.fail('removeListener does not remove chained event'); - }).removeListener('phony') - .on('data2', function (message) { - assert.equal(message, 'message2', 'handle chained event'); - emit(emitter, 'oneTime'); - throw boom; - }).on('error', function (error) { - assert.equal(error, boom, 'error handled in chained event'); - done(); - }); - - emit(emitter, 'data', 'message'); -}; - -require('test').run(exports); - diff --git a/addon-sdk-1.16/test/test-event-utils.js b/addon-sdk-1.16/test/test-event-utils.js deleted file mode 100644 index 649c3a3b..00000000 --- a/addon-sdk-1.16/test/test-event-utils.js +++ /dev/null @@ -1,282 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -'use strict'; - -const { on, emit } = require("sdk/event/core"); -const { filter, map, merge, expand, pipe, stripListeners } = require("sdk/event/utils"); -const $ = require("./event/helpers"); - -function isEven(x) !(x % 2) -function inc(x) x + 1 - -exports["test filter events"] = function(assert) { - let input = {}; - let evens = filter(input, isEven); - let actual = []; - on(evens, "data", function(e) actual.push(e)); - - [1, 2, 3, 4, 5, 6, 7].forEach(function(x) emit(input, "data", x)); - - assert.deepEqual(actual, [2, 4, 6], "only even numbers passed through"); -}; - -exports["test filter emits"] = $.emits(function(input, assert) { - let output = filter(input, isEven); - assert(output, [1, 2, 3, 4, 5], [2, 4], "this is `output` & evens passed"); -});; - -exports["test filter reg once"] = $.registerOnce(function(input, assert) { - assert(filter(input, isEven), [1, 2, 3, 4, 5, 6], [2, 4, 6], - "listener can be registered only once"); -}); - -exports["test filter ignores new"] = $.ignoreNew(function(input, assert) { - assert(filter(input, isEven), [1, 2, 3], [2], - "new listener is ignored") -}); - -exports["test filter is FIFO"] = $.FIFO(function(input, assert) { - assert(filter(input, isEven), [1, 2, 3, 4], [2, 4], - "listeners are invoked in fifo order") -}); - -exports["test map events"] = function(assert) { - let input = {}; - let incs = map(input, inc); - let actual = []; - on(incs, "data", function(e) actual.push(e)); - - [1, 2, 3, 4].forEach(function(x) emit(input, "data", x)); - - assert.deepEqual(actual, [2, 3, 4, 5], "all numbers were incremented"); -}; - -exports["test map emits"] = $.emits(function(input, assert) { - let output = map(input, inc); - assert(output, [1, 2, 3], [2, 3, 4], "this is `output` & evens passed"); -});; - -exports["test map reg once"] = $.registerOnce(function(input, assert) { - assert(map(input, inc), [1, 2, 3], [2, 3, 4], - "listener can be registered only once"); -}); - -exports["test map ignores new"] = $.ignoreNew(function(input, assert) { - assert(map(input, inc), [1], [2], - "new listener is ignored") -}); - -exports["test map is FIFO"] = $.FIFO(function(input, assert) { - assert(map(input, inc), [1, 2, 3, 4], [2, 3, 4, 5], - "listeners are invoked in fifo order") -}); - -exports["test merge stream[stream]"] = function(assert) { - let a = {}, b = {}, c = {}; - let inputs = {}; - let actual = []; - - on(merge(inputs), "data", function($) actual.push($)) - - emit(inputs, "data", a); - emit(a, "data", "a1"); - emit(inputs, "data", b); - emit(b, "data", "b1"); - emit(a, "data", "a2"); - emit(inputs, "data", c); - emit(c, "data", "c1"); - emit(c, "data", "c2"); - emit(b, "data", "b2"); - emit(a, "data", "a3"); - - assert.deepEqual(actual, ["a1", "b1", "a2", "c1", "c2", "b2", "a3"], - "all inputs data merged into one"); -}; - -exports["test merge array[stream]"] = function(assert) { - let a = {}, b = {}, c = {}; - let inputs = {}; - let actual = []; - - on(merge([a, b, c]), "data", function($) actual.push($)) - - emit(a, "data", "a1"); - emit(b, "data", "b1"); - emit(a, "data", "a2"); - emit(c, "data", "c1"); - emit(c, "data", "c2"); - emit(b, "data", "b2"); - emit(a, "data", "a3"); - - assert.deepEqual(actual, ["a1", "b1", "a2", "c1", "c2", "b2", "a3"], - "all inputs data merged into one"); -}; - -exports["test merge emits"] = $.emits(function(input, assert) { - let evens = filter(input, isEven) - let output = merge([evens, input]); - assert(output, [1, 2, 3], [1, 2, 2, 3], "this is `output` & evens passed"); -}); - - -exports["test merge reg once"] = $.registerOnce(function(input, assert) { - let evens = filter(input, isEven) - let output = merge([input, evens]); - assert(output, [1, 2, 3, 4], [1, 2, 2, 3, 4, 4], - "listener can be registered only once"); -}); - -exports["test merge ignores new"] = $.ignoreNew(function(input, assert) { - let evens = filter(input, isEven) - let output = merge([input, evens]) - assert(output, [1], [1], - "new listener is ignored") -}); - -exports["test marge is FIFO"] = $.FIFO(function(input, assert) { - let evens = filter(input, isEven) - let output = merge([input, evens]) - - assert(output, [1, 2, 3, 4], [1, 2, 2, 3, 4, 4], - "listeners are invoked in fifo order") -}); - -exports["test expand"] = function(assert) { - let a = {}, b = {}, c = {}; - let inputs = {}; - let actual = []; - - on(expand(inputs, function($) $()), "data", function($) actual.push($)) - - emit(inputs, "data", function() a); - emit(a, "data", "a1"); - emit(inputs, "data", function() b); - emit(b, "data", "b1"); - emit(a, "data", "a2"); - emit(inputs, "data", function() c); - emit(c, "data", "c1"); - emit(c, "data", "c2"); - emit(b, "data", "b2"); - emit(a, "data", "a3"); - - assert.deepEqual(actual, ["a1", "b1", "a2", "c1", "c2", "b2", "a3"], - "all inputs data merged into one"); -}; - -exports["test pipe"] = function (assert, done) { - let src = {}; - let dest = {}; - - let aneventCount = 0, multiargsCount = 0; - let wildcardCount = {}; - - on(dest, 'an-event', arg => { - assert.equal(arg, 'my-arg', 'piped argument to event'); - ++aneventCount; - check(); - }); - on(dest, 'multiargs', (...data) => { - assert.equal(data[0], 'a', 'multiple arguments passed via pipe'); - assert.equal(data[1], 'b', 'multiple arguments passed via pipe'); - assert.equal(data[2], 'c', 'multiple arguments passed via pipe'); - ++multiargsCount; - check(); - }); - - on(dest, '*', (name, ...data) => { - wildcardCount[name] = (wildcardCount[name] || 0) + 1; - if (name === 'multiargs') { - assert.equal(data[0], 'a', 'multiple arguments passed via pipe, wildcard'); - assert.equal(data[1], 'b', 'multiple arguments passed via pipe, wildcard'); - assert.equal(data[2], 'c', 'multiple arguments passed via pipe, wildcard'); - } - if (name === 'an-event') - assert.equal(data[0], 'my-arg', 'argument passed via pipe, wildcard'); - check(); - }); - - pipe(src, dest); - - for (let i = 0; i < 3; i++) - emit(src, 'an-event', 'my-arg'); - - emit(src, 'multiargs', 'a', 'b', 'c'); - - function check () { - if (aneventCount === 3 && multiargsCount === 1 && - wildcardCount['an-event'] === 3 && - wildcardCount['multiargs'] === 1) - done(); - } -}; - -exports["test pipe multiple targets"] = function (assert) { - let src1 = {}; - let src2 = {}; - let middle = {}; - let dest = {}; - - pipe(src1, middle); - pipe(src2, middle); - pipe(middle, dest); - - let middleFired = 0; - let destFired = 0; - let src1Fired = 0; - let src2Fired = 0; - - on(src1, '*', () => src1Fired++); - on(src2, '*', () => src2Fired++); - on(middle, '*', () => middleFired++); - on(dest, '*', () => destFired++); - - emit(src1, 'ev'); - assert.equal(src1Fired, 1, 'event triggers in source in pipe chain'); - assert.equal(middleFired, 1, 'event passes through the middle of pipe chain'); - assert.equal(destFired, 1, 'event propagates to end of pipe chain'); - assert.equal(src2Fired, 0, 'event does not fire on alternative chain routes'); - - emit(src2, 'ev'); - assert.equal(src2Fired, 1, 'event triggers in source in pipe chain'); - assert.equal(middleFired, 2, - 'event passes through the middle of pipe chain from different src'); - assert.equal(destFired, 2, - 'event propagates to end of pipe chain from different src'); - assert.equal(src1Fired, 1, 'event does not fire on alternative chain routes'); - - emit(middle, 'ev'); - assert.equal(middleFired, 3, - 'event triggers in source of pipe chain'); - assert.equal(destFired, 3, - 'event propagates to end of pipe chain from middle src'); - assert.equal(src1Fired, 1, 'event does not fire on alternative chain routes'); - assert.equal(src2Fired, 1, 'event does not fire on alternative chain routes'); -}; - -exports['test stripListeners'] = function (assert) { - var options = { - onAnEvent: noop1, - onMessage: noop2, - verb: noop1, - value: 100 - }; - - var stripped = stripListeners(options); - assert.ok(stripped !== options, 'stripListeners should return a new object'); - assert.equal(options.onAnEvent, noop1, 'stripListeners does not affect original'); - assert.equal(options.onMessage, noop2, 'stripListeners does not affect original'); - assert.equal(options.verb, noop1, 'stripListeners does not affect original'); - assert.equal(options.value, 100, 'stripListeners does not affect original'); - - assert.ok(!stripped.onAnEvent, 'stripListeners removes `on*` values'); - assert.ok(!stripped.onMessage, 'stripListeners removes `on*` values'); - assert.equal(stripped.verb, noop1, 'stripListeners leaves not `on*` values'); - assert.equal(stripped.value, 100, 'stripListeners leaves not `on*` values'); - - function noop1 () {} - function noop2 () {} -}; - -require('test').run(exports); diff --git a/addon-sdk-1.16/test/test-events.js b/addon-sdk-1.16/test/test-events.js deleted file mode 100644 index 99786adf..00000000 --- a/addon-sdk-1.16/test/test-events.js +++ /dev/null @@ -1,281 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -'use strict'; - -const { LoaderWithHookedConsole } = require("sdk/test/loader"); - -// Exposing private methods as public in order to test -const EventEmitter = require('sdk/deprecated/events').EventEmitter.compose({ - listeners: function(type) this._listeners(type), - emit: function() this._emit.apply(this, arguments), - emitOnObject: function() this._emitOnObject.apply(this, arguments), - removeAllListeners: function(type) this._removeAllListeners(type) -}); - -exports['test:add listeners'] = function(assert) { - let e = new EventEmitter(); - - let events_new_listener_emited = []; - let times_hello_emited = 0; - - e.on("newListener", function (event, listener) { - events_new_listener_emited.push(event) - }) - - e.on("hello", function (a, b) { - times_hello_emited += 1 - assert.equal("a", a) - assert.equal("b", b) - assert.equal(this, e, '`this` pseudo-variable is bound to instance'); - }) - - e.emit("hello", "a", "b") -}; - -exports['test:removeListener'] = function(assert) { - let count = 0; - - function listener1 () { - count++; - } - function listener2 () { - count++; - } - - // test adding and removing listener - let e1 = new EventEmitter(); - assert.equal(0, e1.listeners('hello').length); - e1.on("hello", listener1); - assert.equal(1, e1.listeners('hello').length); - assert.equal(listener1, e1.listeners('hello')[0]); - e1.removeListener("hello", listener1); - assert.equal(0, e1.listeners('hello').length); - e1.emit("hello", ""); - assert.equal(0, count); - - // test adding one listener and removing another which was not added - let e2 = new EventEmitter(); - assert.equal(0, e2.listeners('hello').length); - e2.on("hello", listener1); - assert.equal(1, e2.listeners('hello').length); - e2.removeListener("hello", listener2); - assert.equal(1, e2.listeners('hello').length); - assert.equal(listener1, e2.listeners('hello')[0]); - e2.emit("hello", ""); - assert.equal(1, count); - - // test adding 2 listeners, and removing one - let e3 = new EventEmitter(); - assert.equal(0, e3.listeners('hello').length); - e3.on("hello", listener1); - assert.equal(1, e3.listeners('hello').length); - e3.on("hello", listener2); - assert.equal(2, e3.listeners('hello').length); - e3.removeListener("hello", listener1); - assert.equal(1, e3.listeners('hello').length); - assert.equal(listener2, e3.listeners('hello')[0]); - e3.emit("hello", ""); - assert.equal(2, count); -}; - -exports['test:removeAllListeners'] = function(assert) { - let count = 0; - - function listener1 () { - count++; - } - function listener2 () { - count++; - } - - // test adding a listener and removing all of that type - let e1 = new EventEmitter(); - e1.on("hello", listener1); - assert.equal(1, e1.listeners('hello').length); - e1.removeAllListeners("hello"); - assert.equal(0, e1.listeners('hello').length); - e1.emit("hello", ""); - assert.equal(0, count); - - // test adding a listener and removing all of another type - let e2 = new EventEmitter(); - e2.on("hello", listener1); - assert.equal(1, e2.listeners('hello').length); - e2.removeAllListeners('goodbye'); - assert.equal(1, e2.listeners('hello').length); - e2.emit("hello", ""); - assert.equal(1, count); - - // test adding 1+ listeners and removing all of that type - let e3 = new EventEmitter(); - e3.on("hello", listener1); - assert.equal(1, e3.listeners('hello').length); - e3.on("hello", listener2); - assert.equal(2, e3.listeners('hello').length); - e3.removeAllListeners("hello"); - assert.equal(0, e3.listeners('hello').length); - e3.emit("hello", ""); - assert.equal(1, count); - - // test adding 2 listeners for 2 types and removing all listeners - let e4 = new EventEmitter(); - e4.on("hello", listener1); - assert.equal(1, e4.listeners('hello').length); - e4.on('goodbye', listener2); - assert.equal(1, e4.listeners('goodbye').length); - e4.emit("goodbye", ""); - e4.removeAllListeners(); - assert.equal(0, e4.listeners('hello').length); - assert.equal(0, e4.listeners('goodbye').length); - e4.emit("hello", ""); - e4.emit("goodbye", ""); - assert.equal(2, count); -}; - -exports['test: modify in emit'] = function(assert) { - let callbacks_called = [ ]; - let e = new EventEmitter(); - - function callback1() { - callbacks_called.push("callback1"); - e.on("foo", callback2); - e.on("foo", callback3); - e.removeListener("foo", callback1); - } - function callback2() { - callbacks_called.push("callback2"); - e.removeListener("foo", callback2); - } - function callback3() { - callbacks_called.push("callback3"); - e.removeListener("foo", callback3); - } - - e.on("foo", callback1); - assert.equal(1, e.listeners("foo").length); - - e.emit("foo"); - assert.equal(2, e.listeners("foo").length); - assert.equal(1, callbacks_called.length); - assert.equal('callback1', callbacks_called[0]); - - e.emit("foo"); - assert.equal(0, e.listeners("foo").length); - assert.equal(3, callbacks_called.length); - assert.equal('callback1', callbacks_called[0]); - assert.equal('callback2', callbacks_called[1]); - assert.equal('callback3', callbacks_called[2]); - - e.emit("foo"); - assert.equal(0, e.listeners("foo").length); - assert.equal(3, callbacks_called.length); - assert.equal('callback1', callbacks_called[0]); - assert.equal('callback2', callbacks_called[1]); - assert.equal('callback3', callbacks_called[2]); - - e.on("foo", callback1); - e.on("foo", callback2); - assert.equal(2, e.listeners("foo").length); - e.removeAllListeners("foo"); - assert.equal(0, e.listeners("foo").length); - - // Verify that removing callbacks while in emit allows emits to propagate to - // all listeners - callbacks_called = [ ]; - - e.on("foo", callback2); - e.on("foo", callback3); - assert.equal(2, e.listeners("foo").length); - e.emit("foo"); - assert.equal(2, callbacks_called.length); - assert.equal('callback2', callbacks_called[0]); - assert.equal('callback3', callbacks_called[1]); - assert.equal(0, e.listeners("foo").length); -}; - -exports['test:adding same listener'] = function(assert) { - function foo() {} - let e = new EventEmitter(); - e.on("foo", foo); - e.on("foo", foo); - assert.equal( - 1, - e.listeners("foo").length, - "listener reregistration is ignored" - ); -} - -exports['test:errors are reported if listener throws'] = function(assert) { - let e = new EventEmitter(), - reported = false; - e.on('error', function(e) reported = true) - e.on('boom', function() { throw new Error('Boom!') }); - e.emit('boom', 3); - assert.ok(reported, 'error should be reported through event'); -}; - -exports['test:emitOnObject'] = function(assert) { - let e = new EventEmitter(); - - e.on("foo", function() { - assert.equal(this, e, "`this` should be emitter"); - }); - e.emitOnObject(e, "foo"); - - e.on("bar", function() { - assert.equal(this, obj, "`this` should be other object"); - }); - let obj = {}; - e.emitOnObject(obj, "bar"); -}; - -exports['test:once'] = function(assert) { - let e = new EventEmitter(); - let called = false; - - e.once('foo', function(value) { - assert.ok(!called, "listener called only once"); - assert.equal(value, "bar", "correct argument was passed"); - }); - - e.emit('foo', 'bar'); - e.emit('foo', 'baz'); -}; - -exports["test:removing once"] = function(assert) { - let e = require("sdk/deprecated/events").EventEmitterTrait.create(); - e.once("foo", function() { assert.pass("listener was called"); }); - e.once("error", function() { assert.fail("error event was emitted"); }); - e._emit("foo", "bug-656684"); -}; - -// Bug 726967: Ensure that `emit` doesn't do an infinite loop when `error` -// listener throws an exception -exports['test:emitLoop'] = function(assert) { - // Override the console for this test so it doesn't log the exception to the - // test output - let { loader } = LoaderWithHookedConsole(module); - - let EventEmitter = loader.require('sdk/deprecated/events').EventEmitter.compose({ - listeners: function(type) this._listeners(type), - emit: function() this._emit.apply(this, arguments), - emitOnObject: function() this._emitOnObject.apply(this, arguments), - removeAllListeners: function(type) this._removeAllListeners(type) - }); - - let e = new EventEmitter(); - - e.on("foo", function() { - throw new Error("foo"); - }); - - e.on("error", function() { - throw new Error("error"); - }); - e.emit("foo"); - - assert.pass("emit didn't looped"); -}; - -require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-file.js b/addon-sdk-1.16/test/test-file.js deleted file mode 100644 index 9dc6c3b4..00000000 --- a/addon-sdk-1.16/test/test-file.js +++ /dev/null @@ -1,275 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const { pathFor } = require('sdk/system'); -const file = require("sdk/io/file"); -const url = require("sdk/url"); - -const byteStreams = require("sdk/io/byte-streams"); -const textStreams = require("sdk/io/text-streams"); - -const ERRORS = { - FILE_NOT_FOUND: /^path does not exist: .+$/, - NOT_A_DIRECTORY: /^path is not a directory: .+$/, - NOT_A_FILE: /^path is not a file: .+$/, -}; - -// Use profile directory to list / read / write files. -const profilePath = pathFor('ProfD'); -const fileNameInProfile = 'compatibility.ini'; -const dirNameInProfile = 'extensions'; -const filePathInProfile = file.join(profilePath, fileNameInProfile); -const dirPathInProfile = file.join(profilePath, dirNameInProfile); - -exports.testDirName = function(assert) { - assert.equal(file.dirname(dirPathInProfile), profilePath, - "file.dirname() of dir should return parent dir"); - - assert.equal(file.dirname(filePathInProfile), profilePath, - "file.dirname() of file should return its dir"); - - let dir = profilePath; - while (dir) - dir = file.dirname(dir); - - assert.equal(dir, "", - "dirname should return empty string when dir has no parent"); -}; - -exports.testBasename = function(assert) { - // Get the top-most path -- the path with no basename. E.g., on Unix-like - // systems this will be /. We'll use it below to build up some test paths. - // We have to go to this trouble because file.join() needs a legal path as a - // base case; join("foo", "bar") doesn't work unfortunately. - let topPath = profilePath; - let parentPath = file.dirname(topPath); - while (parentPath) { - topPath = parentPath; - parentPath = file.dirname(topPath); - } - - let path = topPath; - assert.equal(file.basename(path), "", - "basename should work on paths with no components"); - - path = file.join(path, "foo"); - assert.equal(file.basename(path), "foo", - "basename should work on paths with a single component"); - - path = file.join(path, "bar"); - assert.equal(file.basename(path), "bar", - "basename should work on paths with multiple components"); -}; - -exports.testList = function(assert) { - let list = file.list(profilePath); - let found = [ true for each (name in list) - if (name === fileNameInProfile) ]; - - if (found.length > 1) - assert.fail("a dir can't contain two files of the same name!"); - assert.equal(found[0], true, "file.list() should work"); - - assert.throws(function() { - file.list(filePathInProfile); - }, ERRORS.NOT_A_DIRECTORY, "file.list() on non-dir should raise error"); - - assert.throws(function() { - file.list(file.join(dirPathInProfile, "does-not-exist")); - }, ERRORS.FILE_NOT_FOUND, "file.list() on nonexistent should raise error"); -}; - -exports.testRead = function(assert) { - let contents = file.read(filePathInProfile); - assert.ok(/Compatibility/.test(contents), - "file.read() should work"); - - assert.throws(function() { - file.read(file.join(dirPathInProfile, "does-not-exists")); - }, ERRORS.FILE_NOT_FOUND, "file.read() on nonexistent file should throw"); - - assert.throws(function() { - file.read(dirPathInProfile); - }, ERRORS.NOT_A_FILE, "file.read() on dir should throw"); -}; - -exports.testJoin = function(assert) { - let baseDir = file.dirname(filePathInProfile); - - assert.equal(file.join(baseDir, fileNameInProfile), - filePathInProfile, "file.join() should work"); -}; - -exports.testOpenNonexistentForRead = function (assert) { - var filename = file.join(profilePath, 'does-not-exists'); - assert.throws(function() { - file.open(filename); - }, ERRORS.FILE_NOT_FOUND, "file.open() on nonexistent file should throw"); - - assert.throws(function() { - file.open(filename, "r"); - }, ERRORS.FILE_NOT_FOUND, "file.open('r') on nonexistent file should throw"); - - assert.throws(function() { - file.open(filename, "zz"); - }, ERRORS.FILE_NOT_FOUND, "file.open('zz') on nonexistent file should throw"); -}; - -exports.testOpenNonexistentForWrite = function (assert) { - let filename = file.join(profilePath, 'open.txt'); - - let stream = file.open(filename, "w"); - stream.close(); - - assert.ok(file.exists(filename), - "file.exists() should return true after file.open('w')"); - file.remove(filename); - assert.ok(!file.exists(filename), - "file.exists() should return false after file.remove()"); - - stream = file.open(filename, "rw"); - stream.close(); - - assert.ok(file.exists(filename), - "file.exists() should return true after file.open('rw')"); - file.remove(filename); - assert.ok(!file.exists(filename), - "file.exists() should return false after file.remove()"); -}; - -exports.testOpenDirectory = function (assert) { - let dir = dirPathInProfile; - assert.throws(function() { - file.open(dir); - }, ERRORS.NOT_A_FILE, "file.open() on directory should throw"); - - assert.throws(function() { - file.open(dir, "w"); - }, ERRORS.NOT_A_FILE, "file.open('w') on directory should throw"); -}; - -exports.testOpenTypes = function (assert) { - let filename = file.join(profilePath, 'open-types.txt'); - - - // Do the opens first to create the data file. - var stream = file.open(filename, "w"); - assert.ok(stream instanceof textStreams.TextWriter, - "open(w) should return a TextWriter"); - stream.close(); - - stream = file.open(filename, "wb"); - assert.ok(stream instanceof byteStreams.ByteWriter, - "open(wb) should return a ByteWriter"); - stream.close(); - - stream = file.open(filename); - assert.ok(stream instanceof textStreams.TextReader, - "open() should return a TextReader"); - stream.close(); - - stream = file.open(filename, "r"); - assert.ok(stream instanceof textStreams.TextReader, - "open(r) should return a TextReader"); - stream.close(); - - stream = file.open(filename, "b"); - assert.ok(stream instanceof byteStreams.ByteReader, - "open(b) should return a ByteReader"); - stream.close(); - - stream = file.open(filename, "rb"); - assert.ok(stream instanceof byteStreams.ByteReader, - "open(rb) should return a ByteReader"); - stream.close(); - - file.remove(filename); -}; - -exports.testMkpathRmdir = function (assert) { - let basePath = profilePath; - let dirs = []; - for (let i = 0; i < 3; i++) - dirs.push("test-file-dir"); - - let paths = []; - for (let i = 0; i < dirs.length; i++) { - let args = [basePath].concat(dirs.slice(0, i + 1)); - paths.unshift(file.join.apply(null, args)); - } - - for (let i = 0; i < paths.length; i++) { - assert.ok(!file.exists(paths[i]), - "Sanity check: path should not exist: " + paths[i]); - } - - file.mkpath(paths[0]); - assert.ok(file.exists(paths[0]), "mkpath should create path: " + paths[0]); - - for (let i = 0; i < paths.length; i++) { - file.rmdir(paths[i]); - assert.ok(!file.exists(paths[i]), - "rmdir should remove path: " + paths[i]); - } -}; - -exports.testMkpathTwice = function (assert) { - let dir = profilePath; - let path = file.join(dir, "test-file-dir"); - assert.ok(!file.exists(path), - "Sanity check: path should not exist: " + path); - file.mkpath(path); - assert.ok(file.exists(path), "mkpath should create path: " + path); - file.mkpath(path); - assert.ok(file.exists(path), - "After second mkpath, path should still exist: " + path); - file.rmdir(path); - assert.ok(!file.exists(path), "rmdir should remove path: " + path); -}; - -exports.testMkpathExistingNondirectory = function (assert) { - var fname = file.join(profilePath, 'conflict.txt'); - file.open(fname, "w").close(); - assert.ok(file.exists(fname), "File should exist"); - assert.throws(function() file.mkpath(fname), - /^The path already exists and is not a directory: .+$/, - "mkpath on file should raise error"); - file.remove(fname); -}; - -exports.testRmdirNondirectory = function (assert) { - var fname = file.join(profilePath, 'not-a-dir') - file.open(fname, "w").close(); - assert.ok(file.exists(fname), "File should exist"); - assert.throws(function() { - file.rmdir(fname); - }, ERRORS.NOT_A_DIRECTORY, "rmdir on file should raise error"); - file.remove(fname); - assert.ok(!file.exists(fname), "File should not exist"); - assert.throws(function () file.rmdir(fname), - ERRORS.FILE_NOT_FOUND, - "rmdir on non-existing file should raise error"); -}; - -exports.testRmdirNonempty = function (assert) { - let dir = profilePath; - let path = file.join(dir, "test-file-dir"); - assert.ok(!file.exists(path), - "Sanity check: path should not exist: " + path); - file.mkpath(path); - let filePath = file.join(path, "file"); - file.open(filePath, "w").close(); - assert.ok(file.exists(filePath), - "Sanity check: path should exist: " + filePath); - assert.throws(function () file.rmdir(path), - /^The directory is not empty: .+$/, - "rmdir on non-empty directory should raise error"); - file.remove(filePath); - file.rmdir(path); - assert.ok(!file.exists(path), "Path should not exist"); -}; - -require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-frame-utils.js b/addon-sdk-1.16/test/test-frame-utils.js deleted file mode 100644 index 402016d1..00000000 --- a/addon-sdk-1.16/test/test-frame-utils.js +++ /dev/null @@ -1,59 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -'use strict'; - -const { create } = require('sdk/frame/utils'); -const { open, close } = require('sdk/window/helpers'); - -exports['test frame creation'] = function(assert, done) { - open('data:text/html;charset=utf-8,Window').then(function (window) { - let frame = create(window.document); - - assert.equal(frame.getAttribute('type'), 'content', - 'frame type is content'); - assert.ok(frame.contentWindow, 'frame has contentWindow'); - assert.equal(frame.contentWindow.location.href, 'about:blank', - 'by default "about:blank" is loaded'); - assert.equal(frame.docShell.allowAuth, false, 'auth disabled by default'); - assert.equal(frame.docShell.allowJavascript, false, 'js disabled by default'); - assert.equal(frame.docShell.allowPlugins, false, - 'plugins disabled by default'); - close(window).then(done); - }); -}; - -exports['test fram has js disabled by default'] = function(assert, done) { - open('data:text/html;charset=utf-8,window').then(function (window) { - let frame = create(window.document, { - uri: 'data:text/html;charset=utf-8,', - }); - frame.contentWindow.addEventListener('DOMContentLoaded', function ready() { - frame.contentWindow.removeEventListener('DOMContentLoaded', ready, false); - assert.ok(!~frame.contentDocument.documentElement.innerHTML.indexOf('JS'), - 'JS was executed'); - - close(window).then(done); - }, false); - }); -}; - -exports['test frame with js enabled'] = function(assert, done) { - open('data:text/html;charset=utf-8,window').then(function (window) { - let frame = create(window.document, { - uri: 'data:text/html;charset=utf-8,', - allowJavascript: true - }); - frame.contentWindow.addEventListener('DOMContentLoaded', function ready() { - frame.contentWindow.removeEventListener('DOMContentLoaded', ready, false); - assert.ok(~frame.contentDocument.documentElement.innerHTML.indexOf('JS'), - 'JS was executed'); - - close(window).then(done); - }, false); - }); -}; - -require('test').run(exports); diff --git a/addon-sdk-1.16/test/test-fs.js b/addon-sdk-1.16/test/test-fs.js deleted file mode 100644 index efb6df70..00000000 --- a/addon-sdk-1.16/test/test-fs.js +++ /dev/null @@ -1,621 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const { pathFor, platform } = require("sdk/system"); -const fs = require("sdk/io/fs"); -const url = require("sdk/url"); -const path = require("sdk/fs/path"); -const { defer } = require("sdk/core/promise"); -const { Buffer } = require("sdk/io/buffer"); -const { is } = require("sdk/system/xul-app"); - -// Use profile directory to list / read / write files. -const profilePath = pathFor("ProfD"); -const fileNameInProfile = "compatibility.ini"; -const dirNameInProfile = "extensions"; -const filePathInProfile = path.join(profilePath, fileNameInProfile); -const dirPathInProfile = path.join(profilePath, dirNameInProfile); -const mkdirPath = path.join(profilePath, "sdk-fixture-mkdir"); -const writePath = path.join(profilePath, "sdk-fixture-writeFile"); -const unlinkPath = path.join(profilePath, "sdk-fixture-unlink"); -const truncatePath = path.join(profilePath, "sdk-fixture-truncate"); -const renameFromPath = path.join(profilePath, "sdk-fixture-rename-from"); -const renameToPath = path.join(profilePath, "sdk-fixture-rename-to"); -const chmodPath = path.join(profilePath, "sdk-fixture-chmod"); - -const profileEntries = [ - "compatibility.ini", - "extensions", - "prefs.js" - // There are likely to be a lot more files but we can"t really - // on consistent list so we limit to this. -]; - -const isWindows = platform.indexOf('win') === 0; - -exports["test readdir"] = function(assert, end) { - var async = false; - fs.readdir(profilePath, function(error, entries) { - assert.ok(async, "readdir is async"); - assert.ok(!error, "there is no error when reading directory"); - assert.ok(profileEntries.length <= entries.length, - "got at least number of entries we expect"); - assert.ok(profileEntries.every(function(entry) { - return entries.indexOf(entry) >= 0; - }), "all profiles are present"); - end(); - }); - - async = true; -}; - -exports["test readdir error"] = function(assert, end) { - var async = false; - var path = profilePath + "-does-not-exists"; - fs.readdir(path, function(error, entries) { - assert.ok(async, "readdir is async"); - assert.equal(error.message, "ENOENT, readdir " + path); - assert.equal(error.code, "ENOENT", "error has a code"); - assert.equal(error.path, path, "error has a path"); - assert.equal(error.errno, 34, "error has a errno"); - end(); - }); - - async = true; -}; - -exports["test readdirSync"] = function(assert) { - var async = false; - var entries = fs.readdirSync(profilePath); - assert.ok(profileEntries.length <= entries.length, - "got at least number of entries we expect"); - assert.ok(profileEntries.every(function(entry) { - return entries.indexOf(entry) >= 0; - }), "all profiles are present"); -}; - -exports["test readdirSync error"] = function(assert) { - var async = false; - var path = profilePath + "-does-not-exists"; - try { - fs.readdirSync(path); - assert.fail(Error("No error was thrown")); - } catch (error) { - assert.equal(error.message, "ENOENT, readdir " + path); - assert.equal(error.code, "ENOENT", "error has a code"); - assert.equal(error.path, path, "error has a path"); - assert.equal(error.errno, 34, "error has a errno"); - } -}; - -exports["test readFile"] = function(assert, end) { - let async = false; - fs.readFile(filePathInProfile, function(error, content) { - assert.ok(async, "readFile is async"); - assert.ok(!error, "error is falsy"); - - assert.ok(Buffer.isBuffer(content), "readFile returns buffer"); - assert.ok(typeof(content.length) === "number", "buffer has length"); - assert.ok(content.toString().indexOf("[Compatibility]") >= 0, - "content contains expected data"); - end(); - }); - async = true; -}; - -exports["test readFile error"] = function(assert, end) { - let async = false; - let path = filePathInProfile + "-does-not-exists"; - fs.readFile(path, function(error, content) { - assert.ok(async, "readFile is async"); - assert.equal(error.message, "ENOENT, open " + path); - assert.equal(error.code, "ENOENT", "error has a code"); - assert.equal(error.path, path, "error has a path"); - assert.equal(error.errno, 34, "error has a errno"); - - end(); - }); - async = true; -}; - -exports["test readFileSync not implemented"] = function(assert) { - let buffer = fs.readFileSync(filePathInProfile); - assert.ok(buffer.toString().indexOf("[Compatibility]") >= 0, - "read content"); -}; - -exports["test fs.stat file"] = function(assert, end) { - let async = false; - let path = filePathInProfile; - fs.stat(path, function(error, stat) { - assert.ok(async, "fs.stat is async"); - assert.ok(!error, "error is falsy"); - assert.ok(!stat.isDirectory(), "not a dir"); - assert.ok(stat.isFile(), "is a file"); - assert.ok(!stat.isSymbolicLink(), "isn't a symlink"); - assert.ok(typeof(stat.size) === "number", "size is a number"); - assert.ok(stat.exists === true, "file exists"); - assert.ok(typeof(stat.isBlockDevice()) === "boolean"); - assert.ok(typeof(stat.isCharacterDevice()) === "boolean"); - assert.ok(typeof(stat.isFIFO()) === "boolean"); - assert.ok(typeof(stat.isSocket()) === "boolean"); - assert.ok(typeof(stat.hidden) === "boolean"); - assert.ok(typeof(stat.writable) === "boolean") - assert.ok(stat.readable === true); - - end(); - }); - async = true; -}; - -exports["test fs.stat dir"] = function(assert, end) { - let async = false; - let path = dirPathInProfile; - fs.stat(path, function(error, stat) { - assert.ok(async, "fs.stat is async"); - assert.ok(!error, "error is falsy"); - assert.ok(stat.isDirectory(), "is a dir"); - assert.ok(!stat.isFile(), "not a file"); - assert.ok(!stat.isSymbolicLink(), "isn't a symlink"); - assert.ok(typeof(stat.size) === "number", "size is a number"); - assert.ok(stat.exists === true, "file exists"); - assert.ok(typeof(stat.isBlockDevice()) === "boolean"); - assert.ok(typeof(stat.isCharacterDevice()) === "boolean"); - assert.ok(typeof(stat.isFIFO()) === "boolean"); - assert.ok(typeof(stat.isSocket()) === "boolean"); - assert.ok(typeof(stat.hidden) === "boolean"); - assert.ok(typeof(stat.writable) === "boolean") - assert.ok(typeof(stat.readable) === "boolean"); - - end(); - }); - async = true; -}; - -exports["test fs.stat error"] = function(assert, end) { - let async = false; - let path = filePathInProfile + "-does-not-exists"; - fs.stat(path, function(error, stat) { - assert.ok(async, "fs.stat is async"); - assert.equal(error.message, "ENOENT, stat " + path); - assert.equal(error.code, "ENOENT", "error has a code"); - assert.equal(error.path, path, "error has a path"); - assert.equal(error.errno, 34, "error has a errno"); - - end(); - }); - async = true; -}; - -exports["test fs.exists NO"] = function(assert, end) { - let async = false - let path = filePathInProfile + "-does-not-exists"; - fs.exists(path, function(error, value) { - assert.ok(async, "fs.exists is async"); - assert.ok(!error, "error is falsy"); - assert.ok(!value, "file does not exists"); - end(); - }); - async = true; -}; - -exports["test fs.exists YES"] = function(assert, end) { - let async = false - let path = filePathInProfile - fs.exists(path, function(error, value) { - assert.ok(async, "fs.exists is async"); - assert.ok(!error, "error is falsy"); - assert.ok(value, "file exists"); - end(); - }); - async = true; -}; - -exports["test fs.exists NO"] = function(assert, end) { - let async = false - let path = filePathInProfile + "-does-not-exists"; - fs.exists(path, function(error, value) { - assert.ok(async, "fs.exists is async"); - assert.ok(!error, "error is falsy"); - assert.ok(!value, "file does not exists"); - end(); - }); - async = true; -}; - -exports["test fs.existsSync"] = function(assert) { - let path = filePathInProfile - assert.equal(fs.existsSync(path), true, "exists"); - assert.equal(fs.existsSync(path + "-does-not-exists"), false, "exists"); -}; - -exports["test fs.mkdirSync fs.rmdirSync"] = function(assert) { - let path = mkdirPath; - - assert.equal(fs.existsSync(path), false, "does not exists"); - fs.mkdirSync(path); - assert.equal(fs.existsSync(path), true, "dir was created"); - try { - fs.mkdirSync(path); - assert.fail(Error("mkdir on existing should throw")); - } catch (error) { - assert.equal(error.message, "EEXIST, mkdir " + path); - assert.equal(error.code, "EEXIST", "error has a code"); - assert.equal(error.path, path, "error has a path"); - assert.equal(error.errno, 47, "error has a errno"); - } - fs.rmdirSync(path); - assert.equal(fs.existsSync(path), false, "dir was removed"); -}; - -exports["test fs.mkdir"] = function(assert, end) { - let path = mkdirPath; - - if (!fs.existsSync(path)) { - let async = false; - fs.mkdir(path, function(error) { - assert.ok(async, "mkdir is async"); - assert.ok(!error, "no error"); - assert.equal(fs.existsSync(path), true, "dir was created"); - fs.rmdirSync(path); - assert.equal(fs.existsSync(path), false, "dir was removed"); - end(); - }); - async = true; - } -}; - -exports["test fs.mkdir error"] = function(assert, end) { - let path = mkdirPath; - - if (!fs.existsSync(path)) { - fs.mkdirSync(path); - let async = false; - fs.mkdir(path, function(error) { - assert.ok(async, "mkdir is async"); - assert.equal(error.message, "EEXIST, mkdir " + path); - assert.equal(error.code, "EEXIST", "error has a code"); - assert.equal(error.path, path, "error has a path"); - assert.equal(error.errno, 47, "error has a errno"); - fs.rmdirSync(path); - assert.equal(fs.existsSync(path), false, "dir was removed"); - end(); - }); - async = true; - } -}; - -exports["test fs.rmdir"] = function(assert, end) { - let path = mkdirPath; - - if (!fs.existsSync(path)) { - fs.mkdirSync(path); - assert.equal(fs.existsSync(path), true, "dir exists"); - let async = false; - fs.rmdir(path, function(error) { - assert.ok(async, "mkdir is async"); - assert.ok(!error, "no error"); - assert.equal(fs.existsSync(path), false, "dir was removed"); - end(); - }); - async = true; - } -}; - - -exports["test fs.rmdir error"] = function(assert, end) { - let path = mkdirPath; - - if (!fs.existsSync(path)) { - assert.equal(fs.existsSync(path), false, "dir doesn't exists"); - let async = false; - fs.rmdir(path, function(error) { - assert.ok(async, "mkdir is async"); - assert.equal(error.message, "ENOENT, remove " + path); - assert.equal(error.code, "ENOENT", "error has a code"); - assert.equal(error.path, path, "error has a path"); - assert.equal(error.errno, 34, "error has a errno"); - assert.equal(fs.existsSync(path), false, "dir is removed"); - end(); - }); - async = true; - } -}; - -exports["test fs.truncateSync fs.unlinkSync"] = function(assert) { - let path = truncatePath; - - assert.equal(fs.existsSync(path), false, "does not exists"); - fs.truncateSync(path); - assert.equal(fs.existsSync(path), true, "file was created"); - fs.truncateSync(path); - fs.unlinkSync(path); - assert.equal(fs.existsSync(path), false, "file was removed"); -}; - - -exports["test fs.truncate"] = function(assert, end) { - let path = truncatePath; - if (!fs.existsSync(path)) { - let async = false; - fs.truncate(path, 0, function(error) { - assert.ok(async, "truncate is async"); - assert.ok(!error, "no error"); - assert.equal(fs.existsSync(path), true, "file was created"); - fs.unlinkSync(path); - assert.equal(fs.existsSync(path), false, "file was removed"); - end(); - }) - async = true; - } -}; - -exports["test fs.unlink"] = function(assert, end) { - let path = unlinkPath; - let async = false; - assert.ok(!fs.existsSync(path), "file doesn't exists yet"); - fs.truncateSync(path, 0); - assert.ok(fs.existsSync(path), "file was created"); - fs.unlink(path, function(error) { - assert.ok(async, "fs.unlink is async"); - assert.ok(!error, "error is falsy"); - assert.ok(!fs.existsSync(path), "file was removed"); - end(); - }); - async = true; -}; - -exports["test fs.unlink error"] = function(assert, end) { - let path = unlinkPath; - let async = false; - assert.ok(!fs.existsSync(path), "file doesn't exists yet"); - fs.unlink(path, function(error) { - assert.ok(async, "fs.unlink is async"); - assert.equal(error.message, "ENOENT, remove " + path); - assert.equal(error.code, "ENOENT", "error has a code"); - assert.equal(error.path, path, "error has a path"); - assert.equal(error.errno, 34, "error has a errno"); - end(); - }); - async = true; -}; - -exports["test fs.rename"] = function(assert, end) { - let fromPath = renameFromPath; - let toPath = renameToPath; - - fs.truncateSync(fromPath); - assert.ok(fs.existsSync(fromPath), "source file exists"); - assert.ok(!fs.existsSync(toPath), "destination doesn't exists"); - var async = false; - fs.rename(fromPath, toPath, function(error) { - assert.ok(async, "fs.rename is async"); - assert.ok(!error, "error is falsy"); - assert.ok(!fs.existsSync(fromPath), "source path no longer exists"); - assert.ok(fs.existsSync(toPath), "destination file exists"); - fs.unlinkSync(toPath); - assert.ok(!fs.existsSync(toPath), "cleaned up properly"); - end(); - }); - async = true; -}; - -exports["test fs.rename (missing source file)"] = function(assert, end) { - let fromPath = renameFromPath; - let toPath = renameToPath; - - assert.ok(!fs.existsSync(fromPath), "source file doesn't exists"); - assert.ok(!fs.existsSync(toPath), "destination doesn't exists"); - var async = false; - fs.rename(fromPath, toPath, function(error) { - assert.ok(async, "fs.rename is async"); - assert.equal(error.message, "ENOENT, rename " + fromPath); - assert.equal(error.code, "ENOENT", "error has a code"); - assert.equal(error.path, fromPath, "error has a path"); - assert.equal(error.errno, 34, "error has a errno"); - end(); - }); - async = true; -}; - -exports["test fs.rename (existing target file)"] = function(assert, end) { - let fromPath = renameFromPath; - let toPath = renameToPath; - - fs.truncateSync(fromPath); - fs.truncateSync(toPath); - assert.ok(fs.existsSync(fromPath), "source file exists"); - assert.ok(fs.existsSync(toPath), "destination file exists"); - var async = false; - fs.rename(fromPath, toPath, function(error) { - assert.ok(async, "fs.rename is async"); - assert.ok(!error, "error is falsy"); - assert.ok(!fs.existsSync(fromPath), "source path no longer exists"); - assert.ok(fs.existsSync(toPath), "destination file exists"); - fs.unlinkSync(toPath); - assert.ok(!fs.existsSync(toPath), "cleaned up properly"); - end(); - }); - async = true; -}; - -exports["test fs.writeFile"] = function(assert, end) { - let path = writePath; - let content = ["hello world", - "this is some text"].join("\n"); - - var async = false; - fs.writeFile(path, content, function(error) { - assert.ok(async, "fs write is async"); - assert.ok(!error, "error is falsy"); - assert.ok(fs.existsSync(path), "file was created"); - assert.equal(fs.readFileSync(path).toString(), - content, - "contet was written"); - fs.unlinkSync(path); - assert.ok(!fs.exists(path), "file was removed"); - - end(); - }); - async = true; -}; - -exports["test fs.writeFile (with large files)"] = function(assert, end) { - let path = writePath; - let content = ""; - - for (var i = 0; i < 100000; i++) { - content += "buffer\n"; - } - - var async = false; - fs.writeFile(path, content, function(error) { - assert.ok(async, "fs write is async"); - assert.ok(!error, "error is falsy"); - assert.ok(fs.existsSync(path), "file was created"); - assert.equal(fs.readFileSync(path).toString(), - content, - "contet was written"); - fs.unlinkSync(path); - assert.ok(!fs.exists(path), "file was removed"); - - end(); - }); - async = true; -}; - -exports["test fs.writeFile error"] = function (assert, done) { - try { - fs.writeFile({}, 'content', function (err) { - assert.fail('Error thrown from TypeError should not be caught'); - }); - } catch (e) { - assert.ok(e, - 'writeFile with a non-string error should not be caught'); - assert.equal(e.name, 'TypeError', 'error should be TypeError'); - } - fs.writeFile('not/a/valid/path', 'content', function (err) { - assert.ok(err, 'error caught and handled in callback'); - done(); - }); -}; - -exports["test fs.chmod"] = function (assert, done) { - let content = ["hej från sverige"]; - - fs.writeFile(chmodPath, content, function (err) { - testPerm("0755")() - .then(testPerm("0777")) - .then(testPerm("0666")) - .then(testPerm(parseInt("0511", 8))) - .then(testPerm(parseInt("0200", 8))) - .then(testPerm("0040")) - .then(testPerm("0000")) - .then(testPermSync(parseInt("0777", 8))) - .then(testPermSync(parseInt("0666", 8))) - .then(testPermSync("0511")) - .then(testPermSync("0200")) - .then(testPermSync("0040")) - .then(testPermSync("0000")) - .then(() => { - assert.pass("Successful chmod passes"); - }, assert.fail) - // Test invalid paths - .then(() => chmod("not-a-valid-file", parseInt("0755", 8))) - .then(assert.fail, (err) => { - checkPermError(err, "not-a-valid-file"); - }) - .then(() => chmod("not-a-valid-file", parseInt("0755", 8), "sync")) - .then(assert.fail, (err) => { - checkPermError(err, "not-a-valid-file"); - }) - // Test invalid files - .then(() => chmod("resource://not-a-real-file", parseInt("0755", 8))) - .then(assert.fail, (err) => { - checkPermError(err, "resource://not-a-real-file"); - }) - .then(() => chmod("resource://not-a-real-file", parseInt("0755", 8), 'sync')) - .then(assert.fail, (err) => { - checkPermError(err, "resource://not-a-real-file"); - }) - .then(done, assert.fail); - }); - - function checkPermError (err, path) { - assert.equal(err.message, "ENOENT, chmod " + path); - assert.equal(err.code, "ENOENT", "error has a code"); - assert.equal(err.path, path, "error has a path"); - assert.equal(err.errno, 34, "error has a errno"); - } - - function chmod (path, mode, sync) { - let { promise, resolve, reject } = defer(); - if (!sync) { - fs.chmod(path, mode, (err) => { - if (err) reject(err); - else resolve(); - }); - } else { - fs.chmodSync(path, mode); - resolve(); - } - return promise; - } - - function testPerm (mode, sync) { - return function () { - return chmod(chmodPath, mode, sync) - .then(() => getPerm(chmodPath)) - .then(perm => { - let nMode = normalizeMode(mode); - if (isWindows) - assert.equal(perm, nMode, - "mode correctly set to " + mode + " (" + nMode + " on windows)"); - else - assert.equal(perm, nMode, "mode correctly set to " + nMode); - }); - }; - } - - function testPermSync (mode) { - return testPerm(mode, true); - } - - function getPerm (path) { - let { promise, resolve, reject } = defer(); - fs.stat(path, function (err, stat) { - if (err) reject(err); - else resolve(stat.mode); - }); - return promise; - } - - /* - * Converts a unix mode `0755` into a Windows version of unix permissions - */ - function normalizeMode (mode) { - if (typeof mode === "string") - mode = parseInt(mode, 8); - - if (!isWindows) - return mode; - - var ANY_READ = parseInt("0444", 8); - var ANY_WRITE = parseInt("0222", 8); - var winMode = 0; - - // On Windows, if WRITE is true, then READ is also true - if (mode & ANY_WRITE) - winMode |= ANY_WRITE | ANY_READ; - // Minimum permissions are READ for Windows - else - winMode |= ANY_READ; - - return winMode; - } -}; - -require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-functional.js b/addon-sdk-1.16/test/test-functional.js deleted file mode 100644 index 84c587fc..00000000 --- a/addon-sdk-1.16/test/test-functional.js +++ /dev/null @@ -1,422 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -"use strict"; - -const { setTimeout } = require('sdk/timers'); -const utils = require('sdk/lang/functional'); -const { invoke, defer, partial, compose, memoize, once, is, isnt, - delay, wrap, curry, chainable, field, query, isInstance } = utils; -const { LoaderWithHookedConsole } = require('sdk/test/loader'); - -exports['test forwardApply'] = function(assert) { - function sum(b, c) { return this.a + b + c; } - assert.equal(invoke(sum, [2, 3], { a: 1 }), 6, - 'passed arguments and pseoude-variable are used'); - - assert.equal(invoke(sum.bind({ a: 2 }), [2, 3], { a: 1 }), 7, - 'bounded `this` pseoudo variable is used'); -}; - -exports['test deferred function'] = function(assert, done) { - let nextTurn = false; - function sum(b, c) { - assert.ok(nextTurn, 'enqueued is called in next turn of event loop'); - assert.equal(this.a + b + c, 6, - 'passed arguments an pseoude-variable are used'); - done(); - } - - let fixture = { a: 1, method: defer(sum) }; - fixture.method(2, 3); - nextTurn = true; -}; - -exports['test partial function'] = function(assert) { - function sum(b, c) { return this.a + b + c; } - - let foo = { a : 5 }; - - foo.sum7 = partial(sum, 7); - foo.sum8and4 = partial(sum, 8, 4); - - assert.equal(foo.sum7(2), 14, 'partial one arguments works'); - - assert.equal(foo.sum8and4(), 17, 'partial both arguments works'); -}; - -exports["test curry defined numeber of arguments"] = function(assert) { - var sum = curry(function(a, b, c) { - return a + b + c; - }); - - assert.equal(sum(2, 2, 1), 5, "sum(2, 2, 1) => 5"); - assert.equal(sum(2, 4)(1), 7, "sum(2, 4)(1) => 7"); - assert.equal(sum(2)(4, 2), 8, "sum(2)(4, 2) => 8"); - assert.equal(sum(2)(4)(3), 9, "sum(2)(4)(3) => 9"); -}; - -exports['test compose'] = function(assert) { - let greet = function(name) { return 'hi: ' + name; }; - let exclaim = function(sentence) { return sentence + '!'; }; - - assert.equal(compose(exclaim, greet)('moe'), 'hi: moe!', - 'can compose a function that takes another'); - - assert.equal(compose(greet, exclaim)('moe'), 'hi: moe!', - 'in this case, the functions are also commutative'); - - let target = { - name: 'Joe', - greet: compose(function exclaim(sentence) { - return sentence + '!'; - }, function(title) { - return 'hi : ' + title + ' ' + this.name; - }) - }; - - assert.equal(target.greet('Mr'), 'hi : Mr Joe!', - 'this can be passed in'); - assert.equal(target.greet.call({ name: 'Alex' }, 'Dr'), 'hi : Dr Alex!', - 'this can be applied'); - - let single = compose(function(value) { - return value + ':suffix'; - }); - - assert.equal(single('text'), 'text:suffix', 'works with single function'); - - let identity = compose(); - assert.equal(identity('bla'), 'bla', 'works with zero functions'); -}; - -exports['test wrap'] = function(assert) { - let greet = function(name) { return 'hi: ' + name; }; - let backwards = wrap(greet, function(f, name) { - return f(name) + ' ' + name.split('').reverse().join(''); - }); - - assert.equal(backwards('moe'), 'hi: moe eom', - 'wrapped the saluation function'); - - let inner = function () { return 'Hello '; }; - let target = { - name: 'Matteo', - hi: wrap(inner, function(f) { return f() + this.name; }) - }; - - assert.equal(target.hi(), 'Hello Matteo', 'works with this'); - - function noop() { } - let wrapped = wrap(noop, function(f) { - return Array.slice(arguments); - }); - - let actual = wrapped([ 'whats', 'your' ], 'vector', 'victor'); - assert.deepEqual(actual, [ noop, ['whats', 'your'], 'vector', 'victor' ], - 'works with fancy stuff'); -}; - -exports['test memoize'] = function(assert) { - const fib = n => n < 2 ? n : fib(n - 1) + fib(n - 2); - let fibnitro = memoize(fib); - - assert.equal(fib(10), 55, - 'a memoized version of fibonacci produces identical results'); - assert.equal(fibnitro(10), 55, - 'a memoized version of fibonacci produces identical results'); - - function o(key, value) { return value; } - let oo = memoize(o), v1 = {}, v2 = {}; - - - assert.equal(oo(1, v1), v1, 'returns value back'); - assert.equal(oo(1, v2), v1, 'memoized by a first argument'); - assert.equal(oo(2, v2), v2, 'returns back value if not memoized'); - assert.equal(oo(2), v2, 'memoized new value'); - assert.notEqual(oo(1), oo(2), 'values do not override'); - assert.equal(o(3, v2), oo(2, 3), 'returns same value as un-memoized'); - - let get = memoize(function(attribute) { return this[attribute]; }); - let target = { name: 'Bob', get: get }; - - assert.equal(target.get('name'), 'Bob', 'has correct `this`'); - assert.equal(target.get.call({ name: 'Jack' }, 'name'), 'Bob', - 'name is memoized'); - assert.equal(get('name'), 'Bob', 'once memoized can be called without this'); -}; - -exports['test delay'] = function(assert, done) { - let delayed = false; - delay(function() { - assert.ok(delayed, 'delayed the function'); - done(); - }, 1); - delayed = true; -}; - -exports['test delay with this'] = function(assert, done) { - let context = {}; - delay.call(context, function(name) { - assert.equal(this, context, 'this was passed in'); - assert.equal(name, 'Tom', 'argument was passed in'); - done(); - }, 10, 'Tom'); -}; - -exports['test once'] = function(assert) { - let n = 0; - let increment = once(function() { n ++; }); - - increment(); - increment(); - - assert.equal(n, 1, 'only incremented once'); - - let target = { - state: 0, - update: once(function() { - return this.state ++; - }) - }; - - target.update(); - target.update(); - - assert.equal(target.state, 1, 'this was passed in and called only once'); -}; - -exports['test once with argument'] = function(assert) { - let n = 0; - let increment = once(a => n++); - - increment(); - increment('foo'); - - assert.equal(n, 1, 'only incremented once'); - - increment(); - increment('foo'); - - assert.equal(n, 1, 'only incremented once'); -}; - -exports['test complement'] = assert => { - let { complement } = require("sdk/lang/functional"); - - let isOdd = x => Boolean(x % 2); - - assert.equal(isOdd(1), true); - assert.equal(isOdd(2), false); - - let isEven = complement(isOdd); - - assert.equal(isEven(1), false); - assert.equal(isEven(2), true); - - let foo = {}; - let isFoo = function() { return this === foo; }; - let insntFoo = complement(isFoo); - - assert.equal(insntFoo.call(foo), false); - assert.equal(insntFoo.call({}), true); -}; - -exports['test constant'] = assert => { - let { constant } = require("sdk/lang/functional"); - - let one = constant(1); - - assert.equal(one(1), 1); - assert.equal(one(2), 1); -}; - -exports['test apply'] = assert => { - let { apply } = require("sdk/lang/functional"); - - let dashify = (...args) => args.join("-"); - - assert.equal(apply(dashify, 1, [2, 3]), "1-2-3"); - assert.equal(apply(dashify, "a"), "a"); - assert.equal(apply(dashify, ["a", "b"]), "a-b"); - assert.equal(apply(dashify, ["a", "b"], "c"), "a,b-c"); - assert.equal(apply(dashify, [1, 2], [3, 4]), "1,2-3-4"); -}; - -exports['test flip'] = assert => { - let { flip } = require("sdk/lang/functional"); - - let append = (left, right) => left + " " + right; - let prepend = flip(append); - - assert.equal(append("hello", "world"), "hello world"); - assert.equal(prepend("hello", "world"), "world hello"); - - let wrap = function(left, right) { - return left + " " + this + " " + right; - }; - let invertWrap = flip(wrap); - - assert.equal(wrap.call("@", "hello", "world"), "hello @ world"); - assert.equal(invertWrap.call("@", "hello", "world"), "world @ hello"); - - let reverse = flip((...args) => args); - - assert.deepEqual(reverse(1, 2, 3, 4), [4, 3, 2, 1]); - assert.deepEqual(reverse(1), [1]); - assert.deepEqual(reverse(), []); - - // currying still works - let prependr = curry(prepend); - - assert.equal(prependr("hello", "world"), "world hello"); - assert.equal(prependr("hello")("world"), "world hello"); -}; - -exports["test when"] = assert => { - let { when } = require("sdk/lang/functional"); - - let areNums = (...xs) => xs.every(x => typeof(x) === "number"); - - let sum = when(areNums, (...xs) => xs.reduce((y, x) => x + y, 0)); - - assert.equal(sum(1, 2, 3), 6); - assert.equal(sum(1, 2, "3"), undefined); - - let multiply = when(areNums, - (...xs) => xs.reduce((y, x) => x * y, 1), - (...xs) => xs); - - assert.equal(multiply(2), 2); - assert.equal(multiply(2, 3), 6); - assert.deepEqual(multiply(2, "4"), [2, "4"]); - - function Point(x, y) { - this.x = x; - this.y = y; - } - - let isPoint = x => x instanceof Point; - - let inc = when(isPoint, ({x, y}) => new Point(x + 1, y + 1)); - - assert.equal(inc({}), undefined); - assert.deepEqual(inc(new Point(0, 0)), { x: 1, y: 1 }); - - let axis = when(isPoint, - ({ x, y }) => [x, y], - _ => [0, 0]); - - assert.deepEqual(axis(new Point(1, 4)), [1, 4]); - assert.deepEqual(axis({ foo: "bar" }), [0, 0]); -}; - -exports["test chainable"] = function(assert) { - let Player = function () { this.volume = 5; }; - Player.prototype = { - setBand: chainable(function (band) { return (this.band = band); }), - incVolume: chainable(function () { return this.volume++; }) - }; - let player = new Player(); - player - .setBand('Animals As Leaders') - .incVolume().incVolume().incVolume().incVolume().incVolume().incVolume(); - - assert.equal(player.band, 'Animals As Leaders', 'passes arguments into chained'); - assert.equal(player.volume, 11, 'accepts no arguments in chain'); -}; - -exports["test field"] = assert => { - let Num = field("constructor", 0); - assert.equal(Num.name, Number.name); - assert.ok(typeof(Num), "function"); - - let x = field("x"); - - [ - [field("foo", { foo: 1 }), 1], - [field("foo")({ foo: 1 }), 1], - [field("bar", {}), undefined], - [field("bar")({}), undefined], - [field("hey", undefined), undefined], - [field("hey")(undefined), undefined], - [field("how", null), null], - [field("how")(null), null], - [x(1), undefined], - [x(undefined), undefined], - [x(null), null], - [x({ x: 1 }), 1], - [x({ x: 2 }), 2], - ].forEach(([actual, expected]) => assert.equal(actual, expected)); -}; - -exports["test query"] = assert => { - let Num = query("constructor", 0); - assert.equal(Num.name, Number.name); - assert.ok(typeof(Num), "function"); - - let x = query("x"); - let xy = query("x.y"); - - [ - [query("foo", { foo: 1 }), 1], - [query("foo")({ foo: 1 }), 1], - [query("foo.bar", { foo: { bar: 2 } }), 2], - [query("foo.bar")({ foo: { bar: 2 } }), 2], - [query("foo.bar", { foo: 1 }), undefined], - [query("foo.bar")({ foo: 1 }), undefined], - [x(1), undefined], - [x(undefined), undefined], - [x(null), null], - [x({ x: 1 }), 1], - [x({ x: 2 }), 2], - [xy(1), undefined], - [xy(undefined), undefined], - [xy(null), null], - [xy({ x: 1 }), undefined], - [xy({ x: 2 }), undefined], - [xy({ x: { y: 1 } }), 1], - [xy({ x: { y: 2 } }), 2] - ].forEach(([actual, expected]) => assert.equal(actual, expected)); -}; - -exports["test isInstance"] = assert => { - function X() {} - function Y() {} - let isX = isInstance(X); - - [ - isInstance(X, new X()), - isInstance(X)(new X()), - !isInstance(X, new Y()), - !isInstance(X)(new Y()), - isX(new X()), - !isX(new Y()) - ].forEach(x => assert.ok(x)); -}; - -exports["test is"] = assert => { - - assert.deepEqual([ 1, 0, 1, 0, 1 ].map(is(1)), - [ true, false, true, false, true ], - "is can be partially applied"); - - assert.ok(is(1, 1)); - assert.ok(!is({}, {})); - assert.ok(is()(1)()(1), "is is curried"); - assert.ok(!is()(1)()(2)); -}; - -exports["test isnt"] = assert => { - - assert.deepEqual([ 1, 0, 1, 0, 1 ].map(isnt(0)), - [ true, false, true, false, true ], - "is can be partially applied"); - - assert.ok(!isnt(1, 1)); - assert.ok(isnt({}, {})); - assert.ok(!isnt()(1)()(1)); - assert.ok(isnt()(1)()(2)); -}; - -require('test').run(exports); diff --git a/addon-sdk-1.16/test/test-globals.js b/addon-sdk-1.16/test/test-globals.js deleted file mode 100644 index ba38ed7f..00000000 --- a/addon-sdk-1.16/test/test-globals.js +++ /dev/null @@ -1,30 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -'use strict'; - -Object.defineProperty(this, 'global', { value: this }); - -exports.testGlobals = function(assert) { - // the only globals in module scope should be: - // module, exports, require, dump, console - assert.equal(typeof module, 'object', 'have "module", good'); - assert.equal(typeof exports, 'object', 'have "exports", good'); - assert.equal(typeof require, 'function', 'have "require", good'); - assert.equal(typeof dump, 'function', 'have "dump", good'); - assert.equal(typeof console, 'object', 'have "console", good'); - - // in particular, these old globals should no longer be present - assert.ok(!('packaging' in global), "no 'packaging', good"); - assert.ok(!('memory' in global), "no 'memory', good"); - assert.ok(/test-globals\.js$/.test(module.uri), - 'should contain filename'); -}; - -exports.testComponent = function (assert) { - assert.throws(() => { - Components; - }, /`Components` is not available/, 'using `Components` throws'); -}; - -require('test').run(exports); diff --git a/addon-sdk-1.16/test/test-heritage.js b/addon-sdk-1.16/test/test-heritage.js deleted file mode 100644 index f94861d7..00000000 --- a/addon-sdk-1.16/test/test-heritage.js +++ /dev/null @@ -1,302 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const { Class, extend, mix, obscure } = require('sdk/core/heritage'); - -exports['test extend'] = function(assert) { - let ancestor = { a: 1 }; - let descendant = extend(ancestor, { - b: 2, - get c() { return 3 }, - d: function() { return 4 } - }); - - assert.ok(ancestor.isPrototypeOf(descendant), - 'descendant inherits from ancestor'); - assert.ok(descendant.b, 2, 'proprety was implemented'); - assert.ok(descendant.c, 3, 'getter was implemented'); - assert.ok(descendant.d(), 4, 'method was implemented'); - - /* Will be fixed once Bug 674195 is shipped. - assert.ok(Object.isFrozen(descendant), - 'extend returns frozen objects'); - */ -}; - -exports['test mix'] = function(assert) { - let ancestor = { a: 1 } - let mixed = mix(extend(ancestor, { b: 1, c: 1 }), { c: 2 }, { d: 3 }); - - assert.deepEqual(JSON.parse(JSON.stringify(mixed)), { b: 1, c: 2, d: 3 }, - 'properties mixed as expected'); - assert.ok(ancestor.isPrototypeOf(mixed), - 'first arguments ancestor is ancestor of result'); -}; - -exports['test obscure'] = function(assert) { - let fixture = mix({ a: 1 }, obscure({ b: 2 })); - - assert.equal(fixture.a, 1, 'a property is included'); - assert.equal(fixture.b, 2, 'b proprety is included'); - assert.ok(!Object.getOwnPropertyDescriptor(fixture, 'b').enumerable, - 'obscured properties are non-enumerable'); -}; - -exports['test inheritance'] = function(assert) { - let Ancestor = Class({ - name: 'ancestor', - method: function () { - return 'hello ' + this.name; - } - }); - - assert.ok(Ancestor() instanceof Ancestor, - 'can be instantiated without new'); - assert.ok(new Ancestor() instanceof Ancestor, - 'can also be instantiated with new'); - assert.ok(Ancestor() instanceof Class, - 'if ancestor not specified than defaults to Class'); - assert.ok(Ancestor.prototype.extends, Class.prototype, - 'extends of prototype points to ancestors prototype'); - - - assert.equal(Ancestor().method(), 'hello ancestor', - 'instance inherits defined properties'); - - let Descendant = Class({ - extends: Ancestor, - name: 'descendant' - }); - - assert.ok(Descendant() instanceof Descendant, - 'instantiates correctly'); - assert.ok(Descendant() instanceof Ancestor, - 'Inherits for passed `extends`'); - assert.equal(Descendant().method(), 'hello descendant', - 'propreties inherited'); -}; - -exports['test immunity against __proto__'] = function(assert) { - let Foo = Class({ name: 'foo', hacked: false }); - - let Bar = Class({ extends: Foo, name: 'bar' }); - - assert.throws(function() { - Foo.prototype.__proto__ = { hacked: true }; - if (Foo() instanceof Base && !Foo().hacked) - throw Error('can not change prototype chain'); - }, 'prototype chain is immune to __proto__ hacks'); - - assert.throws(function() { - Foo.prototype.__proto__ = { hacked: true }; - if (Bar() instanceof Foo && !Bar().hacked) - throw Error('can not change prototype chain'); - }, 'prototype chain of decedants immune to __proto__ hacks'); -}; - -exports['test super'] = function(assert) { - var Foo = Class({ - initialize: function initialize(options) { - this.name = options.name; - } - }); - - var Bar = Class({ - extends: Foo, - initialize: function Bar(options) { - Foo.prototype.initialize.call(this, options); - this.type = 'bar'; - } - }); - - var bar = Bar({ name: 'test' }); - - assert.equal(bar.type, 'bar', 'bar initializer was called'); - assert.equal(bar.name, 'test', 'bar initializer called Foo initializer'); -}; - -exports['test initialize'] = function(assert) { - var Dog = Class({ - initialize: function initialize(name) { - this.name = name; - }, - type: 'dog', - bark: function bark() { - return 'Ruff! Ruff!' - } - }); - - var fluffy = Dog('Fluffy'); // instatiation - assert.ok(fluffy instanceof Dog, - 'instanceof works as expected'); - assert.ok(fluffy instanceof Class, - 'inherits form Class if not specified otherwise'); - assert.ok(fluffy.name, 'fluffy', - 'initialize unless specified otherwise'); -}; - -exports['test complements regular inheritace'] = function(assert) { - let Base = Class({ name: 'base' }); - - function Type() { - // ... - } - Type.prototype = Object.create(Base.prototype); - Type.prototype.run = function() { - // ... - }; - - let value = new Type(); - - assert.ok(value instanceof Type, 'creates instance of Type'); - assert.ok(value instanceof Base, 'inherits from Base'); - assert.equal(value.name, 'base', 'inherits properties from Base'); - - - let SubType = Class({ - extends: Type, - sub: 'type' - }); - - let fixture = SubType(); - - assert.ok(fixture instanceof Base, 'is instance of Base'); - assert.ok(fixture instanceof Type, 'is instance of Type'); - assert.ok(fixture instanceof SubType, 'is instance of SubType'); - - assert.equal(fixture.sub, 'type', 'proprety is defined'); - assert.equal(fixture.run, Type.prototype.run, 'proprety is inherited'); - assert.equal(fixture.name, 'base', 'inherits base properties'); -}; - -exports['test extends object'] = function(assert) { - let prototype = { constructor: function() { return this; }, name: 'me' }; - let Foo = Class({ - extends: prototype, - value: 2 - }); - let foo = new Foo(); - - assert.ok(foo instanceof Foo, 'instance of Foo'); - assert.ok(!(foo instanceof Class), 'is not instance of Class'); - assert.ok(prototype.isPrototypeOf(foo), 'inherits from given prototype'); - assert.equal(Object.getPrototypeOf(Foo.prototype), prototype, - 'contsructor prototype inherits from extends option'); - assert.equal(foo.value, 2, 'property is defined'); - assert.equal(foo.name, 'me', 'prototype proprety is inherited'); -}; - - -var HEX = Class({ - hex: function hex() { - return '#' + this.color; - } -}); - -var RGB = Class({ - red: function red() { - return parseInt(this.color.substr(0, 2), 16); - }, - green: function green() { - return parseInt(this.color.substr(2, 2), 16); - }, - blue: function blue() { - return parseInt(this.color.substr(4, 2), 16); - } -}); - -var CMYK = Class({ - black: function black() { - var color = Math.max(Math.max(this.red(), this.green()), this.blue()); - return (1 - color / 255).toFixed(4); - }, - magenta: function magenta() { - var K = this.black(); - return (((1 - this.green() / 255).toFixed(4) - K) / (1 - K)).toFixed(4); - }, - yellow: function yellow() { - var K = this.black(); - return (((1 - this.blue() / 255).toFixed(4) - K) / (1 - K)).toFixed(4); - }, - cyan: function cyan() { - var K = this.black(); - return (((1 - this.red() / 255).toFixed(4) - K) / (1 - K)).toFixed(4); - } -}); - -var Color = Class({ - implements: [ HEX, RGB, CMYK ], - initialize: function initialize(color) { - this.color = color; - } -}); - -exports['test composition'] = function(assert) { - var pink = Color('FFC0CB'); - - assert.equal(pink.red(), 255, 'red() works'); - assert.equal(pink.green(), 192, 'green() works'); - assert.equal(pink.blue(), 203, 'blue() works'); - - assert.equal(pink.magenta(), 0.2471, 'magenta() works'); - assert.equal(pink.yellow(), 0.2039, 'yellow() works'); - assert.equal(pink.cyan(), 0.0000, 'cyan() works'); - - assert.ok(pink instanceof Color, 'is instance of Color'); - assert.ok(pink instanceof Class, 'is instance of Class'); -}; - -var Point = Class({ - initialize: function initialize(x, y) { - this.x = x; - this.y = y; - }, - toString: function toString() { - return this.x + ':' + this.y; - } -}) - -var Pixel = Class({ - extends: Point, - implements: [ Color ], - initialize: function initialize(x, y, color) { - Color.prototype.initialize.call(this, color); - Point.prototype.initialize.call(this, x, y); - }, - toString: function toString() { - return this.hex() + '@' + Point.prototype.toString.call(this) - } -}); - -exports['test compostion with inheritance'] = function(assert) { - var pixel = Pixel(11, 23, 'CC3399'); - - assert.equal(pixel.toString(), '#CC3399@11:23', 'stringifies correctly'); - assert.ok(pixel instanceof Pixel, 'instance of Pixel'); - assert.ok(pixel instanceof Point, 'instance of Point'); -}; - -exports['test composition with objects'] = function(assert) { - var A = { a: 1, b: 1 }; - var B = Class({ b: 2, c: 2 }); - var C = { c: 3 }; - var D = { d: 4 }; - - var ABCD = Class({ - implements: [ A, B, C, D ], - e: 5 - }); - - var f = ABCD(); - - assert.equal(f.a, 1, 'inherits A.a'); - assert.equal(f.b, 2, 'inherits B.b overrides A.b'); - assert.equal(f.c, 3, 'inherits C.c overrides B.c'); - assert.equal(f.d, 4, 'inherits D.d'); - assert.equal(f.e, 5, 'implements e'); -}; - -require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-hidden-frame.js b/addon-sdk-1.16/test/test-hidden-frame.js deleted file mode 100644 index 3a375b9b..00000000 --- a/addon-sdk-1.16/test/test-hidden-frame.js +++ /dev/null @@ -1,71 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -"use strict"; - -const { Loader } = require("sdk/test/loader"); -const hiddenFrames = require("sdk/frame/hidden-frame"); -const { HiddenFrame } = hiddenFrames; - -exports["test Frame"] = function(assert, done) { - let url = "data:text/html;charset=utf-8,"; - - let hiddenFrame = hiddenFrames.add(HiddenFrame({ - onReady: function () { - assert.equal(this.element.contentWindow.location, "about:blank", - "HiddenFrame loads about:blank by default."); - - function onDOMReady() { - hiddenFrame.element.removeEventListener("DOMContentLoaded", onDOMReady, - false); - assert.equal(hiddenFrame.element.contentWindow.location, url, - "HiddenFrame loads the specified content."); - done(); - } - - this.element.addEventListener("DOMContentLoaded", onDOMReady, false); - this.element.setAttribute("src", url); - } - })); -}; - -exports["test frame removed properly"] = function(assert, done) { - let url = "data:text/html;charset=utf-8,"; - - let hiddenFrame = hiddenFrames.add(HiddenFrame({ - onReady: function () { - let frame = this.element; - assert.ok(frame.parentNode, "frame has a parent node"); - hiddenFrames.remove(hiddenFrame); - assert.ok(!frame.parentNode, "frame no longer has a parent node"); - done(); - } - })); -}; - -exports["test unload detaches panels"] = function(assert, done) { - let loader = Loader(module); - let { add, remove, HiddenFrame } = loader.require("sdk/frame/hidden-frame"); - let frames = [] - - function ready() { - frames.push(this.element); - if (frames.length === 2) complete(); - } - - add(HiddenFrame({ onReady: ready })); - add(HiddenFrame({ onReady: ready })); - - function complete() { - frames.forEach(function(frame) { - assert.ok(frame.parentNode, "frame is in the document"); - }) - loader.unload(); - frames.forEach(function(frame) { - assert.ok(!frame.parentNode, "frame isn't in the document'"); - }); - done(); - } -}; - -require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-host-events.js b/addon-sdk-1.16/test/test-host-events.js deleted file mode 100644 index 72e03f39..00000000 --- a/addon-sdk-1.16/test/test-host-events.js +++ /dev/null @@ -1,99 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -'use strict'; - -const { Cc, Ci } = require('chrome'); -const { defer, all } = require('sdk/core/promise'); -const { setTimeout } = require('sdk/timers'); -const { request, response } = require('sdk/addon/host'); -const { send } = require('sdk/addon/events'); -const { filter } = require('sdk/event/utils'); -const { on, emit, off } = require('sdk/event/core'); - -let stream = filter(request, (data) => /sdk-x-test/.test(data.event)); - -exports.testSend = function (assert, done) { - on(stream, 'data', handle); - send('sdk-x-test-simple', { title: 'my test data' }).then((data) => { - assert.equal(data.title, 'my response', 'response is handled'); - off(stream, 'data', handle); - done(); - }, (reason) => { - assert.fail('should not call reject'); - }); - function handle (e) { - assert.equal(e.event, 'sdk-x-test-simple', 'correct event name'); - assert.ok(e.id != null, 'message has an ID'); - assert.equal(e.data.title, 'my test data', 'serialized data passes'); - e.data.title = 'my response'; - emit(response, 'data', e); - } -}; - -exports.testSendError = function (assert, done) { - on(stream, 'data', handle); - send('sdk-x-test-error', { title: 'my test data' }).then((data) => { - assert.fail('should not call success'); - }, (reason) => { - assert.equal(reason, 'ErrorInfo', 'should reject with error/reason'); - off(stream, 'data', handle); - done(); - }); - function handle (e) { - e.error = 'ErrorInfo'; - emit(response, 'data', e); - } -}; - -exports.testMultipleSends = function (assert, done) { - let count = 0; - let ids = []; - on(stream, 'data', handle); - ['firefox', 'thunderbird', 'rust'].map(data => - send('sdk-x-test-multi', { data: data }).then(val => { - assert.ok(val === 'firefox' || val === 'rust', 'successful calls resolve correctly'); - if (++count === 3) { - off(stream, 'data', handle); - done(); - } - }, reason => { - assert.equal(reason.error, 'too blue', 'rejected calls are rejected'); - if (++count === 3) { - off(stream, 'data', handle); - done(); - } - })); - - function handle (e) { - if (e.data !== 'firefox' || e.data !== 'rust') - e.error = { data: e.data, error: 'too blue' }; - assert.ok(!~ids.indexOf(e.id), 'ID should be unique'); - assert.equal(e.event, 'sdk-x-test-multi', 'has event name'); - ids.push(e.id); - emit(response, 'data', e); - } -}; - -exports.testSerialization = function (assert, done) { - on(stream, 'data', handle); - let object = { title: 'my test data' }; - let resObject; - send('sdk-x-test-serialize', object).then(data => { - data.title = 'another title'; - assert.equal(object.title, 'my test data', 'original object not modified'); - assert.equal(resObject.title, 'new title', 'object passed by value from host'); - off(stream, 'data', handle); - done(); - }, (reason) => { - assert.fail('should not call reject'); - }); - function handle (e) { - e.data.title = 'new title'; - assert.equal(object.title, 'my test data', 'object passed by value to host'); - resObject = e.data; - emit(response, 'data', e); - } -}; - -require('test').run(exports); diff --git a/addon-sdk-1.16/test/test-hotkeys.js b/addon-sdk-1.16/test/test-hotkeys.js deleted file mode 100644 index 77fe9a45..00000000 --- a/addon-sdk-1.16/test/test-hotkeys.js +++ /dev/null @@ -1,164 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const { Hotkey } = require("sdk/hotkeys"); -const { keyDown } = require("sdk/dom/events/keys"); -const { Loader } = require('sdk/test/loader'); -const timer = require("sdk/timers"); -const winUtils = require("sdk/deprecated/window-utils"); - -exports["test hotkey: function key"] = function(assert, done) { - var element = winUtils.activeBrowserWindow.document.documentElement; - var showHotKey = Hotkey({ - combo: "f1", - onPress: function() { - assert.pass("first callback is called"); - assert.equal(this, showHotKey, - 'Context `this` in `onPress` should be the hotkey object'); - keyDown(element, "f2"); - showHotKey.destroy(); - } - }); - - var hideHotKey = Hotkey({ - combo: "f2", - onPress: function() { - assert.pass("second callback is called"); - hideHotKey.destroy(); - done(); - } - }); - - keyDown(element, "f1"); -}; - -exports["test hotkey: accel alt shift"] = function(assert, done) { - var element = winUtils.activeBrowserWindow.document.documentElement; - var showHotKey = Hotkey({ - combo: "accel-shift-6", - onPress: function() { - assert.pass("first callback is called"); - keyDown(element, "accel-alt-shift-6"); - showHotKey.destroy(); - } - }); - - var hideHotKey = Hotkey({ - combo: "accel-alt-shift-6", - onPress: function() { - assert.pass("second callback is called"); - hideHotKey.destroy(); - done(); - } - }); - - keyDown(element, "accel-shift-6"); -}; - -exports["test hotkey meta & control"] = function(assert, done) { - var element = winUtils.activeBrowserWindow.document.documentElement; - var showHotKey = Hotkey({ - combo: "meta-3", - onPress: function() { - assert.pass("first callback is called"); - keyDown(element, "alt-control-shift-b"); - showHotKey.destroy(); - } - }); - - var hideHotKey = Hotkey({ - combo: "Ctrl-Alt-Shift-B", - onPress: function() { - assert.pass("second callback is called"); - hideHotKey.destroy(); - done(); - } - }); - - keyDown(element, "meta-3"); -}; - -exports["test hotkey: control-1 / meta--"] = function(assert, done) { - var element = winUtils.activeBrowserWindow.document.documentElement; - var showHotKey = Hotkey({ - combo: "control-1", - onPress: function() { - assert.pass("first callback is called"); - keyDown(element, "meta--"); - showHotKey.destroy(); - } - }); - - var hideHotKey = Hotkey({ - combo: "meta--", - onPress: function() { - assert.pass("second callback is called"); - hideHotKey.destroy(); - done(); - } - }); - - keyDown(element, "control-1"); -}; - -exports["test invalid combos"] = function(assert) { - assert.throws(function() { - Hotkey({ - combo: "d", - onPress: function() {} - }); - }, "throws if no modifier is present"); - assert.throws(function() { - Hotkey({ - combo: "alt", - onPress: function() {} - }); - }, "throws if no key is present"); - assert.throws(function() { - Hotkey({ - combo: "alt p b", - onPress: function() {} - }); - }, "throws if more then one key is present"); -}; - -exports["test no exception on unmodified keypress"] = function(assert) { - var element = winUtils.activeBrowserWindow.document.documentElement; - var someHotkey = Hotkey({ - combo: "control-alt-1", - onPress: function() { - } - }); - keyDown(element, "a"); - assert.pass("No exception throw, unmodified keypress passed"); -}; - -exports["test hotkey: automatic destroy"] = function(assert, done) { - // Hacky way to be able to create unloadable modules via makeSandboxedLoader. - let loader = Loader(module); - - var called = false; - var element = loader.require("sdk/deprecated/window-utils").activeBrowserWindow.document.documentElement; - var hotkey = loader.require("sdk/hotkeys").Hotkey({ - combo: "accel-shift-x", - onPress: function() { - called = true; - } - }); - - // Unload the module so that previous hotkey is automatically destroyed - loader.unload(); - - // Ensure that the hotkey is really destroyed - keyDown(element, "accel-shift-x"); - - timer.setTimeout(function () { - assert.ok(!called, "Hotkey is destroyed and not called."); - done(); - }, 0); -}; - -require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-httpd.js b/addon-sdk-1.16/test/test-httpd.js deleted file mode 100644 index 2426148e..00000000 --- a/addon-sdk-1.16/test/test-httpd.js +++ /dev/null @@ -1,73 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -const port = 8099; -const file = require("sdk/io/file"); -const { pathFor } = require("sdk/system"); -const { Loader } = require("sdk/test/loader"); -const options = require("@test/options"); - -const loader = Loader(module); -const httpd = loader.require("sdk/test/httpd"); -if (options.parseable || options.verbose) - loader.sandbox("sdk/test/httpd").DEBUG = true; - -exports.testBasicHTTPServer = function(assert, done) { - // Use the profile directory for the temporary file as that will be deleted - // when tests are complete - let basePath = pathFor("ProfD"); - let filePath = file.join(basePath, 'test-httpd.txt'); - let content = "This is the HTTPD test file.\n"; - let fileStream = file.open(filePath, 'w'); - fileStream.write(content); - fileStream.close(); - - let srv = httpd.startServerAsync(port, basePath); - - // Request this very file. - let Request = require('sdk/request').Request; - Request({ - url: "http://localhost:" + port + "/test-httpd.txt", - onComplete: function (response) { - assert.equal(response.text, content); - srv.stop(done); - } - }).get(); -}; - -exports.testDynamicServer = function (assert, done) { - let content = "This is the HTTPD test file.\n"; - - let srv = httpd.startServerAsync(port); - - // See documentation here: - //http://doxygen.db48x.net/mozilla/html/interfacensIHttpServer.html#a81fc7e7e29d82aac5ce7d56d0bedfb3a - //http://doxygen.db48x.net/mozilla/html/interfacensIHttpRequestHandler.html - srv.registerPathHandler("/test-httpd.txt", function handle(request, response) { - // Add text content type, only to avoid error in `Request` API - response.setHeader("Content-Type", "text/plain", false); - response.write(content); - }); - - // Request this very file. - let Request = require('sdk/request').Request; - Request({ - url: "http://localhost:" + port + "/test-httpd.txt", - onComplete: function (response) { - assert.equal(response.text, content); - srv.stop(done); - } - }).get(); -}; - -exports.testAutomaticPortSelection = function (assert, done) { - const srv = httpd.startServerAsync(-1); - - const port = srv.identity.primaryPort; - assert.ok(0 <= port && port <= 65535); - - srv.stop(done); -}; - -require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-indexed-db.js b/addon-sdk-1.16/test/test-indexed-db.js deleted file mode 100644 index 4f66859e..00000000 --- a/addon-sdk-1.16/test/test-indexed-db.js +++ /dev/null @@ -1,128 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -let xulApp = require("sdk/system/xul-app"); -if (xulApp.versionInRange(xulApp.platformVersion, "16.0a1", "*")) { -new function tests() { - -const { indexedDB, IDBKeyRange, DOMException - } = require("sdk/indexed-db"); - -exports["test indexedDB is frozen"] = function(assert){ - let original = indexedDB.open; - let f = function(){}; - assert.throws(function(){indexedDB.open = f}); - assert.equal(indexedDB.open,original); - assert.notEqual(indexedDB.open,f); - -}; - -exports["test db variables"] = function(assert) { - [ indexedDB, IDBKeyRange, DOMException - ].forEach(function(value) { - assert.notEqual(typeof(value), "undefined", "variable is defined"); - }); -} - -exports["test open"] = function(assert, done) { - let request = indexedDB.open("MyTestDatabase"); - request.onerror = function(event) { - assert.fail("Failed to open indexedDB") - done(); - }; - request.onsuccess = function(event) { - assert.pass("IndexedDB was open"); - done(); - }; -}; - -exports["test dbname is unprefixed"] = function(assert, done) { - // verify fixes in https://bugzilla.mozilla.org/show_bug.cgi?id=786688 - let dbName = "dbname-unprefixed"; - let request = indexedDB.open(dbName); - request.onerror = function(event) { - assert.fail("Failed to open db"); - done(); - }; - request.onsuccess = function(event) { - assert.equal(request.result.name, dbName); - done(); - }; -}; - -exports["test structuring the database"] = function(assert, done) { - // This is what our customer data looks like. - let customerData = [ - { ssn: "444-44-4444", name: "Bill", age: 35, email: "bill@company.com" }, - { ssn: "555-55-5555", name: "Donna", age: 32, email: "donna@home.org" } - ]; - let dbName = "the_name"; - let request = indexedDB.open(dbName, 2); - request.onerror = function(event) { - assert.fail("Failed to open db"); - done(); - }; - request.onsuccess = function(event) { - assert.pass("transaction is complete"); - testRead(assert, done); - } - request.onupgradeneeded = function(event) { - assert.pass("data base upgrade") - - var db = event.target.result; - - // Create an objectStore to hold information about our customers. We"re - // going to use "ssn" as our key path because it"s guaranteed to be - // unique. - var objectStore = db.createObjectStore("customers", { keyPath: "ssn" }); - - // Create an index to search customers by name. We may have duplicates - // so we can"t use a unique index. - objectStore.createIndex("name", "name", { unique: false }); - - // Create an index to search customers by email. We want to ensure that - // no two customers have the same email, so use a unique index. - objectStore.createIndex("email", "email", { unique: true }); - - // Store values in the newly created objectStore. - customerData.forEach(function(data) { - objectStore.add(data); - }); - assert.pass("data added to object store"); - }; -}; - -function testRead(assert, done) { - let dbName = "the_name"; - let request = indexedDB.open(dbName, 2); - request.onsuccess = function(event) { - assert.pass("data opened") - var db = event.target.result; - let transaction = db.transaction(["customers"]); - var objectStore = transaction.objectStore("customers"); - var request = objectStore.get("444-44-4444"); - request.onerror = function(event) { - assert.fail("Failed to retrive data") - }; - request.onsuccess = function(event) { - // Do something with the request.result! - assert.equal(request.result.name, "Bill", "Name is correct"); - done(); - }; - }; - request.onerror = function() { - assert.fail("failed to open db"); - }; -}; - -} -} else { - exports.testDB = function(assert) { - assert.pass("IndexedDB is not implemented") - } -} - -require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-keyboard-observer.js b/addon-sdk-1.16/test/test-keyboard-observer.js deleted file mode 100644 index 091ec73c..00000000 --- a/addon-sdk-1.16/test/test-keyboard-observer.js +++ /dev/null @@ -1,37 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const { keyPress } = require("sdk/dom/events/keys"); -const { Loader } = require("sdk/test/loader"); -const timer = require("sdk/timers"); - -exports["test unload keyboard observer"] = function(assert, done) { - let loader = Loader(module); - let element = loader.require("sdk/deprecated/window-utils"). - activeBrowserWindow.document.documentElement; - let observer = loader.require("sdk/keyboard/observer"). - observer; - let called = 0; - - observer.on("keypress", function () { called++; }); - - // dispatching "keypress" event to trigger observer listeners. - keyPress(element, "accel-%"); - - // Unload the module. - loader.unload(); - - // dispatching "keypress" even once again. - keyPress(element, "accel-%"); - - // Enqueuing asserts to make sure that assertion is not performed early. - timer.setTimeout(function () { - assert.equal(called, 1, "observer was called before unload only."); - done(); - }, 0); -}; - -require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-keyboard-utils.js b/addon-sdk-1.16/test/test-keyboard-utils.js deleted file mode 100644 index 19981dec..00000000 --- a/addon-sdk-1.16/test/test-keyboard-utils.js +++ /dev/null @@ -1,62 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const utils = require("sdk/keyboard/utils"); -const runtime = require("sdk/system/runtime"); - -const isMac = runtime.OS === "Darwin"; - -exports["test toString"] = function(assert) { - assert.equal(utils.toString({ - key: "B", - modifiers: [ "Shift", "Ctrl" ] - }), "Shift-Ctrl-B", "toString does not normalizes JSON"); - - assert.equal(utils.toString({ - key: "C", - modifiers: [], - }), "C", "Works with objects with empty array of modifiers"); - - assert.equal(utils.toString(Object.create((function Type() {}).prototype, { - key: { value: "d" }, - modifiers: { value: [ "alt" ] }, - method: { value: function() {} } - })), "alt-d", "Works with non-json objects"); - - assert.equal(utils.toString({ - modifiers: [ "shift", "alt" ] - }), "shift-alt-", "works with only modifiers"); -}; - -exports["test toJSON"] = function(assert) { - assert.deepEqual(utils.toJSON("Shift-Ctrl-B"), { - key: "b", - modifiers: [ "control", "shift" ] - }, "toJSON normalizes input"); - - assert.deepEqual(utils.toJSON("Meta-Alt-option-C"), { - key: "c", - modifiers: [ "alt", "meta" ] - }, "removes dublicates"); - - assert.deepEqual(utils.toJSON("AccEl+sHiFt+Z", "+"), { - key: "z", - modifiers: isMac ? [ "meta", "shift" ] : [ "control", "shift" ] - }, "normalizes OS specific keys and adjustes seperator"); -}; - -exports["test normalize"] = function assert(assert) { - assert.equal(utils.normalize("Shift Ctrl A control ctrl", " "), - "control shift a", "removes reapeted modifiers"); - assert.equal(utils.normalize("shift-ctrl-left"), "control-shift-left", - "normilizes non printed characters"); - - assert.throws(function() { - utils.normalize("shift-alt-b-z"); - }, "throws if contains more then on non-modifier key"); -}; - -require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-l10n-locale.js b/addon-sdk-1.16/test/test-l10n-locale.js deleted file mode 100644 index 87be1ca4..00000000 --- a/addon-sdk-1.16/test/test-l10n-locale.js +++ /dev/null @@ -1,143 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -const { getPreferedLocales, findClosestLocale } = require("sdk/l10n/locale"); -const prefs = require("sdk/preferences/service"); -const { Cc, Ci, Cu } = require("chrome"); -const { Services } = Cu.import("resource://gre/modules/Services.jsm"); -const BundleService = Cc["@mozilla.org/intl/stringbundle;1"].getService(Ci.nsIStringBundleService); - -const PREF_MATCH_OS_LOCALE = "intl.locale.matchOS"; -const PREF_SELECTED_LOCALE = "general.useragent.locale"; -const PREF_ACCEPT_LANGUAGES = "intl.accept_languages"; - -function assertPrefered(assert, expected, msg) { - assert.equal(JSON.stringify(getPreferedLocales()), JSON.stringify(expected), - msg); -} - -exports.testGetPreferedLocales = function(assert) { - prefs.set(PREF_MATCH_OS_LOCALE, false); - prefs.set(PREF_SELECTED_LOCALE, ""); - prefs.set(PREF_ACCEPT_LANGUAGES, ""); - assertPrefered(assert, ["en-us"], - "When all preferences are empty, we only have en-us"); - - prefs.set(PREF_SELECTED_LOCALE, "fr"); - prefs.set(PREF_ACCEPT_LANGUAGES, "jp"); - assertPrefered(assert, ["fr", "jp", "en-us"], - "We first have useragent locale, then web one and finally en-US"); - - prefs.set(PREF_SELECTED_LOCALE, "en-US"); - prefs.set(PREF_ACCEPT_LANGUAGES, "en-US"); - assertPrefered(assert, ["en-us"], - "We do not have duplicates"); - - prefs.set(PREF_SELECTED_LOCALE, "en-US"); - prefs.set(PREF_ACCEPT_LANGUAGES, "fr"); - assertPrefered(assert, ["en-us", "fr"], - "en-US can be first if specified by higher priority preference"); - - // Reset what we changed - prefs.reset(PREF_MATCH_OS_LOCALE); - prefs.reset(PREF_SELECTED_LOCALE); - prefs.reset(PREF_ACCEPT_LANGUAGES); -} - -// In some cases, mainly on Fennec and on Linux version, -// `general.useragent.locale` is a special 'localized' value, like: -// "chrome://global/locale/intl.properties" -exports.testPreferedLocalizedLocale = function(assert) { - prefs.set(PREF_MATCH_OS_LOCALE, false); - let bundleURL = "chrome://global/locale/intl.properties"; - prefs.setLocalized(PREF_SELECTED_LOCALE, bundleURL); - let contentLocale = "ja"; - prefs.set(PREF_ACCEPT_LANGUAGES, contentLocale); - - // Read manually the expected locale value from the property file - let expectedLocale = BundleService.createBundle(bundleURL). - GetStringFromName(PREF_SELECTED_LOCALE). - toLowerCase(); - - // First add the useragent locale - let expectedLocaleList = [expectedLocale]; - - // Then the content locale - if (expectedLocaleList.indexOf(contentLocale) == -1) - expectedLocaleList.push(contentLocale); - - // Add default "en-us" fallback if the main language is not already en-us - if (expectedLocaleList.indexOf("en-us") == -1) - expectedLocaleList.push("en-us"); - - assertPrefered(assert, expectedLocaleList, "test localized pref value"); - - // Reset what we have changed - prefs.reset(PREF_MATCH_OS_LOCALE); - prefs.reset(PREF_SELECTED_LOCALE); - prefs.reset(PREF_ACCEPT_LANGUAGES); -} - -exports.testPreferedOsLocale = function(assert) { - prefs.set(PREF_MATCH_OS_LOCALE, true); - prefs.set(PREF_SELECTED_LOCALE, ""); - prefs.set(PREF_ACCEPT_LANGUAGES, ""); - - let expectedLocale = Services.locale.getLocaleComponentForUserAgent(). - toLowerCase(); - let expectedLocaleList = [expectedLocale]; - - // Add default "en-us" fallback if the main language is not already en-us - if (expectedLocale != "en-us") - expectedLocaleList.push("en-us"); - - assertPrefered(assert, expectedLocaleList, "Ensure that we select OS locale when related preference is set"); - - // Reset what we have changed - prefs.reset(PREF_MATCH_OS_LOCALE); - prefs.reset(PREF_SELECTED_LOCALE); - prefs.reset(PREF_ACCEPT_LANGUAGES); -} - -exports.testFindClosestLocale = function(assert) { - // Second param of findClosestLocale (aMatchLocales) have to be in lowercase - assert.equal(findClosestLocale([], []), null, - "When everything is empty we get null"); - - assert.equal(findClosestLocale(["en", "en-US"], ["en"]), - "en", "We always accept exact match first 1/5"); - assert.equal(findClosestLocale(["en-US", "en"], ["en"]), - "en", "We always accept exact match first 2/5"); - assert.equal(findClosestLocale(["en", "en-US"], ["en-us"]), - "en-US", "We always accept exact match first 3/5"); - assert.equal(findClosestLocale(["ja-JP-mac", "ja", "ja-JP"], ["ja-jp"]), - "ja-JP", "We always accept exact match first 4/5"); - assert.equal(findClosestLocale(["ja-JP-mac", "ja", "ja-JP"], ["ja-jp-mac"]), - "ja-JP-mac", "We always accept exact match first 5/5"); - - assert.equal(findClosestLocale(["en", "en-GB"], ["en-us"]), - "en", "We accept more generic locale, when there is no exact match 1/2"); - assert.equal(findClosestLocale(["en-ZA", "en"], ["en-gb"]), - "en", "We accept more generic locale, when there is no exact match 2/2"); - - assert.equal(findClosestLocale(["ja-JP"], ["ja"]), - "ja-JP", "We accept more specialized locale, when there is no exact match 1/2"); - // Better to select "ja" in this case but behave same as current AddonManager - assert.equal(findClosestLocale(["ja-JP-mac", "ja"], ["ja-jp"]), - "ja-JP-mac", "We accept more specialized locale, when there is no exact match 2/2"); - - assert.equal(findClosestLocale(["en-US"], ["en-us"]), - "en-US", "We keep the original one as result 1/2"); - assert.equal(findClosestLocale(["en-us"], ["en-us"]), - "en-us", "We keep the original one as result 2/2"); - - assert.equal(findClosestLocale(["ja-JP-mac"], ["ja-jp-mac"]), - "ja-JP-mac", "We accept locale with 3 parts"); - assert.equal(findClosestLocale(["ja-JP"], ["ja-jp-mac"]), - "ja-JP", "We accept locale with 2 parts from locale with 3 parts"); - assert.equal(findClosestLocale(["ja"], ["ja-jp-mac"]), - "ja", "We accept locale with 1 part from locale with 3 parts"); -}; - -require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-l10n-plural-rules.js b/addon-sdk-1.16/test/test-l10n-plural-rules.js deleted file mode 100644 index 6c56ec49..00000000 --- a/addon-sdk-1.16/test/test-l10n-plural-rules.js +++ /dev/null @@ -1,84 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -const { getRulesForLocale } = require("sdk/l10n/plural-rules"); - -// For more information, please visit unicode website: -// http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/language_plural_rules.html - -function map(assert, f, n, form) { - assert.equal(f(n), form, n + " maps to '" + form + "'"); -} - -exports.testFrench = function(assert) { - let f = getRulesForLocale("fr"); - map(assert, f, -1, "other"); - map(assert, f, 0, "one"); - map(assert, f, 1, "one"); - map(assert, f, 1.5, "one"); - map(assert, f, 2, "other"); - map(assert, f, 100, "other"); -} - -exports.testEnglish = function(assert) { - let f = getRulesForLocale("en"); - map(assert, f, -1, "other"); - map(assert, f, 0, "other"); - map(assert, f, 1, "one"); - map(assert, f, 1.5, "other"); - map(assert, f, 2, "other"); - map(assert, f, 100, "other"); -} - -exports.testArabic = function(assert) { - let f = getRulesForLocale("ar"); - map(assert, f, -1, "other"); - map(assert, f, 0, "zero"); - map(assert, f, 0.5, "other"); - - map(assert, f, 1, "one"); - map(assert, f, 1.5, "other"); - - map(assert, f, 2, "two"); - map(assert, f, 2.5, "other"); - - map(assert, f, 3, "few"); - map(assert, f, 3.5, "few"); // I'd expect it to be 'other', but the unicode.org - // algorithm computes 'few'. - map(assert, f, 5, "few"); - map(assert, f, 10, "few"); - map(assert, f, 103, "few"); - map(assert, f, 105, "few"); - map(assert, f, 110, "few"); - map(assert, f, 203, "few"); - map(assert, f, 205, "few"); - map(assert, f, 210, "few"); - - map(assert, f, 11, "many"); - map(assert, f, 50, "many"); - map(assert, f, 99, "many"); - map(assert, f, 111, "many"); - map(assert, f, 150, "many"); - map(assert, f, 199, "many"); - - map(assert, f, 100, "other"); - map(assert, f, 101, "other"); - map(assert, f, 102, "other"); - map(assert, f, 200, "other"); - map(assert, f, 201, "other"); - map(assert, f, 202, "other"); -} - -exports.testJapanese = function(assert) { - // Japanese doesn't have plural forms. - let f = getRulesForLocale("ja"); - map(assert, f, -1, "other"); - map(assert, f, 0, "other"); - map(assert, f, 1, "other"); - map(assert, f, 1.5, "other"); - map(assert, f, 2, "other"); - map(assert, f, 100, "other"); -} - -require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-light-traits.js b/addon-sdk-1.16/test/test-light-traits.js deleted file mode 100644 index 7c5ca42d..00000000 --- a/addon-sdk-1.16/test/test-light-traits.js +++ /dev/null @@ -1,11 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -exports["test traits from objects"] = require("./traits/object-tests"); -exports["test traits from descriptors"] = require("./traits/descriptor-tests"); -exports["test inheritance"] = require("./traits/inheritance-tests"); - -require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-list.js b/addon-sdk-1.16/test/test-list.js deleted file mode 100644 index 10be4bee..00000000 --- a/addon-sdk-1.16/test/test-list.js +++ /dev/null @@ -1,58 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -'use strict'; - -const { List, addListItem, removeListItem } = require('sdk/util/list'); -const { Class } = require('sdk/core/heritage'); - -exports.testList = function(assert) { - let list = List(); - addListItem(list, 1); - - for (let key in list) { - assert.equal(key, 0, 'key is correct'); - assert.equal(list[key], 1, 'value is correct'); - } - - let count = 0; - for each (let ele in list) { - assert.equal(ele, 1, 'ele is correct'); - assert.equal(++count, 1, 'count is correct'); - } - - count = 0; - for (let ele of list) { - assert.equal(ele, 1, 'ele is correct'); - assert.equal(++count, 1, 'count is correct'); - } - - removeListItem(list, 1); - assert.equal(list.length, 0, 'remove worked'); -}; - -exports.testImplementsList = function(assert) { - let List2 = Class({ - implements: [List], - initialize: function() { - List.prototype.initialize.apply(this, [0, 1, 2]); - } - }); - let list2 = List2(); - let count = 0; - - for each (let ele in list2) { - assert.equal(ele, count++, 'ele is correct'); - } - - count = 0; - for (let ele of list2) { - assert.equal(ele, count++, 'ele is correct'); - } - - addListItem(list2, 3); - assert.equal(list2.length, 4, '3 was added'); - assert.equal(list2[list2.length-1], 3, '3 was added'); -} - -require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-loader.js b/addon-sdk-1.16/test/test-loader.js deleted file mode 100644 index 4ddcc008..00000000 --- a/addon-sdk-1.16/test/test-loader.js +++ /dev/null @@ -1,346 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -'use strict'; - -let { - Loader, main, unload, parseStack, generateMap, resolve, join -} = require('toolkit/loader'); -let { readURI } = require('sdk/net/url'); - -let root = module.uri.substr(0, module.uri.lastIndexOf('/')) - - -// The following adds Debugger constructor to the global namespace. -const { Cu } = require('chrome'); -const { addDebuggerToGlobal } = Cu.import('resource://gre/modules/jsdebugger.jsm', {}); -addDebuggerToGlobal(this); - -exports['test resolve'] = function (assert) { - let cuddlefish_id = 'sdk/loader/cuddlefish'; - assert.equal(resolve('../index.js', './dir/c.js'), './index.js'); - assert.equal(resolve('./index.js', './dir/c.js'), './dir/index.js'); - assert.equal(resolve('./dir/c.js', './index.js'), './dir/c.js'); - assert.equal(resolve('../utils/file.js', './dir/b.js'), './utils/file.js'); - - assert.equal(resolve('../utils/./file.js', './dir/b.js'), './utils/file.js'); - assert.equal(resolve('../utils/file.js', './'), './../utils/file.js'); - assert.equal(resolve('./utils/file.js', './'), './utils/file.js'); - assert.equal(resolve('./utils/file.js', './index.js'), './utils/file.js'); - - assert.equal(resolve('../utils/./file.js', cuddlefish_id), 'sdk/utils/file.js'); - assert.equal(resolve('../utils/file.js', cuddlefish_id), 'sdk/utils/file.js'); - assert.equal(resolve('./utils/file.js', cuddlefish_id), 'sdk/loader/utils/file.js'); - - assert.equal(resolve('..//index.js', './dir/c.js'), './index.js'); - assert.equal(resolve('../../gre/modules/XPCOMUtils.jsm', 'resource://thing/utils/file.js'), 'resource://gre/modules/XPCOMUtils.jsm'); - assert.equal(resolve('../../gre/modules/XPCOMUtils.jsm', 'chrome://thing/utils/file.js'), 'chrome://gre/modules/XPCOMUtils.jsm'); - assert.equal(resolve('../../a/b/c.json', 'file:///thing/utils/file.js'), 'file:///a/b/c.json'); - - // Does not change absolute paths - assert.equal(resolve('resource://gre/modules/file.js', './dir/b.js'), - 'resource://gre/modules/file.js'); - assert.equal(resolve('file:///gre/modules/file.js', './dir/b.js'), - 'file:///gre/modules/file.js'); - assert.equal(resolve('/root.js', './dir/b.js'), - '/root.js'); -}; - -exports['test join'] = function (assert) { - assert.equal(join('a/path', '../../../module'), '../module'); - assert.equal(join('a/path/to', '../module'), 'a/path/module'); - assert.equal(join('a/path/to', './module'), 'a/path/to/module'); - assert.equal(join('a/path/to', '././../module'), 'a/path/module'); - assert.equal(join('resource://my/path/yeah/yuh', '../whoa'), - 'resource://my/path/yeah/whoa'); - assert.equal(join('resource://my/path/yeah/yuh', './whoa'), - 'resource://my/path/yeah/yuh/whoa'); - assert.equal(join('file:///my/path/yeah/yuh', '../whoa'), - 'file:///my/path/yeah/whoa'); - assert.equal(join('file:///my/path/yeah/yuh', './whoa'), - 'file:///my/path/yeah/yuh/whoa'); - assert.equal(join('a/path/to', '..//module'), 'a/path/module'); -}; - -exports['test dependency cycles'] = function(assert) { - let uri = root + '/fixtures/loader/cycles/'; - let loader = Loader({ paths: { '': uri } }); - - let program = main(loader, 'main'); - - assert.equal(program.a.b, program.b, 'module `a` gets correct `b`'); - assert.equal(program.b.a, program.a, 'module `b` gets correct `a`'); - assert.equal(program.c.main, program, 'module `c` gets correct `main`'); - - unload(loader); -} - -exports['test syntax errors'] = function(assert) { - let uri = root + '/fixtures/loader/syntax-error/'; - let loader = Loader({ paths: { '': uri } }); - - try { - let program = main(loader, 'main'); - } catch (error) { - assert.equal(error.name, "SyntaxError", "throws syntax error"); - assert.equal(error.fileName.split("/").pop(), "error.js", - "Error contains filename"); - assert.equal(error.lineNumber, 11, "error is on line 11"); - let stack = parseStack(error.stack); - - assert.equal(stack.pop().fileName, uri + "error.js", - "last frame file containing syntax error"); - assert.equal(stack.pop().fileName, uri + "main.js", - "previous frame is a requirer module"); - assert.equal(stack.pop().fileName, module.uri, - "previous to it is a test module"); - - } finally { - unload(loader); - } -} - -exports['test sandboxes are not added if error'] = function (assert) { - let uri = root + '/fixtures/loader/missing-twice/'; - let loader = Loader({ paths: { '': uri } }); - let program = main(loader, 'main'); - assert.ok(!(uri + 'not-found.js' in loader.sandboxes), 'not-found.js not in loader.sandboxes'); -} - -exports['test missing module'] = function(assert) { - let uri = root + '/fixtures/loader/missing/' - let loader = Loader({ paths: { '': uri } }); - - try { - let program = main(loader, 'main') - } catch (error) { - assert.equal(error.message, "Module `not-found` is not found at " + - uri + "not-found.js", "throws if error not found"); - - assert.equal(error.fileName.split("/").pop(), "main.js", - "Error fileName is requirer module"); - - assert.equal(error.lineNumber, 7, "error is on line 7"); - - let stack = parseStack(error.stack); - - assert.equal(stack.pop().fileName, uri + "main.js", - "loader stack is omitted"); - - assert.equal(stack.pop().fileName, module.uri, - "previous in the stack is test module"); - } finally { - unload(loader); - } -} - -exports["test invalid module not cached and throws everytime"] = function(assert) { - let uri = root + "/fixtures/loader/missing-twice/"; - let loader = Loader({ paths: { "": uri } }); - - let { firstError, secondError, invalidJSON1, invalidJSON2 } = main(loader, "main"); - assert.equal(firstError.message, "Module `not-found` is not found at " + - uri + "not-found.js", "throws on first invalid require"); - assert.equal(firstError.lineNumber, 8, "first error is on line 7"); - assert.equal(secondError.message, "Module `not-found` is not found at " + - uri + "not-found.js", "throws on second invalid require"); - assert.equal(secondError.lineNumber, 14, "second error is on line 14"); - - assert.equal(invalidJSON1.message, - "JSON.parse: unexpected character at line 1 column 1 of the JSON data", - "throws on invalid JSON"); - assert.equal(invalidJSON2.message, - "JSON.parse: unexpected character at line 1 column 1 of the JSON data", - "throws on invalid JSON second time"); -}; - -exports['test exceptions in modules'] = function(assert) { - let uri = root + '/fixtures/loader/exceptions/' - - let loader = Loader({ paths: { '': uri } }); - - try { - let program = main(loader, 'main') - } catch (error) { - assert.equal(error.message, "Boom!", "thrown errors propagate"); - - assert.equal(error.fileName.split("/").pop(), "boomer.js", - "Error comes from the module that threw it"); - - assert.equal(error.lineNumber, 8, "error is on line 8"); - - let stack = parseStack(error.stack); - - let frame = stack.pop() - assert.equal(frame.fileName, uri + "boomer.js", - "module that threw is first in the stack"); - assert.equal(frame.name, "exports.boom", - "name is in the stack"); - - frame = stack.pop() - assert.equal(frame.fileName, uri + "main.js", - "module that called it is next in the stack"); - assert.equal(frame.lineNumber, 9, "caller line is in the stack"); - - - assert.equal(stack.pop().fileName, module.uri, - "this test module is next in the stack"); - } finally { - unload(loader); - } -} - -exports['test early errors in module'] = function(assert) { - let uri = root + '/fixtures/loader/errors/'; - let loader = Loader({ paths: { '': uri } }); - - try { - let program = main(loader, 'main') - } catch (error) { - assert.equal(String(error), - "Error: opening input stream (invalid filename?)", - "thrown errors propagate"); - - assert.equal(error.fileName.split("/").pop(), "boomer.js", - "Error comes from the module that threw it"); - - assert.equal(error.lineNumber, 7, "error is on line 7"); - - let stack = parseStack(error.stack); - - let frame = stack.pop() - assert.equal(frame.fileName, uri + "boomer.js", - "module that threw is first in the stack"); - - frame = stack.pop() - assert.equal(frame.fileName, uri + "main.js", - "module that called it is next in the stack"); - assert.equal(frame.lineNumber, 7, "caller line is in the stack"); - - - assert.equal(stack.pop().fileName, module.uri, - "this test module is next in the stack"); - } finally { - unload(loader); - } -}; - -exports['test require json'] = function (assert) { - let data = require('./fixtures/loader/json/manifest.json'); - assert.equal(data.name, 'Jetpack Loader Test', 'loads json with strings'); - assert.equal(data.version, '1.0.1', 'loads json with strings'); - assert.equal(data.dependencies.async, '*', 'loads json with objects'); - assert.equal(data.dependencies.underscore, '*', 'loads json with objects'); - assert.equal(data.contributors.length, 4, 'loads json with arrays'); - assert.ok(Array.isArray(data.contributors), 'loads json with arrays'); - data.version = '2.0.0'; - let newdata = require('./fixtures/loader/json/manifest.json'); - assert.equal(newdata.version, '2.0.0', - 'JSON objects returned should be cached and the same instance'); - - try { - require('./fixtures/loader/json/invalid.json'); - assert.fail('Error not thrown when loading invalid json'); - } catch (err) { - assert.ok(err, 'error thrown when loading invalid json'); - assert.ok(/JSON\.parse/.test(err.message), - 'should thrown an error from JSON.parse, not attempt to load .json.js'); - } - - // Try again to ensure an empty module isn't loaded from cache - try { - require('./fixtures/loader/json/invalid.json'); - assert.fail('Error not thrown when loading invalid json a second time'); - } catch (err) { - assert.ok(err, - 'error thrown when loading invalid json a second time'); - assert.ok(/JSON\.parse/.test(err.message), - 'should thrown an error from JSON.parse a second time, not attempt to load .json.js'); - } -}; - -exports['test setting metadata for newly created sandboxes'] = function(assert) { - let addonID = 'random-addon-id'; - let uri = root + '/fixtures/loader/cycles/'; - let loader = Loader({ paths: { '': uri }, id: addonID }); - - let dbg = new Debugger(); - dbg.onNewGlobalObject = function(global) { - dbg.onNewGlobalObject = undefined; - - let metadata = Cu.getSandboxMetadata(global.unsafeDereference()); - assert.ok(metadata, 'this global has attached metadata'); - assert.equal(metadata.URI, uri + 'main.js', 'URI is set properly'); - assert.equal(metadata.addonID, addonID, 'addon ID is set'); - } - - let program = main(loader, 'main'); -}; - -exports['test require .json, .json.js'] = function (assert) { - let testjson = require('./fixtures/loader/json/test.json'); - assert.equal(testjson.filename, 'test.json', - 'require("./x.json") should load x.json, not x.json.js'); - - let nodotjson = require('./fixtures/loader/json/nodotjson.json'); - assert.equal(nodotjson.filename, 'nodotjson.json.js', - 'require("./x.json") should load x.json.js when x.json does not exist'); - nodotjson.data.prop = 'hydralisk'; - - // require('nodotjson.json') and require('nodotjson.json.js') - // should resolve to the same file - let nodotjsonjs = require('./fixtures/loader/json/nodotjson.json.js'); - assert.equal(nodotjsonjs.data.prop, 'hydralisk', - 'js modules are cached whether access via .json.js or .json'); -}; - -exports['test invisibleToDebugger: false'] = function (assert) { - let uri = root + '/fixtures/loader/cycles/'; - let loader = Loader({ paths: { '': uri } }); - main(loader, 'main'); - - let dbg = new Debugger(); - let sandbox = loader.sandboxes[uri + 'main.js']; - - try { - dbg.addDebuggee(sandbox); - assert.ok(true, 'debugger added visible value'); - } catch(e) { - assert.fail('debugger could not add visible value'); - } -}; - -exports['test invisibleToDebugger: true'] = function (assert) { - let uri = root + '/fixtures/loader/cycles/'; - let loader = Loader({ paths: { '': uri }, invisibleToDebugger: true }); - main(loader, 'main'); - - let dbg = new Debugger(); - let sandbox = loader.sandboxes[uri + 'main.js']; - - try { - dbg.addDebuggee(sandbox); - assert.fail('debugger added invisible value'); - } catch(e) { - assert.ok(true, 'debugger did not add invisible value'); - } -}; - -exports['test console global by default'] = function (assert) { - let uri = root + '/fixtures/loader/globals/'; - let loader = Loader({ paths: { '': uri }}); - let program = main(loader, 'main'); - - assert.ok(typeof program.console === 'object', 'global `console` exists'); - assert.ok(typeof program.console.log === 'function', 'global `console.log` exists'); - - let loader2 = Loader({ paths: { '': uri }, globals: { console: fakeConsole }}); - let program2 = main(loader2, 'main'); - - assert.equal(program2.console, fakeConsole, - 'global console can be overridden with Loader options'); - function fakeConsole () {}; -}; - -require('test').run(exports); diff --git a/addon-sdk-1.16/test/test-match-pattern.js b/addon-sdk-1.16/test/test-match-pattern.js deleted file mode 100644 index 7e970899..00000000 --- a/addon-sdk-1.16/test/test-match-pattern.js +++ /dev/null @@ -1,141 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -"use strict"; - -const { MatchPattern } = require("sdk/util/match-pattern"); - -exports.testMatchPatternTestTrue = function(assert) { - function ok(pattern, url) { - let mp = new MatchPattern(pattern); - assert.ok(mp.test(url), pattern + " should match " + url); - } - - ok("*", "http://example.com"); - ok("*", "https://example.com"); - ok("*", "ftp://example.com"); - - ok("*.example.com", "http://example.com"); - ok("*.example.com", "http://hamburger.example.com"); - ok("*.example.com", "http://hotdog.hamburger.example.com"); - - ok("http://example.com*", "http://example.com"); - ok("http://example.com*", "http://example.com/"); - ok("http://example.com/*", "http://example.com/"); - ok("http://example.com/*", "http://example.com/potato-salad"); - ok("http://example.com/pickles/*", "http://example.com/pickles/"); - ok("http://example.com/pickles/*", "http://example.com/pickles/lemonade"); - - ok("http://example.com", "http://example.com"); - ok("http://example.com/ice-cream", "http://example.com/ice-cream"); - - ok(/.*zilla.*/, "https://bugzilla.redhat.com/show_bug.cgi?id=569753"); - ok(/https:.*zilla.*/, "https://bugzilla.redhat.com/show_bug.cgi?id=569753"); - ok('*.sample.com', 'http://ex.sample.com/foo.html'); - ok('*.amp.le.com', 'http://ex.amp.le.com'); - - ok('data:*', 'data:text/html;charset=utf-8,'); -}; - -exports.testMatchPatternTestFalse = function(assert) { - function ok(pattern, url) { - let mp = new MatchPattern(pattern); - assert.ok(!mp.test(url), pattern + " should not match " + url); - } - - ok("*", null); - ok("*", ""); - ok("*", "bogus"); - ok("*", "chrome://browser/content/browser.xul"); - ok("*", "nttp://example.com"); - - ok("*.example.com", null); - ok("*.example.com", ""); - ok("*.example.com", "bogus"); - ok("*.example.com", "http://example.net"); - ok("*.example.com", "http://foo.com"); - ok("*.example.com", "http://example.com.foo"); - ok("*.example2.com", "http://example.com"); - - ok("http://example.com/*", null); - ok("http://example.com/*", ""); - ok("http://example.com/*", "bogus"); - ok("http://example.com/*", "http://example.com"); - ok("http://example.com/*", "http://foo.com/"); - - ok("http://example.com", null); - ok("http://example.com", ""); - ok("http://example.com", "bogus"); - ok("http://example.com", "http://example.com/"); - - ok(/zilla.*/, "https://bugzilla.redhat.com/show_bug.cgi?id=569753"); - ok(/.*zilla/, "https://bugzilla.redhat.com/show_bug.cgi?id=569753"); - ok(/.*Zilla.*/, "https://bugzilla.redhat.com/show_bug.cgi?id=655464"); // bug 655464 - ok(/https:.*zilla/, "https://bugzilla.redhat.com/show_bug.cgi?id=569753"); - - // bug 856913 - ok('*.ign.com', 'http://www.design.com'); - ok('*.ign.com', 'http://design.com'); - ok('*.zilla.com', 'http://bugzilla.mozilla.com'); - ok('*.zilla.com', 'http://mo-zilla.com'); - ok('*.amp.le.com', 'http://amp-le.com'); - ok('*.amp.le.com', 'http://examp.le.com'); -}; - -exports.testMatchPatternErrors = function(assert) { - assert.throws( - function() new MatchPattern("*.google.com/*"), - /There can be at most one/, - "MatchPattern throws when supplied multiple '*'" - ); - - assert.throws( - function() new MatchPattern("google.com"), - /expected to be either an exact URL/, - "MatchPattern throws when the wildcard doesn't use '*' and doesn't " + - "look like a URL" - ); - - assert.throws( - function() new MatchPattern("http://google*.com"), - /expected to be the first or the last/, - "MatchPattern throws when a '*' is in the middle of the wildcard" - ); - - assert.throws( - function() new MatchPattern(/ /g), - /^A RegExp match pattern cannot be set to `global` \(i\.e\. \/\/g\)\.$/, - "MatchPattern throws on a RegExp set to `global` (i.e. //g)." - ); - - assert.throws( - function() new MatchPattern(/ /i), - /^A RegExp match pattern cannot be set to `ignoreCase` \(i\.e\. \/\/i\)\.$/, - "MatchPattern throws on a RegExp set to `ignoreCase` (i.e. //i)." - ); - - assert.throws( - function() new MatchPattern( / /m ), - /^A RegExp match pattern cannot be set to `multiline` \(i\.e\. \/\/m\)\.$/, - "MatchPattern throws on a RegExp set to `multiline` (i.e. //m)." - ); -}; - -exports.testMatchPatternInternals = function(assert) { - assert.equal( - new MatchPattern("http://google.com/test").exactURL, - "http://google.com/test" - ); - - assert.equal( - new MatchPattern("http://google.com/test/*").urlPrefix, - "http://google.com/test/" - ); - - assert.equal( - new MatchPattern("*.example.com").domain, - "example.com" - ); -}; - -require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-memory.js b/addon-sdk-1.16/test/test-memory.js deleted file mode 100644 index 694172d0..00000000 --- a/addon-sdk-1.16/test/test-memory.js +++ /dev/null @@ -1,22 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -"use strict"; - -const memory = require("sdk/deprecated/memory"); -const { gc } = require("sdk/test/memory"); - -exports.testMemory = function(assert) { - var obj = {}; - memory.track(obj, "testMemory.testObj"); - - var objs = memory.getObjects("testMemory.testObj"); - assert.equal(objs[0].weakref.get(), obj); - obj = null; - - gc().then(function() { - assert.equal(objs[0].weakref.get(), null); - }); -}; - -require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-method.js b/addon-sdk-1.16/test/test-method.js deleted file mode 100644 index 0478593c..00000000 --- a/addon-sdk-1.16/test/test-method.js +++ /dev/null @@ -1,7 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -module.exports = require("method/test/common"); diff --git a/addon-sdk-1.16/test/test-module.js b/addon-sdk-1.16/test/test-module.js deleted file mode 100644 index 6c9c6c2b..00000000 --- a/addon-sdk-1.16/test/test-module.js +++ /dev/null @@ -1,37 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -/** Disabled because of Bug 672199 -exports["test module exports are frozen"] = function(assert) { - assert.ok(Object.isFrozen(require("sdk/hotkeys")), - "module exports are frozen"); -}; - -exports["test redefine exported property"] = function(assert) { - let hotkeys = require("sdk/hotkeys"); - let { Hotkey } = hotkeys; - try { Object.defineProperty(hotkeys, 'Hotkey', { value: {} }); } catch(e) {} - assert.equal(hotkeys.Hotkey, Hotkey, "exports can't be redefined"); -}; -*/ - -exports["test can't delete exported property"] = function(assert) { - let hotkeys = require("sdk/hotkeys"); - let { Hotkey } = hotkeys; - - try { delete hotkeys.Hotkey; } catch(e) {} - assert.equal(hotkeys.Hotkey, Hotkey, "exports can't be deleted"); -}; - -exports["test can't override exported property"] = function(assert) { - let hotkeys = require("sdk/hotkeys"); - let { Hotkey } = hotkeys; - - try { hotkeys.Hotkey = Object } catch(e) {} - assert.equal(hotkeys.Hotkey, Hotkey, "exports can't be overriden"); -}; - -require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-modules.js b/addon-sdk-1.16/test/test-modules.js deleted file mode 100644 index ee9d3d9b..00000000 --- a/addon-sdk-1.16/test/test-modules.js +++ /dev/null @@ -1,150 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -exports.testDefine = function(assert) { - let tiger = require('./modules/tiger'); - assert.equal(tiger.name, 'tiger', 'name proprety was exported properly'); - assert.equal(tiger.type, 'cat', 'property form other module exported'); -}; - -exports.testDefineInoresNonFactory = function(assert) { - let mod = require('./modules/async2'); - assert.equal(mod.name, 'async2', 'name proprety was exported properly'); - assert.ok(mod.traditional2Name !== 'traditional2', '1st is ignored'); -}; -/* Disable test that require AMD specific functionality: - -// define() that exports a function as the module value, -// specifying a module name. -exports.testDefExport = function(assert) { - var add = require('modules/add'); - assert.equal(add(1, 1), 2, 'Named define() exporting a function'); -}; - -// define() that exports function as a value, but is anonymous -exports.testAnonDefExport = function (assert) { - var subtract = require('modules/subtract'); - assert.equal(subtract(4, 2), 2, - 'Anonymous define() exporting a function'); -} - -// using require([], function () {}) to load modules. -exports.testSimpleRequire = function (assert) { - require(['modules/blue', 'modules/orange'], function (blue, orange) { - assert.equal(blue.name, 'blue', 'Simple require for blue'); - assert.equal(orange.name, 'orange', 'Simple require for orange'); - assert.equal(orange.parentType, 'color', - 'Simple require dependency check for orange'); - }); -} - -// using nested require([]) calls. -exports.testSimpleRequireNested = function (assert) { - require(['modules/blue', 'modules/orange', 'modules/green'], - function (blue, orange, green) { - - require(['modules/orange', 'modules/red'], function (orange, red) { - assert.equal(red.name, 'red', 'Simple require for red'); - assert.equal(red.parentType, 'color', - 'Simple require dependency check for red'); - assert.equal(blue.name, 'blue', 'Simple require for blue'); - assert.equal(orange.name, 'orange', 'Simple require for orange'); - assert.equal(orange.parentType, 'color', - 'Simple require dependency check for orange'); - assert.equal(green.name, 'green', 'Simple require for green'); - assert.equal(green.parentType, 'color', - 'Simple require dependency check for green'); - }); - - }); -} - -// requiring a traditional module, that uses async, that use traditional and -// async, with a circular reference -exports.testMixedCircular = function (assert) { - var t = require('modules/traditional1'); - assert.equal(t.name, 'traditional1', 'Testing name'); - assert.equal(t.traditional2Name, 'traditional2', - 'Testing dependent name'); - assert.equal(t.traditional1Name, 'traditional1', 'Testing circular name'); - assert.equal(t.async2Name, 'async2', 'Testing async2 name'); - assert.equal(t.async2Traditional2Name, 'traditional2', - 'Testing nested traditional2 name'); -} - -// Testing define()(function(require) {}) with some that use exports, -// some that use return. -exports.testAnonExportsReturn = function (assert) { - var lion = require('modules/lion'); - require(['modules/tiger', 'modules/cheetah'], function (tiger, cheetah) { - assert.equal('lion', lion, 'Check lion name'); - assert.equal('tiger', tiger.name, 'Check tiger name'); - assert.equal('cat', tiger.type, 'Check tiger type'); - assert.equal('cheetah', cheetah(), 'Check cheetah name'); - }); -} - -// circular dependency -exports.testCircular = function (assert) { - var pollux = require('modules/pollux'), - castor = require('modules/castor'); - - assert.equal(pollux.name, 'pollux', 'Pollux\'s name'); - assert.equal(pollux.getCastorName(), - 'castor', 'Castor\'s name from Pollux.'); - assert.equal(castor.name, 'castor', 'Castor\'s name'); - assert.equal(castor.getPolluxName(), 'pollux', - 'Pollux\'s name from Castor.'); -} - -// test a bad module that asks for exports but also does a define() return -exports.testBadExportAndReturn = function (assert) { - var passed = false; - try { - var bad = require('modules/badExportAndReturn'); - } catch(e) { - passed = /cannot use exports and also return/.test(e.toString()); - } - assert.equal(passed, true, 'Make sure exports and return fail'); -} - -// test a bad circular dependency, where an exported value is needed, but -// the return value happens too late, a module already asked for the exported -// value. -exports.testBadExportAndReturnCircular = function (assert) { - var passed = false; - try { - var bad = require('modules/badFirst'); - } catch(e) { - passed = /after another module has referenced its exported value/ - .test(e.toString()); - } - assert.equal(passed, true, 'Make sure return after an exported ' + - 'value is grabbed by another module fails.'); -} - -// only allow one define call per file. -exports.testOneDefine = function (assert) { - var passed = false; - try { - var dupe = require('modules/dupe'); - } catch(e) { - passed = /Only one call to define/.test(e.toString()); - } - assert.equal(passed, true, 'Only allow one define call per module'); -} - -// only allow one define call per file, testing a bad nested define call. -exports.testOneDefineNested = function (assert) { - var passed = false; - try { - var dupe = require('modules/dupeNested'); - } catch(e) { - passed = /Only one call to define/.test(e.toString()); - } - assert.equal(passed, true, 'Only allow one define call per module'); -} -*/ - -require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-namespace.js b/addon-sdk-1.16/test/test-namespace.js deleted file mode 100644 index 46f194a3..00000000 --- a/addon-sdk-1.16/test/test-namespace.js +++ /dev/null @@ -1,121 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const { ns } = require("sdk/core/namespace"); -const { Cc, Ci, Cu } = require("chrome"); -const { setTimeout } = require("sdk/timers") - -exports["test post GC references"] = function (assert, done) { - var target = {}, local = ns() - local(target).there = true - - assert.equal(local(target).there, true, "namespaced preserved"); - - Cu.schedulePreciseGC(function() { - assert.equal(local(target).there, true, "namespace is preserved post GC"); - done(); - }); -}; - -exports["test namsepace basics"] = function(assert) { - var privates = ns(); - var object = { foo: function foo() { return "hello foo"; } }; - - assert.notEqual(privates(object), object, - "namespaced object is not the same"); - assert.ok(!('foo' in privates(object)), - "public properties are not in the namespace"); - - assert.equal(privates(object), privates(object), - "same namespaced object is returned on each call"); -}; - -exports["test namespace overlays"] = function(assert) { - var _ = ns(); - var object = { foo: 'foo' }; - - _(object).foo = 'bar'; - - assert.equal(_(object).foo, "bar", - "namespaced property `foo` changed value"); - - assert.equal(object.foo, "foo", - "public property `foo` has original value"); - - object.foo = "baz"; - assert.equal(_(object).foo, "bar", - "property changes do not affect namespaced properties"); - - object.bar = "foo"; - assert.ok(!("bar" in _(object)), - "new public properties are not reflected in namespace"); -}; - -exports["test shared namespaces"] = function(assert) { - var _ = ns(); - - var f1 = { hello: 1 }; - var f2 = { foo: 'foo', hello: 2 }; - _(f1).foo = _(f2).foo = 'bar'; - - assert.equal(_(f1).hello, _(f2).hello, "namespace can be shared"); - assert.notEqual(f1.hello, _(f1).hello, "shared namespace can overlay"); - assert.notEqual(f2.hello, _(f2).hello, "target is not affected"); - - _(f1).hello = 3; - - assert.notEqual(_(f1).hello, _(f2).hello, - "namespaced property can be overided"); - assert.equal(_(f2).hello, _({}).hello, "namespace does not change"); -}; - -exports["test multi namespace"] = function(assert) { - var n1 = ns(); - var n2 = ns(); - var object = { baz: 1 }; - n1(object).foo = 1; - n2(object).foo = 2; - n1(object).bar = n2(object).bar = 3; - - assert.notEqual(n1(object).foo, n2(object).foo, - "object can have multiple namespaces"); - assert.equal(n1(object).bar, n2(object).bar, - "object can have matching props in diff namespaces"); -}; - -exports["test ns alias"] = function(assert) { - assert.strictEqual(ns, require('sdk/core/namespace').Namespace, - "ns is an alias of Namespace"); -}; - -exports["test ns inheritance"] = function(assert) { - let _ = ns(); - - let prototype = { level: 1 }; - let object = Object.create(prototype); - let delegee = Object.create(object); - - _(prototype).foo = {}; - - assert.ok(!Object.prototype.hasOwnProperty.call(_(delegee), "foo"), - "namespaced property is not copied to descendants"); - assert.equal(_(delegee).foo, _(prototype).foo, - "namespaced properties are inherited by descendants"); - - _(object).foo = {}; - assert.notEqual(_(object).foo, _(prototype).foo, - "namespaced properties may be shadowed"); - assert.equal(_(object).foo, _(delegee).foo, - "shadwed properties are inherited by descendants"); - - _(object).bar = {}; - assert.ok(!("bar" in _(prototype)), - "descendants properties are not copied to ancestors"); - assert.ok(_(object).bar, _(delegee).bar, - "descendants properties are inherited"); -}; - -require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-native-loader.js b/addon-sdk-1.16/test/test-native-loader.js deleted file mode 100644 index d787d407..00000000 --- a/addon-sdk-1.16/test/test-native-loader.js +++ /dev/null @@ -1,234 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -'use strict'; - -let { - Loader, main, unload, parseStack, generateMap, resolve, nodeResolve -} = require('toolkit/loader'); -let { readURI } = require('sdk/net/url'); -let { all } = require('sdk/core/promise'); -let testOptions = require('@test/options'); - -let root = module.uri.substr(0, module.uri.lastIndexOf('/')) -// The following adds Debugger constructor to the global namespace. -const { Cu } = require('chrome'); -const { addDebuggerToGlobal } = Cu.import('resource://gre/modules/jsdebugger.jsm', {}); -addDebuggerToGlobal(this); - - -exports['test nodeResolve'] = function (assert) { - let rootURI = root + '/fixtures/native-addon-test/'; - let manifest = {}; - manifest.dependencies = {}; - - // Handles extensions - resolveTest('../package.json', './dir/c.js', './package.json'); - resolveTest('../dir/b.js', './dir/c.js', './dir/b.js'); - - resolveTest('./dir/b', './index.js', './dir/b.js'); - resolveTest('../index', './dir/b.js', './index.js'); - resolveTest('../', './dir/b.js', './index.js'); - resolveTest('./dir/a', './index.js', './dir/a.js', 'Precedence dir/a.js over dir/a/'); - resolveTest('../utils', './dir/a.js', './utils/index.js', 'Requiring a directory defaults to dir/index.js'); - resolveTest('../newmodule', './dir/c.js', './newmodule/lib/file.js', 'Uses package.json main in dir to load appropriate "main"'); - resolveTest('test-math', './utils/index.js', './node_modules/test-math/index.js', - 'Dependencies default to their index.js'); - resolveTest('test-custom-main', './utils/index.js', './node_modules/test-custom-main/lib/custom-entry.js', - 'Dependencies use "main" entry'); - resolveTest('test-math/lib/sqrt', './utils/index.js', './node_modules/test-math/lib/sqrt.js', - 'Dependencies\' files can be consumed via "/"'); - - resolveTest('sdk/tabs/utils', './index.js', undefined, - 'correctly ignores SDK references in paths'); - resolveTest('fs', './index.js', undefined, - 'correctly ignores built in node modules in paths'); - - resolveTest('test-add', './node_modules/test-math/index.js', - './node_modules/test-math/node_modules/test-add/index.js', - 'Dependencies\' dependencies can be found'); - - - function resolveTest (id, requirer, expected, msg) { - let result = nodeResolve(id, requirer, { manifest: manifest, rootURI: rootURI }); - assert.equal(result, expected, 'nodeResolve ' + id + ' from ' + requirer + ' ' +msg); - } -} - -/* -// TODO not working in current env -exports['test bundle'] = function (assert, done) { - loadAddon('/native-addons/native-addon-test/') -}; -*/ - -exports['test generateMap()'] = function (assert, done) { - getJSON('/fixtures/native-addon-test/expectedmap.json').then(expected => { - generateMap({ - rootURI: root + '/fixtures/native-addon-test/' - }, map => { - assert.deepEqual(map, expected, 'generateMap returns expected mappings'); - assert.equal(map['./index.js']['./dir/a'], './dir/a.js', - 'sanity check on correct mappings'); - done(); - }); - }).then(null, (reason) => console.error(reason)); -}; - -exports['test JSM loading'] = function (assert, done) { - getJSON('/fixtures/jsm-package/package.json').then(manifest => { - let rootURI = root + '/fixtures/jsm-package/'; - let loader = Loader({ - paths: makePaths(rootURI), - rootURI: rootURI, - manifest: manifest, - isNative: true - }); - - let program = main(loader); - assert.ok(program.localJSMCached, 'local relative JSMs are cached'); - assert.ok(program.isCachedJSAbsolute , 'absolute resource:// js are cached'); - assert.ok(program.isCachedPath, 'JSMs resolved in paths are cached'); - assert.ok(program.isCachedAbsolute, 'absolute resource:// JSMs are cached'); - - assert.ok(program.localJSM, 'able to load local relative JSMs'); - all([ - program.isLoadedPath(10), - program.isLoadedAbsolute(20), - program.isLoadedJSAbsolute(30) - ]).then(([path, absolute, jsabsolute]) => { - assert.equal(path, 10, 'JSM files resolved from path work'); - assert.equal(absolute, 20, 'JSM files resolved from full resource:// work'); - assert.equal(jsabsolute, 30, 'JS files resolved from full resource:// work'); - }).then(done, console.error); - - }).then(null, console.error); -}; - -exports['test native Loader with mappings'] = function (assert, done) { - all([ - getJSON('/fixtures/native-addon-test/expectedmap.json'), - getJSON('/fixtures/native-addon-test/package.json') - ]).then(([expectedMap, manifest]) => { - - // Override dummy module and point it to `test-math` to see if the - // require is pulling from the mapping - expectedMap['./index.js']['./dir/dummy'] = './dir/a.js'; - - let rootURI = root + '/fixtures/native-addon-test/'; - let loader = Loader({ - paths: makePaths(rootURI), - rootURI: rootURI, - manifest: manifest, - requireMap: expectedMap, - isNative: true - }); - - let program = main(loader); - assert.equal(program.dummyModule, 'dir/a', - 'The lookup uses the information given in the mapping'); - - testLoader(program, assert); - unload(loader); - done(); - }).then(null, (reason) => console.error(reason)); -}; - -exports['test native Loader without mappings'] = function (assert, done) { - getJSON('/fixtures/native-addon-test/package.json').then(manifest => { - let rootURI = root + '/fixtures/native-addon-test/'; - let loader = Loader({ - paths: makePaths(rootURI), - rootURI: rootURI, - manifest: manifest, - isNative: true - }); - - let program = main(loader); - testLoader(program, assert); - unload(loader); - done(); - }).then(null, (reason) => console.error(reason)); -}; - -function testLoader (program, assert) { - // Test 'main' entries - // no relative custom main `lib/index.js` - assert.equal(program.customMainModule, 'custom entry file', - 'a node_module dependency correctly uses its `main` entry in manifest'); - // relative custom main `./lib/index.js` - assert.equal(program.customMainModuleRelative, 'custom entry file relative', - 'a node_module dependency correctly uses its `main` entry in manifest with relative ./'); - // implicit './index.js' - assert.equal(program.defaultMain, 'default main', - 'a node_module dependency correctly defautls to index.js for main'); - - // Test directory exports - assert.equal(program.directoryDefaults, 'utils', - '`require`ing a directory defaults to dir/index.js'); - assert.equal(program.directoryMain, 'main from new module', - '`require`ing a directory correctly loads the `main` entry and not index.js'); - assert.equal(program.resolvesJSoverDir, 'dir/a', - '`require`ing "a" resolves "a.js" over "a/index.js"'); - - // Test dependency's dependencies - assert.ok(program.math.add, - 'correctly defaults to index.js of a module'); - assert.equal(program.math.add(10, 5), 15, - 'node dependencies correctly include their own dependencies'); - assert.equal(program.math.subtract(10, 5), 5, - 'node dependencies correctly include their own dependencies'); - assert.equal(program.mathInRelative.subtract(10, 5), 5, - 'relative modules can also include node dependencies'); - - // Test SDK natives - assert.ok(program.promise.defer, 'main entry can include SDK modules with no deps'); - assert.ok(program.promise.resolve, 'main entry can include SDK modules with no deps'); - assert.ok(program.eventCore.on, 'main entry can include SDK modules that have dependencies'); - assert.ok(program.eventCore.off, 'main entry can include SDK modules that have dependencies'); - - // Test JSMs - assert.ok(program.promisejsm.defer, 'can require JSM files in path'); - assert.equal(program.localJSM.test, 'this is a jsm', - 'can require relative JSM files'); - - // Other tests - assert.equal(program.areModulesCached, true, - 'modules are correctly cached'); - assert.equal(program.testJSON.dependencies['test-math'], '*', - 'correctly requires JSON files'); -} - -function getJSON (uri) { - return readURI(root + uri).then(manifest => JSON.parse(manifest)); -} - -function makePaths (uri) { - // Uses development SDK modules if overloaded in loader - let sdkPaths = testOptions.paths ? testOptions.paths[''] : 'resource://gre/modules/commonjs/'; - return { - './': uri, - 'sdk/': sdkPaths + 'sdk/', - 'toolkit/': sdkPaths + 'toolkit/', - 'modules/': 'resource://gre/modules/' - }; -} - -function loadAddon (uri, map) { - let rootURI = root + uri; - getJSON(uri + '/package.json').then(manifest => { - let loader = Loader({ - paths: makePaths(rootURI), - rootURI: rootURI, - manifest: manifest, - isNative: true, - modules: { - '@test/options': testOptions - } - }); - let program = main(loader); - }).then(null, console.error); -} - -require('test').run(exports); diff --git a/addon-sdk-1.16/test/test-net-url.js b/addon-sdk-1.16/test/test-net-url.js deleted file mode 100644 index 6ce2a328..00000000 --- a/addon-sdk-1.16/test/test-net-url.js +++ /dev/null @@ -1,212 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const { readURI, readURISync } = require("sdk/net/url"); -const data = require("./fixtures"); - -const utf8text = "Hello, ゼロ!"; -const latin1text = "Hello, ゼロ!"; - -const dataURIutf8 = "data:text/plain;charset=utf-8," + encodeURIComponent(utf8text); -const dataURIlatin1 = "data:text/plain;charset=ISO-8859-1," + escape(latin1text); -const chromeURI = "chrome://global-platform/locale/accessible.properties"; - -exports["test async readURI"] = function(assert, done) { - let content = ""; - - readURI(data.url("test-net-url.txt")).then(function(data) { - content = data; - assert.equal(content, utf8text, "The URL content is loaded properly"); - done(); - }, function() { - assert.fail("should not reject"); - done(); - }) - - assert.equal(content, "", "The URL content is not load yet"); -} - -exports["test sync readURI"] = function(assert) { - let content = ""; - - readURI(data.url("test-net-url.txt"), { sync: true }).then(function(data) { - content = data; - }, function() { - assert.fail("should not reject"); - }) - - assert.equal(content, utf8text, "The URL content is loaded properly"); -} - -exports["test readURISync"] = function(assert) { - let content = readURISync(data.url("test-net-url.txt")); - - assert.equal(content, utf8text, "The URL content is loaded properly"); -} - -exports["test async readURI with ISO-8859-1 charset"] = function(assert, done) { - let content = ""; - - readURI(data.url("test-net-url.txt"), { charset : "ISO-8859-1"}).then(function(data) { - content = data; - assert.equal(content, latin1text, "The URL content is loaded properly"); - done(); - }, function() { - assert.fail("should not reject"); - done(); - }) - - assert.equal(content, "", "The URL content is not load yet"); -} - -exports["test sync readURI with ISO-8859-1 charset"] = function(assert) { - let content = ""; - - readURI(data.url("test-net-url.txt"), { - sync: true, - charset: "ISO-8859-1" - }).then(function(data) { - content = data; - }, function() { - assert.fail("should not reject"); - }) - - assert.equal(content, latin1text, "The URL content is loaded properly"); -} - -exports["test readURISync with ISO-8859-1 charset"] = function(assert) { - let content = readURISync(data.url("test-net-url.txt"), "ISO-8859-1"); - - assert.equal(content, latin1text, "The URL content is loaded properly"); -} - -exports["test async readURI with not existing file"] = function(assert, done) { - readURI(data.url("test-net-url-fake.txt")).then(function(data) { - assert.fail("should not resolve"); - done(); - }, function(reason) { - assert.ok(reason.indexOf("Failed to read:") === 0); - done(); - }) -} - -exports["test sync readURI with not existing file"] = function(assert) { - readURI(data.url("test-net-url-fake.txt"), { sync: true }).then(function(data) { - assert.fail("should not resolve"); - }, function(reason) { - assert.ok(reason.indexOf("Failed to read:") === 0); - }) -} - -exports["test readURISync with not existing file"] = function(assert) { - assert.throws(function() { - readURISync(data.url("test-net-url-fake.txt")); - }, /NS_ERROR_FILE_NOT_FOUND/); -} - -exports["test async readURI with data URI"] = function(assert, done) { - let content = ""; - - readURI(dataURIutf8).then(function(data) { - content = data; - assert.equal(content, utf8text, "The URL content is loaded properly"); - done(); - }, function() { - assert.fail("should not reject"); - done(); - }) - - assert.equal(content, "", "The URL content is not load yet"); -} - -exports["test sync readURI with data URI"] = function(assert) { - let content = ""; - - readURI(dataURIutf8, { sync: true }).then(function(data) { - content = data; - }, function() { - assert.fail("should not reject"); - }) - - assert.equal(content, utf8text, "The URL content is loaded properly"); -} - -exports["test readURISync with data URI"] = function(assert) { - let content = readURISync(dataURIutf8); - - assert.equal(content, utf8text, "The URL content is loaded properly"); -} - -exports["test async readURI with data URI and ISO-8859-1 charset"] = function(assert, done) { - let content = ""; - - readURI(dataURIlatin1, { charset : "ISO-8859-1"}).then(function(data) { - content = unescape(data); - assert.equal(content, latin1text, "The URL content is loaded properly"); - done(); - }, function() { - assert.fail("should not reject"); - done(); - }) - - assert.equal(content, "", "The URL content is not load yet"); -} - -exports["test sync readURI with data URI and ISO-8859-1 charset"] = function(assert) { - let content = ""; - - readURI(dataURIlatin1, { - sync: true, - charset: "ISO-8859-1" - }).then(function(data) { - content = unescape(data); - }, function() { - assert.fail("should not reject"); - }) - - assert.equal(content, latin1text, "The URL content is loaded properly"); -} - -exports["test readURISync with data URI and ISO-8859-1 charset"] = function(assert) { - let content = unescape(readURISync(dataURIlatin1, "ISO-8859-1")); - - assert.equal(content, latin1text, "The URL content is loaded properly"); -} - -exports["test readURISync with chrome URI"] = function(assert) { - let content = readURISync(chromeURI); - - assert.ok(content, "The URL content is loaded properly"); -} - -exports["test async readURI with chrome URI"] = function(assert, done) { - let content = ""; - - readURI(chromeURI).then(function(data) { - content = data; - assert.equal(content, readURISync(chromeURI), "The URL content is loaded properly"); - done(); - }, function() { - assert.fail("should not reject"); - done(); - }) - - assert.equal(content, "", "The URL content is not load yet"); -} - -exports["test sync readURI with chrome URI"] = function(assert) { - let content = ""; - - readURI(chromeURI, { sync: true }).then(function(data) { - content = data; - }, function() { - assert.fail("should not reject"); - }) - - assert.equal(content, readURISync(chromeURI), "The URL content is loaded properly"); -} - -require("test").run(exports) diff --git a/addon-sdk-1.16/test/test-node-os.js b/addon-sdk-1.16/test/test-node-os.js deleted file mode 100644 index 4ac2eb3a..00000000 --- a/addon-sdk-1.16/test/test-node-os.js +++ /dev/null @@ -1,33 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -"use strict"; - -let os = require("node/os"); -let system = require("sdk/system"); - -exports["test os"] = function (assert) { - assert.equal(os.tmpdir(), system.pathFor("TmpD"), "os.tmpdir() matches temp dir"); - assert.ok(os.endianness() === "BE" || os.endianness() === "LE", "os.endianness is BE or LE"); - - assert.ok(os.arch().length > 0, "os.arch() returns a value"); - assert.equal(typeof os.arch(), "string", "os.arch() returns a string"); - assert.ok(os.type().length > 0, "os.type() returns a value"); - assert.equal(typeof os.type(), "string", "os.type() returns a string"); - assert.ok(os.platform().length > 0, "os.platform() returns a value"); - assert.equal(typeof os.platform(), "string", "os.platform() returns a string"); - - assert.ok(os.release().length > 0, "os.release() returns a value"); - assert.equal(typeof os.release(), "string", "os.release() returns a string"); - assert.ok(os.hostname().length > 0, "os.hostname() returns a value"); - assert.equal(typeof os.hostname(), "string", "os.hostname() returns a string"); - assert.ok(os.EOL === "\n" || os.EOL === "\r\n", "os.EOL returns a correct EOL char"); - - assert.deepEqual(os.loadavg(), [0, 0, 0], "os.loadavg() returns an array of 0s"); - - ["uptime", "totalmem", "freemem", "cpus"].forEach(method => { - assert.throws(() => os[method](), "os." + method + " throws"); - }); -}; - -require("test").run(exports); diff --git a/addon-sdk-1.16/test/test-notifications.js b/addon-sdk-1.16/test/test-notifications.js deleted file mode 100644 index d154e390..00000000 --- a/addon-sdk-1.16/test/test-notifications.js +++ /dev/null @@ -1,93 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -const { Loader } = require('sdk/test/loader'); - -exports.testOnClick = function (assert) { - let [loader, mockAlertServ] = makeLoader(module); - let notifs = loader.require("sdk/notifications"); - let data = "test data"; - let opts = { - onClick: function (clickedData) { - assert.equal(this, notifs, "|this| should be notifications module"); - assert.equal(clickedData, data, - "data passed to onClick should be correct"); - }, - data: data, - title: "test title", - text: "test text", - iconURL: "test icon URL" - }; - notifs.notify(opts); - mockAlertServ.click(); - loader.unload(); -}; - -exports['test:numbers and URLs in options'] = function(assert) { - let [loader] = makeLoader(module); - let notifs = loader.require('sdk/notifications'); - let opts = { - title: 123, - text: 45678, - // must use in-loader `sdk/url` module for the validation type check to work - iconURL: loader.require('sdk/url').URL('data:image/png,blah') - }; - try { - notifs.notify(opts); - assert.pass('using numbers and URLs in options works'); - } catch (e) { - assert.fail('using numbers and URLs in options must not throw'); - } - loader.unload(); -} - -exports['test:new tag, dir and lang options'] = function(assert) { - let [loader] = makeLoader(module); - let notifs = loader.require('sdk/notifications'); - let opts = { - title: 'best', - tag: 'tagging', - lang: 'en' - }; - - try { - opts.dir = 'ttb'; - notifs.notify(opts); - assert.fail('`dir` option must not accept TopToBottom direction.'); - } catch (e) { - assert.equal(e.message, - '`dir` option must be one of: "auto", "ltr" or "rtl".'); - } - - try { - opts.dir = 'rtl'; - notifs.notify(opts); - assert.pass('`dir` option accepts "rtl" direction.'); - } catch (e) { - assert.fail('`dir` option must accept "rtl" direction.'); - } - - loader.unload(); -} - -// Returns [loader, mockAlertService]. -function makeLoader(module) { - let loader = Loader(module); - let mockAlertServ = { - showAlertNotification: function (imageUrl, title, text, textClickable, - cookie, alertListener, name) { - this._cookie = cookie; - this._alertListener = alertListener; - }, - click: function () { - this._alertListener.observe(null, "alertclickcallback", this._cookie); - } - }; - loader.require("sdk/notifications"); - let scope = loader.sandbox("sdk/notifications"); - scope.notify = mockAlertServ.showAlertNotification.bind(mockAlertServ); - return [loader, mockAlertServ]; -} - -require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-object.js b/addon-sdk-1.16/test/test-object.js deleted file mode 100644 index 128cb010..00000000 --- a/addon-sdk-1.16/test/test-object.js +++ /dev/null @@ -1,36 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -'use strict'; - -const { merge, extend, has, each } = require('sdk/util/object'); - -let o = { - 'paper': 0, - 'rock': 1, - 'scissors': 2 -}; - -//exports.testMerge = function(assert) {} -//exports.testExtend = function(assert) {} - -exports.testHas = function(assert) { - assert.equal(has(o, 'paper'), true, 'has correctly finds key'); - assert.equal(has(o, 'rock'), true, 'has correctly finds key'); - assert.equal(has(o, 'scissors'), true, 'has correctly finds key'); - assert.equal(has(o, 'nope'), false, 'has correctly does not find key'); - assert.equal(has(o, '__proto__'), false, 'has correctly does not find key'); - assert.equal(has(o, 'isPrototypeOf'), false, 'has correctly does not find key'); -}; - -exports.testEach = function(assert) { - var keys = new Set(); - each(o, function (value, key, object) { - keys.add(key); - assert.equal(o[key], value, 'Key and value pairs passed in'); - assert.equal(o, object, 'Object passed in'); - }); - assert.equal(keys.size, 3, 'All keys have been iterated upon'); -}; - -require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-packaging.js b/addon-sdk-1.16/test/test-packaging.js deleted file mode 100644 index 6721633e..00000000 --- a/addon-sdk-1.16/test/test-packaging.js +++ /dev/null @@ -1,46 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - "use strict"; - -var options = require("@loader/options"); - -exports.testPackaging = function(assert) { - assert.equal(options.metadata.description, - "Add-on development made easy.", - "packaging metadata should be available"); - try { - options.metadata.description = 'new description'; - assert.fail('should not have been able to set options.metadata property'); - } - catch (e) {} - - assert.equal(options.metadata.description, - "Add-on development made easy.", - "packaging metadata should be frozen"); - - assert.equal(options.metadata.permissions['private-browsing'], undefined, - "private browsing metadata should be undefined"); - assert.equal(options.metadata['private-browsing'], undefined, - "private browsing metadata should be be frozen"); - assert.equal(options['private-browsing'], undefined, - "private browsing metadata should be be frozen"); - - try { - options.metadata['private-browsing'] = true; - assert.fail('should not have been able to set options.metadata property'); - } - catch(e) {} - assert.equal(options.metadata['private-browsing'], undefined, - "private browsing metadata should be be frozen"); - - try { - options.metadata.permissions['private-browsing'] = true; - assert.fail('should not have been able to set options.metadata.permissions property'); - } - catch (e) {} - assert.equal(options.metadata.permissions['private-browsing'], undefined, - "private browsing metadata should be be frozen"); -}; - -require('sdk/test').run(exports); diff --git a/addon-sdk-1.16/test/test-page-mod.js b/addon-sdk-1.16/test/test-page-mod.js deleted file mode 100644 index 6e494bcb..00000000 --- a/addon-sdk-1.16/test/test-page-mod.js +++ /dev/null @@ -1,1202 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -"use strict"; - -const { PageMod } = require("sdk/page-mod"); -const testPageMod = require("./pagemod-test-helpers").testPageMod; -const { Loader } = require('sdk/test/loader'); -const tabs = require("sdk/tabs"); -const timer = require("sdk/timers"); -const { Cc, Ci, Cu } = require("chrome"); -const { open, getFrames, getMostRecentBrowserWindow } = require('sdk/window/utils'); -const windowUtils = require('sdk/deprecated/window-utils'); -const { getTabContentWindow, getActiveTab, setTabURL, openTab, closeTab } = require('sdk/tabs/utils'); -const xulApp = require("sdk/system/xul-app"); -const { isPrivateBrowsingSupported } = require('sdk/self'); -const { isPrivate } = require('sdk/private-browsing'); -const { openWebpage } = require('./private-browsing/helper'); -const { isTabPBSupported, isWindowPBSupported, isGlobalPBSupported } = require('sdk/private-browsing/utils'); -const promise = require("sdk/core/promise"); -const { pb } = require('./private-browsing/helper'); -const { URL } = require("sdk/url"); - -const { waitUntil } = require("sdk/test/utils"); -const data = require("./fixtures"); - -const testPageURI = data.url("test.html"); - -// The following adds Debugger constructor to the global namespace. -const { addDebuggerToGlobal } = - Cu.import('resource://gre/modules/jsdebugger.jsm', {}); -addDebuggerToGlobal(this); - -function Isolate(worker) { - return "(" + worker + ")()"; -} - -/* Tests for the PageMod APIs */ - -exports.testPageMod1 = function(assert, done) { - let mods = testPageMod(assert, done, "about:", [{ - include: /about:/, - contentScriptWhen: 'end', - contentScript: 'new ' + function WorkerScope() { - window.document.body.setAttribute("JEP-107", "worked"); - }, - onAttach: function() { - assert.equal(this, mods[0], "The 'this' object is the page mod."); - } - }], - function(win, done) { - assert.equal( - win.document.body.getAttribute("JEP-107"), - "worked", - "PageMod.onReady test" - ); - done(); - } - ); -}; - -exports.testPageMod2 = function(assert, done) { - testPageMod(assert, done, "about:", [{ - include: "about:*", - contentScript: [ - 'new ' + function contentScript() { - window.AUQLUE = function() { return 42; } - try { - window.AUQLUE() - } - catch(e) { - throw new Error("PageMod scripts executed in order"); - } - document.documentElement.setAttribute("first", "true"); - }, - 'new ' + function contentScript() { - document.documentElement.setAttribute("second", "true"); - } - ] - }], function(win, done) { - assert.equal(win.document.documentElement.getAttribute("first"), - "true", - "PageMod test #2: first script has run"); - assert.equal(win.document.documentElement.getAttribute("second"), - "true", - "PageMod test #2: second script has run"); - assert.equal("AUQLUE" in win, false, - "PageMod test #2: scripts get a wrapped window"); - done(); - }); -}; - -exports.testPageModIncludes = function(assert, done) { - var asserts = []; - function createPageModTest(include, expectedMatch) { - // Create an 'onload' test function... - asserts.push(function(test, win) { - var matches = include in win.localStorage; - assert.ok(expectedMatch ? matches : !matches, - "'" + include + "' match test, expected: " + expectedMatch); - }); - // ...and corresponding PageMod options - return { - include: include, - contentScript: 'new ' + function() { - self.on("message", function(msg) { - window.localStorage[msg] = true; - }); - }, - // The testPageMod callback with test assertions is called on 'end', - // and we want this page mod to be attached before it gets called, - // so we attach it on 'start'. - contentScriptWhen: 'start', - onAttach: function(worker) { - worker.postMessage(this.include[0]); - } - }; - } - - testPageMod(assert, done, testPageURI, [ - createPageModTest("*", false), - createPageModTest("*.google.com", false), - createPageModTest("resource:*", true), - createPageModTest("resource:", false), - createPageModTest(testPageURI, true) - ], - function (win, done) { - waitUntil(function () win.localStorage[testPageURI], - testPageURI + " page-mod to be executed") - .then(function () { - asserts.forEach(function(fn) { - fn(assert, win); - }); - done(); - }); - } - ); -}; - -exports.testPageModErrorHandling = function(assert) { - assert.throws(function() { - new PageMod(); - }, - /The `include` option must always contain atleast one rule/, - "PageMod() throws when 'include' option is not specified."); -}; - -/* Tests for internal functions. */ -exports.testCommunication1 = function(assert, done) { - let workerDone = false, - callbackDone = null; - - testPageMod(assert, done, "about:", [{ - include: "about:*", - contentScriptWhen: 'end', - contentScript: 'new ' + function WorkerScope() { - self.on('message', function(msg) { - document.body.setAttribute('JEP-107', 'worked'); - self.postMessage(document.body.getAttribute('JEP-107')); - }) - }, - onAttach: function(worker) { - worker.on('error', function(e) { - assert.fail('Errors where reported'); - }); - worker.on('message', function(value) { - assert.equal( - "worked", - value, - "test comunication" - ); - workerDone = true; - if (callbackDone) - callbackDone(); - }); - worker.postMessage('do it!') - } - }], - function(win, done) { - (callbackDone = function() { - if (workerDone) { - assert.equal( - 'worked', - win.document.body.getAttribute('JEP-107'), - 'attribute should be modified' - ); - done(); - } - })(); - } - ); -}; - -exports.testCommunication2 = function(assert, done) { - let callbackDone = null, - window; - - testPageMod(assert, done, "about:license", [{ - include: "about:*", - contentScriptWhen: 'start', - contentScript: 'new ' + function WorkerScope() { - document.documentElement.setAttribute('AUQLUE', 42); - window.addEventListener('load', function listener() { - self.postMessage('onload'); - }, false); - self.on("message", function() { - self.postMessage(document.documentElement.getAttribute("test")) - }); - }, - onAttach: function(worker) { - worker.on('error', function(e) { - assert.fail('Errors where reported'); - }); - worker.on('message', function(msg) { - if ('onload' == msg) { - assert.equal( - '42', - window.document.documentElement.getAttribute('AUQLUE'), - 'PageMod scripts executed in order' - ); - window.document.documentElement.setAttribute('test', 'changes in window'); - worker.postMessage('get window.test') - } else { - assert.equal( - 'changes in window', - msg, - 'PageMod test #2: second script has run' - ) - callbackDone(); - } - }); - } - }], - function(win, done) { - window = win; - callbackDone = done; - } - ); -}; - -exports.testEventEmitter = function(assert, done) { - let workerDone = false, - callbackDone = null; - - testPageMod(assert, done, "about:", [{ - include: "about:*", - contentScript: 'new ' + function WorkerScope() { - self.port.on('addon-to-content', function(data) { - self.port.emit('content-to-addon', data); - }); - }, - onAttach: function(worker) { - worker.on('error', function(e) { - assert.fail('Errors were reported : '+e); - }); - worker.port.on('content-to-addon', function(value) { - assert.equal( - "worked", - value, - "EventEmitter API works!" - ); - if (callbackDone) - callbackDone(); - else - workerDone = true; - }); - worker.port.emit('addon-to-content', 'worked'); - } - }], - function(win, done) { - if (workerDone) - done(); - else - callbackDone = done; - } - ); -}; - -// Execute two concurrent page mods on same document to ensure that their -// JS contexts are different -exports.testMixedContext = function(assert, done) { - let doneCallback = null; - let messages = 0; - let modObject = { - include: "data:text/html;charset=utf-8,", - contentScript: 'new ' + function WorkerScope() { - // Both scripts will execute this, - // context is shared if one script see the other one modification. - let isContextShared = "sharedAttribute" in document; - self.postMessage(isContextShared); - document.sharedAttribute = true; - }, - onAttach: function(w) { - w.on("message", function (isContextShared) { - if (isContextShared) { - assert.fail("Page mod contexts are mixed."); - doneCallback(); - } - else if (++messages == 2) { - assert.pass("Page mod contexts are different."); - doneCallback(); - } - }); - } - }; - testPageMod(assert, done, "data:text/html;charset=utf-8,", [modObject, modObject], - function(win, done) { - doneCallback = done; - } - ); -}; - -exports.testHistory = function(assert, done) { - // We need a valid url in order to have a working History API. - // (i.e do not work on data: or about: pages) - // Test bug 679054. - let url = data.url("test-page-mod.html"); - let callbackDone = null; - testPageMod(assert, done, url, [{ - include: url, - contentScriptWhen: 'end', - contentScript: 'new ' + function WorkerScope() { - history.pushState({}, "", "#"); - history.replaceState({foo: "bar"}, "", "#"); - self.postMessage(history.state); - }, - onAttach: function(worker) { - worker.on('message', function (data) { - assert.equal(JSON.stringify(data), JSON.stringify({foo: "bar"}), - "History API works!"); - callbackDone(); - }); - } - }], - function(win, done) { - callbackDone = done; - } - ); -}; - -exports.testRelatedTab = function(assert, done) { - let tab; - let pageMod = new PageMod({ - include: "about:*", - onAttach: function(worker) { - assert.ok(!!worker.tab, "Worker.tab exists"); - assert.equal(tab, worker.tab, "Worker.tab is valid"); - pageMod.destroy(); - tab.close(done); - } - }); - - tabs.open({ - url: "about:", - onOpen: function onOpen(t) { - tab = t; - } - }); -}; - -exports.testRelatedTabNoRequireTab = function(assert, done) { - let loader = Loader(module); - let tab; - let url = "data:text/html;charset=utf-8," + encodeURI("Test related worker tab 2"); - let { PageMod } = loader.require("sdk/page-mod"); - let pageMod = new PageMod({ - include: url, - onAttach: function(worker) { - assert.equal(worker.tab.url, url, "Worker.tab.url is valid"); - worker.tab.close(function() { - pageMod.destroy(); - loader.unload(); - done(); - }); - } - }); - - tabs.open(url); -}; - -exports.testRelatedTabNoOtherReqs = function(assert, done) { - let loader = Loader(module); - let { PageMod } = loader.require("sdk/page-mod"); - let pageMod = new PageMod({ - include: "about:blank?testRelatedTabNoOtherReqs", - onAttach: function(worker) { - assert.ok(!!worker.tab, "Worker.tab exists"); - pageMod.destroy(); - worker.tab.close(function() { - worker.destroy(); - loader.unload(); - done(); - }); - } - }); - - tabs.open({ - url: "about:blank?testRelatedTabNoOtherReqs" - }); -}; - -exports.testWorksWithExistingTabs = function(assert, done) { - let url = "data:text/html;charset=utf-8," + encodeURI("Test unique document"); - let { PageMod } = require("sdk/page-mod"); - tabs.open({ - url: url, - onReady: function onReady(tab) { - let pageModOnExisting = new PageMod({ - include: url, - attachTo: ["existing", "top", "frame"], - onAttach: function(worker) { - assert.ok(!!worker.tab, "Worker.tab exists"); - assert.equal(tab, worker.tab, "A worker has been created on this existing tab"); - - timer.setTimeout(function() { - pageModOnExisting.destroy(); - pageModOffExisting.destroy(); - tab.close(done); - }, 0); - } - }); - - let pageModOffExisting = new PageMod({ - include: url, - onAttach: function(worker) { - assert.fail("pageModOffExisting page-mod should not have attached to anything"); - } - }); - } - }); -}; - -exports.testExistingFrameDoesntMatchInclude = function(assert, done) { - let iframeURL = 'data:text/html;charset=utf-8,UNIQUE-TEST-STRING-42'; - let iframe = '